aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-08-22 14:56:12 +0100
committerRon Yorston <rmy@pobox.com>2017-08-22 14:56:12 +0100
commitce9af1cc5ea23f754587448cf35b5120c77bfeef (patch)
tree69e5eaba5e75ab909ed92d5045393471b8ff3c13
parentc170026700eabb10147dd848c45c06995b43a32e (diff)
parente837a0dbbebf4229306df98fe9ee3b9bb30630c4 (diff)
downloadbusybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.tar.gz
busybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.tar.bz2
busybox-w32-ce9af1cc5ea23f754587448cf35b5120c77bfeef.zip
Merge branch 'busybox' into merge
-rw-r--r--Config.in21
-rw-r--r--Makefile1
-rw-r--r--NOFORK_NOEXEC.lst431
-rw-r--r--archival/ar.c15
-rw-r--r--archival/bbunzip.c7
-rw-r--r--archival/bzip2.c8
-rw-r--r--archival/cpio.c7
-rw-r--r--archival/dpkg.c3
-rw-r--r--archival/dpkg_deb.c5
-rw-r--r--archival/gzip.c7
-rw-r--r--archival/libarchive/Kbuild.src2
-rw-r--r--archival/libarchive/data_extract_all.c37
-rw-r--r--archival/libarchive/decompress_unxz.c1
-rw-r--r--archival/libarchive/open_transformer.c40
-rw-r--r--archival/libarchive/unsafe_symlink_target.c48
-rw-r--r--archival/lzop.c2
-rw-r--r--archival/rpm.c412
-rw-r--r--archival/rpm2cpio.c96
-rw-r--r--archival/tar.c55
-rw-r--r--archival/unzip.c10
-rw-r--r--configs/TEST_nommu_defconfig1
-rw-r--r--configs/TEST_noprintf_defconfig1
-rw-r--r--configs/TEST_rh9_defconfig1
-rw-r--r--configs/android2_defconfig1
-rw-r--r--configs/android_502_defconfig1
-rw-r--r--configs/android_defconfig1
-rw-r--r--configs/android_ndk_defconfig1
-rw-r--r--configs/cygwin_defconfig1
-rw-r--r--configs/freebsd_defconfig1
-rw-r--r--console-tools/chvt.c2
-rw-r--r--console-tools/clear.c2
-rw-r--r--console-tools/deallocvt.c2
-rw-r--r--console-tools/dumpkmap.c21
-rw-r--r--console-tools/fgconsole.c2
-rw-r--r--console-tools/kbd_mode.c35
-rw-r--r--console-tools/loadfont.c60
-rw-r--r--console-tools/loadkmap.c2
-rw-r--r--console-tools/openvt.c4
-rw-r--r--console-tools/reset.c2
-rw-r--r--console-tools/resize.c4
-rw-r--r--console-tools/setconsole.c34
-rw-r--r--console-tools/setkeycodes.c15
-rw-r--r--console-tools/setlogcons.c20
-rw-r--r--coreutils/cat.c8
-rw-r--r--coreutils/chmod.c3
-rw-r--r--coreutils/chown.c8
-rw-r--r--coreutils/chroot.c5
-rw-r--r--coreutils/cksum.c1
-rw-r--r--coreutils/comm.c3
-rw-r--r--coreutils/cp.c23
-rw-r--r--coreutils/cut.c9
-rw-r--r--coreutils/date.c39
-rw-r--r--coreutils/dd.c2
-rw-r--r--coreutils/df.c17
-rw-r--r--coreutils/dos2unix.c3
-rw-r--r--coreutils/du.c14
-rw-r--r--coreutils/echo.c2
-rw-r--r--coreutils/env.c23
-rw-r--r--coreutils/expand.c58
-rw-r--r--coreutils/expr.c7
-rw-r--r--coreutils/id.c9
-rw-r--r--coreutils/install.c25
-rw-r--r--coreutils/link.c8
-rw-r--r--coreutils/ln.c3
-rw-r--r--coreutils/ls.c61
-rw-r--r--coreutils/md5_sha1_sum.c7
-rw-r--r--coreutils/mkdir.c33
-rw-r--r--coreutils/mktemp.c5
-rw-r--r--coreutils/mv.c42
-rw-r--r--coreutils/nice.c2
-rw-r--r--coreutils/nl.c5
-rw-r--r--coreutils/nohup.c6
-rw-r--r--coreutils/nproc.c3
-rw-r--r--coreutils/od.c2
-rw-r--r--coreutils/od_bloaty.c7
-rw-r--r--coreutils/printf.c2
-rw-r--r--coreutils/readlink.c10
-rw-r--r--coreutils/realpath.c3
-rw-r--r--coreutils/rm.c8
-rw-r--r--coreutils/rmdir.c27
-rw-r--r--coreutils/seq.c5
-rw-r--r--coreutils/shuf.c7
-rw-r--r--coreutils/sort.c10
-rw-r--r--coreutils/split.c7
-rw-r--r--coreutils/stat.c10
-rw-r--r--coreutils/stty.c13
-rw-r--r--coreutils/sync.c3
-rw-r--r--coreutils/tail.c8
-rw-r--r--coreutils/touch.c13
-rw-r--r--coreutils/tr.c4
-rw-r--r--coreutils/truncate.c3
-rw-r--r--coreutils/tty.c2
-rw-r--r--coreutils/uname.c11
-rw-r--r--coreutils/unlink.c5
-rw-r--r--coreutils/usleep.c7
-rw-r--r--coreutils/uudecode.c6
-rw-r--r--coreutils/uuencode.c3
-rw-r--r--coreutils/who.c11
-rw-r--r--coreutils/yes.c5
-rw-r--r--debianutils/run_parts.c17
-rw-r--r--debianutils/start_stop_daemon.c94
-rw-r--r--debianutils/which.c7
-rw-r--r--docs/new-applet-HOWTO.txt15
-rw-r--r--docs/nofork_noexec.txt82
-rw-r--r--e2fsprogs/chattr.c88
-rw-r--r--e2fsprogs/e2fs_lib.h2
-rw-r--r--e2fsprogs/lsattr.c3
-rw-r--r--e2fsprogs/tune2fs.c5
-rw-r--r--editors/awk.c8
-rw-r--r--editors/cmp.c10
-rw-r--r--editors/diff.c12
-rw-r--r--editors/ed.c6
-rw-r--r--editors/patch_bbox.c3
-rw-r--r--editors/sed.c18
-rw-r--r--findutils/find.c2
-rw-r--r--findutils/grep.c11
-rw-r--r--findutils/xargs.c9
-rw-r--r--include/bb_archive.h4
-rw-r--r--include/libbb.h70
-rw-r--r--include/platform.h5
-rw-r--r--klibc-utils/Config.src10
-rw-r--r--klibc-utils/Kbuild.src9
-rw-r--r--klibc-utils/minips.c12
-rw-r--r--klibc-utils/nuke.c46
-rw-r--r--klibc-utils/resume.c115
-rw-r--r--klibc-utils/run-init.c27
-rw-r--r--libbb/appletlib.c15
-rw-r--r--libbb/bb_pwd.c3
-rw-r--r--libbb/capability.c126
-rw-r--r--libbb/change_identity.c2
-rw-r--r--libbb/copy_file.c5
-rw-r--r--libbb/correct_password.c2
-rw-r--r--libbb/dump.c2
-rw-r--r--libbb/get_console.c1
-rw-r--r--libbb/getopt32.c198
-rw-r--r--libbb/getopt_allopts.c27
-rw-r--r--libbb/lineedit.c23
-rw-r--r--libbb/parse_config.c27
-rw-r--r--libbb/progress.c2
-rw-r--r--libbb/pw_encrypt_des.c2
-rw-r--r--libbb/run_shell.c2
-rw-r--r--libbb/setup_environment.c2
-rw-r--r--libbb/trim.c12
-rw-r--r--libbb/ubi.c1
-rw-r--r--libbb/vfork_daemon_rexec.c191
-rw-r--r--libbb/xfuncs.c2
-rw-r--r--libpwdgrp/uidgid_get.c2
-rw-r--r--loginutils/add-remove-shell.c7
-rw-r--r--loginutils/addgroup.c21
-rw-r--r--loginutils/adduser.c36
-rw-r--r--loginutils/chpasswd.c7
-rw-r--r--loginutils/cryptpw.c13
-rw-r--r--loginutils/deluser.c10
-rw-r--r--loginutils/getty.c5
-rw-r--r--loginutils/login.c4
-rw-r--r--loginutils/sulogin.c4
-rw-r--r--loginutils/vlock.c3
-rw-r--r--mailutils/mail.c21
-rw-r--r--mailutils/popmaildir.c6
-rw-r--r--mailutils/reformime.c6
-rw-r--r--mailutils/sendmail.c17
-rw-r--r--miscutils/adjtimex.c39
-rw-r--r--miscutils/chat.c4
-rw-r--r--miscutils/conspy.c6
-rw-r--r--miscutils/crond.c14
-rw-r--r--miscutils/crontab.c5
-rw-r--r--miscutils/flash_eraseall.c4
-rw-r--r--miscutils/flash_lock_unlock.c1
-rw-r--r--miscutils/flashcp.c4
-rw-r--r--miscutils/i2c_tools.c26
-rw-r--r--miscutils/lsscsi.c5
-rw-r--r--miscutils/makedevs.c5
-rw-r--r--miscutils/man.c3
-rw-r--r--miscutils/microcom.c4
-rw-r--r--miscutils/nandwrite.c13
-rw-r--r--miscutils/partprobe.c2
-rw-r--r--miscutils/raidautorun.c2
-rw-r--r--miscutils/runlevel.c2
-rw-r--r--miscutils/setserial.c97
-rw-r--r--miscutils/time.c15
-rw-r--r--miscutils/ttysize.c2
-rw-r--r--miscutils/ubi_tools.c91
-rw-r--r--miscutils/ubirename.c6
-rw-r--r--miscutils/watchdog.c5
-rw-r--r--modutils/insmod.c2
-rw-r--r--modutils/lsmod.c2
-rw-r--r--modutils/modinfo.c5
-rw-r--r--modutils/modprobe-small.c18
-rw-r--r--modutils/modprobe.c11
-rw-r--r--modutils/modutils-24.c4
-rw-r--r--modutils/rmmod.c2
-rw-r--r--networking/arping.c10
-rw-r--r--networking/brctl.c2
-rw-r--r--networking/ether-wake.c3
-rw-r--r--networking/ftpd.c17
-rw-r--r--networking/ftpgetput.c28
-rw-r--r--networking/hostname.c20
-rw-r--r--networking/httpd.c10
-rw-r--r--networking/ifenslave.c24
-rw-r--r--networking/inetd.c1
-rw-r--r--networking/ipcalc.c43
-rw-r--r--networking/nameif.c2
-rw-r--r--networking/nbd-client.c6
-rw-r--r--networking/nc_bloaty.c14
-rw-r--r--networking/ntpd.c9
-rw-r--r--networking/ping.c14
-rw-r--r--networking/pscan.c7
-rw-r--r--networking/slattach.c170
-rw-r--r--networking/tcpudp.c7
-rw-r--r--networking/telnet.c6
-rw-r--r--networking/telnetd.c10
-rw-r--r--networking/tftp.c15
-rw-r--r--networking/traceroute.c10
-rw-r--r--networking/tunctl.c20
-rw-r--r--networking/udhcp/d6_dhcpc.c9
-rw-r--r--networking/udhcp/dhcpc.c12
-rw-r--r--networking/udhcp/dhcpd.c7
-rw-r--r--networking/udhcp/dumpleases.c11
-rw-r--r--networking/vconfig.c2
-rw-r--r--networking/wget.c15
-rw-r--r--networking/whois.c3
-rw-r--r--networking/zcip.c5
-rw-r--r--printutils/lpd.c5
-rw-r--r--procps/free.c7
-rw-r--r--procps/fuser.c3
-rw-r--r--procps/iostat.c3
-rw-r--r--procps/kill.c10
-rw-r--r--procps/mpstat.c1
-rw-r--r--procps/pgrep.c6
-rw-r--r--procps/pidof.c4
-rw-r--r--procps/pmap.c4
-rw-r--r--procps/powertop.c14
-rw-r--r--procps/ps.c185
-rw-r--r--procps/pstree.c6
-rw-r--r--procps/pwdx.c6
-rw-r--r--procps/sysctl.c64
-rw-r--r--procps/top.c19
-rw-r--r--procps/uptime.c2
-rw-r--r--procps/watch.c6
-rw-r--r--runit/chpst.c21
-rw-r--r--runit/runit_lib.h2
-rw-r--r--runit/runsv.c2
-rw-r--r--runit/runsvdir.c5
-rw-r--r--runit/sv.c32
-rw-r--r--runit/svlogd.c9
-rw-r--r--scripts/Makefile.IMA3
-rw-r--r--scripts/echo.c2
-rw-r--r--selinux/chcon.c50
-rw-r--r--selinux/matchpathcon.c10
-rw-r--r--selinux/runcon.c27
-rw-r--r--selinux/sestatus.c3
-rw-r--r--selinux/setfiles.c22
-rw-r--r--shell/ash.c152
-rw-r--r--shell/ash_LINENO.patch498
-rw-r--r--shell/ash_test/ash-getopts/getopt_optarg.right18
-rwxr-xr-xshell/ash_test/ash-getopts/getopt_optarg.tests18
-rw-r--r--shell/ash_test/ash-getopts/getopt_positional.right6
-rwxr-xr-xshell/ash_test/ash-getopts/getopt_positional.tests8
-rw-r--r--shell/ash_test/ash-getopts/getopt_silent.right6
-rwxr-xr-xshell/ash_test/ash-getopts/getopt_silent.tests23
-rw-r--r--shell/ash_test/ash-getopts/getopt_simple.right34
-rwxr-xr-xshell/ash_test/ash-getopts/getopt_simple.tests75
-rw-r--r--shell/ash_test/ash-getopts/getopt_test_libc_bug.right26
-rwxr-xr-xshell/ash_test/ash-getopts/getopt_test_libc_bug.tests38
-rw-r--r--shell/ash_test/ash-parsing/groups_and_keywords2.right3
-rwxr-xr-xshell/ash_test/ash-parsing/groups_and_keywords2.tests9
-rw-r--r--shell/ash_test/ash-vars/param_expand_assign.right2
-rw-r--r--shell/ash_test/printenv.c2
-rw-r--r--shell/cttyhack.c2
-rw-r--r--shell/hush.c270
-rw-r--r--shell/hush_test/hush-getopts/getopt_optarg.right18
-rwxr-xr-xshell/hush_test/hush-getopts/getopt_optarg.tests24
-rw-r--r--shell/hush_test/hush-getopts/getopt_positional.right6
-rwxr-xr-xshell/hush_test/hush-getopts/getopt_positional.tests13
-rw-r--r--shell/hush_test/hush-getopts/getopt_silent.right6
-rwxr-xr-xshell/hush_test/hush-getopts/getopt_silent.tests29
-rw-r--r--shell/hush_test/hush-getopts/getopt_simple.right34
-rwxr-xr-xshell/hush_test/hush-getopts/getopt_simple.tests81
-rw-r--r--shell/hush_test/hush-getopts/getopt_test_libc_bug.right26
-rwxr-xr-xshell/hush_test/hush-getopts/getopt_test_libc_bug.tests44
-rw-r--r--shell/hush_test/hush-parsing/groups_and_keywords2.right3
-rwxr-xr-xshell/hush_test/hush-parsing/groups_and_keywords2.tests9
-rwxr-xr-xshell/hush_test/run-all2
-rw-r--r--shell/math.c2
-rw-r--r--shell/shell_common.c9
-rw-r--r--shell/shell_common.h8
-rw-r--r--sysklogd/logger.c6
-rw-r--r--sysklogd/syslogd.c11
-rw-r--r--sysklogd/syslogd_and_logger.c11
-rwxr-xr-xtestsuite/mdev.tests4
-rwxr-xr-xtestsuite/parse.tests44
-rwxr-xr-xtestsuite/tar.tests10
-rw-r--r--util-linux/acpid.c8
-rw-r--r--util-linux/blkdiscard.c7
-rw-r--r--util-linux/blkid.c4
-rw-r--r--util-linux/blockdev.c2
-rw-r--r--util-linux/cal.c2
-rw-r--r--util-linux/chrt.c5
-rw-r--r--util-linux/eject.c5
-rw-r--r--util-linux/fallocate.c3
-rw-r--r--util-linux/fatattr.c2
-rw-r--r--util-linux/fdformat.c3
-rw-r--r--util-linux/fdisk.c4
-rw-r--r--util-linux/fdisk_osf.c2
-rw-r--r--util-linux/flock.c6
-rw-r--r--util-linux/freeramdisk.c14
-rw-r--r--util-linux/fsck_minix.c12
-rw-r--r--util-linux/fsfreeze.c14
-rw-r--r--util-linux/fstrim.c10
-rw-r--r--util-linux/getopt.c24
-rw-r--r--util-linux/hexdump_xxd.c5
-rw-r--r--util-linux/hwclock.c33
-rw-r--r--util-linux/ionice.c2
-rw-r--r--util-linux/ipcrm.c42
-rw-r--r--util-linux/ipcs.c42
-rw-r--r--util-linux/losetup.c7
-rw-r--r--util-linux/lspci.c2
-rw-r--r--util-linux/lsusb.c2
-rw-r--r--util-linux/mesg.c9
-rw-r--r--util-linux/mkfs_minix.c11
-rw-r--r--util-linux/mkfs_reiser.c3
-rw-r--r--util-linux/mkfs_vfat.c5
-rw-r--r--util-linux/mkswap.c3
-rw-r--r--util-linux/mount.c13
-rw-r--r--util-linux/mountpoint.c5
-rw-r--r--util-linux/nsenter.c35
-rw-r--r--util-linux/pivot_root.c6
-rw-r--r--util-linux/rdate.c3
-rw-r--r--util-linux/rdev.c2
-rw-r--r--util-linux/readprofile.c6
-rw-r--r--util-linux/renice.c2
-rw-r--r--util-linux/rtcwake.c8
-rw-r--r--util-linux/script.c54
-rw-r--r--util-linux/scriptreplay.c3
-rw-r--r--util-linux/setarch.c8
-rw-r--r--util-linux/setpriv.c152
-rw-r--r--util-linux/setsid.c4
-rw-r--r--util-linux/switch_root.c177
-rw-r--r--util-linux/taskset.c5
-rw-r--r--util-linux/uevent.c5
-rw-r--r--util-linux/umount.c15
-rw-r--r--util-linux/unshare.c22
342 files changed, 4878 insertions, 2519 deletions
diff --git a/Config.in b/Config.in
index e131dafaf..ba7ba1026 100644
--- a/Config.in
+++ b/Config.in
@@ -51,6 +51,19 @@ config EXTRA_COMPAT
51 some GNU extensions in libc. You probably only need this option 51 some GNU extensions in libc. You probably only need this option
52 if you plan to run busybox on desktop. 52 if you plan to run busybox on desktop.
53 53
54config FEDORA_COMPAT
55 bool "Building for Fedora distribution"
56 default n
57 help
58 This option makes some tools behave like they do on Fedora.
59
60 At the time of this writing (2017-08) this only affects uname:
61 normally, uname -p (processor) and uname -i (platform)
62 are shown as "unknown", but with this option uname -p
63 shows the same string as uname -m (machine type),
64 and so does uname -i unless machine type is i486/i586/i686 -
65 then uname -i shows "i386".
66
54config INCLUDE_SUSv2 67config INCLUDE_SUSv2
55 bool "Enable obsolete features removed before SUSv3" 68 bool "Enable obsolete features removed before SUSv3"
56 default y 69 default y
@@ -366,13 +379,6 @@ config FEATURE_SYSLOG
366 #This option is auto-selected when you select any applet which may 379 #This option is auto-selected when you select any applet which may
367 #send its output to syslog. You do not need to select it manually. 380 #send its output to syslog. You do not need to select it manually.
368 381
369config FEATURE_HAVE_RPC
370 bool #No description makes it a hidden option
371 default n
372 #help
373 #This is automatically selected if any of enabled applets need it.
374 #You do not need to select it manually.
375
376config PLATFORM_LINUX 382config PLATFORM_LINUX
377 bool #No description makes it a hidden option 383 bool #No description makes it a hidden option
378 default n 384 default n
@@ -721,6 +727,7 @@ source archival/Config.in
721source coreutils/Config.in 727source coreutils/Config.in
722source console-tools/Config.in 728source console-tools/Config.in
723source debianutils/Config.in 729source debianutils/Config.in
730source klibc-utils/Config.in
724source editors/Config.in 731source editors/Config.in
725source findutils/Config.in 732source findutils/Config.in
726source init/Config.in 733source init/Config.in
diff --git a/Makefile b/Makefile
index 47bdaf7ad..5b31a41a6 100644
--- a/Makefile
+++ b/Makefile
@@ -471,6 +471,7 @@ libs-y := \
471 coreutils/ \ 471 coreutils/ \
472 coreutils/libcoreutils/ \ 472 coreutils/libcoreutils/ \
473 debianutils/ \ 473 debianutils/ \
474 klibc-utils/ \
474 e2fsprogs/ \ 475 e2fsprogs/ \
475 editors/ \ 476 editors/ \
476 findutils/ \ 477 findutils/ \
diff --git a/NOFORK_NOEXEC.lst b/NOFORK_NOEXEC.lst
new file mode 100644
index 000000000..3070a321b
--- /dev/null
+++ b/NOFORK_NOEXEC.lst
@@ -0,0 +1,431 @@
1Why an applet can't be NOFORK or NOEXEC?
2
3Why can't be NOFORK:
4interactive: may wait for user input, ^C has to work
5spawner: "tool PROG ARGS" which changes program state and execs - must fork
6changes state: e.g. environment, signal handlers
7leaks: does not free allocated memory or opened fds
8 alloc+xfunc: xmalloc, then xfunc - leaks memory if xfunc dies
9 open+xfunc: opens fd, then calls xfunc - fd is leaked if xfunc dies
10talks to network/serial/etc: it's not known how long the delay can be,
11 it's reasonable to expect it might be many seconds
12 (even if usually it is not), so ^C has to work
13runner: sometimes may run for long(ish) time, and/or works with network:
14 ^C has to work (cat BIGFILE, chmod -R, ftpget, nc)
15
16"runners" can become eligible after shell is taught ^C to interrupt NOFORKs,
17need to be inspected that they do not fall into alloc+xfunc, open+xfunc,
18leak categories.
19
20Why can't be NOEXEC:
21suid: runs under different uid - must fork+exec
22if it's important that /proc/PID/cmdline and comm are correct.
23 ("pkill sh" killing itself before it kills real "sh" is no fun)
24
25Why shouldn't be NOFORK/NOEXEC:
26rare: not started often enough to bother optimizing (example: poweroff)
27daemon: runs indefinitely; these are also always fit "rare" category
28longterm: often runs for a long time (many seconds), execing makes
29 memory footprint smaller
30complex: no immediately obvious reason why NOFORK wouldn't work,
31 but does some non-obvoius operations (example: fuser, lsof, losetup);
32 detailed audit often turns out that it's a leaker
33hardware: performs unusual hardware ops which may take long,
34 or even hang due to hardware or firmware bugs
35
36Interesting example of "interactive" applet which is nevertheless can be
37(and is) NOEXEC is "rm". Yes, "rm -i" is interactive - but it's not that typical
38for users to keep it waiting for many minutes, whereas running "rm" in shell
39is very typical, and speeding up this common use via NOEXEC is useful.
40IOW: rm is "interactive", but not "longterm".
41
42Interesting example of an applet which can be NOFORK but if not,
43then should not be NOEXEC, is "usleep". As NOFORK, it amount to simply
44nanosleep()ing in the calling program (usually shell). No memory wasted.
45But if ran as NOEXEC, it would create a potentially long-term process,
46which would be taking more memory because it did not exec
47and did not free much of the copied memory of the parent
48(COW helps with this only as long as parent doesn't modify its memory).
49
50
51[ - NOFORK
52[[ - NOFORK
53acpid - daemon
54add-shell - noexec. leaks: open+xfunc
55addgroup - noexec. leaks
56adduser - noexec. leaks
57adjtimex - NOFORK
58ar - runner
59arch - NOFORK
60arp - talks to network: arp -n queries DNS
61arping - longterm
62ash - interactive, longterm
63awk - noexec. runner
64base64 - runner
65basename - NOFORK
66beep - longterm: beep -r 999999999
67blkdiscard - noexec. leaks: open+xioctl
68blkid - noexec
69blockdev - noexec. leaks fd
70bootchartd - daemon
71brctl - noexec
72bunzip2 - runner
73bzcat - runner
74bzip2 - runner
75cal - runner: cal -n9999
76cat - runner: cat HUGEFILE
77chat - longterm (when used as intended - talking to modem over stdin/out)
78chattr - noexec. runner
79chgrp - noexec. runner
80chmod - noexec. runner
81chown - noexec. runner
82chpasswd - longterm? (list of "user:password"s from stdin)
83chpst - noexec. spawner
84chroot - noexec. spawner
85chrt - noexec. spawner
86chvt - noexec. leaks: get_console_fd_or_die() may open a new fd, or return one of stdio fds
87cksum - noexec. runner
88clear - NOFORK
89cmp - runner
90comm - runner
91conspy - interactive, longterm
92cp - noexec. runner
93cpio - runner
94crond - daemon
95crontab - longterm (runs $EDITOR), leaks: open+xasprintf
96cryptpw - noexec. changes state: with --password-fd=N, moves N to stdin
97cttyhack - noexec. spawner
98cut - noexec. runner
99date - noexec. nofork candidate(needs to stop messing up env, free xasprintf result, not use xfuncs after xasprintf)
100dc - longterm (eats stdin if no params)
101dd - noexec. runner
102deallocvt - noexec. leaks: get_console_fd_or_die() may open a new fd, or return one of stdio fds
103delgroup - noexec. leaks
104deluser - noexec. leaks
105depmod - longterm(ish)
106devmem - hardware (access to device memory may hang)
107df - noexec. leaks: nested allocs
108dhcprelay - daemon
109diff - runner
110dirname - NOFORK
111dmesg - runner
112dnsd - daemon
113dnsdomainname - noexec. talks to network (may query DNS)
114dos2unix - noexec. runner
115dpkg - runner
116du - runner
117dumpkmap - noexec. leaks: get_console_fd_or_die() may open a new fd, or return one of stdio fds
118dumpleases - noexec. leaks: open+xread
119echo - NOFORK
120ed - interactive, longterm
121egrep - longterm runner ("CMD | egrep ..." may run indefinitely, better to exec to conserve memory)
122eject - hardware, leaks: open+ioctl_or_perror_and_die, changes state (moves fds)
123env - noexec. spawner, changes state (env)
124envdir - noexec. spawner
125envuidgid - noexec. spawner
126expand - runner
127expr - noexec. leaks: nested allocs
128factor - longterm (eats stdin if no params)
129fakeidentd - daemon
130false - NOFORK
131fatattr - noexec. leaks: open+xioctl, complex
132fbset - hardware, leaks: open+xfunc
133fbsplash - runner, longterm
134fdflush - hardware, leaks: open+ioctl_or_perror_and_die
135fdformat - hardware, longterm
136fdisk - interactive, longterm
137fgconsole - noexec. leaks: get_console_fd_or_die() may open a new fd, or return one of stdio fds
138fgrep - longterm runner ("CMD | fgrep ..." may run indefinitely, better to exec to conserve memory)
139find - noexec. runner
140findfs - suid
141flash_eraseall - hardware
142flash_lock - hardware
143flash_unlock - hardware
144flashcp - hardware
145flock - spawner, changes state (file locks), let's play safe and not be noexec
146fold - noexec. runner
147free - noexec. nofork candidate(struct globals, needs to close /proc/meminfo fd)
148freeramdisk - noexec. leaks: open+ioctl_or_perror_and_die
149fsck - interactive, longterm
150fsck.minix - needs ^C
151fsfreeze - noexec. leaks: open+xioctl
152fstrim - noexec. leaks: open+xioctl, find_block_device -> readdir+xstrdup
153fsync - NOFORK
154ftpd - daemon
155ftpget - runner
156ftpput - runner
157fuser - complex
158getopt - noexec. leaks: many allocs
159getty - interactive, longterm
160grep - longterm runner ("CMD | grep ..." may run indefinitely, better to exec to conserve memory)
161groups - noexec
162gunzip - runner
163gzip - runner
164halt - rare
165hd - noexec. runner
166hdparm - hardware
167head - noexec. runner
168hexdump - noexec. runner
169hostid - NOFORK
170hostname - noexec. talks to network (hostname -d may query DNS)
171httpd - daemon
172hush - interactive, longterm
173hwclock - hardware (xioctl(RTC_RD_TIME))
174i2cdetect - hardware
175i2cdump - hardware
176i2cget - hardware
177i2cset - hardware
178id - noexec
179ifconfig - hardware? (mem_start NN io_addr NN irq NN), leaks: xsocket+ioctl_or_perror_and_die
180ifenslave - noexec. leaks: xsocket+bb_perror_msg_and_die
181ifplugd - daemon
182inetd - daemon
183init - daemon
184inotifyd - daemon
185insmod - noexec
186install - runner
187ionice - noexec. spawner
188iostat - longterm: "iostat 1" runs indefinitely
189ip - noexec candidate
190ipaddr - noexec candidate
191ipcalc - noexec. ipcalc -h talks to network
192ipcrm - noexec
193ipcs - noexec
194iplink - noexec candidate
195ipneigh - noexec candidate
196iproute - noexec candidate
197iprule - noexec candidate
198iptunnel - noexec candidate
199kbd_mode - noexec. leaks: xopen_nonblocking+xioctl
200kill - NOFORK
201killall - NOFORK
202killall5 - NOFORK
203klogd - daemon
204last - runner (I've got 1300 lines of output when tried it)
205less - interactive, longterm
206link - NOFORK
207linux32 - noexec. spawner
208linux64 - noexec. spawner
209linuxrc - daemon
210ln - noexec
211loadfont - noexec. leaks: config_open+bb_error_msg_and_die("map format")
212loadkmap - noexec. leaks: get_console_fd_or_die() may open a new fd, or return one of stdio fds
213logger - runner
214login - suid, interactive, longterm
215logname - NOFORK
216losetup - noexec. complex
217lpd - daemon
218lpq - runner
219lpr - runner
220ls - noexec. runner
221lsattr - noexec. runner
222lsmod - noexec
223lsof - complex
224lspci - noexec. too rare to bother for nofork
225lsscsi - noexec. too rare to bother for nofork
226lsusb - noexec. too rare to bother for nofork
227lzcat - runner
228lzma - runner
229lzop - runner
230lzopcat - runner
231makedevs - noexec
232makemime - runner
233man - spawner, interactive, longterm
234md5sum - noexec. runner
235mdev - daemon
236mesg - NOFORK
237microcom - interactive, longterm
238mkdir - NOFORK
239mkdosfs - needs ^C
240mke2fs - needs ^C
241mkfifo - noexec
242mkfs.ext2 - needs ^C
243mkfs.minix - needs ^C
244mkfs.vfat - needs ^C
245mknod - noexec
246mkpasswd - noexec. changes state: with --password-fd=N, moves N to stdin
247mkswap - needs ^C
248mktemp - noexec. leaks: xstrdup+concat_path_file
249modinfo - noexec
250modprobe - noexec
251more - interactive, longterm
252mount - suid
253mountpoint - noexec. leaks: option -n "print dev name": find_block_device -> readdir+xstrdup
254mpstat - longterm: "mpstat 1" runs indefinitely
255mt - hardware
256mv - noexec candidate, runner
257nameif - noexec. openlog(), leaks: config_open2+ioctl_or_perror_and_die
258nbd-client - noexec
259nc - runner
260netstat - longterm with -c (continuous listing)
261nice - noexec. spawner
262nl - runner
263nmeter - longterm
264nohup - noexec. spawner
265nproc - NOFORK
266ntpd - daemon
267od - runner
268openvt - longterm: spawns a child and waits for it
269partprobe - noexec. leaks: open+ioctl_or_perror_and_die(BLKRRPART)
270passwd - suid
271paste - noexec. runner
272patch - needs ^C
273pgrep - must fork+exec to get correct /proc/PID/cmdline and comm field
274pidof - must fork+exec to get correct /proc/PID/cmdline and comm field
275ping - suid, longterm
276ping6 - suid, longterm
277pipe_progress - longterm
278pivot_root - NOFORK
279pkill - must fork+exec to get correct /proc/PID/cmdline and comm field
280pmap - noexec candidate, leaks: open+xstrdup
281popmaildir - runner
282poweroff - rare
283powertop - interactive, longterm
284printenv - NOFORK
285printf - NOFORK
286ps - noexec
287pscan - talks to network
288pstree - noexec
289pwd - NOFORK
290pwdx - NOFORK
291raidautorun - noexec. very simple. leaks: open+xioctl
292rdate - talks to network
293rdev - noexec. leaks: find_block_device -> readdir+xstrdup
294readlink - NOFORK
295readprofile - reads /boot/System.map and /proc/profile, better to free more memory by execing?
296realpath - NOFORK
297reboot - rare
298reformime - runner
299remove-shell - noexec. leaks: open+xfunc
300renice - noexec. nofork candidate(uses getpwnam, is that ok?)
301reset - noexec. spawner (execs "stty")
302resize - noexec. changes state (signal handlers)
303rev - runner
304rm - noexec. rm -i interactive
305rmdir - NOFORK
306rmmod - noexec
307route - talks to network (may query DNS to convert IPs to names)
308rpm - runner
309rpm2cpio - runner
310rtcwake - longterm: puts system to sleep, optimizing this for speed is pointless
311run-parts - longterm
312runlevel - noexec. can be nofork if "endutxent()" is called unconditionally, but too rare to bother?
313runsv - daemon
314runsvdir - daemon
315rx - runner
316script - longterm: pumps script output from slave pty
317scriptreplay - longterm: plays back "script" saved output, sleeping as necessary.
318sed - runner
319sendmail - runner
320seq - noexec. runner
321setarch - noexec. spawner
322setconsole - noexec
323setfont - noexec. leaks a lot of stuff
324setkeycodes - noexec
325setlogcons - noexec
326setpriv - spawner, changes state, let's play safe and not be noexec
327setserial - noexec
328setsid - spawner, uses fork_or_rexec() [not audited to work in noexec], let's play safe and not be noexec
329setuidgid - noexec. spawner
330sha1sum - noexec. runner
331sha256sum - noexec. runner
332sha3sum - noexec. runner
333sha512sum - noexec. runner
334showkey - interactive, longterm
335shred - runner
336shuf - noexec. runner
337slattach - longterm (may sleep forever), uses bb_common_bufsiz1
338sleep - longterm. Could be nofork, if not the problem of "killall sleep" not killing it.
339smemcap - runner
340softlimit - noexec. spawner
341sort - noexec. runner
342split - runner
343ssl_client - longterm
344start-stop-daemon - not noexec: uses bb_common_bufsiz1
345stat - noexec. nofork candidate(needs fewer allocs)
346strings - runner
347stty - noexec. nofork candidate: has no allocs or opens except xmove_fd(xopen("-F DEVICE"),STDIN). tcsetattr(STDIN) is not a problem: it would work the same across processes sharing this fd
348su - suid, spawner
349sulogin - noexec. spawner
350sum - runner
351sv - noexec. needs ^C (uses usleep(420000))
352svc - noexec. needs ^C (uses usleep(420000))
353svlogd - daemon
354swapoff - longterm: may cause memory pressure, execing is beneficial
355swapon - rare
356switch_root - spawner, rare, changes state (oh yes), execing may be important to free binary's inode
357sync - NOFORK
358sysctl - noexec. leaks: xstrdup+xmalloc_read
359syslogd - daemon
360tac - noexec. runner
361tail - runner
362tar - runner
363taskset - noexec. spawner
364tcpsvd - daemon
365tee - runner
366telnet - interactive, longterm
367telnetd - daemon
368test - NOFORK
369tftp - runner
370tftpd - daemon
371time - spawner, longterm, changes state (signals)
372timeout - spawner, longterm, changes state (signals)
373top - interactive, longterm
374touch - NOFORK
375tr - runner
376traceroute - suid, longterm
377traceroute6 - suid, longterm
378true - NOFORK
379truncate - NOFORK
380tty - NOFORK
381ttysize - NOFORK
382tunctl - noexec
383tune2fs - noexec. leaks: open+xfunc
384ubiattach - hardware
385ubidetach - hardware
386ubimkvol - hardware
387ubirename - hardware
388ubirmvol - hardware
389ubirsvol - hardware
390ubiupdatevol - hardware
391udhcpc - daemon
392udhcpd - daemon
393udpsvd - daemon
394uevent - daemon
395umount - noexec. leaks: nested xmalloc
396uname - NOFORK
397uncompress - runner
398unexpand - runner
399uniq - runner
400unix2dos - noexec. runner
401unlink - NOFORK
402unlzma - runner
403unlzop - runner
404unxz - runner
405unzip - runner
406uptime - noexec. nofork candidate(is getutxent ok?)
407users - noexec. nofork candidate(is getutxent ok?)
408usleep - NOFORK. But what about "killall usleep"?
409uudecode - runner
410uuencode - runner
411vconfig - noexec. leaks: xsocket+ioctl_or_perror_and_die
412vi - interactive, longterm
413vlock - suid
414volname - hardware (reads CDROM, this can take long-ish if need to spin up)
415w - noexec. nofork candidate(is getutxent ok?)
416wall - suid
417watch - longterm
418watchdog - daemon
419wc - runner
420wget - longterm
421which - NOFORK
422who - noexec. nofork candidate(is getutxent ok?)
423whoami - NOFORK
424whois - talks to network
425xargs - noexec. spawner
426xxd - noexec. runner
427xz - runner
428xzcat - runner
429yes - noexec. runner
430zcat - runner
431zcip - daemon
diff --git a/archival/ar.c b/archival/ar.c
index ea36bda88..d113bc6ca 100644
--- a/archival/ar.c
+++ b/archival/ar.c
@@ -254,11 +254,16 @@ int ar_main(int argc UNUSED_PARAM, char **argv)
254 254
255 archive_handle = init_handle(); 255 archive_handle = init_handle();
256 256
257 /* --: prepend '-' to the first argument if required */ 257 /* prepend '-' to the first argument if required */
258 /* -1: at least one param is reqd */ 258 if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0')
259 /* one of p,t,x[,r] is required */ 259 argv[1] = xasprintf("-%s", argv[1]);
260 opt_complementary = "--:-1:p:t:x"IF_FEATURE_AR_CREATE(":r"); 260 opt = getopt32(argv, "^"
261 opt = getopt32(argv, "voc""ptx"IF_FEATURE_AR_CREATE("r")); 261 "voc""ptx"IF_FEATURE_AR_CREATE("r")
262 "\0"
263 /* -1: at least one arg is reqd */
264 /* one of p,t,x[,r] is required */
265 "-1:p:t:x"IF_FEATURE_AR_CREATE(":r")
266 );
262 argv += optind; 267 argv += optind;
263 268
264 t = opt / FIRST_CMD; 269 t = opt / FIRST_CMD;
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index f91dd25eb..34a2e8e96 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -319,7 +319,7 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
319//config: select FEATURE_GZIP_DECOMPRESS 319//config: select FEATURE_GZIP_DECOMPRESS
320//config: help 320//config: help
321//config: gunzip is used to decompress archives created by gzip. 321//config: gunzip is used to decompress archives created by gzip.
322//config: You can use the `-t' option to test the integrity of 322//config: You can use the '-t' option to test the integrity of
323//config: an archive, without decompressing it. 323//config: an archive, without decompressing it.
324//config: 324//config:
325//config:config ZCAT 325//config:config ZCAT
@@ -391,9 +391,10 @@ int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
391int gunzip_main(int argc UNUSED_PARAM, char **argv) 391int gunzip_main(int argc UNUSED_PARAM, char **argv)
392{ 392{
393#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS 393#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS
394 applet_long_options = gunzip_longopts; 394 getopt32long(argv, "cfkvqdtn", gunzip_longopts);
395#endif 395#else
396 getopt32(argv, "cfkvqdtn"); 396 getopt32(argv, "cfkvqdtn");
397#endif
397 argv += optind; 398 argv += optind;
398 399
399 /* If called as zcat... 400 /* If called as zcat...
diff --git a/archival/bzip2.c b/archival/bzip2.c
index 0b9c508df..d578eb7ad 100644
--- a/archival/bzip2.c
+++ b/archival/bzip2.c
@@ -195,9 +195,11 @@ int bzip2_main(int argc UNUSED_PARAM, char **argv)
195 * --best alias for -9 195 * --best alias for -9
196 */ 196 */
197 197
198 opt_complementary = "s2"; /* -s means -2 (compatibility) */ 198 opt = getopt32(argv, "^"
199 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ 199 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
200 opt = getopt32(argv, "cfkv" IF_FEATURE_BZIP2_DECOMPRESS("dt") "123456789qzs"); 200 "cfkv" IF_FEATURE_BZIP2_DECOMPRESS("dt") "123456789qzs"
201 "\0" "s2" /* -s means -2 (compatibility) */
202 );
201#if ENABLE_FEATURE_BZIP2_DECOMPRESS /* bunzip2_main may not be visible... */ 203#if ENABLE_FEATURE_BZIP2_DECOMPRESS /* bunzip2_main may not be visible... */
202 if (opt & 0x30) // -d and/or -t 204 if (opt & 0x30) // -d and/or -t
203 return bunzip2_main(argc, argv); 205 return bunzip2_main(argc, argv);
diff --git a/archival/cpio.c b/archival/cpio.c
index 38bab8257..f2165be3a 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -360,9 +360,8 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
360 char *cpio_owner; 360 char *cpio_owner;
361 IF_FEATURE_CPIO_O(const char *cpio_fmt = "";) 361 IF_FEATURE_CPIO_O(const char *cpio_fmt = "";)
362 unsigned opt; 362 unsigned opt;
363
364#if ENABLE_LONG_OPTS 363#if ENABLE_LONG_OPTS
365 applet_long_options = 364 const char *long_opts =
366 "extract\0" No_argument "i" 365 "extract\0" No_argument "i"
367 "list\0" No_argument "t" 366 "list\0" No_argument "t"
368#if ENABLE_FEATURE_CPIO_O 367#if ENABLE_FEATURE_CPIO_O
@@ -390,9 +389,9 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
390 /* -L makes sense only with -o or -p */ 389 /* -L makes sense only with -o or -p */
391 390
392#if !ENABLE_FEATURE_CPIO_O 391#if !ENABLE_FEATURE_CPIO_O
393 opt = getopt32(argv, OPTION_STR, &cpio_filename, &cpio_owner); 392 opt = getopt32long(argv, OPTION_STR, long_opts, &cpio_filename, &cpio_owner);
394#else 393#else
395 opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), 394 opt = getopt32long(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), long_opts,
396 &cpio_filename, &cpio_owner, &cpio_fmt); 395 &cpio_filename, &cpio_owner, &cpio_fmt);
397#endif 396#endif
398 argv += optind; 397 argv += optind;
diff --git a/archival/dpkg.c b/archival/dpkg.c
index 90ad8766c..852e0cac2 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -1766,8 +1766,7 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
1766 1766
1767 INIT_G(); 1767 INIT_G();
1768 1768
1769 IF_LONG_OPTS(applet_long_options = dpkg_longopts); 1769 opt = getopt32long(argv, "CilPruF:", dpkg_longopts, &str_f);
1770 opt = getopt32(argv, "CilPruF:", &str_f);
1771 argv += optind; 1770 argv += optind;
1772 //if (opt & OPT_configure) ... // -C 1771 //if (opt & OPT_configure) ... // -C
1773 if (opt & OPT_force) { // -F (--force in official dpkg) 1772 if (opt & OPT_force) { // -F (--force in official dpkg)
diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c
index 029bc4af1..f6bf9eb04 100644
--- a/archival/dpkg_deb.c
+++ b/archival/dpkg_deb.c
@@ -80,8 +80,9 @@ int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
80#endif 80#endif
81 81
82 /* Must have 1 or 2 args */ 82 /* Must have 1 or 2 args */
83 opt_complementary = "-1:?2:c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX"; 83 opt = getopt32(argv, "^" "cefXx"
84 opt = getopt32(argv, "cefXx"); 84 "\0" "-1:?2:c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX"
85 );
85 argv += optind; 86 argv += optind;
86 //argc -= optind; 87 //argc -= optind;
87 88
diff --git a/archival/gzip.c b/archival/gzip.c
index 4cf34ac28..9c53895e9 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -2216,11 +2216,12 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
2216 SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2)) 2216 SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2))
2217 + sizeof(struct globals)); 2217 + sizeof(struct globals));
2218 2218
2219#if ENABLE_FEATURE_GZIP_LONG_OPTIONS
2220 applet_long_options = gzip_longopts;
2221#endif
2222 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ 2219 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
2220#if ENABLE_FEATURE_GZIP_LONG_OPTIONS
2221 opt = getopt32long(argv, "cfkv" IF_FEATURE_GZIP_DECOMPRESS("dt") "qn123456789", gzip_longopts);
2222#else
2223 opt = getopt32(argv, "cfkv" IF_FEATURE_GZIP_DECOMPRESS("dt") "qn123456789"); 2223 opt = getopt32(argv, "cfkv" IF_FEATURE_GZIP_DECOMPRESS("dt") "qn123456789");
2224#endif
2224#if ENABLE_FEATURE_GZIP_DECOMPRESS /* gunzip_main may not be visible... */ 2225#if ENABLE_FEATURE_GZIP_DECOMPRESS /* gunzip_main may not be visible... */
2225 if (opt & 0x30) // -d and/or -t 2226 if (opt & 0x30) // -d and/or -t
2226 return gunzip_main(argc, argv); 2227 return gunzip_main(argc, argv);
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
index 942e755b9..e1a8a7529 100644
--- a/archival/libarchive/Kbuild.src
+++ b/archival/libarchive/Kbuild.src
@@ -12,6 +12,8 @@ COMMON_FILES:= \
12 data_extract_all.o \ 12 data_extract_all.o \
13 data_extract_to_stdout.o \ 13 data_extract_to_stdout.o \
14\ 14\
15 unsafe_symlink_target.o \
16\
15 filter_accept_all.o \ 17 filter_accept_all.o \
16 filter_accept_list.o \ 18 filter_accept_list.o \
17 filter_accept_reject_list.o \ 19 filter_accept_reject_list.o \
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 1ce927c2f..e658444e0 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -129,9 +129,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
129 if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { 129 if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
130 /* shared message */ 130 /* shared message */
131 bb_perror_msg("can't create %slink '%s' to '%s'", 131 bb_perror_msg("can't create %slink '%s' to '%s'",
132 "hard", 132 "hard", dst_name, hard_link
133 dst_name,
134 hard_link
135 ); 133 );
136 } 134 }
137 /* Hardlinks have no separate mode/ownership, skip chown/chmod */ 135 /* Hardlinks have no separate mode/ownership, skip chown/chmod */
@@ -181,7 +179,9 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
181//TODO: what if file_header->link_target == NULL (say, corrupted tarball?) 179//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
182 180
183 /* To avoid a directory traversal attack via symlinks, 181 /* To avoid a directory traversal attack via symlinks,
184 * for certain link targets postpone creation of symlinks. 182 * do not restore symlinks with ".." components
183 * or symlinks starting with "/", unless a magic
184 * envvar is set.
185 * 185 *
186 * For example, consider a .tar created via: 186 * For example, consider a .tar created via:
187 * $ tar cvf bug.tar anything.txt 187 * $ tar cvf bug.tar anything.txt
@@ -199,24 +199,17 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
199 * 199 *
200 * Untarring bug.tar would otherwise place evil.py in '/tmp'. 200 * Untarring bug.tar would otherwise place evil.py in '/tmp'.
201 */ 201 */
202 if (file_header->link_target[0] == '/' 202 if (!unsafe_symlink_target(file_header->link_target)) {
203 || strstr(file_header->link_target, "..") 203 res = symlink(file_header->link_target, dst_name);
204 ) { 204 if (res != 0
205 llist_add_to(&archive_handle->symlink_placeholders, 205 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
206 xasprintf("%s%c%s", file_header->name, '\0', file_header->link_target) 206 ) {
207 ); 207 /* shared message */
208 break; 208 bb_perror_msg("can't create %slink '%s' to '%s'",
209 } 209 "sym",
210 res = symlink(file_header->link_target, dst_name); 210 dst_name, file_header->link_target
211 if (res != 0 211 );
212 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) 212 }
213 ) {
214 /* shared message */
215 bb_perror_msg("can't create %slink '%s' to '%s'",
216 "sym",
217 dst_name,
218 file_header->link_target
219 );
220 } 213 }
221 break; 214 break;
222 case S_IFSOCK: 215 case S_IFSOCK:
diff --git a/archival/libarchive/decompress_unxz.c b/archival/libarchive/decompress_unxz.c
index 350e5358a..0be85500c 100644
--- a/archival/libarchive/decompress_unxz.c
+++ b/archival/libarchive/decompress_unxz.c
@@ -37,7 +37,6 @@ static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
37 || !defined(put_unaligned_be32) 37 || !defined(put_unaligned_be32)
38# error get_unaligned_le32 accessors are not defined 38# error get_unaligned_le32 accessors are not defined
39#endif 39#endif
40#define get_le32(p) (*(uint32_t*)(p))
41 40
42#include "unxz/xz_dec_bcj.c" 41#include "unxz/xz_dec_bcj.c"
43#include "unxz/xz_dec_lzma2.c" 42#include "unxz/xz_dec_lzma2.c"
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index 641256787..7d912152c 100644
--- a/archival/libarchive/open_transformer.c
+++ b/archival/libarchive/open_transformer.c
@@ -251,18 +251,8 @@ static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_comp
251 return xstate; 251 return xstate;
252} 252}
253 253
254/* Used by e.g. rpm which gives us a fd without filename, 254static void fork_transformer_and_free(transformer_state_t *xstate)
255 * thus we can't guess the format from filename's extension.
256 */
257int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
258{ 255{
259 transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
260
261 if (!xstate || !xstate->xformer) {
262 free(xstate);
263 return 1;
264 }
265
266# if BB_MMU 256# if BB_MMU
267 fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer); 257 fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
268# else 258# else
@@ -270,13 +260,39 @@ int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
270 * an external unzipper that wants 260 * an external unzipper that wants
271 * file position at the start of the file. 261 * file position at the start of the file.
272 */ 262 */
273 xlseek(fd, - xstate->signature_skipped, SEEK_CUR); 263 xlseek(xstate->src_fd, - xstate->signature_skipped, SEEK_CUR);
274 xstate->signature_skipped = 0; 264 xstate->signature_skipped = 0;
275 fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog); 265 fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog);
276# endif 266# endif
277 free(xstate); 267 free(xstate);
268}
269
270/* Used by e.g. rpm which gives us a fd without filename,
271 * thus we can't guess the format from filename's extension.
272 */
273int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
274{
275 transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
276
277 if (!xstate->xformer) {
278 free(xstate);
279 return 1;
280 }
281
282 fork_transformer_and_free(xstate);
278 return 0; 283 return 0;
279} 284}
285#if ENABLE_FEATURE_SEAMLESS_LZMA
286/* ...and custom version for LZMA */
287void FAST_FUNC setup_lzma_on_fd(int fd)
288{
289 transformer_state_t *xstate = xzalloc(sizeof(*xstate));
290 xstate->src_fd = fd;
291 xstate->xformer = unpack_lzma_stream;
292 USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
293 fork_transformer_and_free(xstate);
294}
295#endif
280 296
281static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed) 297static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed)
282{ 298{
diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c
new file mode 100644
index 000000000..441ba8b24
--- /dev/null
+++ b/archival/libarchive/unsafe_symlink_target.c
@@ -0,0 +1,48 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5#include "libbb.h"
6#include "bb_archive.h"
7
8int FAST_FUNC unsafe_symlink_target(const char *target)
9{
10 const char *dot;
11
12 if (target[0] == '/') {
13 const char *var;
14 unsafe:
15 var = getenv("EXTRACT_UNSAFE_SYMLINKS");
16 if (var) {
17 if (LONE_CHAR(var, '1'))
18 return 0; /* pretend it's safe */
19 return 1; /* "UNSAFE!" */
20 }
21 bb_error_msg("skipping unsafe symlink to '%s' in archive,"
22 " set %s=1 to extract",
23 target,
24 "EXTRACT_UNSAFE_SYMLINKS"
25 );
26 /* Prevent further messages */
27 setenv("EXTRACT_UNSAFE_SYMLINKS", "0", 0);
28 return 1; /* "UNSAFE!" */
29 }
30
31 dot = target;
32 for (;;) {
33 dot = strchr(dot, '.');
34 if (!dot)
35 return 0; /* safe target */
36
37 /* Is it a path component starting with ".."? */
38 if ((dot[1] == '.')
39 && (dot == target || dot[-1] == '/')
40 /* Is it exactly ".."? */
41 && (dot[2] == '/' || dot[2] == '\0')
42 ) {
43 goto unsafe;
44 }
45 /* NB: it can even be trailing ".", should only add 1 */
46 dot += 1;
47 }
48}
diff --git a/archival/lzop.c b/archival/lzop.c
index df18ff170..1bf954f4f 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -1138,7 +1138,7 @@ int lzop_main(int argc UNUSED_PARAM, char **argv)
1138 /* -U is "anti -k", invert bit for bbunpack(): */ 1138 /* -U is "anti -k", invert bit for bbunpack(): */
1139 option_mask32 ^= OPT_KEEP; 1139 option_mask32 ^= OPT_KEEP;
1140 /* -k disables -U (if any): */ 1140 /* -k disables -U (if any): */
1141 /* opt_complementary = "k-U"; - nope, only handles -Uk, not -kU */ 1141 /* opt_complementary "k-U"? - nope, only handles -Uk, not -kU */
1142 if (option_mask32 & OPT_k) 1142 if (option_mask32 & OPT_k)
1143 option_mask32 |= OPT_KEEP; 1143 option_mask32 |= OPT_KEEP;
1144 1144
diff --git a/archival/rpm.c b/archival/rpm.c
index 98039d499..790eeb59d 100644
--- a/archival/rpm.c
+++ b/archival/rpm.c
@@ -6,7 +6,6 @@
6 * 6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */ 8 */
9
10//config:config RPM 9//config:config RPM
11//config: bool "rpm (33 kb)" 10//config: bool "rpm (33 kb)"
12//config: default y 11//config: default y
@@ -14,19 +13,8 @@
14//config: Mini RPM applet - queries and extracts RPM packages. 13//config: Mini RPM applet - queries and extracts RPM packages.
15 14
16//applet:IF_RPM(APPLET(rpm, BB_DIR_BIN, BB_SUID_DROP)) 15//applet:IF_RPM(APPLET(rpm, BB_DIR_BIN, BB_SUID_DROP))
17//kbuild:lib-$(CONFIG_RPM) += rpm.o
18 16
19//usage:#define rpm_trivial_usage 17//kbuild:lib-$(CONFIG_RPM) += rpm.o
20//usage: "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
21//usage:#define rpm_full_usage "\n\n"
22//usage: "Manipulate RPM packages\n"
23//usage: "\nCommands:"
24//usage: "\n -i Install package"
25//usage: "\n -qp Query package"
26//usage: "\n -qpi Show information"
27//usage: "\n -qpl List contents"
28//usage: "\n -qpd List documents"
29//usage: "\n -qpc List config files"
30 18
31#include "libbb.h" 19#include "libbb.h"
32#include "common_bufsiz.h" 20#include "common_bufsiz.h"
@@ -68,6 +56,8 @@
68#define TAG_DIRINDEXES 1116 56#define TAG_DIRINDEXES 1116
69#define TAG_BASENAMES 1117 57#define TAG_BASENAMES 1117
70#define TAG_DIRNAMES 1118 58#define TAG_DIRNAMES 1118
59#define TAG_PAYLOADCOMPRESSOR 1125
60
71 61
72#define RPMFILE_CONFIG (1 << 0) 62#define RPMFILE_CONFIG (1 << 0)
73#define RPMFILE_DOC (1 << 1) 63#define RPMFILE_DOC (1 << 1)
@@ -91,148 +81,145 @@ typedef struct {
91 81
92struct globals { 82struct globals {
93 void *map; 83 void *map;
94 rpm_index **mytags; 84 rpm_index *mytags;
95 int tagcount; 85 int tagcount;
86 unsigned mapsize, pagesize;
96} FIX_ALIASING; 87} FIX_ALIASING;
97#define G (*(struct globals*)bb_common_bufsiz1) 88#define G (*(struct globals*)bb_common_bufsiz1)
98#define INIT_G() do { setup_common_bufsiz(); } while (0) 89#define INIT_G() do { setup_common_bufsiz(); } while (0)
99 90
100static void extract_cpio(int fd, const char *source_rpm) 91static int rpm_gettags(const char *filename)
101{
102 archive_handle_t *archive_handle;
103
104 if (source_rpm != NULL) {
105 /* Binary rpm (it was built from some SRPM), install to root */
106 xchdir("/");
107 } /* else: SRPM, install to current dir */
108
109 /* Initialize */
110 archive_handle = init_handle();
111 archive_handle->seek = seek_by_read;
112 archive_handle->action_data = data_extract_all;
113#if 0 /* For testing (rpm -i only lists the files in internal cpio): */
114 archive_handle->action_header = header_list;
115 archive_handle->action_data = data_skip;
116#endif
117 archive_handle->ah_flags = ARCHIVE_RESTORE_DATE | ARCHIVE_CREATE_LEADING_DIRS
118 /* compat: overwrite existing files.
119 * try "rpm -i foo.src.rpm" few times in a row -
120 * standard rpm will not complain.
121 */
122 | ARCHIVE_REPLACE_VIA_RENAME;
123 archive_handle->src_fd = fd;
124 /*archive_handle->offset = 0; - init_handle() did it */
125
126 setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 1);
127 while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
128 continue;
129}
130
131static rpm_index **rpm_gettags(int fd, int *num_tags)
132{ 92{
133 /* We should never need more than 200 (shrink via realloc later) */ 93 rpm_index *tags;
134 rpm_index **tags = xzalloc(200 * sizeof(tags[0])); 94 int fd;
135 int pass, tagindex = 0; 95 unsigned pass, idx;
136 96 unsigned storepos;
137 xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */ 97
98 if (!filename) { /* rpm2cpio w/o filename? */
99 filename = bb_msg_standard_output;
100 fd = 0;
101 } else {
102 fd = xopen(filename, O_RDONLY);
103 }
138 104
105 storepos = xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
106 G.tagcount = 0;
107 tags = NULL;
108 idx = 0;
139 /* 1st pass is the signature headers, 2nd is the main stuff */ 109 /* 1st pass is the signature headers, 2nd is the main stuff */
140 for (pass = 0; pass < 2; pass++) { 110 for (pass = 0; pass < 2; pass++) {
141 struct rpm_header header; 111 struct rpm_header header;
142 rpm_index *tmpindex; 112 unsigned cnt;
143 int storepos;
144 113
145 xread(fd, &header, sizeof(header)); 114 xread(fd, &header, sizeof(header));
146 if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER)) 115 if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER))
147 return NULL; /* Invalid magic, or not version 1 */ 116 bb_error_msg_and_die("invalid RPM header magic in '%s'", filename);
148 header.size = ntohl(header.size); 117 header.size = ntohl(header.size);
149 header.entries = ntohl(header.entries); 118 cnt = ntohl(header.entries);
150 storepos = xlseek(fd, 0, SEEK_CUR) + header.entries * 16; 119 storepos += sizeof(header) + cnt * 16;
151 120
152 while (header.entries--) { 121 G.tagcount += cnt;
153 tmpindex = tags[tagindex++] = xmalloc(sizeof(*tmpindex)); 122 tags = xrealloc(tags, sizeof(tags[0]) * G.tagcount);
154 xread(fd, tmpindex, sizeof(*tmpindex)); 123 xread(fd, &tags[idx], sizeof(tags[0]) * cnt);
155 tmpindex->tag = ntohl(tmpindex->tag); 124 while (cnt--) {
156 tmpindex->type = ntohl(tmpindex->type); 125 rpm_index *tag = &tags[idx];
157 tmpindex->count = ntohl(tmpindex->count); 126 tag->tag = ntohl(tag->tag);
158 tmpindex->offset = storepos + ntohl(tmpindex->offset); 127 tag->type = ntohl(tag->type);
128 tag->count = ntohl(tag->count);
129 tag->offset = storepos + ntohl(tag->offset);
159 if (pass == 0) 130 if (pass == 0)
160 tmpindex->tag -= 743; 131 tag->tag -= 743;
132 idx++;
161 } 133 }
162 storepos = xlseek(fd, header.size, SEEK_CUR); /* Seek past store */
163 /* Skip padding to 8 byte boundary after reading signature headers */ 134 /* Skip padding to 8 byte boundary after reading signature headers */
164 if (pass == 0) 135 if (pass == 0)
165 xlseek(fd, (-storepos) & 0x7, SEEK_CUR); 136 while (header.size & 7)
137 header.size++;
138 /* Seek past store */
139 storepos = xlseek(fd, header.size, SEEK_CUR);
166 } 140 }
167 /* realloc tags to save space */ 141 G.mytags = tags;
168 tags = xrealloc(tags, tagindex * sizeof(tags[0])); 142
169 *num_tags = tagindex; 143 /* Map the store */
170 /* All done, leave the file at the start of the gzipped cpio archive */ 144 storepos = (storepos + G.pagesize) & -(int)G.pagesize;
171 return tags; 145 /* remember size for munmap */
146 G.mapsize = storepos;
147 /* some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */
148 G.map = mmap(0, storepos, PROT_READ, MAP_PRIVATE, fd, 0);
149 if (G.map == MAP_FAILED)
150 bb_perror_msg_and_die("mmap '%s'", filename);
151
152 return fd;
172} 153}
173 154
174static int bsearch_rpmtag(const void *key, const void *item) 155static int bsearch_rpmtag(const void *key, const void *item)
175{ 156{
176 int *tag = (int *)key; 157 int *tag = (int *)key;
177 rpm_index **tmp = (rpm_index **) item; 158 rpm_index *tmp = (rpm_index *) item;
178 return (*tag - tmp[0]->tag); 159 return (*tag - tmp->tag);
179}
180
181static int rpm_getcount(int tag)
182{
183 rpm_index **found;
184 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
185 if (!found)
186 return 0;
187 return found[0]->count;
188} 160}
189 161
190static char *rpm_getstr(int tag, int itemindex) 162static char *rpm_getstr(int tag, int itemindex)
191{ 163{
192 rpm_index **found; 164 rpm_index *found;
193 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); 165 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag);
194 if (!found || itemindex >= found[0]->count) 166 if (!found || itemindex >= found->count)
195 return NULL; 167 return NULL;
196 if (found[0]->type == RPM_STRING_TYPE 168 if (found->type == RPM_STRING_TYPE
197 || found[0]->type == RPM_I18NSTRING_TYPE 169 || found->type == RPM_I18NSTRING_TYPE
198 || found[0]->type == RPM_STRING_ARRAY_TYPE 170 || found->type == RPM_STRING_ARRAY_TYPE
199 ) { 171 ) {
200 int n; 172 int n;
201 char *tmpstr = (char *) G.map + found[0]->offset; 173 char *tmpstr = (char *) G.map + found->offset;
202 for (n = 0; n < itemindex; n++) 174 for (n = 0; n < itemindex; n++)
203 tmpstr = tmpstr + strlen(tmpstr) + 1; 175 tmpstr = tmpstr + strlen(tmpstr) + 1;
204 return tmpstr; 176 return tmpstr;
205 } 177 }
206 return NULL; 178 return NULL;
207} 179}
180static char *rpm_getstr0(int tag)
181{
182 return rpm_getstr(tag, 0);
183}
184
185#if ENABLE_RPM
208 186
209static int rpm_getint(int tag, int itemindex) 187static int rpm_getint(int tag, int itemindex)
210{ 188{
211 rpm_index **found; 189 rpm_index *found;
212 char *tmpint; 190 char *tmpint;
213 191
214 /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ... 192 /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
215 * it's ok to ignore it because tag won't be used as a pointer */ 193 * it's ok to ignore it because tag won't be used as a pointer */
216 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); 194 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag);
217 if (!found || itemindex >= found[0]->count) 195 if (!found || itemindex >= found->count)
218 return -1; 196 return -1;
219 197
220 tmpint = (char *) G.map + found[0]->offset; 198 tmpint = (char *) G.map + found->offset;
221 if (found[0]->type == RPM_INT32_TYPE) { 199 if (found->type == RPM_INT32_TYPE) {
222 tmpint += itemindex*4; 200 tmpint += itemindex*4;
223 return ntohl(*(int32_t*)tmpint); 201 return ntohl(*(int32_t*)tmpint);
224 } 202 }
225 if (found[0]->type == RPM_INT16_TYPE) { 203 if (found->type == RPM_INT16_TYPE) {
226 tmpint += itemindex*2; 204 tmpint += itemindex*2;
227 return ntohs(*(int16_t*)tmpint); 205 return ntohs(*(int16_t*)tmpint);
228 } 206 }
229 if (found[0]->type == RPM_INT8_TYPE) { 207 if (found->type == RPM_INT8_TYPE) {
230 tmpint += itemindex; 208 tmpint += itemindex;
231 return *(int8_t*)tmpint; 209 return *(int8_t*)tmpint;
232 } 210 }
233 return -1; 211 return -1;
234} 212}
235 213
214static int rpm_getcount(int tag)
215{
216 rpm_index *found;
217 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag);
218 if (!found)
219 return 0;
220 return found->count;
221}
222
236static void fileaction_dobackup(char *filename, int fileref) 223static void fileaction_dobackup(char *filename, int fileref)
237{ 224{
238 struct stat oldfile; 225 struct stat oldfile;
@@ -273,11 +260,103 @@ static void loop_through_files(int filetag, void (*fileaction)(char *filename, i
273 } 260 }
274} 261}
275 262
263#if 0 //DEBUG
264static void print_all_tags(void)
265{
266 unsigned i = 0;
267 while (i < G.tagcount) {
268 rpm_index *tag = &G.mytags[i];
269 if (tag->type == RPM_STRING_TYPE
270 || tag->type == RPM_I18NSTRING_TYPE
271 || tag->type == RPM_STRING_ARRAY_TYPE
272 ) {
273 unsigned n;
274 char *str = (char *) G.map + tag->offset;
275
276 printf("tag[%u] %08x type %08x offset %08x count %d '%s'\n",
277 i, tag->tag, tag->type, tag->offset, tag->count, str
278 );
279 for (n = 1; n < tag->count; n++) {
280 str += strlen(str) + 1;
281 printf("\t'%s'\n", str);
282 }
283 }
284 i++;
285 }
286}
287#else
288#define print_all_tags() ((void)0)
289#endif
290
291static void extract_cpio(int fd, const char *source_rpm)
292{
293 archive_handle_t *archive_handle;
294
295 if (source_rpm != NULL) {
296 /* Binary rpm (it was built from some SRPM), install to root */
297 xchdir("/");
298 } /* else: SRPM, install to current dir */
299
300 /* Initialize */
301 archive_handle = init_handle();
302 archive_handle->seek = seek_by_read;
303 archive_handle->action_data = data_extract_all;
304#if 0 /* For testing (rpm -i only lists the files in internal cpio): */
305 archive_handle->action_header = header_list;
306 archive_handle->action_data = data_skip;
307#endif
308 archive_handle->ah_flags = ARCHIVE_RESTORE_DATE | ARCHIVE_CREATE_LEADING_DIRS
309 /* compat: overwrite existing files.
310 * try "rpm -i foo.src.rpm" few times in a row -
311 * standard rpm will not complain.
312 */
313 | ARCHIVE_REPLACE_VIA_RENAME;
314 archive_handle->src_fd = fd;
315 /*archive_handle->offset = 0; - init_handle() did it */
316
317 setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 1);
318 while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
319 continue;
320}
321
322//usage:#define rpm_trivial_usage
323//usage: "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
324//usage:#define rpm_full_usage "\n\n"
325//usage: "Manipulate RPM packages\n"
326//usage: "\nCommands:"
327//usage: "\n -i Install package"
328//usage: "\n -qp Query package"
329//usage: "\n -qpi Show information"
330//usage: "\n -qpl List contents"
331//usage: "\n -qpd List documents"
332//usage: "\n -qpc List config files"
333
334/* RPM version 4.13.0.1:
335 * Unlike -q, -i seems to imply -p: -i, -ip and -pi work the same.
336 * OTOH, with -q order is important: "-piq FILE.rpm" works as -qp, not -qpi
337 * (IOW: shows only package name, not package info).
338 * "-iq ARG" works as -q: treats ARG as package name, not a file.
339 *
340 * "man rpm" on -l option and options implying it:
341 * -l, --list List files in package.
342 * -c, --configfiles List only configuration files (implies -l).
343 * -d, --docfiles List only documentation files (implies -l).
344 * -L, --licensefiles List only license files (implies -l).
345 * --dump Dump file information as follows (implies -l):
346 * path size mtime digest mode owner group isconfig isdoc rdev symlink
347 * -s, --state Display the states of files in the package (implies -l).
348 * The state of each file is one of normal, not installed, or replaced.
349 *
350 * Looks like we can switch to getopt32 here: in practice, people
351 * do place -q first if they intend to use it (misinterpreting "-piq" wouldn't matter).
352 */
276int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 353int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
277int rpm_main(int argc, char **argv) 354int rpm_main(int argc, char **argv)
278{ 355{
279 int opt, func = 0; 356 int opt, func = 0;
280 const unsigned pagesize = getpagesize(); 357
358 INIT_G();
359 G.pagesize = getpagesize();
281 360
282 while ((opt = getopt(argc, argv, "iqpldc")) != -1) { 361 while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
283 switch (opt) { 362 switch (opt) {
@@ -289,17 +368,17 @@ int rpm_main(int argc, char **argv)
289 if (func) bb_show_usage(); 368 if (func) bb_show_usage();
290 func = rpm_query; 369 func = rpm_query;
291 break; 370 break;
292 case 'p': /* Query a package */ 371 case 'p': /* Query a package (IOW: .rpm file, we are not querying RPMDB) */
293 func |= rpm_query_package; 372 func |= rpm_query_package;
294 break; 373 break;
295 case 'l': /* List files in a package */ 374 case 'l': /* List files in a package */
296 func |= rpm_query_list; 375 func |= rpm_query_list;
297 break; 376 break;
298 case 'd': /* List doc files in a package (implies list) */ 377 case 'd': /* List doc files in a package (implies -l) */
299 func |= rpm_query_list; 378 func |= rpm_query_list;
300 func |= rpm_query_list_doc; 379 func |= rpm_query_list_doc;
301 break; 380 break;
302 case 'c': /* List config files in a package (implies list) */ 381 case 'c': /* List config files in a package (implies -l) */
303 func |= rpm_query_list; 382 func |= rpm_query_list;
304 func |= rpm_query_list_config; 383 func |= rpm_query_list_config;
305 break; 384 break;
@@ -313,24 +392,18 @@ int rpm_main(int argc, char **argv)
313 bb_show_usage(); 392 bb_show_usage();
314 } 393 }
315 394
316 while (*argv) { 395 for (;;) {
317 int rpm_fd; 396 int rpm_fd;
318 unsigned mapsize;
319 const char *source_rpm; 397 const char *source_rpm;
320 398
321 rpm_fd = xopen(*argv++, O_RDONLY); 399 rpm_fd = rpm_gettags(*argv);
322 G.mytags = rpm_gettags(rpm_fd, &G.tagcount); 400 print_all_tags();
323 if (!G.mytags)
324 bb_error_msg_and_die("error reading rpm header");
325 mapsize = xlseek(rpm_fd, 0, SEEK_CUR);
326 mapsize = (mapsize + pagesize) & -(int)pagesize;
327 /* Some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */
328 G.map = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
329//FIXME: error check?
330 401
331 source_rpm = rpm_getstr(TAG_SOURCERPM, 0); 402 source_rpm = rpm_getstr0(TAG_SOURCERPM);
332 403
333 if (func & rpm_install) { 404 if (func & rpm_install) {
405 /* -i (and not -qi) */
406
334 /* Backup any config files */ 407 /* Backup any config files */
335 loop_through_files(TAG_BASENAMES, fileaction_dobackup); 408 loop_through_files(TAG_BASENAMES, fileaction_dobackup);
336 /* Extact the archive */ 409 /* Extact the archive */
@@ -338,10 +411,13 @@ int rpm_main(int argc, char **argv)
338 /* Set the correct file uid/gid's */ 411 /* Set the correct file uid/gid's */
339 loop_through_files(TAG_BASENAMES, fileaction_setowngrp); 412 loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
340 } 413 }
341 else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) { 414 else
415 if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
416 /* -qp */
417
342 if (!(func & (rpm_query_info|rpm_query_list))) { 418 if (!(func & (rpm_query_info|rpm_query_list))) {
343 /* If just a straight query, just give package name */ 419 /* If just a straight query, just give package name */
344 printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0)); 420 printf("%s-%s-%s\n", rpm_getstr0(TAG_NAME), rpm_getstr0(TAG_VERSION), rpm_getstr0(TAG_RELEASE));
345 } 421 }
346 if (func & rpm_query_info) { 422 if (func & rpm_query_info) {
347 /* Do the nice printout */ 423 /* Do the nice printout */
@@ -350,30 +426,33 @@ int rpm_main(int argc, char **argv)
350 char bdatestring[50]; 426 char bdatestring[50];
351 const char *p; 427 const char *p;
352 428
353 printf("%-12s: %s\n", "Name" , rpm_getstr(TAG_NAME, 0)); 429 printf("%-12s: %s\n", "Name" , rpm_getstr0(TAG_NAME));
354 /* TODO compat: add "Epoch" here */ 430 /* TODO compat: add "Epoch" here */
355 printf("%-12s: %s\n", "Version" , rpm_getstr(TAG_VERSION, 0)); 431 printf("%-12s: %s\n", "Version" , rpm_getstr0(TAG_VERSION));
356 printf("%-12s: %s\n", "Release" , rpm_getstr(TAG_RELEASE, 0)); 432 printf("%-12s: %s\n", "Release" , rpm_getstr0(TAG_RELEASE));
357 /* add "Architecture" */ 433 /* add "Architecture" */
358 printf("%-12s: %s\n", "Install Date", "(not installed)"); 434 /* printf("%-12s: %s\n", "Install Date", "(not installed)"); - we don't know */
359 printf("%-12s: %s\n", "Group" , rpm_getstr(TAG_GROUP, 0)); 435 printf("%-12s: %s\n", "Group" , rpm_getstr0(TAG_GROUP));
360 printf("%-12s: %d\n", "Size" , rpm_getint(TAG_SIZE, 0)); 436 printf("%-12s: %d\n", "Size" , rpm_getint(TAG_SIZE, 0));
361 printf("%-12s: %s\n", "License" , rpm_getstr(TAG_LICENSE, 0)); 437 printf("%-12s: %s\n", "License" , rpm_getstr0(TAG_LICENSE));
362 /* add "Signature" */ 438 /* add "Signature" */
363 printf("%-12s: %s\n", "Source RPM" , source_rpm ? source_rpm : "(none)"); 439 printf("%-12s: %s\n", "Source RPM" , source_rpm ? source_rpm : "(none)");
364 bdate_time = rpm_getint(TAG_BUILDTIME, 0); 440 bdate_time = rpm_getint(TAG_BUILDTIME, 0);
365 bdate_ptm = localtime(&bdate_time); 441 bdate_ptm = localtime(&bdate_time);
366 strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm); 442 strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm);
367 printf("%-12s: %s\n", "Build Date" , bdatestring); 443 printf("%-12s: %s\n", "Build Date" , bdatestring);
368 printf("%-12s: %s\n", "Build Host" , rpm_getstr(TAG_BUILDHOST, 0)); 444 printf("%-12s: %s\n", "Build Host" , rpm_getstr0(TAG_BUILDHOST));
369 p = rpm_getstr(TAG_PREFIXS, 0); 445 p = rpm_getstr0(TAG_PREFIXS);
370 printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)"); 446 printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)");
371 /* add "Packager" */ 447 /* add "Packager" */
372 p = rpm_getstr(TAG_VENDOR, 0); 448 p = rpm_getstr0(TAG_VENDOR);
373 printf("%-12s: %s\n", "Vendor" , p ? p : "(none)"); 449 if (p) /* rpm 4.13.0.1 does not show "(none)" for Vendor: */
374 printf("%-12s: %s\n", "URL" , rpm_getstr(TAG_URL, 0)); 450 printf("%-12s: %s\n", "Vendor" , p);
375 printf("%-12s: %s\n", "Summary" , rpm_getstr(TAG_SUMMARY, 0)); 451 p = rpm_getstr0(TAG_URL);
376 printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0)); 452 if (p) /* rpm 4.13.0.1 does not show "(none)"/"(null)" for URL: */
453 printf("%-12s: %s\n", "URL" , p);
454 printf("%-12s: %s\n", "Summary" , rpm_getstr0(TAG_SUMMARY));
455 printf("Description :\n%s\n", rpm_getstr0(TAG_DESCRIPTION));
377 } 456 }
378 if (func & rpm_query_list) { 457 if (func & rpm_query_list) {
379 int count, it, flags; 458 int count, it, flags;
@@ -396,10 +475,85 @@ int rpm_main(int argc, char **argv)
396 rpm_getstr(TAG_BASENAMES, it)); 475 rpm_getstr(TAG_BASENAMES, it));
397 } 476 }
398 } 477 }
478 } else {
479 /* Unsupported (help text shows what we support) */
480 bb_show_usage();
399 } 481 }
400 munmap(G.map, mapsize); 482 if (!*++argv)
483 break;
484 munmap(G.map, G.mapsize);
401 free(G.mytags); 485 free(G.mytags);
402 close(rpm_fd); 486 close(rpm_fd);
403 } 487 }
488
404 return 0; 489 return 0;
405} 490}
491
492#endif /* RPM */
493
494/*
495 * Mini rpm2cpio implementation for busybox
496 *
497 * Copyright (C) 2001 by Laurence Anderson
498 *
499 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
500 */
501//config:config RPM2CPIO
502//config: bool "rpm2cpio (20 kb)"
503//config: default y
504//config: help
505//config: Converts a RPM file into a CPIO archive.
506
507//applet:IF_RPM2CPIO(APPLET(rpm2cpio, BB_DIR_USR_BIN, BB_SUID_DROP))
508
509//kbuild:lib-$(CONFIG_RPM2CPIO) += rpm.o
510
511//usage:#define rpm2cpio_trivial_usage
512//usage: "PACKAGE.rpm"
513//usage:#define rpm2cpio_full_usage "\n\n"
514//usage: "Output a cpio archive of the rpm file"
515
516#if ENABLE_RPM2CPIO
517
518/* No getopt required */
519int rpm2cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
520int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
521{
522 const char *str;
523 int rpm_fd;
524
525 INIT_G();
526 G.pagesize = getpagesize();
527
528 rpm_fd = rpm_gettags(argv[1]);
529
530 //if (SEAMLESS_COMPRESSION) - we do this at the end instead.
531 // /* We need to know whether child (gzip/bzip/etc) exits abnormally */
532 // signal(SIGCHLD, check_errors_in_children);
533
534 if (ENABLE_FEATURE_SEAMLESS_LZMA
535 && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL
536 && strcmp(str, "lzma") == 0
537 ) {
538 // lzma compression can't be detected
539 // set up decompressor without detection
540 setup_lzma_on_fd(rpm_fd);
541 } else {
542 setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1);
543 }
544
545 if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0)
546 bb_error_msg_and_die("error unpacking");
547
548 if (ENABLE_FEATURE_CLEAN_UP) {
549 close(rpm_fd);
550 }
551
552 if (SEAMLESS_COMPRESSION) {
553 check_errors_in_children(0);
554 return bb_got_signal;
555 }
556 return EXIT_SUCCESS;
557}
558
559#endif /* RPM2CPIO */
diff --git a/archival/rpm2cpio.c b/archival/rpm2cpio.c
deleted file mode 100644
index 3e4a6a249..000000000
--- a/archival/rpm2cpio.c
+++ /dev/null
@@ -1,96 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini rpm2cpio implementation for busybox
4 *
5 * Copyright (C) 2001 by Laurence Anderson
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10//config:config RPM2CPIO
11//config: bool "rpm2cpio (20 kb)"
12//config: default y
13//config: help
14//config: Converts a RPM file into a CPIO archive.
15
16//applet:IF_RPM2CPIO(APPLET(rpm2cpio, BB_DIR_USR_BIN, BB_SUID_DROP))
17//kbuild:lib-$(CONFIG_RPM2CPIO) += rpm2cpio.o
18
19//usage:#define rpm2cpio_trivial_usage
20//usage: "package.rpm"
21//usage:#define rpm2cpio_full_usage "\n\n"
22//usage: "Output a cpio archive of the rpm file"
23
24#include "libbb.h"
25#include "bb_archive.h"
26#include "rpm.h"
27
28enum { rpm_fd = STDIN_FILENO };
29
30static unsigned skip_header(void)
31{
32 struct rpm_header header;
33 unsigned len;
34
35 xread(rpm_fd, &header, sizeof(header));
36// if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC_STR, 3) != 0) {
37// bb_error_msg_and_die("invalid RPM header magic");
38// }
39// if (header.version != 1) {
40// bb_error_msg_and_die("unsupported RPM header version");
41// }
42 if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER)) {
43 bb_error_msg_and_die("invalid RPM header magic or unsupported version");
44 // ": %x != %x", header.magic_and_ver, htonl(RPM_HEADER_MAGICnVER));
45 }
46
47 /* Seek past index entries, and past store */
48 len = 16 * ntohl(header.entries) + ntohl(header.size);
49 seek_by_jump(rpm_fd, len);
50
51 return sizeof(header) + len;
52}
53
54/* No getopt required */
55int rpm2cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
56int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
57{
58 struct rpm_lead lead;
59 unsigned pos;
60
61 if (argv[1]) {
62 xmove_fd(xopen(argv[1], O_RDONLY), rpm_fd);
63 }
64 xread(rpm_fd, &lead, sizeof(lead));
65
66 /* Just check the magic, the rest is irrelevant */
67 if (lead.magic != htonl(RPM_LEAD_MAGIC)) {
68 bb_error_msg_and_die("invalid RPM magic");
69 }
70
71 /* Skip the signature header, align to 8 bytes */
72 pos = skip_header();
73 seek_by_jump(rpm_fd, (-(int)pos) & 7);
74
75 /* Skip the main header */
76 skip_header();
77
78 //if (SEAMLESS_COMPRESSION)
79 // /* We need to know whether child (gzip/bzip/etc) exits abnormally */
80 // signal(SIGCHLD, check_errors_in_children);
81
82 /* This works, but doesn't report uncompress errors (they happen in child) */
83 setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1);
84 if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0)
85 bb_error_msg_and_die("error unpacking");
86
87 if (ENABLE_FEATURE_CLEAN_UP) {
88 close(rpm_fd);
89 }
90
91 if (SEAMLESS_COMPRESSION) {
92 check_errors_in_children(0);
93 return bb_got_signal;
94 }
95 return EXIT_SUCCESS;
96}
diff --git a/archival/tar.c b/archival/tar.c
index d90a5dc4f..503444796 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -278,23 +278,6 @@ static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
278 xwrite(fd, hp, sizeof(*hp)); 278 xwrite(fd, hp, sizeof(*hp));
279} 279}
280 280
281static void replace_symlink_placeholders(llist_t *list)
282{
283 while (list) {
284 char *target;
285
286 target = list->data + strlen(list->data) + 1;
287 if (symlink(target, list->data)) {
288 /* shared message */
289 bb_error_msg_and_die("can't create %slink '%s' to '%s'",
290 "sym",
291 list->data, target
292 );
293 }
294 list = list->link;
295 }
296}
297
298#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 281#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
299static void writeLongname(int fd, int type, const char *name, int dir) 282static void writeLongname(int fd, int type, const char *name, int dir)
300{ 283{
@@ -969,6 +952,11 @@ static const char tar_longopts[] ALIGN1 =
969 "exclude\0" Required_argument "\xff" 952 "exclude\0" Required_argument "\xff"
970# endif 953# endif
971 ; 954 ;
955# define GETOPT32 getopt32long
956# define LONGOPTS ,tar_longopts
957#else
958# define GETOPT32 getopt32
959# define LONGOPTS
972#endif 960#endif
973 961
974int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 962int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -995,21 +983,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
995 tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; 983 tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM;
996 984
997 /* Prepend '-' to the first argument if required */ 985 /* Prepend '-' to the first argument if required */
998 opt_complementary = "--:" // first arg is options 986 if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0')
999 "tt:vv:" // count -t,-v 987 argv[1] = xasprintf("-%s", argv[1]);
1000#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
1001 "\xff::" // --exclude=PATTERN is a list
1002#endif
1003 IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
1004 IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
1005 IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive
1006#if ENABLE_FEATURE_TAR_LONG_OPTIONS
1007 ":\xf9+" // --strip-components=NUM
1008#endif
1009 ;
1010#if ENABLE_FEATURE_TAR_LONG_OPTIONS
1011 applet_long_options = tar_longopts;
1012#endif
1013#if ENABLE_DESKTOP 988#if ENABLE_DESKTOP
1014 /* Lie to buildroot when it starts asking stupid questions. */ 989 /* Lie to buildroot when it starts asking stupid questions. */
1015 if (argv[1] && strcmp(argv[1], "--version") == 0) { 990 if (argv[1] && strcmp(argv[1], "--version") == 0) {
@@ -1046,7 +1021,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1046 } 1021 }
1047 } 1022 }
1048#endif 1023#endif
1049 opt = getopt32(argv, 1024 opt = GETOPT32(argv, "^"
1050 "txC:f:Oopvk" 1025 "txC:f:Oopvk"
1051 IF_FEATURE_TAR_CREATE( "ch" ) 1026 IF_FEATURE_TAR_CREATE( "ch" )
1052 IF_FEATURE_SEAMLESS_BZ2( "j" ) 1027 IF_FEATURE_SEAMLESS_BZ2( "j" )
@@ -1057,6 +1032,18 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1057 IF_FEATURE_SEAMLESS_Z( "Z" ) 1032 IF_FEATURE_SEAMLESS_Z( "Z" )
1058 IF_FEATURE_TAR_NOPRESERVE_TIME("m") 1033 IF_FEATURE_TAR_NOPRESERVE_TIME("m")
1059 IF_FEATURE_TAR_LONG_OPTIONS("\xf9:") // --strip-components 1034 IF_FEATURE_TAR_LONG_OPTIONS("\xf9:") // --strip-components
1035 "\0"
1036 "tt:vv:" // count -t,-v
1037#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
1038 "\xff::" // --exclude=PATTERN is a list
1039#endif
1040 IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
1041 IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
1042 IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive
1043#if ENABLE_FEATURE_TAR_LONG_OPTIONS
1044 ":\xf9+" // --strip-components=NUM
1045#endif
1046 LONGOPTS
1060 , &base_dir // -C dir 1047 , &base_dir // -C dir
1061 , &tar_filename // -f filename 1048 , &tar_filename // -f filename
1062 IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T 1049 IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
@@ -1280,8 +1267,6 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1280 while (get_header_tar(tar_handle) == EXIT_SUCCESS) 1267 while (get_header_tar(tar_handle) == EXIT_SUCCESS)
1281 bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ 1268 bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */
1282 1269
1283 replace_symlink_placeholders(tar_handle->symlink_placeholders);
1284
1285 /* Check that every file that should have been extracted was */ 1270 /* Check that every file that should have been extracted was */
1286 while (tar_handle->accept) { 1271 while (tar_handle->accept) {
1287 if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) 1272 if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
diff --git a/archival/unzip.c b/archival/unzip.c
index f37ea3519..233487697 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -371,9 +371,15 @@ static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn)
371 target[xstate.mem_output_size] = '\0'; 371 target[xstate.mem_output_size] = '\0';
372#endif 372#endif
373 } 373 }
374 if (!unsafe_symlink_target(target)) {
374//TODO: libbb candidate 375//TODO: libbb candidate
375 if (symlink(target, dst_fn)) 376 if (symlink(target, dst_fn)) {
376 bb_perror_msg_and_die("can't create symlink '%s'", dst_fn); 377 /* shared message */
378 bb_perror_msg_and_die("can't create %slink '%s' to '%s'",
379 "sym", dst_fn, target
380 );
381 }
382 }
377 free(target); 383 free(target);
378} 384}
379#endif 385#endif
diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig
index 6ff68a092..415f5a802 100644
--- a/configs/TEST_nommu_defconfig
+++ b/configs/TEST_nommu_defconfig
@@ -37,7 +37,6 @@ CONFIG_SELINUX=y
37CONFIG_FEATURE_PREFER_APPLETS=y 37CONFIG_FEATURE_PREFER_APPLETS=y
38CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 38CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
39CONFIG_FEATURE_SYSLOG=y 39CONFIG_FEATURE_SYSLOG=y
40CONFIG_FEATURE_HAVE_RPC=y
41 40
42# 41#
43# Build Options 42# Build Options
diff --git a/configs/TEST_noprintf_defconfig b/configs/TEST_noprintf_defconfig
index 4b2ef402a..9b378fd55 100644
--- a/configs/TEST_noprintf_defconfig
+++ b/configs/TEST_noprintf_defconfig
@@ -47,7 +47,6 @@ CONFIG_FEATURE_PIDFILE=y
47# CONFIG_FEATURE_PREFER_APPLETS is not set 47# CONFIG_FEATURE_PREFER_APPLETS is not set
48CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 48CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
49# CONFIG_FEATURE_SYSLOG is not set 49# CONFIG_FEATURE_SYSLOG is not set
50# CONFIG_FEATURE_HAVE_RPC is not set
51 50
52# 51#
53# Build Options 52# Build Options
diff --git a/configs/TEST_rh9_defconfig b/configs/TEST_rh9_defconfig
index 52f3e4670..4ac62b9da 100644
--- a/configs/TEST_rh9_defconfig
+++ b/configs/TEST_rh9_defconfig
@@ -46,7 +46,6 @@ CONFIG_FEATURE_SUID_CONFIG_QUIET=y
46# CONFIG_FEATURE_PREFER_APPLETS is not set 46# CONFIG_FEATURE_PREFER_APPLETS is not set
47CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 47CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
48CONFIG_FEATURE_SYSLOG=y 48CONFIG_FEATURE_SYSLOG=y
49CONFIG_FEATURE_HAVE_RPC=y
50 49
51# 50#
52# Build Options 51# Build Options
diff --git a/configs/android2_defconfig b/configs/android2_defconfig
index 9202320a4..03323654d 100644
--- a/configs/android2_defconfig
+++ b/configs/android2_defconfig
@@ -49,7 +49,6 @@ CONFIG_LAST_SUPPORTED_WCHAR=0
49# CONFIG_FEATURE_PREFER_APPLETS is not set 49# CONFIG_FEATURE_PREFER_APPLETS is not set
50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
51CONFIG_FEATURE_SYSLOG=y 51CONFIG_FEATURE_SYSLOG=y
52# CONFIG_FEATURE_HAVE_RPC is not set
53 52
54# 53#
55# Build Options 54# Build Options
diff --git a/configs/android_502_defconfig b/configs/android_502_defconfig
index 1901bdbb0..6b640bfb7 100644
--- a/configs/android_502_defconfig
+++ b/configs/android_502_defconfig
@@ -123,7 +123,6 @@ CONFIG_FEATURE_SUID_CONFIG_QUIET=y
123# CONFIG_FEATURE_PREFER_APPLETS is not set 123# CONFIG_FEATURE_PREFER_APPLETS is not set
124CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 124CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
125CONFIG_FEATURE_SYSLOG=y 125CONFIG_FEATURE_SYSLOG=y
126# CONFIG_FEATURE_HAVE_RPC is not set
127 126
128# 127#
129# Build Options 128# Build Options
diff --git a/configs/android_defconfig b/configs/android_defconfig
index ea6e8a79e..3b34f37aa 100644
--- a/configs/android_defconfig
+++ b/configs/android_defconfig
@@ -49,7 +49,6 @@ CONFIG_LAST_SUPPORTED_WCHAR=0
49# CONFIG_FEATURE_PREFER_APPLETS is not set 49# CONFIG_FEATURE_PREFER_APPLETS is not set
50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
51CONFIG_FEATURE_SYSLOG=y 51CONFIG_FEATURE_SYSLOG=y
52# CONFIG_FEATURE_HAVE_RPC is not set
53 52
54# 53#
55# Build Options 54# Build Options
diff --git a/configs/android_ndk_defconfig b/configs/android_ndk_defconfig
index 61871fcb1..7f65d544c 100644
--- a/configs/android_ndk_defconfig
+++ b/configs/android_ndk_defconfig
@@ -52,7 +52,6 @@ CONFIG_PID_FILE_PATH=""
52# CONFIG_FEATURE_PREFER_APPLETS is not set 52# CONFIG_FEATURE_PREFER_APPLETS is not set
53CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 53CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
54CONFIG_FEATURE_SYSLOG=y 54CONFIG_FEATURE_SYSLOG=y
55# CONFIG_FEATURE_HAVE_RPC is not set
56 55
57# 56#
58# Build Options 57# Build Options
diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig
index 54aa44470..ee370a61d 100644
--- a/configs/cygwin_defconfig
+++ b/configs/cygwin_defconfig
@@ -49,7 +49,6 @@ CONFIG_FEATURE_SUID=y
49# CONFIG_FEATURE_PREFER_APPLETS is not set 49# CONFIG_FEATURE_PREFER_APPLETS is not set
50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
51CONFIG_FEATURE_SYSLOG=y 51CONFIG_FEATURE_SYSLOG=y
52# CONFIG_FEATURE_HAVE_RPC is not set
53 52
54# 53#
55# Build Options 54# Build Options
diff --git a/configs/freebsd_defconfig b/configs/freebsd_defconfig
index fadbca13b..47e705963 100644
--- a/configs/freebsd_defconfig
+++ b/configs/freebsd_defconfig
@@ -49,7 +49,6 @@ CONFIG_FEATURE_SUID_CONFIG_QUIET=y
49# CONFIG_FEATURE_PREFER_APPLETS is not set 49# CONFIG_FEATURE_PREFER_APPLETS is not set
50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 50CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
51CONFIG_FEATURE_SYSLOG=y 51CONFIG_FEATURE_SYSLOG=y
52# CONFIG_FEATURE_HAVE_RPC is not set
53 52
54# 53#
55# Build Options 54# Build Options
diff --git a/console-tools/chvt.c b/console-tools/chvt.c
index d8152de6b..75380a90b 100644
--- a/console-tools/chvt.c
+++ b/console-tools/chvt.c
@@ -14,7 +14,7 @@
14//config: This program is used to change to another terminal. 14//config: This program is used to change to another terminal.
15//config: Example: chvt 4 (change to terminal /dev/tty4) 15//config: Example: chvt 4 (change to terminal /dev/tty4)
16 16
17//applet:IF_CHVT(APPLET(chvt, BB_DIR_USR_BIN, BB_SUID_DROP)) 17//applet:IF_CHVT(APPLET_NOEXEC(chvt, chvt, BB_DIR_USR_BIN, BB_SUID_DROP, chvt))
18 18
19//kbuild:lib-$(CONFIG_CHVT) += chvt.o 19//kbuild:lib-$(CONFIG_CHVT) += chvt.o
20 20
diff --git a/console-tools/clear.c b/console-tools/clear.c
index 13eec498b..3cc16257b 100644
--- a/console-tools/clear.c
+++ b/console-tools/clear.c
@@ -12,7 +12,7 @@
12//config: help 12//config: help
13//config: This program clears the terminal screen. 13//config: This program clears the terminal screen.
14 14
15//applet:IF_CLEAR(APPLET(clear, BB_DIR_USR_BIN, BB_SUID_DROP)) 15//applet:IF_CLEAR(APPLET_NOFORK(clear, clear, BB_DIR_USR_BIN, BB_SUID_DROP, clear))
16 16
17//kbuild:lib-$(CONFIG_CLEAR) += clear.o 17//kbuild:lib-$(CONFIG_CLEAR) += clear.o
18 18
diff --git a/console-tools/deallocvt.c b/console-tools/deallocvt.c
index 6ffb1471e..05731fb78 100644
--- a/console-tools/deallocvt.c
+++ b/console-tools/deallocvt.c
@@ -14,7 +14,7 @@
14//config: help 14//config: help
15//config: This program deallocates unused virtual consoles. 15//config: This program deallocates unused virtual consoles.
16 16
17//applet:IF_DEALLOCVT(APPLET(deallocvt, BB_DIR_USR_BIN, BB_SUID_DROP)) 17//applet:IF_DEALLOCVT(APPLET_NOEXEC(deallocvt, deallocvt, BB_DIR_USR_BIN, BB_SUID_DROP, deallocvt))
18 18
19//kbuild:lib-$(CONFIG_DEALLOCVT) += deallocvt.o 19//kbuild:lib-$(CONFIG_DEALLOCVT) += deallocvt.o
20 20
diff --git a/console-tools/dumpkmap.c b/console-tools/dumpkmap.c
index d4e2cf281..b803e579a 100644
--- a/console-tools/dumpkmap.c
+++ b/console-tools/dumpkmap.c
@@ -15,7 +15,8 @@
15//config: This program dumps the kernel's keyboard translation table to 15//config: This program dumps the kernel's keyboard translation table to
16//config: stdout, in binary format. You can then use loadkmap to load it. 16//config: stdout, in binary format. You can then use loadkmap to load it.
17 17
18//applet:IF_DUMPKMAP(APPLET(dumpkmap, BB_DIR_BIN, BB_SUID_DROP)) 18//applet:IF_DUMPKMAP(APPLET_NOEXEC(dumpkmap, dumpkmap, BB_DIR_BIN, BB_SUID_DROP, dumpkmap))
19/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */
19 20
20//kbuild:lib-$(CONFIG_DUMPKMAP) += dumpkmap.o 21//kbuild:lib-$(CONFIG_DUMPKMAP) += dumpkmap.o
21 22
@@ -47,8 +48,6 @@ int dumpkmap_main(int argc UNUSED_PARAM, char **argv)
47{ 48{
48 struct kbentry ke; 49 struct kbentry ke;
49 int i, j, fd; 50 int i, j, fd;
50#define flags bb_common_bufsiz1
51 setup_common_bufsiz();
52 51
53 /* When user accidentally runs "dumpkmap FILE" 52 /* When user accidentally runs "dumpkmap FILE"
54 * instead of "dumpkmap >FILE", we'd dump binary stuff to tty. 53 * instead of "dumpkmap >FILE", we'd dump binary stuff to tty.
@@ -60,19 +59,8 @@ int dumpkmap_main(int argc UNUSED_PARAM, char **argv)
60 59
61 fd = get_console_fd_or_die(); 60 fd = get_console_fd_or_die();
62 61
63#if 0 62#define flags bb_common_bufsiz1
64 write(STDOUT_FILENO, "bkeymap", 7); 63 setup_common_bufsiz();
65 /* Here we want to set everything to 0 except for indexes:
66 * [0-2] [4-6] [8-10] [12]
67 */
68 /*memset(flags, 0x00, MAX_NR_KEYMAPS); - already is */
69 memset(flags, 0x01, 13);
70 flags[3] = flags[7] = flags[11] = 0;
71 /* dump flags */
72 write(STDOUT_FILENO, flags, MAX_NR_KEYMAPS);
73#define flags7 flags
74#else
75 /* Same effect */
76 /* 0 1 2 3 4 5 6 7 8 9 a b c=12 */ 64 /* 0 1 2 3 4 5 6 7 8 9 a b c=12 */
77 memcpy(flags, "bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1", 65 memcpy(flags, "bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1",
78 /* Can use sizeof, or sizeof-1. sizeof is even, using that */ 66 /* Can use sizeof, or sizeof-1. sizeof is even, using that */
@@ -80,7 +68,6 @@ int dumpkmap_main(int argc UNUSED_PARAM, char **argv)
80 ); 68 );
81 write(STDOUT_FILENO, flags, 7 + MAX_NR_KEYMAPS); 69 write(STDOUT_FILENO, flags, 7 + MAX_NR_KEYMAPS);
82#define flags7 (flags + 7) 70#define flags7 (flags + 7)
83#endif
84 71
85 for (i = 0; i < 13; i++) { 72 for (i = 0; i < 13; i++) {
86 if (flags7[i]) { 73 if (flags7[i]) {
diff --git a/console-tools/fgconsole.c b/console-tools/fgconsole.c
index 64311f6ea..a353becd5 100644
--- a/console-tools/fgconsole.c
+++ b/console-tools/fgconsole.c
@@ -13,7 +13,7 @@
13//config: help 13//config: help
14//config: This program prints active (foreground) console number. 14//config: This program prints active (foreground) console number.
15 15
16//applet:IF_FGCONSOLE(APPLET(fgconsole, BB_DIR_USR_BIN, BB_SUID_DROP)) 16//applet:IF_FGCONSOLE(APPLET_NOEXEC(fgconsole, fgconsole, BB_DIR_USR_BIN, BB_SUID_DROP, fgconsole))
17 17
18//kbuild:lib-$(CONFIG_FGCONSOLE) += fgconsole.o 18//kbuild:lib-$(CONFIG_FGCONSOLE) += fgconsole.o
19 19
diff --git a/console-tools/kbd_mode.c b/console-tools/kbd_mode.c
index d81c56e92..f16449dcd 100644
--- a/console-tools/kbd_mode.c
+++ b/console-tools/kbd_mode.c
@@ -15,19 +15,19 @@
15//config: help 15//config: help
16//config: This program reports and sets keyboard mode. 16//config: This program reports and sets keyboard mode.
17 17
18//applet:IF_KBD_MODE(APPLET(kbd_mode, BB_DIR_BIN, BB_SUID_DROP)) 18//applet:IF_KBD_MODE(APPLET_NOEXEC(kbd_mode, kbd_mode, BB_DIR_BIN, BB_SUID_DROP, kbd_mode))
19 19
20//kbuild:lib-$(CONFIG_KBD_MODE) += kbd_mode.o 20//kbuild:lib-$(CONFIG_KBD_MODE) += kbd_mode.o
21 21
22//usage:#define kbd_mode_trivial_usage 22//usage:#define kbd_mode_trivial_usage
23//usage: "[-a|k|s|u] [-C TTY]" 23//usage: "[-a|k|s|u] [-C TTY]"
24//usage:#define kbd_mode_full_usage "\n\n" 24//usage:#define kbd_mode_full_usage "\n\n"
25//usage: "Report or set the keyboard mode\n" 25//usage: "Report or set VT console keyboard mode\n"
26//usage: "\n -a Default (ASCII)" 26//usage: "\n -a Default (ASCII)"
27//usage: "\n -k Medium-raw (keyboard)" 27//usage: "\n -k Medium-raw (keycode)"
28//usage: "\n -s Raw (scancode)" 28//usage: "\n -s Raw (scancode)"
29//usage: "\n -u Unicode (utf-8)" 29//usage: "\n -u Unicode (utf-8)"
30//usage: "\n -C TTY Affect TTY instead of /dev/tty" 30//usage: "\n -C TTY Affect TTY"
31 31
32#include "libbb.h" 32#include "libbb.h"
33#include <linux/kd.h> 33#include <linux/kd.h>
@@ -43,11 +43,20 @@ int kbd_mode_main(int argc UNUSED_PARAM, char **argv)
43 }; 43 };
44 int fd; 44 int fd;
45 unsigned opt; 45 unsigned opt;
46 const char *tty_name = CURRENT_TTY; 46 const char *tty_name;
47 47
48 opt = getopt32(argv, "sakuC:", &tty_name); 48 opt = getopt32(argv, "sakuC:", &tty_name);
49 fd = xopen_nonblocking(tty_name); 49 if (opt & 0x10) {
50 opt &= 0xf; /* clear -C bit, see (*) */ 50 opt &= 0xf; /* clear -C bit, see (*) */
51 fd = xopen_nonblocking(tty_name);
52 } else {
53 /* kbd-2.0.3 tries in sequence:
54 * fd#0, /dev/tty, /dev/tty0.
55 * get_console_fd_or_die: /dev/console, /dev/tty0, /dev/tty.
56 * kbd-2.0.3 checks KDGKBTYPE, get_console_fd_or_die checks too.
57 */
58 fd = get_console_fd_or_die();
59 }
51 60
52 if (!opt) { /* print current setting */ 61 if (!opt) { /* print current setting */
53 const char *mode = "unknown"; 62 const char *mode = "unknown";
@@ -62,9 +71,19 @@ int kbd_mode_main(int argc UNUSED_PARAM, char **argv)
62 mode = "mediumraw (keycode)"; 71 mode = "mediumraw (keycode)";
63 else if (m == K_UNICODE) 72 else if (m == K_UNICODE)
64 mode = "Unicode (UTF-8)"; 73 mode = "Unicode (UTF-8)";
74 else if (m == 4 /*K_OFF*/) /* kbd-2.0.3 does not show this mode, says "unknown" */
75 mode = "off";
65 printf("The keyboard is in %s mode\n", mode); 76 printf("The keyboard is in %s mode\n", mode);
66 } else { 77 } else {
67 /* here we depend on specific bits assigned to options (*) */ 78 /* here we depend on specific bits assigned to options (*)
79 * KDSKBMODE constants have these values:
80 * #define K_RAW 0x00
81 * #define K_XLATE 0x01
82 * #define K_MEDIUMRAW 0x02
83 * #define K_UNICODE 0x03
84 * #define K_OFF 0x04
85 * (looks like "-ak" together would cause the same effect as -u)
86 */
68 opt = opt & UNICODE ? 3 : opt >> 1; 87 opt = opt & UNICODE ? 3 : opt >> 1;
69 /* double cast prevents warnings about widening conversion */ 88 /* double cast prevents warnings about widening conversion */
70 xioctl(fd, KDSKBMODE, (void*)(ptrdiff_t)opt); 89 xioctl(fd, KDSKBMODE, (void*)(ptrdiff_t)opt);
diff --git a/console-tools/loadfont.c b/console-tools/loadfont.c
index 6dc8fa831..81d0c3db4 100644
--- a/console-tools/loadfont.c
+++ b/console-tools/loadfont.c
@@ -51,31 +51,12 @@
51//config: default y 51//config: default y
52//config: depends on LOADFONT || SETFONT 52//config: depends on LOADFONT || SETFONT
53 53
54//applet:IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP)) 54//applet:IF_LOADFONT(APPLET_NOEXEC(loadfont, loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP, loadfont))
55//applet:IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP)) 55//applet:IF_SETFONT(APPLET_NOEXEC(setfont, setfont, BB_DIR_USR_SBIN, BB_SUID_DROP, setfont))
56 56
57//kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o 57//kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o
58//kbuild:lib-$(CONFIG_SETFONT) += loadfont.o 58//kbuild:lib-$(CONFIG_SETFONT) += loadfont.o
59 59
60//usage:#define loadfont_trivial_usage
61//usage: "< font"
62//usage:#define loadfont_full_usage "\n\n"
63//usage: "Load a console font from stdin"
64/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */
65//usage:
66//usage:#define loadfont_example_usage
67//usage: "$ loadfont < /etc/i18n/fontname\n"
68//usage:
69//usage:#define setfont_trivial_usage
70//usage: "FONT [-m MAPFILE] [-C TTY]"
71//usage:#define setfont_full_usage "\n\n"
72//usage: "Load a console font\n"
73//usage: "\n -m MAPFILE Load console screen map"
74//usage: "\n -C TTY Affect TTY instead of /dev/tty"
75//usage:
76//usage:#define setfont_example_usage
77//usage: "$ setfont -m koi8-r /etc/i18n/fontname\n"
78
79#include "libbb.h" 60#include "libbb.h"
80#include <sys/kd.h> 61#include <sys/kd.h>
81 62
@@ -352,6 +333,14 @@ static void do_load(int fd, unsigned char *buffer, size_t len)
352 333
353 334
354#if ENABLE_LOADFONT 335#if ENABLE_LOADFONT
336//usage:#define loadfont_trivial_usage
337//usage: "< font"
338//usage:#define loadfont_full_usage "\n\n"
339//usage: "Load a console font from stdin"
340/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */
341//usage:
342//usage:#define loadfont_example_usage
343//usage: "$ loadfont < /etc/i18n/fontname\n"
355int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 344int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
356int loadfont_main(int argc UNUSED_PARAM, char **argv) 345int loadfont_main(int argc UNUSED_PARAM, char **argv)
357{ 346{
@@ -359,8 +348,7 @@ int loadfont_main(int argc UNUSED_PARAM, char **argv)
359 unsigned char *buffer; 348 unsigned char *buffer;
360 349
361 // no arguments allowed! 350 // no arguments allowed!
362 opt_complementary = "=0"; 351 getopt32(argv, "^" "" "\0" "=0");
363 getopt32(argv, "");
364 352
365 /* 353 /*
366 * We used to look at the length of the input file 354 * We used to look at the length of the input file
@@ -380,11 +368,9 @@ int loadfont_main(int argc UNUSED_PARAM, char **argv)
380} 368}
381#endif 369#endif
382 370
383#if ENABLE_SETFONT
384
385/*
386kbd-1.12:
387 371
372#if ENABLE_SETFONT
373/* kbd-1.12:
388setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig] 374setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
389[-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console] 375[-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
390[-hNN] [-v] [-V] 376[-hNN] [-v] [-V]
@@ -414,8 +400,17 @@ setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
414-v Verbose 400-v Verbose
415-V Version 401-V Version
416*/ 402*/
403//usage:#define setfont_trivial_usage
404//usage: "FONT [-m MAPFILE] [-C TTY]"
405//usage:#define setfont_full_usage "\n\n"
406//usage: "Load a console font\n"
407//usage: "\n -m MAPFILE Load console screen map"
408//usage: "\n -C TTY Affect TTY instead of /dev/tty"
409//usage:
410//usage:#define setfont_example_usage
411//usage: "$ setfont -m koi8-r /etc/i18n/fontname\n"
417 412
418#if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP 413# if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
419static int ctoi(char *s) 414static int ctoi(char *s)
420{ 415{
421 if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0') 416 if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
@@ -429,7 +424,7 @@ static int ctoi(char *s)
429 return -1; 424 return -1;
430 return xstrtoul(s, 0); 425 return xstrtoul(s, 0);
431} 426}
432#endif 427# endif
433 428
434int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 429int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
435int setfont_main(int argc UNUSED_PARAM, char **argv) 430int setfont_main(int argc UNUSED_PARAM, char **argv)
@@ -441,8 +436,7 @@ int setfont_main(int argc UNUSED_PARAM, char **argv)
441 char *mapfilename; 436 char *mapfilename;
442 const char *tty_name = CURRENT_TTY; 437 const char *tty_name = CURRENT_TTY;
443 438
444 opt_complementary = "=1"; 439 opts = getopt32(argv, "^" "m:C:" "\0" "=1", &mapfilename, &tty_name);
445 opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
446 argv += optind; 440 argv += optind;
447 441
448 fd = xopen_nonblocking(tty_name); 442 fd = xopen_nonblocking(tty_name);
@@ -480,7 +474,7 @@ int setfont_main(int argc UNUSED_PARAM, char **argv)
480 if (len == 2*E_TABSZ) 474 if (len == 2*E_TABSZ)
481 mode = PIO_UNISCRNMAP; 475 mode = PIO_UNISCRNMAP;
482 } 476 }
483#if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP 477# if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
484 // assume textual Unicode console maps: 478 // assume textual Unicode console maps:
485 // 0x00 U+0000 # NULL (NUL) 479 // 0x00 U+0000 # NULL (NUL)
486 // 0x01 U+0001 # START OF HEADING (SOH) 480 // 0x01 U+0001 # START OF HEADING (SOH)
@@ -527,7 +521,7 @@ int setfont_main(int argc UNUSED_PARAM, char **argv)
527 } 521 }
528#undef unicodes 522#undef unicodes
529 } 523 }
530#endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP 524# endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
531 525
532 // do set screen map 526 // do set screen map
533 xioctl(fd, mode, map); 527 xioctl(fd, mode, map);
diff --git a/console-tools/loadkmap.c b/console-tools/loadkmap.c
index 839dc2083..404aba1fb 100644
--- a/console-tools/loadkmap.c
+++ b/console-tools/loadkmap.c
@@ -14,7 +14,7 @@
14//config: This program loads a keyboard translation table from 14//config: This program loads a keyboard translation table from
15//config: standard input. 15//config: standard input.
16 16
17//applet:IF_LOADKMAP(APPLET(loadkmap, BB_DIR_SBIN, BB_SUID_DROP)) 17//applet:IF_LOADKMAP(APPLET_NOEXEC(loadkmap, loadkmap, BB_DIR_SBIN, BB_SUID_DROP, loadkmap))
18 18
19//kbuild:lib-$(CONFIG_LOADKMAP) += loadkmap.o 19//kbuild:lib-$(CONFIG_LOADKMAP) += loadkmap.o
20 20
diff --git a/console-tools/openvt.c b/console-tools/openvt.c
index f3db28367..423122fe9 100644
--- a/console-tools/openvt.c
+++ b/console-tools/openvt.c
@@ -99,7 +99,7 @@ static int find_free_vtno(void)
99 /*xfunc_error_retval = 3; - do we need compat? */ 99 /*xfunc_error_retval = 3; - do we need compat? */
100 if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0) 100 if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0)
101 bb_perror_msg_and_die("can't find open VT"); 101 bb_perror_msg_and_die("can't find open VT");
102// Not really needed, grep for DAEMON_ONLY_SANITIZE 102// Not really needed, grep for DAEMON_CLOSE_EXTRA_FDS
103// if (fd > 2) 103// if (fd > 2)
104// close(fd); 104// close(fd);
105 return vtno; 105 return vtno;
@@ -155,7 +155,7 @@ int openvt_main(int argc UNUSED_PARAM, char **argv)
155 /* Grab new VT */ 155 /* Grab new VT */
156 sprintf(vtname, VC_FORMAT, vtno); 156 sprintf(vtname, VC_FORMAT, vtno);
157 /* (Try to) clean up stray open fds above fd 2 */ 157 /* (Try to) clean up stray open fds above fd 2 */
158 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL); 158 bb_daemon_helper(DAEMON_CLOSE_EXTRA_FDS);
159 close(STDIN_FILENO); 159 close(STDIN_FILENO);
160 /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */ 160 /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */
161 xopen(vtname, O_RDWR); 161 xopen(vtname, O_RDWR);
diff --git a/console-tools/reset.c b/console-tools/reset.c
index 04e5b0ca1..f2b900ddb 100644
--- a/console-tools/reset.c
+++ b/console-tools/reset.c
@@ -16,7 +16,7 @@
16//config: This program is used to reset the terminal screen, if it 16//config: This program is used to reset the terminal screen, if it
17//config: gets messed up. 17//config: gets messed up.
18 18
19//applet:IF_RESET(APPLET(reset, BB_DIR_USR_BIN, BB_SUID_DROP)) 19//applet:IF_RESET(APPLET_NOEXEC(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset))
20 20
21//kbuild:lib-$(CONFIG_RESET) += reset.o 21//kbuild:lib-$(CONFIG_RESET) += reset.o
22 22
diff --git a/console-tools/resize.c b/console-tools/resize.c
index 62928a01e..8aa487c41 100644
--- a/console-tools/resize.c
+++ b/console-tools/resize.c
@@ -23,7 +23,8 @@
23//config: E.g.: 23//config: E.g.:
24//config: COLUMNS=80;LINES=44;export COLUMNS LINES; 24//config: COLUMNS=80;LINES=44;export COLUMNS LINES;
25 25
26//applet:IF_RESIZE(APPLET(resize, BB_DIR_USR_BIN, BB_SUID_DROP)) 26//applet:IF_RESIZE(APPLET_NOEXEC(resize, resize, BB_DIR_USR_BIN, BB_SUID_DROP, resize))
27/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */
27 28
28//kbuild:lib-$(CONFIG_RESIZE) += resize.o 29//kbuild:lib-$(CONFIG_RESIZE) += resize.o
29 30
@@ -63,6 +64,7 @@ int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
63 */ 64 */
64 65
65 tcgetattr(STDERR_FILENO, old_termios_p); /* fiddle echo */ 66 tcgetattr(STDERR_FILENO, old_termios_p); /* fiddle echo */
67//TODO: die if the above fails?
66 memcpy(&new, old_termios_p, sizeof(new)); 68 memcpy(&new, old_termios_p, sizeof(new));
67 new.c_cflag |= (CLOCAL | CREAD); 69 new.c_cflag |= (CLOCAL | CREAD);
68 new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 70 new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
diff --git a/console-tools/setconsole.c b/console-tools/setconsole.c
index ad0f756ca..bad2b76e4 100644
--- a/console-tools/setconsole.c
+++ b/console-tools/setconsole.c
@@ -12,23 +12,31 @@
12//config: default y 12//config: default y
13//config: select PLATFORM_LINUX 13//config: select PLATFORM_LINUX
14//config: help 14//config: help
15//config: This program redirects the system console to another device, 15//config: Redirect writes to /dev/console to another device,
16//config: like the current tty while logged in via telnet. 16//config: like the current tty while logged in via telnet.
17//config: This does not redirect kernel log, only writes
18//config: from user space.
17//config: 19//config:
18//config:config FEATURE_SETCONSOLE_LONG_OPTIONS 20//config:config FEATURE_SETCONSOLE_LONG_OPTIONS
19//config: bool "Enable long options" 21//config: bool "Enable long options"
20//config: default y 22//config: default y
21//config: depends on SETCONSOLE && LONG_OPTS 23//config: depends on SETCONSOLE && LONG_OPTS
22 24
23//applet:IF_SETCONSOLE(APPLET(setconsole, BB_DIR_SBIN, BB_SUID_DROP)) 25//applet:IF_SETCONSOLE(APPLET_NOEXEC(setconsole, setconsole, BB_DIR_SBIN, BB_SUID_DROP, setconsole))
24 26
25//kbuild:lib-$(CONFIG_SETCONSOLE) += setconsole.o 27//kbuild:lib-$(CONFIG_SETCONSOLE) += setconsole.o
26 28
27//usage:#define setconsole_trivial_usage 29//usage:#define setconsole_trivial_usage
28//usage: "[-r" IF_FEATURE_SETCONSOLE_LONG_OPTIONS("|--reset") "] [DEVICE]" 30//usage: "[-r] [DEVICE]"
29//usage:#define setconsole_full_usage "\n\n" 31//usage:#define setconsole_full_usage "\n\n"
30//usage: "Redirect system console output to DEVICE (default: /dev/tty)\n" 32//usage: "Make writes to /dev/console appear on DEVICE (default: /dev/tty)."
31//usage: "\n -r Reset output to /dev/console" 33//usage: "\n""Does not redirect kernel log output or reads from /dev/console."
34//usage: "\n"
35//usage: "\n"" -r Reset: writes to /dev/console go to kernel log tty(s)"
36
37/* It was a bbox-specific invention, but SUSE does have a similar utility.
38 * SUSE has no -r option, though.
39 */
32 40
33#include "libbb.h" 41#include "libbb.h"
34 42
@@ -36,17 +44,10 @@ int setconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
36int setconsole_main(int argc UNUSED_PARAM, char **argv) 44int setconsole_main(int argc UNUSED_PARAM, char **argv)
37{ 45{
38 const char *device = CURRENT_TTY; 46 const char *device = CURRENT_TTY;
39 bool reset; 47 int reset;
40 48
41#if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS
42 static const char setconsole_longopts[] ALIGN1 =
43 "reset\0" No_argument "r"
44 ;
45 applet_long_options = setconsole_longopts;
46#endif
47 /* at most one non-option argument */ 49 /* at most one non-option argument */
48 opt_complementary = "?1"; 50 reset = getopt32(argv, "^" "r" "\0" "?1");
49 reset = getopt32(argv, "r");
50 51
51 argv += 1 + reset; 52 argv += 1 + reset;
52 if (*argv) { 53 if (*argv) {
@@ -56,6 +57,9 @@ int setconsole_main(int argc UNUSED_PARAM, char **argv)
56 device = DEV_CONSOLE; 57 device = DEV_CONSOLE;
57 } 58 }
58 59
60//TODO: fails if TIOCCONS redir is already active to some tty.
61//I think SUSE version first does TIOCCONS on /dev/console fd (iow: resets)
62//then TIOCCONS to new tty?
59 xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL); 63 xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL);
60 return EXIT_SUCCESS; 64 return EXIT_SUCCESS;
61} 65}
diff --git a/console-tools/setkeycodes.c b/console-tools/setkeycodes.c
index 543fbe3e0..1363ac9d1 100644
--- a/console-tools/setkeycodes.c
+++ b/console-tools/setkeycodes.c
@@ -16,17 +16,16 @@
16//config: This program loads entries into the kernel's scancode-to-keycode 16//config: This program loads entries into the kernel's scancode-to-keycode
17//config: map, allowing unusual keyboards to generate usable keycodes. 17//config: map, allowing unusual keyboards to generate usable keycodes.
18 18
19//applet:IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP)) 19//applet:IF_SETKEYCODES(APPLET_NOEXEC(setkeycodes, setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP, setkeycodes))
20 20
21//kbuild:lib-$(CONFIG_SETKEYCODES) += setkeycodes.o 21//kbuild:lib-$(CONFIG_SETKEYCODES) += setkeycodes.o
22 22
23//usage:#define setkeycodes_trivial_usage 23//usage:#define setkeycodes_trivial_usage
24//usage: "SCANCODE KEYCODE..." 24//usage: "{ SCANCODE KEYCODE }..."
25//usage:#define setkeycodes_full_usage "\n\n" 25//usage:#define setkeycodes_full_usage "\n\n"
26//usage: "Set entries into the kernel's scancode-to-keycode map,\n" 26//usage: "Modify kernel's scancode-to-keycode map,\n"
27//usage: "allowing unusual keyboards to generate usable keycodes.\n\n" 27//usage: "allowing unusual keyboards to generate usable keycodes.\n\n"
28//usage: "SCANCODE may be either xx or e0xx (hexadecimal),\n" 28//usage: "SCANCODE is either xx or e0xx (hexadecimal), KEYCODE is decimal."
29//usage: "and KEYCODE is given in decimal."
30//usage: 29//usage:
31//usage:#define setkeycodes_example_usage 30//usage:#define setkeycodes_example_usage
32//usage: "$ setkeycodes e030 127\n" 31//usage: "$ setkeycodes e030 127\n"
@@ -45,7 +44,6 @@ int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
45int setkeycodes_main(int argc, char **argv) 44int setkeycodes_main(int argc, char **argv)
46{ 45{
47 int fd; 46 int fd;
48 struct kbkeycode a;
49 47
50 if (!(argc & 1) /* if even */ || argc < 2) { 48 if (!(argc & 1) /* if even */ || argc < 2) {
51 bb_show_usage(); 49 bb_show_usage();
@@ -54,7 +52,10 @@ int setkeycodes_main(int argc, char **argv)
54 fd = get_console_fd_or_die(); 52 fd = get_console_fd_or_die();
55 53
56 while (argv[1]) { 54 while (argv[1]) {
57 int sc = xstrtoul_range(argv[1], 16, 0, 0xe07f); 55 struct kbkeycode a;
56 int sc;
57
58 sc = xstrtoul_range(argv[1], 16, 0, 0xe07f);
58 if (sc >= 0xe000) { 59 if (sc >= 0xe000) {
59 sc -= 0xe000; 60 sc -= 0xe000;
60 sc += 0x0080; 61 sc += 0x0080;
diff --git a/console-tools/setlogcons.c b/console-tools/setlogcons.c
index 1b5814eee..6778a4d2b 100644
--- a/console-tools/setlogcons.c
+++ b/console-tools/setlogcons.c
@@ -15,14 +15,26 @@
15//config: help 15//config: help
16//config: This program redirects the output console of kernel messages. 16//config: This program redirects the output console of kernel messages.
17 17
18//applet:IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP)) 18//applet:IF_SETLOGCONS(APPLET_NOEXEC(setlogcons, setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP, setlogcons))
19 19
20//kbuild:lib-$(CONFIG_SETLOGCONS) += setlogcons.o 20//kbuild:lib-$(CONFIG_SETLOGCONS) += setlogcons.o
21 21
22//usage:#define setlogcons_trivial_usage 22//usage:#define setlogcons_trivial_usage
23//usage: "[N]" 23//usage: "[N]"
24//usage:#define setlogcons_full_usage "\n\n" 24//usage:#define setlogcons_full_usage "\n\n"
25//usage: "Redirect the kernel output to console N. Default:0 (current console)" 25//usage: "Pin kernel output to VT console N. Default:0 (do not pin)"
26
27// Comment from kernel source:
28/* ...
29 * By default, the kernel messages are always printed on the current virtual
30 * console. However, the user may modify that default with the
31 * TIOCL_SETKMSGREDIRECT ioctl call.
32 *
33 * This function sets the kernel message console to be @new. It returns the old
34 * virtual console number. The virtual terminal number 0 (both as parameter and
35 * return value) means no redirection (i.e. always printed on the currently
36 * active console).
37 */
26 38
27#include "libbb.h" 39#include "libbb.h"
28 40
@@ -33,8 +45,8 @@ int setlogcons_main(int argc UNUSED_PARAM, char **argv)
33 char fn; 45 char fn;
34 char subarg; 46 char subarg;
35 } arg = { 47 } arg = {
36 11, /* redirect kernel messages */ 48 11, /* redirect kernel messages (TIOCL_SETKMSGREDIRECT) */
37 0 /* to specified console (current as default) */ 49 0
38 }; 50 };
39 51
40 if (argv[1]) 52 if (argv[1])
diff --git a/coreutils/cat.c b/coreutils/cat.c
index 390254512..7e35fa5ee 100644
--- a/coreutils/cat.c
+++ b/coreutils/cat.c
@@ -170,9 +170,11 @@ int cat_main(int argc UNUSED_PARAM, char **argv)
170{ 170{
171 unsigned opts; 171 unsigned opts;
172 172
173 IF_FEATURE_CATV(opt_complementary = "Aetv"; /* -A == -vet */) 173 opts = getopt32(argv, IF_FEATURE_CATV("^")
174 /* -u is ignored ("unbuffered") */ 174 /* -u is ignored ("unbuffered") */
175 opts = getopt32(argv, IF_FEATURE_CATV("etvA") IF_FEATURE_CATN("nb") "u"); 175 IF_FEATURE_CATV("etvA")IF_FEATURE_CATN("nb")"u"
176 IF_FEATURE_CATV("\0" "Aetv" /* -A == -vet */)
177 );
176 argv += optind; 178 argv += optind;
177 179
178 /* Read from stdin if there's nothing else to do. */ 180 /* Read from stdin if there's nothing else to do. */
diff --git a/coreutils/chmod.c b/coreutils/chmod.c
index 2174334d1..88989bf67 100644
--- a/coreutils/chmod.c
+++ b/coreutils/chmod.c
@@ -123,8 +123,7 @@ int chmod_main(int argc UNUSED_PARAM, char **argv)
123 } 123 }
124 124
125 /* Parse options */ 125 /* Parse options */
126 opt_complementary = "-2"; 126 getopt32(argv, "^" OPT_STR "\0" "-2");
127 getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */
128 argv += optind; 127 argv += optind;
129 128
130 /* Restore option-like mode if needed */ 129 /* Restore option-like mode if needed */
diff --git a/coreutils/chown.c b/coreutils/chown.c
index 1bfc725cc..985d18d6f 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -55,7 +55,7 @@
55/* This is a NOEXEC applet. Be very careful! */ 55/* This is a NOEXEC applet. Be very careful! */
56 56
57 57
58#define OPT_STR ("Rh" IF_DESKTOP("vcfLHP")) 58#define OPT_STR "Rh" IF_DESKTOP("vcfLHP")
59#define BIT_RECURSE 1 59#define BIT_RECURSE 1
60#define OPT_RECURSE (opt & 1) 60#define OPT_RECURSE (opt & 1)
61#define OPT_NODEREF (opt & 2) 61#define OPT_NODEREF (opt & 2)
@@ -128,10 +128,10 @@ int chown_main(int argc UNUSED_PARAM, char **argv)
128 struct param_t param; 128 struct param_t param;
129 129
130#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS 130#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
131 applet_long_options = chown_longopts; 131 opt = getopt32long(argv, "^" OPT_STR "\0" "=2", chown_longopts);
132#else
133 opt = getopt32(argv, "^" OPT_STR "\0" "=2");
132#endif 134#endif
133 opt_complementary = "-2";
134 opt = getopt32(argv, OPT_STR);
135 argv += optind; 135 argv += optind;
136 136
137 /* This matches coreutils behavior (almost - see below) */ 137 /* This matches coreutils behavior (almost - see below) */
diff --git a/coreutils/chroot.c b/coreutils/chroot.c
index 44a587fe0..78751df84 100644
--- a/coreutils/chroot.c
+++ b/coreutils/chroot.c
@@ -11,9 +11,9 @@
11//config: default y 11//config: default y
12//config: help 12//config: help
13//config: chroot is used to change the root directory and run a command. 13//config: chroot is used to change the root directory and run a command.
14//config: The default command is `/bin/sh'. 14//config: The default command is '/bin/sh'.
15 15
16//applet:IF_CHROOT(APPLET(chroot, BB_DIR_USR_SBIN, BB_SUID_DROP)) 16//applet:IF_CHROOT(APPLET_NOEXEC(chroot, chroot, BB_DIR_USR_SBIN, BB_SUID_DROP, chroot))
17 17
18//kbuild:lib-$(CONFIG_CHROOT) += chroot.o 18//kbuild:lib-$(CONFIG_CHROOT) += chroot.o
19 19
@@ -40,6 +40,7 @@ int chroot_main(int argc UNUSED_PARAM, char **argv)
40 ++argv; 40 ++argv;
41 if (!*argv) 41 if (!*argv)
42 bb_show_usage(); 42 bb_show_usage();
43
43 xchroot(*argv); 44 xchroot(*argv);
44 45
45 ++argv; 46 ++argv;
diff --git a/coreutils/cksum.c b/coreutils/cksum.c
index c0cf65d2a..059a33310 100644
--- a/coreutils/cksum.c
+++ b/coreutils/cksum.c
@@ -13,6 +13,7 @@
13//config: cksum is used to calculate the CRC32 checksum of a file. 13//config: cksum is used to calculate the CRC32 checksum of a file.
14 14
15//applet:IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum)) 15//applet:IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum))
16/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */
16 17
17//kbuild:lib-$(CONFIG_CKSUM) += cksum.o 18//kbuild:lib-$(CONFIG_CKSUM) += cksum.o
18 19
diff --git a/coreutils/comm.c b/coreutils/comm.c
index 5be11468c..4bee77677 100644
--- a/coreutils/comm.c
+++ b/coreutils/comm.c
@@ -62,8 +62,7 @@ int comm_main(int argc UNUSED_PARAM, char **argv)
62 int i; 62 int i;
63 int order; 63 int order;
64 64
65 opt_complementary = "=2"; 65 getopt32(argv, "^" "123" "\0" "=2");
66 getopt32(argv, "123");
67 argv += optind; 66 argv += optind;
68 67
69 for (i = 0; i < 2; ++i) { 68 for (i = 0; i < 2; ++i) {
diff --git a/coreutils/cp.c b/coreutils/cp.c
index 092e39583..5b34c27e7 100644
--- a/coreutils/cp.c
+++ b/coreutils/cp.c
@@ -73,15 +73,17 @@ int cp_main(int argc, char **argv)
73#endif 73#endif
74 }; 74 };
75 75
76 // Need at least two arguments
77 // Soft- and hardlinking doesn't mix
78 // -P and -d are the same (-P is POSIX, -d is GNU)
79 // -r and -R are the same
80 // -R (and therefore -r) turns on -d (coreutils does this)
81 // -a = -pdR
82 opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR";
83#if ENABLE_FEATURE_CP_LONG_OPTIONS 76#if ENABLE_FEATURE_CP_LONG_OPTIONS
84 applet_long_options = 77 flags = getopt32long(argv, "^"
78 FILEUTILS_CP_OPTSTR
79 "\0"
80 // Need at least two arguments
81 // Soft- and hardlinking doesn't mix
82 // -P and -d are the same (-P is POSIX, -d is GNU)
83 // -r and -R are the same
84 // -R (and therefore -r) turns on -d (coreutils does this)
85 // -a = -pdR
86 "-2:l--s:s--l:Pd:rRd:Rd:apdR",
85 "archive\0" No_argument "a" 87 "archive\0" No_argument "a"
86 "force\0" No_argument "f" 88 "force\0" No_argument "f"
87 "interactive\0" No_argument "i" 89 "interactive\0" No_argument "i"
@@ -94,9 +96,10 @@ int cp_main(int argc, char **argv)
94 "update\0" No_argument "u" 96 "update\0" No_argument "u"
95 "remove-destination\0" No_argument "\xff" 97 "remove-destination\0" No_argument "\xff"
96 "parents\0" No_argument "\xfe" 98 "parents\0" No_argument "\xfe"
97 ; 99 );
98#endif 100#else
99 flags = getopt32(argv, FILEUTILS_CP_OPTSTR); 101 flags = getopt32(argv, FILEUTILS_CP_OPTSTR);
102#endif
100 /* Options of cp from GNU coreutils 6.10: 103 /* Options of cp from GNU coreutils 6.10:
101 * -a, --archive 104 * -a, --archive
102 * -f, --force 105 * -f, --force
diff --git a/coreutils/cut.c b/coreutils/cut.c
index 6578ce8ce..cdd90ab44 100644
--- a/coreutils/cut.c
+++ b/coreutils/cut.c
@@ -42,7 +42,7 @@
42 42
43 43
44/* option vars */ 44/* option vars */
45static const char optstring[] ALIGN1 = "b:c:f:d:sn"; 45#define OPT_STR "b:c:f:d:sn"
46#define CUT_OPT_BYTE_FLGS (1 << 0) 46#define CUT_OPT_BYTE_FLGS (1 << 0)
47#define CUT_OPT_CHAR_FLGS (1 << 1) 47#define CUT_OPT_CHAR_FLGS (1 << 1)
48#define CUT_OPT_FIELDS_FLGS (1 << 2) 48#define CUT_OPT_FIELDS_FLGS (1 << 2)
@@ -201,8 +201,11 @@ int cut_main(int argc UNUSED_PARAM, char **argv)
201 char *sopt, *ltok; 201 char *sopt, *ltok;
202 unsigned opt; 202 unsigned opt;
203 203
204 opt_complementary = "b--bcf:c--bcf:f--bcf"; 204 opt = getopt32(argv, "^"
205 opt = getopt32(argv, optstring, &sopt, &sopt, &sopt, &ltok); 205 OPT_STR
206 "\0" "b--bcf:c--bcf:f--bcf",
207 &sopt, &sopt, &sopt, &ltok
208 );
206// argc -= optind; 209// argc -= optind;
207 argv += optind; 210 argv += optind;
208 if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) 211 if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS)))
diff --git a/coreutils/date.c b/coreutils/date.c
index 0fb9f1f00..5b15ce778 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -58,7 +58,8 @@
58//config: the same format. With it on, 'date DATE' additionally supports 58//config: the same format. With it on, 'date DATE' additionally supports
59//config: MMDDhhmm[[YY]YY][.ss] format. 59//config: MMDDhhmm[[YY]YY][.ss] format.
60 60
61//applet:IF_DATE(APPLET(date, BB_DIR_BIN, BB_SUID_DROP)) 61//applet:IF_DATE(APPLET_NOEXEC(date, date, BB_DIR_BIN, BB_SUID_DROP, date))
62/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */
62 63
63//kbuild:lib-$(CONFIG_DATE) += date.o 64//kbuild:lib-$(CONFIG_DATE) += date.o
64 65
@@ -66,7 +67,7 @@
66 * date [OPTION]... [+FORMAT] 67 * date [OPTION]... [+FORMAT]
67 * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] 68 * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
68 * -d, --date=STRING 69 * -d, --date=STRING
69 * display time described by STRING, not `now' 70 * display time described by STRING, not 'now'
70 * -f, --file=DATEFILE 71 * -f, --file=DATEFILE
71 * like --date once for each line of DATEFILE 72 * like --date once for each line of DATEFILE
72 * -r, --reference=FILE 73 * -r, --reference=FILE
@@ -152,12 +153,6 @@ enum {
152 OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */ 153 OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
153}; 154};
154 155
155static void maybe_set_utc(int opt)
156{
157 if (opt & OPT_UTC)
158 putenv((char*)"TZ=UTC0");
159}
160
161#if ENABLE_LONG_OPTS 156#if ENABLE_LONG_OPTS
162static const char date_longopts[] ALIGN1 = 157static const char date_longopts[] ALIGN1 =
163 "rfc-822\0" No_argument "R" 158 "rfc-822\0" No_argument "R"
@@ -170,6 +165,19 @@ static const char date_longopts[] ALIGN1 =
170 ; 165 ;
171#endif 166#endif
172 167
168/* We are a NOEXEC applet.
169 * Obstacles to NOFORK:
170 * - we change env
171 * - xasprintf result not freed
172 * - after xasprintf we use other xfuncs
173 */
174
175static void maybe_set_utc(int opt)
176{
177 if (opt & OPT_UTC)
178 putenv((char*)"TZ=UTC0");
179}
180
173int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 181int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
174int date_main(int argc UNUSED_PARAM, char **argv) 182int date_main(int argc UNUSED_PARAM, char **argv)
175{ 183{
@@ -184,13 +192,16 @@ int date_main(int argc UNUSED_PARAM, char **argv)
184 char *filename; 192 char *filename;
185 char *isofmt_arg = NULL; 193 char *isofmt_arg = NULL;
186 194
187 opt_complementary = "d--s:s--d" 195 opt = getopt32long(argv, "^"
188 IF_FEATURE_DATE_ISOFMT(":R--I:I--R"); 196 "Rs:ud:r:"
189 IF_LONG_OPTS(applet_long_options = date_longopts;) 197 IF_FEATURE_DATE_ISOFMT("I::D:")
190 opt = getopt32(argv, "Rs:ud:r:" 198 "\0"
191 IF_FEATURE_DATE_ISOFMT("I::D:"), 199 "d--s:s--d"
200 IF_FEATURE_DATE_ISOFMT(":R--I:I--R"),
201 date_longopts,
192 &date_str, &date_str, &filename 202 &date_str, &date_str, &filename
193 IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt)); 203 IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt)
204 );
194 argv += optind; 205 argv += optind;
195 maybe_set_utc(opt); 206 maybe_set_utc(opt);
196 207
diff --git a/coreutils/dd.c b/coreutils/dd.c
index f7f1c9564..9ea4897d5 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -19,7 +19,7 @@
19//config: default y 19//config: default y
20//config: depends on DD 20//config: depends on DD
21//config: help 21//config: help
22//config: Sending a SIGUSR1 signal to a running `dd' process makes it 22//config: Sending a SIGUSR1 signal to a running 'dd' process makes it
23//config: print to standard error the number of records read and written 23//config: print to standard error the number of records read and written
24//config: so far, then to resume copying. 24//config: so far, then to resume copying.
25//config: 25//config:
diff --git a/coreutils/df.c b/coreutils/df.c
index 27dd2b5a8..121da970b 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -33,7 +33,7 @@
33//config: -i Inodes 33//config: -i Inodes
34//config: -B <SIZE> Blocksize 34//config: -B <SIZE> Blocksize
35 35
36//applet:IF_DF(APPLET(df, BB_DIR_BIN, BB_SUID_DROP)) 36//applet:IF_DF(APPLET_NOEXEC(df, df, BB_DIR_BIN, BB_SUID_DROP, df))
37 37
38//kbuild:lib-$(CONFIG_DF) += df.o 38//kbuild:lib-$(CONFIG_DF) += df.o
39 39
@@ -115,15 +115,18 @@ int df_main(int argc UNUSED_PARAM, char **argv)
115 115
116 init_unicode(); 116 init_unicode();
117 117
118 opt = getopt32(argv, "^"
119 "kPT"
120 IF_FEATURE_DF_FANCY("aiB:")
121 IF_FEATURE_HUMAN_READABLE("hm")
122 "\0"
118#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY 123#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY
119 opt_complementary = "k-mB:m-Bk:B-km"; 124 "k-mB:m-Bk:B-km"
120#elif ENABLE_FEATURE_HUMAN_READABLE 125#elif ENABLE_FEATURE_HUMAN_READABLE
121 opt_complementary = "k-m:m-k"; 126 "k-m:m-k"
122#endif 127#endif
123 opt = getopt32(argv, "kPT" 128 IF_FEATURE_DF_FANCY(, &chp)
124 IF_FEATURE_DF_FANCY("aiB:") 129 );
125 IF_FEATURE_HUMAN_READABLE("hm")
126 IF_FEATURE_DF_FANCY(, &chp));
127 if (opt & OPT_MEGA) 130 if (opt & OPT_MEGA)
128 df_disp_hr = 1024*1024; 131 df_disp_hr = 1024*1024;
129 132
diff --git a/coreutils/dos2unix.c b/coreutils/dos2unix.c
index a3d008051..9f098e41e 100644
--- a/coreutils/dos2unix.c
+++ b/coreutils/dos2unix.c
@@ -120,8 +120,7 @@ int dos2unix_main(int argc UNUSED_PARAM, char **argv)
120 } 120 }
121 121
122 /* -u convert to unix, -d convert to dos */ 122 /* -u convert to unix, -d convert to dos */
123 opt_complementary = "u--d:d--u"; /* mutually exclusive */ 123 o = getopt32(argv, "^" "du" "\0" "u--d:d--u"); /* mutually exclusive */
124 o = getopt32(argv, "du");
125 124
126 /* Do the conversion requested by an argument else do the default 125 /* Do the conversion requested by an argument else do the default
127 * conversion depending on our name. */ 126 * conversion depending on our name. */
diff --git a/coreutils/du.c b/coreutils/du.c
index 947c46e74..d5ce46cf2 100644
--- a/coreutils/du.c
+++ b/coreutils/du.c
@@ -242,8 +242,11 @@ int du_main(int argc UNUSED_PARAM, char **argv)
242 * ignore -a. This is consistent with -s being equivalent to -d 0. 242 * ignore -a. This is consistent with -s being equivalent to -d 0.
243 */ 243 */
244#if ENABLE_FEATURE_HUMAN_READABLE 244#if ENABLE_FEATURE_HUMAN_READABLE
245 opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s"; 245 opt = getopt32(argv, "^"
246 opt = getopt32(argv, "aHkLsx" "d:+" "lc" "hm", &G.max_print_depth); 246 "aHkLsxd:+lchm"
247 "\0" "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s",
248 &G.max_print_depth
249 );
247 argv += optind; 250 argv += optind;
248 if (opt & OPT_h_for_humans) { 251 if (opt & OPT_h_for_humans) {
249 G.disp_unit = 0; 252 G.disp_unit = 0;
@@ -255,8 +258,11 @@ int du_main(int argc UNUSED_PARAM, char **argv)
255 G.disp_unit = 1024; 258 G.disp_unit = 1024;
256 } 259 }
257#else 260#else
258 opt_complementary = "H-L:L-H:s-d:d-s"; 261 opt = getopt32(argv, "^"
259 opt = getopt32(argv, "aHkLsx" "d:+" "lc", &G.max_print_depth); 262 "aHkLsxd:+lc"
263 "\0" "H-L:L-H:s-d:d-s",
264 &G.max_print_depth
265 );
260 argv += optind; 266 argv += optind;
261#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K 267#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
262 if (opt & OPT_k_kbytes) { 268 if (opt & OPT_k_kbytes) {
diff --git a/coreutils/echo.c b/coreutils/echo.c
index af33319a1..e45b90940 100644
--- a/coreutils/echo.c
+++ b/coreutils/echo.c
@@ -218,7 +218,7 @@ int echo_main(int argc UNUSED_PARAM, char **argv)
218 * may be used to endorse or promote products derived from this software 218 * may be used to endorse or promote products derived from this software
219 * without specific prior written permission. 219 * without specific prior written permission.
220 * 220 *
221 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
222 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 222 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
223 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 223 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
224 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 224 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/coreutils/env.c b/coreutils/env.c
index 8def9c2da..20453e871 100644
--- a/coreutils/env.c
+++ b/coreutils/env.c
@@ -30,11 +30,6 @@
30//config: env is used to set an environment variable and run 30//config: env is used to set an environment variable and run
31//config: a command; without options it displays the current 31//config: a command; without options it displays the current
32//config: environment. 32//config: environment.
33//config:
34//config:config FEATURE_ENV_LONG_OPTIONS
35//config: bool "Enable long options"
36//config: default y
37//config: depends on ENV && LONG_OPTS
38 33
39//applet:IF_ENV(APPLET_NOEXEC(env, env, BB_DIR_USR_BIN, BB_SUID_DROP, env)) 34//applet:IF_ENV(APPLET_NOEXEC(env, env, BB_DIR_USR_BIN, BB_SUID_DROP, env))
40 35
@@ -53,23 +48,17 @@
53 48
54#include "libbb.h" 49#include "libbb.h"
55 50
56#if ENABLE_FEATURE_ENV_LONG_OPTIONS
57static const char env_longopts[] ALIGN1 =
58 "ignore-environment\0" No_argument "i"
59 "unset\0" Required_argument "u"
60 ;
61#endif
62
63int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 51int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
64int env_main(int argc UNUSED_PARAM, char **argv) 52int env_main(int argc UNUSED_PARAM, char **argv)
65{ 53{
66 unsigned opts; 54 unsigned opts;
67 llist_t *unset_env = NULL; 55 llist_t *unset_env = NULL;
68 56
69#if ENABLE_FEATURE_ENV_LONG_OPTIONS 57 opts = getopt32long(argv, "+iu:+",
70 applet_long_options = env_longopts; 58 "ignore-environment\0" No_argument "i"
71#endif 59 "unset\0" Required_argument "u"
72 opts = getopt32(argv, "+iu:+", &unset_env); 60 , &unset_env
61 );
73 argv += optind; 62 argv += optind;
74 if (argv[0] && LONE_DASH(argv[0])) { 63 if (argv[0] && LONE_DASH(argv[0])) {
75 opts |= 1; 64 opts |= 1;
@@ -129,7 +118,7 @@ int env_main(int argc UNUSED_PARAM, char **argv)
129 * may be used to endorse or promote products derived from this software 118 * may be used to endorse or promote products derived from this software
130 * without specific prior written permission. 119 * without specific prior written permission.
131 * 120 *
132 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 121 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
133 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 122 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
134 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 123 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
135 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 124 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/coreutils/expand.c b/coreutils/expand.c
index 64f2a539d..91084b80b 100644
--- a/coreutils/expand.c
+++ b/coreutils/expand.c
@@ -26,21 +26,11 @@
26//config: help 26//config: help
27//config: By default, convert all tabs to spaces. 27//config: By default, convert all tabs to spaces.
28//config: 28//config:
29//config:config FEATURE_EXPAND_LONG_OPTIONS
30//config: bool "Enable long options"
31//config: default y
32//config: depends on EXPAND && LONG_OPTS
33//config:
34//config:config UNEXPAND 29//config:config UNEXPAND
35//config: bool "unexpand (6 kb)" 30//config: bool "unexpand (6 kb)"
36//config: default y 31//config: default y
37//config: help 32//config: help
38//config: By default, convert only leading sequences of blanks to tabs. 33//config: By default, convert only leading sequences of blanks to tabs.
39//config:
40//config:config FEATURE_UNEXPAND_LONG_OPTIONS
41//config: bool "Enable long options"
42//config: default y
43//config: depends on UNEXPAND && LONG_OPTS
44 34
45//applet:IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP)) 35//applet:IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP))
46// APPLET_ODDNAME:name main location suid_type help 36// APPLET_ODDNAME:name main location suid_type help
@@ -53,29 +43,16 @@
53//usage: "[-i] [-t N] [FILE]..." 43//usage: "[-i] [-t N] [FILE]..."
54//usage:#define expand_full_usage "\n\n" 44//usage:#define expand_full_usage "\n\n"
55//usage: "Convert tabs to spaces, writing to stdout\n" 45//usage: "Convert tabs to spaces, writing to stdout\n"
56//usage: IF_FEATURE_EXPAND_LONG_OPTIONS(
57//usage: "\n -i,--initial Don't convert tabs after non blanks"
58//usage: "\n -t,--tabs N Tabstops every N chars"
59//usage: )
60//usage: IF_NOT_FEATURE_EXPAND_LONG_OPTIONS(
61//usage: "\n -i Don't convert tabs after non blanks" 46//usage: "\n -i Don't convert tabs after non blanks"
62//usage: "\n -t Tabstops every N chars" 47//usage: "\n -t Tabstops every N chars"
63//usage: )
64 48
65//usage:#define unexpand_trivial_usage 49//usage:#define unexpand_trivial_usage
66//usage: "[-fa][-t N] [FILE]..." 50//usage: "[-fa][-t N] [FILE]..."
67//usage:#define unexpand_full_usage "\n\n" 51//usage:#define unexpand_full_usage "\n\n"
68//usage: "Convert spaces to tabs, writing to stdout\n" 52//usage: "Convert spaces to tabs, writing to stdout\n"
69//usage: IF_FEATURE_UNEXPAND_LONG_OPTIONS(
70//usage: "\n -a,--all Convert all blanks"
71//usage: "\n -f,--first-only Convert only leading blanks"
72//usage: "\n -t,--tabs N Tabstops every N chars"
73//usage: )
74//usage: IF_NOT_FEATURE_UNEXPAND_LONG_OPTIONS(
75//usage: "\n -a Convert all blanks" 53//usage: "\n -a Convert all blanks"
76//usage: "\n -f Convert only leading blanks" 54//usage: "\n -f Convert only leading blanks"
77//usage: "\n -t N Tabstops every N chars" 55//usage: "\n -t N Tabstops every N chars"
78//usage: )
79 56
80#include "libbb.h" 57#include "libbb.h"
81#include "unicode.h" 58#include "unicode.h"
@@ -188,31 +165,24 @@ int expand_main(int argc UNUSED_PARAM, char **argv)
188 unsigned opt; 165 unsigned opt;
189 int exit_status = EXIT_SUCCESS; 166 int exit_status = EXIT_SUCCESS;
190 167
191#if ENABLE_FEATURE_EXPAND_LONG_OPTIONS
192 static const char expand_longopts[] ALIGN1 =
193 /* name, has_arg, val */
194 "initial\0" No_argument "i"
195 "tabs\0" Required_argument "t"
196 ;
197#endif
198#if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS
199 static const char unexpand_longopts[] ALIGN1 =
200 /* name, has_arg, val */
201 "first-only\0" No_argument "i"
202 "tabs\0" Required_argument "t"
203 "all\0" No_argument "a"
204 ;
205#endif
206 init_unicode(); 168 init_unicode();
207 169
208 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) { 170 if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
209 IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts); 171 opt = getopt32long(argv, "it:",
210 opt = getopt32(argv, "it:", &opt_t); 172 "initial\0" No_argument "i"
173 "tabs\0" Required_argument "t"
174 , &opt_t
175 );
211 } else { 176 } else {
212 IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts); 177 opt = getopt32long(argv, "^"
213 /* -t NUM sets also -a */ 178 "ft:a"
214 opt_complementary = "ta"; 179 "\0"
215 opt = getopt32(argv, "ft:a", &opt_t); 180 "ta" /* -t NUM sets -a */,
181 "first-only\0" No_argument "i"
182 "tabs\0" Required_argument "t"
183 "all\0" No_argument "a"
184 , &opt_t
185 );
216 /* -f --first-only is the default */ 186 /* -f --first-only is the default */
217 if (!(opt & OPT_ALL)) opt |= OPT_INITIAL; 187 if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
218 } 188 }
diff --git a/coreutils/expr.c b/coreutils/expr.c
index 0cf2b9bd9..00bcf60d4 100644
--- a/coreutils/expr.c
+++ b/coreutils/expr.c
@@ -38,7 +38,7 @@
38//config: the applet slightly larger, but will allow computation with very 38//config: the applet slightly larger, but will allow computation with very
39//config: large numbers. 39//config: large numbers.
40 40
41//applet:IF_EXPR(APPLET(expr, BB_DIR_USR_BIN, BB_SUID_DROP)) 41//applet:IF_EXPR(APPLET_NOEXEC(expr, expr, BB_DIR_USR_BIN, BB_SUID_DROP, expr))
42 42
43//kbuild:lib-$(CONFIG_EXPR) += expr.o 43//kbuild:lib-$(CONFIG_EXPR) += expr.o
44 44
@@ -118,7 +118,10 @@ struct globals {
118 char **args; 118 char **args;
119} FIX_ALIASING; 119} FIX_ALIASING;
120#define G (*(struct globals*)bb_common_bufsiz1) 120#define G (*(struct globals*)bb_common_bufsiz1)
121#define INIT_G() do { setup_common_bufsiz(); } while (0) 121#define INIT_G() do { \
122 setup_common_bufsiz(); \
123 /* NB: noexec applet - globals not zeroed */ \
124} while (0)
122 125
123/* forward declarations */ 126/* forward declarations */
124static VALUE *eval(void); 127static VALUE *eval(void);
diff --git a/coreutils/id.c b/coreutils/id.c
index 6043bca61..5a7fb9aaf 100644
--- a/coreutils/id.c
+++ b/coreutils/id.c
@@ -170,9 +170,12 @@ int id_main(int argc UNUSED_PARAM, char **argv)
170 } else { 170 } else {
171 /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/ 171 /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
172 /* Don't allow more than one username */ 172 /* Don't allow more than one username */
173 opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" 173 opt = getopt32(argv, "^"
174 IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G"); 174 "rnugG" IF_SELINUX("Z")
175 opt = getopt32(argv, "rnugG" IF_SELINUX("Z")); 175 "\0"
176 "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
177 IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G")
178 );
176 } 179 }
177 180
178 username = argv[optind]; 181 username = argv[optind];
diff --git a/coreutils/install.c b/coreutils/install.c
index a1342bb13..2e4dc257f 100644
--- a/coreutils/install.c
+++ b/coreutils/install.c
@@ -55,12 +55,17 @@ static const char install_longopts[] ALIGN1 =
55 "target-directory\0" Required_argument "t" 55 "target-directory\0" Required_argument "t"
56/* autofs build insists of using -b --suffix=.orig */ 56/* autofs build insists of using -b --suffix=.orig */
57/* TODO? (short option for --suffix is -S) */ 57/* TODO? (short option for --suffix is -S) */
58#if ENABLE_SELINUX 58# if ENABLE_SELINUX
59 "context\0" Required_argument "Z" 59 "context\0" Required_argument "Z"
60 "preserve_context\0" No_argument "\xff" 60 "preserve_context\0" No_argument "\xff"
61 "preserve-context\0" No_argument "\xff" 61 "preserve-context\0" No_argument "\xff"
62#endif 62# endif
63 ; 63 ;
64# define GETOPT32 getopt32long
65# define LONGOPTS install_longopts,
66#else
67# define GETOPT32 getopt32
68# define LONGOPTS
64#endif 69#endif
65 70
66 71
@@ -135,15 +140,17 @@ int install_main(int argc, char **argv)
135#endif 140#endif
136 }; 141 };
137 142
138#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
139 applet_long_options = install_longopts;
140#endif
141 opt_complementary = "t--d:d--t:s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z"));
142 /* -c exists for backwards compatibility, it's needed */ 143 /* -c exists for backwards compatibility, it's needed */
143 /* -b is ignored ("make a backup of each existing destination file") */ 144 /* -b is ignored ("make a backup of each existing destination file") */
144 opts = getopt32(argv, "cvb" "Ddpsg:m:o:t:" IF_SELINUX("Z:"), 145 opts = GETOPT32(argv, "^"
145 &gid_str, &mode_str, &uid_str, &last 146 "cvb" "Ddpsg:m:o:t:" IF_SELINUX("Z:")
146 IF_SELINUX(, &scontext)); 147 "\0"
148 "t--d:d--t:s--d:d--s"
149 IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")),
150 LONGOPTS
151 &gid_str, &mode_str, &uid_str, &last
152 IF_SELINUX(, &scontext)
153 );
147 argc -= optind; 154 argc -= optind;
148 argv += optind; 155 argv += optind;
149 156
diff --git a/coreutils/link.c b/coreutils/link.c
index 6e20dafe3..d8d583b7b 100644
--- a/coreutils/link.c
+++ b/coreutils/link.c
@@ -27,14 +27,12 @@
27int link_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 27int link_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
28int link_main(int argc UNUSED_PARAM, char **argv) 28int link_main(int argc UNUSED_PARAM, char **argv)
29{ 29{
30 opt_complementary = "=2"; /* exactly 2 params */ 30 getopt32(argv, "^" "" "\0" "=2");
31 getopt32(argv, "");
32 argv += optind; 31 argv += optind;
33 if (link(argv[0], argv[1]) != 0) { 32 if (link(argv[0], argv[1]) != 0) {
34 /* shared message */ 33 /* shared message */
35 bb_perror_msg_and_die("can't create %slink " 34 bb_perror_msg_and_die("can't create %slink '%s' to '%s'",
36 "'%s' to '%s'", "hard", 35 "hard", argv[1], argv[0]
37 argv[1], argv[0]
38 ); 36 );
39 } 37 }
40 return EXIT_SUCCESS; 38 return EXIT_SUCCESS;
diff --git a/coreutils/ln.c b/coreutils/ln.c
index fed96af42..2dda5dae9 100644
--- a/coreutils/ln.c
+++ b/coreutils/ln.c
@@ -62,8 +62,7 @@ int ln_main(int argc, char **argv)
62 struct stat statbuf; 62 struct stat statbuf;
63 int (*link_func)(const char *, const char *); 63 int (*link_func)(const char *, const char *);
64 64
65 opt_complementary = "-1"; /* min one arg */ 65 opts = getopt32(argv, "^" "sfnbS:vT" "\0" "-1", &suffix);
66 opts = getopt32(argv, "sfnbS:vT", &suffix);
67 66
68 last = argv[argc - 1]; 67 last = argv[argc - 1];
69 argv += optind; 68 argv += optind;
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 4c0944bb0..9f0462936 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -206,18 +206,18 @@ SPLIT_SUBDIR = 2,
206/* -SXvhTw GNU options, busybox optionally supports */ 206/* -SXvhTw GNU options, busybox optionally supports */
207/* -T WIDTH Ignored (we don't use tabs on output) */ 207/* -T WIDTH Ignored (we don't use tabs on output) */
208/* -Z SELinux mandated option, busybox optionally supports */ 208/* -Z SELinux mandated option, busybox optionally supports */
209static const char ls_options[] ALIGN1 = 209#define ls_options \
210 "Cadi1lgnsxAk" /* 12 opts, total 12 */ 210 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \
211 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ 211 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \
212 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ 212 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \
213 IF_SELINUX("Z") /* 1, 16 */ 213 IF_SELINUX("Z") /* 1, 16 */ \
214 "Q" /* 1, 17 */ 214 "Q" /* 1, 17 */ \
215 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ 215 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \
216 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ 216 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \
217 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ 217 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \
218 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ 218 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \
219 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ 219 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */
220; 220
221enum { 221enum {
222 OPT_C = (1 << 0), 222 OPT_C = (1 << 0),
223 OPT_a = (1 << 1), 223 OPT_a = (1 << 1),
@@ -1093,25 +1093,26 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1093#endif 1093#endif
1094 1094
1095 /* process options */ 1095 /* process options */
1096 IF_LONG_OPTS(applet_long_options = ls_longopts;) 1096 opt = getopt32long(argv, "^"
1097 opt_complementary = 1097 ls_options
1098 /* -n and -g imply -l */ 1098 "\0"
1099 "nl:gl" 1099 /* -n and -g imply -l */
1100 /* --full-time implies -l */ 1100 "nl:gl"
1101 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l")) 1101 /* --full-time implies -l */
1102 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: 1102 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
1103 * in some pairs of opts, only last one takes effect: 1103 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1104 */ 1104 * in some pairs of opts, only last one takes effect:
1105 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */ 1105 */
1106 // ":m-l:l-m" - we don't have -m 1106 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1107 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H") 1107 // ":m-l:l-m" - we don't have -m
1108 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */ 1108 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1109 ":C-1:1-C" /* bycols/oneline */ 1109 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1110 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */ 1110 ":C-1:1-C" /* bycols/oneline */
1111 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */ 1111 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1112 /* -w NUM: */ 1112 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1113 IF_FEATURE_LS_WIDTH(":w+"); 1113 /* -w NUM: */
1114 opt = getopt32(argv, ls_options 1114 IF_FEATURE_LS_WIDTH(":w+")
1115 , ls_longopts
1115 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width) 1116 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
1116 IF_FEATURE_LS_COLOR(, &color_opt) 1117 IF_FEATURE_LS_COLOR(, &color_opt)
1117 ); 1118 );
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c
index bcccdd64f..89d6cec0b 100644
--- a/coreutils/md5_sha1_sum.c
+++ b/coreutils/md5_sha1_sum.c
@@ -258,15 +258,14 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
258#endif 258#endif
259 259
260 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) { 260 if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) {
261 /* -s and -w require -c */
262 opt_complementary = "s?c:w?c";
263 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ 261 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */
262 /* -s and -w require -c */
264#if ENABLE_SHA3SUM 263#if ENABLE_SHA3SUM
265 if (applet_name[3] == HASH_SHA3) 264 if (applet_name[3] == HASH_SHA3)
266 flags = getopt32(argv, "scwbta:+", &sha3_width); 265 flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width);
267 else 266 else
268#endif 267#endif
269 flags = getopt32(argv, "scwbt"); 268 flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c");
270 } else { 269 } else {
271#if ENABLE_SHA3SUM 270#if ENABLE_SHA3SUM
272 if (applet_name[3] == HASH_SHA3) 271 if (applet_name[3] == HASH_SHA3)
diff --git a/coreutils/mkdir.c b/coreutils/mkdir.c
index 22851187c..986353dc6 100644
--- a/coreutils/mkdir.c
+++ b/coreutils/mkdir.c
@@ -18,11 +18,6 @@
18//config: default y 18//config: default y
19//config: help 19//config: help
20//config: mkdir is used to create directories with the specified names. 20//config: mkdir is used to create directories with the specified names.
21//config:
22//config:config FEATURE_MKDIR_LONG_OPTIONS
23//config: bool "Enable long options"
24//config: default y
25//config: depends on MKDIR && LONG_OPTS
26 21
27//applet:IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir)) 22//applet:IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir))
28 23
@@ -53,19 +48,6 @@
53 48
54/* This is a NOFORK applet. Be very careful! */ 49/* This is a NOFORK applet. Be very careful! */
55 50
56#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS
57static const char mkdir_longopts[] ALIGN1 =
58 "mode\0" Required_argument "m"
59 "parents\0" No_argument "p"
60#if ENABLE_SELINUX
61 "context\0" Required_argument "Z"
62#endif
63#if ENABLE_FEATURE_VERBOSE
64 "verbose\0" No_argument "v"
65#endif
66 ;
67#endif
68
69int mkdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 51int mkdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
70int mkdir_main(int argc UNUSED_PARAM, char **argv) 52int mkdir_main(int argc UNUSED_PARAM, char **argv)
71{ 53{
@@ -78,10 +60,17 @@ int mkdir_main(int argc UNUSED_PARAM, char **argv)
78 security_context_t scontext; 60 security_context_t scontext;
79#endif 61#endif
80 62
81#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS 63 opt = getopt32long(argv, "m:pv" IF_SELINUX("Z:"),
82 applet_long_options = mkdir_longopts; 64 "mode\0" Required_argument "m"
83#endif 65 "parents\0" No_argument "p"
84 opt = getopt32(argv, "m:pv" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext)); 66# if ENABLE_SELINUX
67 "context\0" Required_argument "Z"
68# endif
69# if ENABLE_FEATURE_VERBOSE
70 "verbose\0" No_argument "v"
71# endif
72 , &smode IF_SELINUX(,&scontext)
73 );
85 if (opt & 1) { 74 if (opt & 1) {
86 mode_t mmode = bb_parse_mode(smode, 0777); 75 mode_t mmode = bb_parse_mode(smode, 0777);
87 if (mmode == (mode_t)-1) { 76 if (mmode == (mode_t)-1) {
diff --git a/coreutils/mktemp.c b/coreutils/mktemp.c
index bfef0b4a6..d4ff883fa 100644
--- a/coreutils/mktemp.c
+++ b/coreutils/mktemp.c
@@ -36,7 +36,7 @@
36//config: help 36//config: help
37//config: mktemp is used to create unique temporary files 37//config: mktemp is used to create unique temporary files
38 38
39//applet:IF_MKTEMP(APPLET(mktemp, BB_DIR_BIN, BB_SUID_DROP)) 39//applet:IF_MKTEMP(APPLET_NOEXEC(mktemp, mktemp, BB_DIR_BIN, BB_SUID_DROP, mktemp))
40 40
41//kbuild:lib-$(CONFIG_MKTEMP) += mktemp.o 41//kbuild:lib-$(CONFIG_MKTEMP) += mktemp.o
42 42
@@ -80,8 +80,7 @@ int mktemp_main(int argc UNUSED_PARAM, char **argv)
80 if (!path || path[0] == '\0') 80 if (!path || path[0] == '\0')
81 path = "/tmp"; 81 path = "/tmp";
82 82
83 opt_complementary = "?1"; /* 1 argument max */ 83 opts = getopt32(argv, "^" "dqtp:u" "\0" "?1"/*1 arg max*/, &path);
84 opts = getopt32(argv, "dqtp:u", &path);
85 84
86 chp = argv[optind]; 85 chp = argv[optind];
87 if (!chp) { 86 if (!chp) {
diff --git a/coreutils/mv.c b/coreutils/mv.c
index 147dd3bb2..10cbc506f 100644
--- a/coreutils/mv.c
+++ b/coreutils/mv.c
@@ -16,11 +16,6 @@
16//config: default y 16//config: default y
17//config: help 17//config: help
18//config: mv is used to move or rename files or directories. 18//config: mv is used to move or rename files or directories.
19//config:
20//config:config FEATURE_MV_LONG_OPTIONS
21//config: bool "Enable long options"
22//config: default y
23//config: depends on MV && LONG_OPTS
24 19
25//applet:IF_MV(APPLET(mv, BB_DIR_BIN, BB_SUID_DROP)) 20//applet:IF_MV(APPLET(mv, BB_DIR_BIN, BB_SUID_DROP))
26 21
@@ -41,23 +36,6 @@
41#include "libbb.h" 36#include "libbb.h"
42#include "libcoreutils/coreutils.h" 37#include "libcoreutils/coreutils.h"
43 38
44#if ENABLE_FEATURE_MV_LONG_OPTIONS
45static const char mv_longopts[] ALIGN1 =
46 "interactive\0" No_argument "i"
47 "force\0" No_argument "f"
48 "no-clobber\0" No_argument "n"
49 IF_FEATURE_VERBOSE(
50 "verbose\0" No_argument "v"
51 )
52 ;
53#endif
54
55#define OPT_FORCE (1 << 0)
56#define OPT_INTERACTIVE (1 << 1)
57#define OPT_NOCLOBBER (1 << 2)
58#define OPT_VERBOSE ((1 << 3) * ENABLE_FEATURE_VERBOSE)
59
60
61int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 39int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
62int mv_main(int argc, char **argv) 40int mv_main(int argc, char **argv)
63{ 41{
@@ -69,15 +47,25 @@ int mv_main(int argc, char **argv)
69 int status = 0; 47 int status = 0;
70 int copy_flag = 0; 48 int copy_flag = 0;
71 49
72#if ENABLE_FEATURE_MV_LONG_OPTIONS 50#define OPT_FORCE (1 << 0)
73 applet_long_options = mv_longopts; 51#define OPT_INTERACTIVE (1 << 1)
74#endif 52#define OPT_NOCLOBBER (1 << 2)
53#define OPT_VERBOSE ((1 << 3) * ENABLE_FEATURE_VERBOSE)
75 /* Need at least two arguments. 54 /* Need at least two arguments.
76 * If more than one of -f, -i, -n is specified , only the final one 55 * If more than one of -f, -i, -n is specified , only the final one
77 * takes effect (it unsets previous options). 56 * takes effect (it unsets previous options).
78 */ 57 */
79 opt_complementary = "-2:f-in:i-fn:n-fi"; 58 flags = getopt32long(argv, "^"
80 flags = getopt32(argv, "finv"); 59 "finv"
60 "\0"
61 "-2:f-in:i-fn:n-fi",
62 "interactive\0" No_argument "i"
63 "force\0" No_argument "f"
64 "no-clobber\0" No_argument "n"
65 IF_FEATURE_VERBOSE(
66 "verbose\0" No_argument "v"
67 )
68 );
81 argc -= optind; 69 argc -= optind;
82 argv += optind; 70 argv += optind;
83 last = argv[argc - 1]; 71 last = argv[argc - 1];
diff --git a/coreutils/nice.c b/coreutils/nice.c
index 0bf055299..d6818cf00 100644
--- a/coreutils/nice.c
+++ b/coreutils/nice.c
@@ -12,7 +12,7 @@
12//config: help 12//config: help
13//config: nice runs a program with modified scheduling priority. 13//config: nice runs a program with modified scheduling priority.
14 14
15//applet:IF_NICE(APPLET(nice, BB_DIR_BIN, BB_SUID_DROP)) 15//applet:IF_NICE(APPLET_NOEXEC(nice, nice, BB_DIR_BIN, BB_SUID_DROP, nice))
16 16
17//kbuild:lib-$(CONFIG_NICE) += nice.o 17//kbuild:lib-$(CONFIG_NICE) += nice.o
18 18
diff --git a/coreutils/nl.c b/coreutils/nl.c
index 93e78c490..c2f8b1042 100644
--- a/coreutils/nl.c
+++ b/coreutils/nl.c
@@ -57,14 +57,13 @@ int nl_main(int argc UNUSED_PARAM, char **argv)
57 "starting-line-number\0"Required_argument "v" 57 "starting-line-number\0"Required_argument "v"
58 "number-width\0" Required_argument "w" 58 "number-width\0" Required_argument "w"
59 ; 59 ;
60
61 applet_long_options = nl_longopts;
62#endif 60#endif
63 ns.width = 6; 61 ns.width = 6;
64 ns.start = 1; 62 ns.start = 1;
65 ns.inc = 1; 63 ns.inc = 1;
66 ns.sep = "\t"; 64 ns.sep = "\t";
67 getopt32(argv, "pw:+s:v:+i:+b:", &ns.width, &ns.sep, &ns.start, &ns.inc, &opt_b); 65 getopt32long(argv, "pw:+s:v:+i:+b:", nl_longopts,
66 &ns.width, &ns.sep, &ns.start, &ns.inc, &opt_b);
68 ns.all = (opt_b[0] == 'a'); 67 ns.all = (opt_b[0] == 'a');
69 ns.nonempty = (opt_b[0] == 't'); 68 ns.nonempty = (opt_b[0] == 't');
70 ns.empty_str = xasprintf("%*s\n", ns.width + (int)strlen(ns.sep), ""); 69 ns.empty_str = xasprintf("%*s\n", ns.width + (int)strlen(ns.sep), "");
diff --git a/coreutils/nohup.c b/coreutils/nohup.c
index 8e28f9029..8a70ec4df 100644
--- a/coreutils/nohup.c
+++ b/coreutils/nohup.c
@@ -15,7 +15,7 @@
15//config: help 15//config: help
16//config: run a command immune to hangups, with output to a non-tty. 16//config: run a command immune to hangups, with output to a non-tty.
17 17
18//applet:IF_NOHUP(APPLET(nohup, BB_DIR_USR_BIN, BB_SUID_DROP)) 18//applet:IF_NOHUP(APPLET_NOEXEC(nohup, nohup, BB_DIR_USR_BIN, BB_SUID_DROP, nohup))
19 19
20//kbuild:lib-$(CONFIG_NOHUP) += nohup.o 20//kbuild:lib-$(CONFIG_NOHUP) += nohup.o
21 21
@@ -31,12 +31,12 @@
31 31
32/* Compat info: nohup (GNU coreutils 6.8) does this: 32/* Compat info: nohup (GNU coreutils 6.8) does this:
33# nohup true 33# nohup true
34nohup: ignoring input and appending output to `nohup.out' 34nohup: ignoring input and appending output to 'nohup.out'
35# nohup true 1>/dev/null 35# nohup true 1>/dev/null
36nohup: ignoring input and redirecting stderr to stdout 36nohup: ignoring input and redirecting stderr to stdout
37# nohup true 2>zz 37# nohup true 2>zz
38# cat zz 38# cat zz
39nohup: ignoring input and appending output to `nohup.out' 39nohup: ignoring input and appending output to 'nohup.out'
40# nohup true 2>zz 1>/dev/null 40# nohup true 2>zz 1>/dev/null
41# cat zz 41# cat zz
42nohup: ignoring input 42nohup: ignoring input
diff --git a/coreutils/nproc.c b/coreutils/nproc.c
index 68a831865..336b176ca 100644
--- a/coreutils/nproc.c
+++ b/coreutils/nproc.c
@@ -9,7 +9,7 @@
9//config: help 9//config: help
10//config: Print number of CPUs 10//config: Print number of CPUs
11 11
12//applet:IF_NPROC(APPLET(nproc, BB_DIR_USR_BIN, BB_SUID_DROP)) 12//applet:IF_NPROC(APPLET_NOFORK(nproc, nproc, BB_DIR_USR_BIN, BB_SUID_DROP, nproc))
13 13
14//kbuild:lib-$(CONFIG_NPROC) += nproc.o 14//kbuild:lib-$(CONFIG_NPROC) += nproc.o
15 15
@@ -28,7 +28,6 @@ int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
28 unsigned long mask[1024]; 28 unsigned long mask[1024];
29 unsigned i, count = 0; 29 unsigned i, count = 0;
30 30
31 //applet_long_options = ...;
32 //getopt32(argv, ""); 31 //getopt32(argv, "");
33 32
34 //if --all, count /sys/devices/system/cpu/cpuN dirs, else: 33 //if --all, count /sys/devices/system/cpu/cpuN dirs, else:
diff --git a/coreutils/od.c b/coreutils/od.c
index e3a68435b..9a888dd5f 100644
--- a/coreutils/od.c
+++ b/coreutils/od.c
@@ -223,7 +223,7 @@ int od_main(int argc, char **argv)
223 * may be used to endorse or promote products derived from this software 223 * may be used to endorse or promote products derived from this software
224 * without specific prior written permission. 224 * without specific prior written permission.
225 * 225 *
226 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 226 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
227 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 227 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
228 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 229 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
index 513c8ef37..df7354b7b 100644
--- a/coreutils/od_bloaty.c
+++ b/coreutils/od_bloaty.c
@@ -61,8 +61,8 @@ enum {
61 OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, 61 OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
62}; 62};
63 63
64#define OD_GETOPT32() getopt32(argv, \ 64#define OD_GETOPT32() getopt32long(argv, \
65 "A:N:abcdfhij:lot:*vxsS:w:+:", \ 65 "A:N:abcdfhij:lot:*vxsS:w:+:", od_longopts, \
66 /* -w with optional param */ \ 66 /* -w with optional param */ \
67 /* -S was -s and also had optional parameter */ \ 67 /* -S was -s and also had optional parameter */ \
68 /* but in coreutils 6.3 it was renamed and now has */ \ 68 /* but in coreutils 6.3 it was renamed and now has */ \
@@ -1220,9 +1220,6 @@ int od_main(int argc UNUSED_PARAM, char **argv)
1220 address_pad_len_char = '7'; 1220 address_pad_len_char = '7';
1221 1221
1222 /* Parse command line */ 1222 /* Parse command line */
1223#if ENABLE_LONG_OPTS
1224 applet_long_options = od_longopts;
1225#endif
1226 opt = OD_GETOPT32(); 1223 opt = OD_GETOPT32();
1227 argv += optind; 1224 argv += optind;
1228 if (opt & OPT_A) { 1225 if (opt & OPT_A) {
diff --git a/coreutils/printf.c b/coreutils/printf.c
index d3fc72adb..d1ff183d0 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -43,7 +43,7 @@
43//config: default y 43//config: default y
44//config: help 44//config: help
45//config: printf is used to format and print specified strings. 45//config: printf is used to format and print specified strings.
46//config: It's similar to `echo' except it has more options. 46//config: It's similar to 'echo' except it has more options.
47 47
48//applet:IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf)) 48//applet:IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf))
49 49
diff --git a/coreutils/readlink.c b/coreutils/readlink.c
index 9690290e3..b8e327d11 100644
--- a/coreutils/readlink.c
+++ b/coreutils/readlink.c
@@ -20,7 +20,7 @@
20//config: help 20//config: help
21//config: Enable the readlink option (-f). 21//config: Enable the readlink option (-f).
22 22
23//applet:IF_READLINK(APPLET(readlink, BB_DIR_USR_BIN, BB_SUID_DROP)) 23//applet:IF_READLINK(APPLET_NOFORK(readlink, readlink, BB_DIR_USR_BIN, BB_SUID_DROP, readlink))
24 24
25//kbuild:lib-$(CONFIG_READLINK) += readlink.o 25//kbuild:lib-$(CONFIG_READLINK) += readlink.o
26 26
@@ -71,8 +71,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
71 IF_FEATURE_READLINK_FOLLOW( 71 IF_FEATURE_READLINK_FOLLOW(
72 unsigned opt; 72 unsigned opt;
73 /* We need exactly one non-option argument. */ 73 /* We need exactly one non-option argument. */
74 opt_complementary = "=1"; 74 opt = getopt32(argv, "^" "fnvsq" "\0" "=1");
75 opt = getopt32(argv, "fnvsq");
76 fname = argv[optind]; 75 fname = argv[optind];
77 ) 76 )
78 IF_NOT_FEATURE_READLINK_FOLLOW( 77 IF_NOT_FEATURE_READLINK_FOLLOW(
@@ -85,6 +84,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
85 if (!(opt & 4)) /* not -v */ 84 if (!(opt & 4)) /* not -v */
86 logmode = LOGMODE_NONE; 85 logmode = LOGMODE_NONE;
87 86
87 /* NOFORK: only one alloc is allowed; must free */
88 if (opt & 1) { /* -f */ 88 if (opt & 1) { /* -f */
89 buf = xmalloc_realpath(fname); 89 buf = xmalloc_realpath(fname);
90 } else { 90 } else {
@@ -94,9 +94,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
94 if (!buf) 94 if (!buf)
95 return EXIT_FAILURE; 95 return EXIT_FAILURE;
96 printf((opt & 2) ? "%s" : "%s\n", buf); 96 printf((opt & 2) ? "%s" : "%s\n", buf);
97 97 free(buf);
98 if (ENABLE_FEATURE_CLEAN_UP)
99 free(buf);
100 98
101 fflush_stdout_and_exit(EXIT_SUCCESS); 99 fflush_stdout_and_exit(EXIT_SUCCESS);
102} 100}
diff --git a/coreutils/realpath.c b/coreutils/realpath.c
index 6a61c3dc8..f9c630135 100644
--- a/coreutils/realpath.c
+++ b/coreutils/realpath.c
@@ -13,7 +13,7 @@
13//config: Return the canonicalized absolute pathname. 13//config: Return the canonicalized absolute pathname.
14//config: This isn't provided by GNU shellutils, but where else does it belong. 14//config: This isn't provided by GNU shellutils, but where else does it belong.
15 15
16//applet:IF_REALPATH(APPLET(realpath, BB_DIR_USR_BIN, BB_SUID_DROP)) 16//applet:IF_REALPATH(APPLET_NOFORK(realpath, realpath, BB_DIR_USR_BIN, BB_SUID_DROP, realpath))
17 17
18//kbuild:lib-$(CONFIG_REALPATH) += realpath.o 18//kbuild:lib-$(CONFIG_REALPATH) += realpath.o
19 19
@@ -36,6 +36,7 @@ int realpath_main(int argc UNUSED_PARAM, char **argv)
36 } 36 }
37 37
38 do { 38 do {
39 /* NOFORK: only one alloc is allowed; must free */
39 char *resolved_path = xmalloc_realpath(*argv); 40 char *resolved_path = xmalloc_realpath(*argv);
40 if (resolved_path != NULL) { 41 if (resolved_path != NULL) {
41 puts(resolved_path); 42 puts(resolved_path);
diff --git a/coreutils/rm.c b/coreutils/rm.c
index f91c94570..b68a82dc4 100644
--- a/coreutils/rm.c
+++ b/coreutils/rm.c
@@ -16,7 +16,8 @@
16//config: help 16//config: help
17//config: rm is used to remove files or directories. 17//config: rm is used to remove files or directories.
18 18
19//applet:IF_RM(APPLET_NOFORK(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm)) 19//applet:IF_RM(APPLET_NOEXEC(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm))
20/* was NOFORK, but then "rm -i FILE" can't be ^C'ed if run by hush */
20 21
21//kbuild:lib-$(CONFIG_RM) += rm.o 22//kbuild:lib-$(CONFIG_RM) += rm.o
22 23
@@ -36,7 +37,7 @@
36 37
37#include "libbb.h" 38#include "libbb.h"
38 39
39/* This is a NOFORK applet. Be very careful! */ 40/* This is a NOEXEC applet. Be very careful! */
40 41
41int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 42int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
42int rm_main(int argc UNUSED_PARAM, char **argv) 43int rm_main(int argc UNUSED_PARAM, char **argv)
@@ -45,8 +46,7 @@ int rm_main(int argc UNUSED_PARAM, char **argv)
45 int flags = 0; 46 int flags = 0;
46 unsigned opt; 47 unsigned opt;
47 48
48 opt_complementary = "f-i:i-f"; 49 opt = getopt32(argv, "^" "fiRrv" "\0" "f-i:i-f");
49 opt = getopt32(argv, "fiRrv");
50 argv += optind; 50 argv += optind;
51 if (opt & 1) 51 if (opt & 1)
52 flags |= FILEUTILS_FORCE; 52 flags |= FILEUTILS_FORCE;
diff --git a/coreutils/rmdir.c b/coreutils/rmdir.c
index c04ce78f8..955740494 100644
--- a/coreutils/rmdir.c
+++ b/coreutils/rmdir.c
@@ -11,14 +11,6 @@
11//config: default y 11//config: default y
12//config: help 12//config: help
13//config: rmdir is used to remove empty directories. 13//config: rmdir is used to remove empty directories.
14//config:
15//config:config FEATURE_RMDIR_LONG_OPTIONS
16//config: bool "Enable long options"
17//config: default y
18//config: depends on RMDIR && LONG_OPTS
19//config: help
20//config: Support long options for the rmdir applet, including
21//config: --ignore-fail-on-non-empty for compatibility with GNU rmdir.
22 14
23//applet:IF_RMDIR(APPLET_NOFORK(rmdir, rmdir, BB_DIR_BIN, BB_SUID_DROP, rmdir)) 15//applet:IF_RMDIR(APPLET_NOFORK(rmdir, rmdir, BB_DIR_BIN, BB_SUID_DROP, rmdir))
24 16
@@ -31,12 +23,9 @@
31//usage: "[OPTIONS] DIRECTORY..." 23//usage: "[OPTIONS] DIRECTORY..."
32//usage:#define rmdir_full_usage "\n\n" 24//usage:#define rmdir_full_usage "\n\n"
33//usage: "Remove DIRECTORY if it is empty\n" 25//usage: "Remove DIRECTORY if it is empty\n"
34//usage: IF_FEATURE_RMDIR_LONG_OPTIONS(
35//usage: "\n -p|--parents Include parents"
36//usage: "\n --ignore-fail-on-non-empty"
37//usage: )
38//usage: IF_NOT_FEATURE_RMDIR_LONG_OPTIONS(
39//usage: "\n -p Include parents" 26//usage: "\n -p Include parents"
27//usage: IF_LONG_OPTS(
28//usage: "\n --ignore-fail-on-non-empty"
40//usage: ) 29//usage: )
41//usage: 30//usage:
42//usage:#define rmdir_example_usage 31//usage:#define rmdir_example_usage
@@ -49,7 +38,7 @@
49 38
50#define PARENTS (1 << 0) 39#define PARENTS (1 << 0)
51#define VERBOSE ((1 << 1) * ENABLE_FEATURE_VERBOSE) 40#define VERBOSE ((1 << 1) * ENABLE_FEATURE_VERBOSE)
52#define IGNORE_NON_EMPTY (1 << 2) 41#define IGNORE_NON_EMPTY ((1 << 2) * ENABLE_LONG_OPTS)
53 42
54int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 43int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
55int rmdir_main(int argc UNUSED_PARAM, char **argv) 44int rmdir_main(int argc UNUSED_PARAM, char **argv)
@@ -58,8 +47,7 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv)
58 int flags; 47 int flags;
59 char *path; 48 char *path;
60 49
61#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS 50 flags = getopt32long(argv, "pv",
62 static const char rmdir_longopts[] ALIGN1 =
63 "parents\0" No_argument "p" 51 "parents\0" No_argument "p"
64 /* Debian etch: many packages fail to be purged or installed 52 /* Debian etch: many packages fail to be purged or installed
65 * because they desperately want this option: */ 53 * because they desperately want this option: */
@@ -67,10 +55,7 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv)
67 IF_FEATURE_VERBOSE( 55 IF_FEATURE_VERBOSE(
68 "verbose\0" No_argument "v" 56 "verbose\0" No_argument "v"
69 ) 57 )
70 ; 58 );
71 applet_long_options = rmdir_longopts;
72#endif
73 flags = getopt32(argv, "pv");
74 argv += optind; 59 argv += optind;
75 60
76 if (!*argv) { 61 if (!*argv) {
@@ -86,7 +71,7 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv)
86 } 71 }
87 72
88 if (rmdir(path) < 0) { 73 if (rmdir(path) < 0) {
89#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS 74#if ENABLE_LONG_OPTS
90 if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY) 75 if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY)
91 break; 76 break;
92#endif 77#endif
diff --git a/coreutils/seq.c b/coreutils/seq.c
index f36dbb4ec..c26ff06b9 100644
--- a/coreutils/seq.c
+++ b/coreutils/seq.c
@@ -12,7 +12,8 @@
12//config: help 12//config: help
13//config: print a sequence of numbers 13//config: print a sequence of numbers
14 14
15//applet:IF_SEQ(APPLET_NOFORK(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq)) 15//applet:IF_SEQ(APPLET_NOEXEC(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq))
16/* was NOFORK, but then "seq 1 999999999" can't be ^C'ed if run by hush */
16 17
17//kbuild:lib-$(CONFIG_SEQ) += seq.o 18//kbuild:lib-$(CONFIG_SEQ) += seq.o
18 19
@@ -26,7 +27,7 @@
26 27
27#include "libbb.h" 28#include "libbb.h"
28 29
29/* This is a NOFORK applet. Be very careful! */ 30/* This is a NOEXEC applet. Be very careful! */
30 31
31int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 32int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
32int seq_main(int argc, char **argv) 33int seq_main(int argc, char **argv)
diff --git a/coreutils/shuf.c b/coreutils/shuf.c
index 403041534..d0caaa2ce 100644
--- a/coreutils/shuf.c
+++ b/coreutils/shuf.c
@@ -70,8 +70,11 @@ int shuf_main(int argc, char **argv)
70 unsigned numlines; 70 unsigned numlines;
71 char eol; 71 char eol;
72 72
73 opt_complementary = "e--i:i--e"; /* mutually exclusive */ 73 opts = getopt32(argv, "^"
74 opts = getopt32(argv, OPT_STR, &opt_i_str, &opt_n_str, &opt_o_str); 74 OPT_STR
75 "\0" "e--i:i--e"/* mutually exclusive */,
76 &opt_i_str, &opt_n_str, &opt_o_str
77 );
75 78
76 argc -= optind; 79 argc -= optind;
77 argv += optind; 80 argv += optind;
diff --git a/coreutils/sort.c b/coreutils/sort.c
index 9860dca64..ceea24491 100644
--- a/coreutils/sort.c
+++ b/coreutils/sort.c
@@ -94,7 +94,7 @@
94*/ 94*/
95 95
96/* These are sort types */ 96/* These are sort types */
97static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:*t:"; 97#define OPT_STR "ngMucszbrdfimS:T:o:k:*t:"
98enum { 98enum {
99 FLAG_n = 1, /* Numeric sort */ 99 FLAG_n = 1, /* Numeric sort */
100 FLAG_g = 2, /* Sort using strtod() */ 100 FLAG_g = 2, /* Sort using strtod() */
@@ -378,9 +378,11 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
378 xfunc_error_retval = 2; 378 xfunc_error_retval = 2;
379 379
380 /* Parse command line options */ 380 /* Parse command line options */
381 /* -o and -t can be given at most once */ 381 opts = getopt32(argv, "^"
382 opt_complementary = "o--o:t--t"; /* -t, -o: at most one of each */ 382 OPT_STR
383 opts = getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); 383 "\0" "o--o:t--t"/*-t, -o: at most one of each*/,
384 &str_ignored, &str_ignored, &str_o, &lst_k, &str_t
385 );
384 /* global b strips leading and trailing spaces */ 386 /* global b strips leading and trailing spaces */
385 if (opts & FLAG_b) 387 if (opts & FLAG_b)
386 option_mask32 |= FLAG_bb; 388 option_mask32 |= FLAG_bb;
diff --git a/coreutils/split.c b/coreutils/split.c
index d0c63573a..4e1db190c 100644
--- a/coreutils/split.c
+++ b/coreutils/split.c
@@ -100,8 +100,11 @@ int split_main(int argc UNUSED_PARAM, char **argv)
100 100
101 setup_common_bufsiz(); 101 setup_common_bufsiz();
102 102
103 opt_complementary = "?2"; /* max 2 args; -a N */ 103 opt = getopt32(argv, "^"
104 opt = getopt32(argv, "l:b:a:+", &count_p, &count_p, &suffix_len); 104 "l:b:a:+" /* -a N */
105 "\0" "?2"/*max 2 args*/,
106 &count_p, &count_p, &suffix_len
107 );
105 108
106 if (opt & SPLIT_OPT_l) 109 if (opt & SPLIT_OPT_l)
107 cnt = XATOOFF(count_p); 110 cnt = XATOOFF(count_p);
diff --git a/coreutils/stat.c b/coreutils/stat.c
index 96efa1d5d..177ced2f9 100644
--- a/coreutils/stat.c
+++ b/coreutils/stat.c
@@ -35,7 +35,7 @@
35//config: Without this, stat will not support the '-f' option to display 35//config: Without this, stat will not support the '-f' option to display
36//config: information about filesystem status. 36//config: information about filesystem status.
37 37
38//applet:IF_STAT(APPLET(stat, BB_DIR_BIN, BB_SUID_DROP)) 38//applet:IF_STAT(APPLET_NOEXEC(stat, stat, BB_DIR_BIN, BB_SUID_DROP, stat))
39 39
40//kbuild:lib-$(CONFIG_STAT) += stat.o 40//kbuild:lib-$(CONFIG_STAT) += stat.o
41 41
@@ -761,11 +761,13 @@ int stat_main(int argc UNUSED_PARAM, char **argv)
761 unsigned opts; 761 unsigned opts;
762 statfunc_ptr statfunc = do_stat; 762 statfunc_ptr statfunc = do_stat;
763 763
764 opt_complementary = "-1"; /* min one arg */ 764 opts = getopt32(argv, "^"
765 opts = getopt32(argv, "tL" 765 "tL"
766 IF_FEATURE_STAT_FILESYSTEM("f") 766 IF_FEATURE_STAT_FILESYSTEM("f")
767 IF_SELINUX("Z") 767 IF_SELINUX("Z")
768 IF_FEATURE_STAT_FORMAT("c:", &format) 768 IF_FEATURE_STAT_FORMAT("c:")
769 "\0" "-1" /* min one arg */
770 IF_FEATURE_STAT_FORMAT(,&format)
769 ); 771 );
770#if ENABLE_FEATURE_STAT_FILESYSTEM 772#if ENABLE_FEATURE_STAT_FILESYSTEM
771 if (opts & OPT_FILESYS) /* -f */ 773 if (opts & OPT_FILESYS) /* -f */
diff --git a/coreutils/stty.c b/coreutils/stty.c
index f987fbbcf..57e2cc30d 100644
--- a/coreutils/stty.c
+++ b/coreutils/stty.c
@@ -25,7 +25,7 @@
25//config: help 25//config: help
26//config: stty is used to change and print terminal line settings. 26//config: stty is used to change and print terminal line settings.
27 27
28//applet:IF_STTY(APPLET(stty, BB_DIR_BIN, BB_SUID_DROP)) 28//applet:IF_STTY(APPLET_NOEXEC(stty, stty, BB_DIR_BIN, BB_SUID_DROP, stty))
29 29
30//kbuild:lib-$(CONFIG_STTY) += stty.o 30//kbuild:lib-$(CONFIG_STTY) += stty.o
31 31
@@ -782,12 +782,13 @@ struct globals {
782 unsigned max_col; 782 unsigned max_col;
783 /* Current position, to know when to wrap */ 783 /* Current position, to know when to wrap */
784 unsigned current_col; 784 unsigned current_col;
785 char buf[10];
786} FIX_ALIASING; 785} FIX_ALIASING;
787#define G (*(struct globals*)bb_common_bufsiz1) 786#define G (*(struct globals*)bb_common_bufsiz1)
788#define INIT_G() do { \ 787#define INIT_G() do { \
788 setup_common_bufsiz(); \
789 G.device_name = bb_msg_standard_input; \ 789 G.device_name = bb_msg_standard_input; \
790 G.max_col = 80; \ 790 G.max_col = 80; \
791 G.current_col = 0; /* we are noexec, must clear */ \
791} while (0) 792} while (0)
792 793
793static void set_speed_or_die(enum speed_setting type, const char *arg, 794static void set_speed_or_die(enum speed_setting type, const char *arg,
@@ -1018,6 +1019,8 @@ static void do_display(const struct termios *mode, int all)
1018 1019
1019 for (i = 0; i != CIDX_min; ++i) { 1020 for (i = 0; i != CIDX_min; ++i) {
1020 char ch; 1021 char ch;
1022 char buf10[10];
1023
1021 /* If swtch is the same as susp, don't print both */ 1024 /* If swtch is the same as susp, don't print both */
1022#if VSWTCH == VSUSP 1025#if VSWTCH == VSUSP
1023 if (i == CIDX_swtch) 1026 if (i == CIDX_swtch)
@@ -1033,10 +1036,10 @@ static void do_display(const struct termios *mode, int all)
1033#endif 1036#endif
1034 ch = mode->c_cc[control_info[i].offset]; 1037 ch = mode->c_cc[control_info[i].offset];
1035 if (ch == _POSIX_VDISABLE) 1038 if (ch == _POSIX_VDISABLE)
1036 strcpy(G.buf, "<undef>"); 1039 strcpy(buf10, "<undef>");
1037 else 1040 else
1038 visible(ch, G.buf, 0); 1041 visible(ch, buf10, 0);
1039 wrapf("%s = %s;", nth_string(control_name, i), G.buf); 1042 wrapf("%s = %s;", nth_string(control_name, i), buf10);
1040 } 1043 }
1041#if VEOF == VMIN 1044#if VEOF == VMIN
1042 if ((mode->c_lflag & ICANON) == 0) 1045 if ((mode->c_lflag & ICANON) == 0)
diff --git a/coreutils/sync.c b/coreutils/sync.c
index 66445281a..9be47ab64 100644
--- a/coreutils/sync.c
+++ b/coreutils/sync.c
@@ -59,8 +59,7 @@ int sync_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
59 OPT_SYNCFS = (1 << 1), 59 OPT_SYNCFS = (1 << 1),
60 }; 60 };
61 61
62 opt_complementary = "d--f:f--d"; 62 opts = getopt32(argv, "^" "df" "\0" "d--f:f--d");
63 opts = getopt32(argv, "df");
64 argv += optind; 63 argv += optind;
65 64
66 /* Handle the no-argument case. */ 65 /* Handle the no-argument case. */
diff --git a/coreutils/tail.c b/coreutils/tail.c
index fd310f422..7335ba11e 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -140,9 +140,11 @@ int tail_main(int argc, char **argv)
140#endif 140#endif
141 141
142 /* -s NUM, -F imlies -f */ 142 /* -s NUM, -F imlies -f */
143 IF_FEATURE_FANCY_TAIL(opt_complementary = "Ff";) 143 opt = getopt32(argv, IF_FEATURE_FANCY_TAIL("^")
144 opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:+vF"), 144 "fc:n:"IF_FEATURE_FANCY_TAIL("qs:+vF")
145 &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period)); 145 IF_FEATURE_FANCY_TAIL("\0" "Ff"),
146 &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period)
147 );
146#define FOLLOW (opt & 0x1) 148#define FOLLOW (opt & 0x1)
147#define COUNT_BYTES (opt & 0x2) 149#define COUNT_BYTES (opt & 0x2)
148 //if (opt & 0x1) // -f 150 //if (opt & 0x1) // -f
diff --git a/coreutils/touch.c b/coreutils/touch.c
index 11b40d427..857761578 100644
--- a/coreutils/touch.c
+++ b/coreutils/touch.c
@@ -103,6 +103,11 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
103 "date\0" Required_argument "d" 103 "date\0" Required_argument "d"
104 IF_FEATURE_TOUCH_NODEREF("no-dereference\0" No_argument "h") 104 IF_FEATURE_TOUCH_NODEREF("no-dereference\0" No_argument "h")
105 ; 105 ;
106# define GETOPT32 getopt32long
107# define LONGOPTS ,touch_longopts
108# else
109# define GETOPT32 getopt32
110# define LONGOPTS
106# endif 111# endif
107 char *reference_file = NULL; 112 char *reference_file = NULL;
108 char *date_str = NULL; 113 char *date_str = NULL;
@@ -112,17 +117,17 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
112# define reference_file NULL 117# define reference_file NULL
113# define date_str NULL 118# define date_str NULL
114# define timebuf ((struct timeval*)NULL) 119# define timebuf ((struct timeval*)NULL)
120# define GETOPT32 getopt32
121# define LONGOPTS
115#endif 122#endif
116 123
117#if ENABLE_FEATURE_TOUCH_SUSV3 && ENABLE_LONG_OPTS
118 applet_long_options = touch_longopts;
119#endif
120 /* -d and -t both set time. In coreutils, 124 /* -d and -t both set time. In coreutils,
121 * accepted data format differs a bit between -d and -t. 125 * accepted data format differs a bit between -d and -t.
122 * We accept the same formats for both */ 126 * We accept the same formats for both */
123 opts = getopt32(argv, "c" IF_FEATURE_TOUCH_SUSV3("r:d:t:") 127 opts = GETOPT32(argv, "c" IF_FEATURE_TOUCH_SUSV3("r:d:t:")
124 IF_FEATURE_TOUCH_NODEREF("h") 128 IF_FEATURE_TOUCH_NODEREF("h")
125 /*ignored:*/ "fma" 129 /*ignored:*/ "fma"
130 LONGOPTS
126 IF_FEATURE_TOUCH_SUSV3(, &reference_file) 131 IF_FEATURE_TOUCH_SUSV3(, &reference_file)
127 IF_FEATURE_TOUCH_SUSV3(, &date_str) 132 IF_FEATURE_TOUCH_SUSV3(, &date_str)
128 IF_FEATURE_TOUCH_SUSV3(, &date_str) 133 IF_FEATURE_TOUCH_SUSV3(, &date_str)
diff --git a/coreutils/tr.c b/coreutils/tr.c
index 64e4efc91..c5872434a 100644
--- a/coreutils/tr.c
+++ b/coreutils/tr.c
@@ -298,8 +298,8 @@ int tr_main(int argc UNUSED_PARAM, char **argv)
298 * In POSIX locale, these are the same. 298 * In POSIX locale, these are the same.
299 */ 299 */
300 300
301 opt_complementary = "-1"; 301 /* '+': stop at first non-option */
302 opts = getopt32(argv, "+Ccds"); /* '+': stop at first non-option */ 302 opts = getopt32(argv, "^+" "Ccds" "\0" "-1");
303 argv += optind; 303 argv += optind;
304 304
305 str1_length = expand(*argv++, &str1); 305 str1_length = expand(*argv++, &str1);
diff --git a/coreutils/truncate.c b/coreutils/truncate.c
index f67abaf40..f693570aa 100644
--- a/coreutils/truncate.c
+++ b/coreutils/truncate.c
@@ -50,8 +50,7 @@ int truncate_main(int argc UNUSED_PARAM, char **argv)
50 OPT_SIZE = (1 << 1), 50 OPT_SIZE = (1 << 1),
51 }; 51 };
52 52
53 opt_complementary = "s:-1"; 53 opts = getopt32(argv, "^" "cs:" "\0" "s:-1", &size_str);
54 opts = getopt32(argv, "cs:", &size_str);
55 54
56 if (!(opts & OPT_NOCREATE)) 55 if (!(opts & OPT_NOCREATE))
57 flags |= O_CREAT; 56 flags |= O_CREAT;
diff --git a/coreutils/tty.c b/coreutils/tty.c
index 331941a01..18ad7c566 100644
--- a/coreutils/tty.c
+++ b/coreutils/tty.c
@@ -13,7 +13,7 @@
13//config: tty is used to print the name of the current terminal to 13//config: tty is used to print the name of the current terminal to
14//config: standard output. 14//config: standard output.
15 15
16//applet:IF_TTY(APPLET(tty, BB_DIR_USR_BIN, BB_SUID_DROP)) 16//applet:IF_TTY(APPLET_NOFORK(tty, tty, BB_DIR_USR_BIN, BB_SUID_DROP, tty))
17 17
18//kbuild:lib-$(CONFIG_TTY) += tty.o 18//kbuild:lib-$(CONFIG_TTY) += tty.o
19 19
diff --git a/coreutils/uname.c b/coreutils/uname.c
index aad58cab0..bb2d1fe8d 100644
--- a/coreutils/uname.c
+++ b/coreutils/uname.c
@@ -63,9 +63,9 @@
63//config: help 63//config: help
64//config: Same as uname -m. 64//config: Same as uname -m.
65 65
66//applet:IF_UNAME(APPLET(uname, BB_DIR_BIN, BB_SUID_DROP)) 66// APPLET_NOFORK:name main location suid_type help
67// APPLET_ODDNAME:name main location suid_type help 67//applet:IF_UNAME(APPLET_NOFORK( uname, uname, BB_DIR_BIN, BB_SUID_DROP, uname))
68//applet:IF_BB_ARCH(APPLET_ODDNAME(arch, uname, BB_DIR_BIN, BB_SUID_DROP, arch)) 68//applet:IF_BB_ARCH(APPLET_NOFORK(arch, uname, BB_DIR_BIN, BB_SUID_DROP, arch))
69 69
70//kbuild:lib-$(CONFIG_UNAME) += uname.o 70//kbuild:lib-$(CONFIG_UNAME) += uname.o
71//kbuild:lib-$(CONFIG_BB_ARCH) += uname.o 71//kbuild:lib-$(CONFIG_BB_ARCH) += uname.o
@@ -147,8 +147,7 @@ int uname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
147 "operating-system\0" No_argument "o" 147 "operating-system\0" No_argument "o"
148 ; 148 ;
149# endif 149# endif
150 IF_LONG_OPTS(applet_long_options = uname_longopts); 150 toprint = getopt32long(argv, options, uname_longopts);
151 toprint = getopt32(argv, options);
152 if (argv[optind]) { /* coreutils-6.9 compat */ 151 if (argv[optind]) { /* coreutils-6.9 compat */
153 bb_show_usage(); 152 bb_show_usage();
154 } 153 }
@@ -183,7 +182,7 @@ int uname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
183 strcpy(uname_info.processor, unknown_str); 182 strcpy(uname_info.processor, unknown_str);
184 strcpy(uname_info.platform, unknown_str); 183 strcpy(uname_info.platform, unknown_str);
185 strcpy(uname_info.os, CONFIG_UNAME_OSNAME); 184 strcpy(uname_info.os, CONFIG_UNAME_OSNAME);
186# if 0 185# if ENABLE_FEDORA_COMPAT
187 /* Fedora does something like this */ 186 /* Fedora does something like this */
188 strcpy(uname_info.processor, uname_info.name.machine); 187 strcpy(uname_info.processor, uname_info.name.machine);
189 strcpy(uname_info.platform, uname_info.name.machine); 188 strcpy(uname_info.platform, uname_info.name.machine);
diff --git a/coreutils/unlink.c b/coreutils/unlink.c
index 3322d5b47..56309b1c7 100644
--- a/coreutils/unlink.c
+++ b/coreutils/unlink.c
@@ -11,7 +11,7 @@
11//config: help 11//config: help
12//config: unlink deletes a file by calling unlink() 12//config: unlink deletes a file by calling unlink()
13 13
14//applet:IF_UNLINK(APPLET(unlink, BB_DIR_USR_BIN, BB_SUID_DROP)) 14//applet:IF_UNLINK(APPLET_NOFORK(unlink, unlink, BB_DIR_USR_BIN, BB_SUID_DROP, unlink))
15 15
16//kbuild:lib-$(CONFIG_UNLINK) += unlink.o 16//kbuild:lib-$(CONFIG_UNLINK) += unlink.o
17 17
@@ -25,8 +25,7 @@
25int unlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 25int unlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
26int unlink_main(int argc UNUSED_PARAM, char **argv) 26int unlink_main(int argc UNUSED_PARAM, char **argv)
27{ 27{
28 opt_complementary = "=1"; /* must have exactly 1 param */ 28 getopt32(argv, "^" "" "\0" "=1");
29 getopt32(argv, "");
30 argv += optind; 29 argv += optind;
31 xunlink(argv[0]); 30 xunlink(argv[0]);
32 return 0; 31 return 0;
diff --git a/coreutils/usleep.c b/coreutils/usleep.c
index 7c25aada1..684ab781b 100644
--- a/coreutils/usleep.c
+++ b/coreutils/usleep.c
@@ -38,6 +38,13 @@ int usleep_main(int argc UNUSED_PARAM, char **argv)
38 bb_show_usage(); 38 bb_show_usage();
39 } 39 }
40 40
41 /* Safe wrt NOFORK? (noforks are not allowed to run for
42 * a long time). Try "usleep 99999999" + ^C + "echo $?"
43 * in hush with FEATURE_SH_NOFORK=y.
44 * At least on uclibc, usleep() thanslates to nanosleep()
45 * which returns early on any signal (even caught one),
46 * and uclibc does not loop back on EINTR.
47 */
41 usleep(xatou(argv[1])); 48 usleep(xatou(argv[1]));
42 49
43 return EXIT_SUCCESS; 50 return EXIT_SUCCESS;
diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c
index 4e72e86ee..5ef05ee4d 100644
--- a/coreutils/uudecode.c
+++ b/coreutils/uudecode.c
@@ -120,8 +120,7 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
120 char *outname = NULL; 120 char *outname = NULL;
121 char *line; 121 char *line;
122 122
123 opt_complementary = "?1"; /* 1 argument max */ 123 getopt32(argv, "^" "o:" "\0" "?1"/* 1 arg max*/, &outname);
124 getopt32(argv, "o:", &outname);
125 argv += optind; 124 argv += optind;
126 125
127 if (!argv[0]) 126 if (!argv[0])
@@ -196,8 +195,7 @@ int base64_main(int argc UNUSED_PARAM, char **argv)
196 FILE *src_stream; 195 FILE *src_stream;
197 unsigned opts; 196 unsigned opts;
198 197
199 opt_complementary = "?1"; /* 1 argument max */ 198 opts = getopt32(argv, "^" "d" "\0" "?1"/* 1 arg max*/);
200 opts = getopt32(argv, "d");
201 argv += optind; 199 argv += optind;
202 200
203 if (!argv[0]) 201 if (!argv[0])
diff --git a/coreutils/uuencode.c b/coreutils/uuencode.c
index 7164f838a..d6e077430 100644
--- a/coreutils/uuencode.c
+++ b/coreutils/uuencode.c
@@ -49,8 +49,7 @@ int uuencode_main(int argc UNUSED_PARAM, char **argv)
49 49
50 tbl = bb_uuenc_tbl_std; 50 tbl = bb_uuenc_tbl_std;
51 mode = 0666 & ~umask(0666); 51 mode = 0666 & ~umask(0666);
52 opt_complementary = "-1:?2"; /* must have 1 or 2 args */ 52 if (getopt32(argv, "^" "m" "\0" "-1:?2"/*must have 1 or 2 args*/)) {
53 if (getopt32(argv, "m")) {
54 tbl = bb_uuenc_tbl_base64; 53 tbl = bb_uuenc_tbl_base64;
55 } 54 }
56 argv += optind; 55 argv += optind;
diff --git a/coreutils/who.c b/coreutils/who.c
index 91f99138c..cfe0c921e 100644
--- a/coreutils/who.c
+++ b/coreutils/who.c
@@ -38,10 +38,10 @@
38//config: help 38//config: help
39//config: Print users currently logged on. 39//config: Print users currently logged on.
40 40
41// APPLET_ODDNAME:name main location suid_type help 41// APPLET_NOEXEC:name main location suid_type help
42//applet:IF_USERS(APPLET_ODDNAME(users, who, BB_DIR_USR_BIN, BB_SUID_DROP, users)) 42//applet:IF_USERS(APPLET_NOEXEC(users, who, BB_DIR_USR_BIN, BB_SUID_DROP, users))
43//applet:IF_W( APPLET_ODDNAME(w, who, BB_DIR_USR_BIN, BB_SUID_DROP, w)) 43//applet:IF_W( APPLET_NOEXEC(w, who, BB_DIR_USR_BIN, BB_SUID_DROP, w))
44//applet:IF_WHO( APPLET( who, BB_DIR_USR_BIN, BB_SUID_DROP)) 44//applet:IF_WHO( APPLET_NOEXEC(who, who, BB_DIR_USR_BIN, BB_SUID_DROP, who))
45 45
46//kbuild:lib-$(CONFIG_USERS) += who.o 46//kbuild:lib-$(CONFIG_USERS) += who.o
47//kbuild:lib-$(CONFIG_W) += who.o 47//kbuild:lib-$(CONFIG_W) += who.o
@@ -117,8 +117,7 @@ int who_main(int argc UNUSED_PARAM, char **argv)
117 unsigned opt; 117 unsigned opt;
118 const char *fmt = "%s"; 118 const char *fmt = "%s";
119 119
120 opt_complementary = "=0"; 120 opt = getopt32(argv, do_who ? "^" "aH" "\0" "=0": "^" "" "\0" "=0");
121 opt = getopt32(argv, do_who ? "aH" : "");
122 if ((opt & 2) || do_w) /* -H or we are w */ 121 if ((opt & 2) || do_w) /* -H or we are w */
123 puts("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST"); 122 puts("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST");
124 123
diff --git a/coreutils/yes.c b/coreutils/yes.c
index a5a444249..6dc47355e 100644
--- a/coreutils/yes.c
+++ b/coreutils/yes.c
@@ -15,9 +15,10 @@
15//config: default y 15//config: default y
16//config: help 16//config: help
17//config: yes is used to repeatedly output a specific string, or 17//config: yes is used to repeatedly output a specific string, or
18//config: the default string `y'. 18//config: the default string 'y'.
19 19
20//applet:IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes)) 20//applet:IF_YES(APPLET_NOEXEC(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes))
21/* was NOFORK, but then yes can't be ^C'ed if run by hush */
21 22
22//kbuild:lib-$(CONFIG_YES) += yes.o 23//kbuild:lib-$(CONFIG_YES) += yes.o
23 24
diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c
index c6a90a486..e4d61df35 100644
--- a/debianutils/run_parts.c
+++ b/debianutils/run_parts.c
@@ -159,10 +159,15 @@ static const char runparts_longopts[] ALIGN1 =
159 "reverse\0" No_argument "\xf0" 159 "reverse\0" No_argument "\xf0"
160 "test\0" No_argument "\xf1" 160 "test\0" No_argument "\xf1"
161 "exit-on-error\0" No_argument "\xf2" 161 "exit-on-error\0" No_argument "\xf2"
162#if ENABLE_FEATURE_RUN_PARTS_FANCY 162# if ENABLE_FEATURE_RUN_PARTS_FANCY
163 "list\0" No_argument "\xf3" 163 "list\0" No_argument "\xf3"
164#endif 164# endif
165 ; 165 ;
166# define GETOPT32 getopt32long
167# define LONGOPTS ,runparts_longopts
168#else
169# define GETOPT32 getopt32
170# define LONGOPTS
166#endif 171#endif
167 172
168int run_parts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 173int run_parts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -175,12 +180,10 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
175 180
176 INIT_G(); 181 INIT_G();
177 182
178#if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
179 applet_long_options = runparts_longopts;
180#endif
181 /* We require exactly one argument: the directory name */ 183 /* We require exactly one argument: the directory name */
182 opt_complementary = "=1"; 184 GETOPT32(argv, "^" "a:*u:" "\0" "=1" LONGOPTS,
183 getopt32(argv, "a:*u:", &arg_list, &umask_p); 185 &arg_list, &umask_p
186 );
184 187
185 umask(xstrtou_range(umask_p, 8, 0, 07777)); 188 umask(xstrtou_range(umask_p, 8, 0, 07777));
186 189
diff --git a/debianutils/start_stop_daemon.c b/debianutils/start_stop_daemon.c
index 9d60b2c7f..c8b7fa8f2 100644
--- a/debianutils/start_stop_daemon.c
+++ b/debianutils/start_stop_daemon.c
@@ -79,6 +79,7 @@ Misc options:
79//config: -N|--nicelevel N 79//config: -N|--nicelevel N
80 80
81//applet:IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon)) 81//applet:IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon))
82/* not NOEXEC: uses bb_common_bufsiz1 */
82 83
83//kbuild:lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o 84//kbuild:lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o
84 85
@@ -86,44 +87,15 @@ Misc options:
86//usage: "[OPTIONS] [-S|-K] ... [-- ARGS...]" 87//usage: "[OPTIONS] [-S|-K] ... [-- ARGS...]"
87//usage:#define start_stop_daemon_full_usage "\n\n" 88//usage:#define start_stop_daemon_full_usage "\n\n"
88//usage: "Search for matching processes, and then\n" 89//usage: "Search for matching processes, and then\n"
89//usage: "-K: stop all matching processes.\n" 90//usage: "-K: stop all matching processes\n"
90//usage: "-S: start a process unless a matching process is found.\n" 91//usage: "-S: start a process unless a matching process is found\n"
91//usage: IF_FEATURE_START_STOP_DAEMON_LONG_OPTIONS(
92//usage: "\nProcess matching:"
93//usage: "\n -u,--user USERNAME|UID Match only this user's processes"
94//usage: "\n -n,--name NAME Match processes with NAME"
95//usage: "\n in comm field in /proc/PID/stat"
96//usage: "\n -x,--exec EXECUTABLE Match processes with this command"
97//usage: "\n in /proc/PID/{exe,cmdline}"
98//usage: "\n -p,--pidfile FILE Match a process with PID from the file"
99//usage: "\n All specified conditions must match"
100//usage: "\n-S only:"
101//usage: "\n -x,--exec EXECUTABLE Program to run"
102//usage: "\n -a,--startas NAME Zeroth argument"
103//usage: "\n -b,--background Background"
104//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
105//usage: "\n -N,--nicelevel N Change nice level"
106//usage: )
107//usage: "\n -c,--chuid USER[:[GRP]] Change to user/group"
108//usage: "\n -m,--make-pidfile Write PID to the pidfile specified by -p"
109//usage: "\n-K only:"
110//usage: "\n -s,--signal SIG Signal to send"
111//usage: "\n -t,--test Match only, exit with 0 if a process is found"
112//usage: "\nOther:"
113//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
114//usage: "\n -o,--oknodo Exit with status 0 if nothing is done"
115//usage: "\n -v,--verbose Verbose"
116//usage: )
117//usage: "\n -q,--quiet Quiet"
118//usage: )
119//usage: IF_NOT_FEATURE_START_STOP_DAEMON_LONG_OPTIONS(
120//usage: "\nProcess matching:" 92//usage: "\nProcess matching:"
121//usage: "\n -u USERNAME|UID Match only this user's processes" 93//usage: "\n -u USERNAME|UID Match only this user's processes"
122//usage: "\n -n NAME Match processes with NAME" 94//usage: "\n -n NAME Match processes with NAME"
123//usage: "\n in comm field in /proc/PID/stat" 95//usage: "\n in comm field in /proc/PID/stat"
124//usage: "\n -x EXECUTABLE Match processes with this command" 96//usage: "\n -x EXECUTABLE Match processes with this command"
125//usage: "\n command in /proc/PID/cmdline" 97//usage: "\n command in /proc/PID/cmdline"
126//usage: "\n -p FILE Match a process with PID from the file" 98//usage: "\n -p FILE Match a process with PID from FILE"
127//usage: "\n All specified conditions must match" 99//usage: "\n All specified conditions must match"
128//usage: "\n-S only:" 100//usage: "\n-S only:"
129//usage: "\n -x EXECUTABLE Program to run" 101//usage: "\n -x EXECUTABLE Program to run"
@@ -132,18 +104,17 @@ Misc options:
132//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( 104//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
133//usage: "\n -N N Change nice level" 105//usage: "\n -N N Change nice level"
134//usage: ) 106//usage: )
135//usage: "\n -c USER[:[GRP]] Change to user/group" 107//usage: "\n -c USER[:[GRP]] Change user/group"
136//usage: "\n -m Write PID to the pidfile specified by -p" 108//usage: "\n -m Write PID to pidfile specified by -p"
137//usage: "\n-K only:" 109//usage: "\n-K only:"
138//usage: "\n -s SIG Signal to send" 110//usage: "\n -s SIG Signal to send"
139//usage: "\n -t Match only, exit with 0 if a process is found" 111//usage: "\n -t Match only, exit with 0 if found"
140//usage: "\nOther:" 112//usage: "\nOther:"
141//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( 113//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
142//usage: "\n -o Exit with status 0 if nothing is done" 114//usage: "\n -o Exit with status 0 if nothing is done"
143//usage: "\n -v Verbose" 115//usage: "\n -v Verbose"
144//usage: ) 116//usage: )
145//usage: "\n -q Quiet" 117//usage: "\n -q Quiet"
146//usage: )
147 118
148#include <sys/resource.h> 119#include <sys/resource.h>
149 120
@@ -409,11 +380,11 @@ static const char start_stop_daemon_longopts[] ALIGN1 =
409 "quiet\0" No_argument "q" 380 "quiet\0" No_argument "q"
410 "test\0" No_argument "t" 381 "test\0" No_argument "t"
411 "make-pidfile\0" No_argument "m" 382 "make-pidfile\0" No_argument "m"
412#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY 383# if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
413 "oknodo\0" No_argument "o" 384 "oknodo\0" No_argument "o"
414 "verbose\0" No_argument "v" 385 "verbose\0" No_argument "v"
415 "nicelevel\0" Required_argument "N" 386 "nicelevel\0" Required_argument "N"
416#endif 387# endif
417 "startas\0" Required_argument "a" 388 "startas\0" Required_argument "a"
418 "name\0" Required_argument "n" 389 "name\0" Required_argument "n"
419 "signal\0" Required_argument "s" 390 "signal\0" Required_argument "s"
@@ -421,10 +392,15 @@ static const char start_stop_daemon_longopts[] ALIGN1 =
421 "chuid\0" Required_argument "c" 392 "chuid\0" Required_argument "c"
422 "exec\0" Required_argument "x" 393 "exec\0" Required_argument "x"
423 "pidfile\0" Required_argument "p" 394 "pidfile\0" Required_argument "p"
424#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY 395# if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
425 "retry\0" Required_argument "R" 396 "retry\0" Required_argument "R"
426#endif 397# endif
427 ; 398 ;
399# define GETOPT32 getopt32long
400# define LONGOPTS start_stop_daemon_longopts,
401#else
402# define GETOPT32 getopt32
403# define LONGOPTS
428#endif 404#endif
429 405
430int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 406int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -445,19 +421,18 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
445 421
446 INIT_G(); 422 INIT_G();
447 423
448#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS 424 opt = GETOPT32(argv, "^"
449 applet_long_options = start_stop_daemon_longopts; 425 "KSbqtma:n:s:u:c:x:p:"
450#endif 426 IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:")
451 427 /* -K or -S is required; they are mutually exclusive */
452 /* -K or -S is required; they are mutually exclusive */ 428 /* -p is required if -m is given */
453 /* -p is required if -m is given */ 429 /* -xpun (at least one) is required if -K is given */
454 /* -xpun (at least one) is required if -K is given */ 430 /* -xa (at least one) is required if -S is given */
455 /* -xa (at least one) is required if -S is given */ 431 /* -q turns off -v */
456 /* -q turns off -v */ 432 "\0"
457 opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa" 433 "K:S:K--S:S--K:m?p:K?xpun:S?xa"
458 IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"); 434 IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"),
459 opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:" 435 LONGOPTS
460 IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"),
461 &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile 436 &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
462 IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) 437 IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
463 /* We accept and ignore -R <param> / --retry <param> */ 438 /* We accept and ignore -R <param> / --retry <param> */
@@ -516,6 +491,11 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
516 /* DAEMON_DEVNULL_STDIO is superfluous - 491 /* DAEMON_DEVNULL_STDIO is superfluous -
517 * it's always done by bb_daemonize() */ 492 * it's always done by bb_daemonize() */
518#else 493#else
494 /* Daemons usually call bb_daemonize_or_rexec(), but SSD can do
495 * without: SSD is not itself a daemon, it _execs_ a daemon.
496 * The usual NOMMU problem of "child can't run indefinitely,
497 * it must exec" does not bite us: we exec anyway.
498 */
519 pid_t pid = xvfork(); 499 pid_t pid = xvfork();
520 if (pid != 0) { 500 if (pid != 0) {
521 /* parent */ 501 /* parent */
@@ -525,12 +505,8 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
525 } 505 }
526 /* Child */ 506 /* Child */
527 setsid(); /* detach from controlling tty */ 507 setsid(); /* detach from controlling tty */
528 /* Redirect stdio to /dev/null, close extra FDs. 508 /* Redirect stdio to /dev/null, close extra FDs */
529 * We do not actually daemonize because of DAEMON_ONLY_SANITIZE */ 509 bb_daemon_helper(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS);
530 bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO
531 + DAEMON_CLOSE_EXTRA_FDS
532 + DAEMON_ONLY_SANITIZE,
533 NULL /* argv, unused */ );
534#endif 510#endif
535 } 511 }
536 if (opt & OPT_MAKEPID) { 512 if (opt & OPT_MAKEPID) {
diff --git a/debianutils/which.c b/debianutils/which.c
index 23a481438..fbfd19cdc 100644
--- a/debianutils/which.c
+++ b/debianutils/which.c
@@ -12,7 +12,7 @@
12//config: which is used to find programs in your PATH and 12//config: which is used to find programs in your PATH and
13//config: print out their pathnames. 13//config: print out their pathnames.
14 14
15//applet:IF_WHICH(APPLET(which, BB_DIR_USR_BIN, BB_SUID_DROP)) 15//applet:IF_WHICH(APPLET_NOFORK(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which))
16 16
17//kbuild:lib-$(CONFIG_WHICH) += which.o 17//kbuild:lib-$(CONFIG_WHICH) += which.o
18 18
@@ -37,8 +37,7 @@ int which_main(int argc UNUSED_PARAM, char **argv)
37 if (!env_path) 37 if (!env_path)
38 env_path = bb_default_root_path; 38 env_path = bb_default_root_path;
39 39
40 opt_complementary = "-1"; /* at least one argument */ 40 getopt32(argv, "^" "a" "\0" "-1"/*at least one arg*/);
41 getopt32(argv, "a");
42 argv += optind; 41 argv += optind;
43 42
44 do { 43 do {
@@ -73,6 +72,8 @@ int which_main(int argc UNUSED_PARAM, char **argv)
73 char *tmp; 72 char *tmp;
74 73
75 path = tmp = xstrdup(env_path); 74 path = tmp = xstrdup(env_path);
75//NOFORK FIXME: nested xmallocs (one is inside find_executable())
76//can leak memory on failure
76 while ((p = find_executable(*argv, &tmp)) != NULL) { 77 while ((p = find_executable(*argv, &tmp)) != NULL) {
77 missing = 0; 78 missing = 0;
78 puts(p); 79 puts(p);
diff --git a/docs/new-applet-HOWTO.txt b/docs/new-applet-HOWTO.txt
index 078e77bce..619d47fb8 100644
--- a/docs/new-applet-HOWTO.txt
+++ b/docs/new-applet-HOWTO.txt
@@ -147,17 +147,17 @@ Placement / Directory
147 147
148Find the appropriate directory for your new applet. 148Find the appropriate directory for your new applet.
149 149
150Add the kbuild snippet to the .c file:
151
152//kbuild:lib-$(CONFIG_MU) += mu.o
153
154Add the config snippet to the .c file: 150Add the config snippet to the .c file:
155 151
156//config:config MU 152//config:config MU
157//config: bool "MU" 153//config: bool "MU"
158//config: default y 154//config: default y
159//config: help 155//config: help
160//config: Returns an indeterminate value. 156//config: Returns an indeterminate value.
157
158Add the kbuild snippet to the .c file:
159
160//kbuild:lib-$(CONFIG_MU) += mu.o
161 161
162 162
163Usage String(s) 163Usage String(s)
@@ -168,8 +168,9 @@ This should look like the following:
168 168
169//usage:#define mu_trivial_usage 169//usage:#define mu_trivial_usage
170//usage: "[-abcde] FILE..." 170//usage: "[-abcde] FILE..."
171//usage:#define mu_full_usage 171//usage:#define mu_full_usage "\n\n"
172//usage: "Returns an indeterminate value\n" 172//usage: "Returns an indeterminate value"
173//usage: "\n"
173//usage: "\n -a First function" 174//usage: "\n -a First function"
174//usage: "\n -b Second function" 175//usage: "\n -b Second function"
175//usage: ... 176//usage: ...
diff --git a/docs/nofork_noexec.txt b/docs/nofork_noexec.txt
index a24dd9c27..9d210a1c9 100644
--- a/docs/nofork_noexec.txt
+++ b/docs/nofork_noexec.txt
@@ -10,13 +10,8 @@ of reimplemented Unix commands, and we can do the same trick
10for speeding up busybox shells, and more. NOEXEC and NOFORK applets 10for speeding up busybox shells, and more. NOEXEC and NOFORK applets
11are exactly those applets which are eligible for these tricks. 11are exactly those applets which are eligible for these tricks.
12 12
13Applet will be subject to NOFORK/NOEXEC tricks if it is marked as such 13Applet will be subject to NOFORK/NOEXEC tricks only if it is marked
14in applets.h. FEATURE_PREFER_APPLETS is a config option which 14as such in applets.src.h or in their inline "//applet:" directives.
15globally enables usage of NOFORK/NOEXEC tricks.
16If it is enabled, FEATURE_SH_STANDALONE can be enabled too,
17and then shells will use NOFORK/NOEXEC tricks for ordinary commands.
18NB: shell builtins use these tricks regardless of FEATURE_SH_STANDALONE
19or FEATURE_PREFER_APPLETS.
20 15
21In C, if you want to call a program and wait for it, use 16In C, if you want to call a program and wait for it, use
22spawn_and_wait(argv), BB_EXECVP(prog,argv) or BB_EXECLP(prog,argv0,...). 17spawn_and_wait(argv), BB_EXECVP(prog,argv) or BB_EXECLP(prog,argv0,...).
@@ -24,6 +19,31 @@ They check whether program name is an applet name and optionally
24do NOFORK/NOEXEC thing depending on configuration. 19do NOFORK/NOEXEC thing depending on configuration.
25 20
26 21
22 Relevant CONFIG options
23
24FEATURE_PREFER_APPLETS
25 Globally enables NOFORK/NOEXEC tricks for such programs as xargs
26 and find:
27 BB_EXECVP(cmd, argv) will try to exec /proc/self/exe
28 if command's name matches some applet name;
29 spawn_and_wait(argv) will do NOFORK/NOEXEC tricks
30
31//TODO: the above two things probably should have separate options?
32
33FEATURE_SH_STANDALONE
34 shells will try to exec /proc/self/exe if command's name matches
35 some applet name; shells will do NOEXEC trick on NOEXEC applets
36
37//TODO: split (same as for PREFER_APPLETS)
38
39FEATURE_SH_NOFORK
40 shells will do NOFORK trick on NOFORK applets
41
42NB: shell builtins use these tricks regardless of FEATURE_SH_STANDALONE,
43FEATURE_PREFER_APPLETS or FEATURE_SH_NOFORK. In effect, builtins
44are "always NOFORK".
45
46
27 NOEXEC 47 NOEXEC
28 48
29NOEXEC applet should work correctly if another applet forks and then 49NOEXEC applet should work correctly if another applet forks and then
@@ -52,6 +72,9 @@ xargs, find, shells do it (grep for "spawn_and_wait" and
52This poses much more serious limitations on what applet can do: 72This poses much more serious limitations on what applet can do:
53 73
54* all NOEXEC limitations apply. 74* all NOEXEC limitations apply.
75* do not run for a long time or wait for user input:
76 hush shell only handles signals (like ^C) after you return
77 from APPLET_main().
55* do not ever exit() or exec(). 78* do not ever exit() or exec().
56 - xfuncs are okay. They are using special trick to return 79 - xfuncs are okay. They are using special trick to return
57 to the caller applet instead of dying when they detect "x" condition. 80 to the caller applet instead of dying when they detect "x" condition.
@@ -61,15 +84,27 @@ This poses much more serious limitations on what applet can do:
61* do not use shared global data, or save/restore shared global data 84* do not use shared global data, or save/restore shared global data
62 (e.g. bb_common_bufsiz1) prior to returning. 85 (e.g. bb_common_bufsiz1) prior to returning.
63 - getopt32() is ok to use. You do not need to save/restore option_mask32, 86 - getopt32() is ok to use. You do not need to save/restore option_mask32,
64 it is already done by core code. 87 xfunc_error_retval, and logmode - it is already done by core code.
65* if you allocate memory, you can use xmalloc() only on the very first 88* if you allocate memory, you can use xmalloc() only on the very first
66 allocation. All other allocations should use malloc[_or_warn](). 89 allocation. All other allocations should use malloc[_or_warn]().
67 After first allocation, you cannot use any xfuncs. 90 After first allocation, you cannot use any xfuncs.
68 Otherwise, failing xfunc will return to caller applet 91 Otherwise, failing xfunc will return to caller applet
69 without freeing malloced data! 92 without freeing malloced data!
70* All allocated data, opened files, signal handlers, termios settings, 93* the same applies to other resources, such as open fds: no xfuncs after
71 O_NONBLOCK flags etc should be freed/closed/restored prior to return. 94 acquiring them!
72* ... 95* All allocated data, opened files, signal handlers, termios settings
96 etc should be freed/closed/restored prior to return.
97
98Currently, ash shell signal handling is implemented in a way that signals
99have non-SA_RESTARTed handlers. This means that system calls can
100return EINTR. An example of such problem is "yes" applet:
101it is implemented so that it has a writing loop, this loop is exited on
102any write error, and in the case of user pressing ^C the error was EINTR.
103The problem is, the error causes stdout FILE* object to get into error
104state, needing clearerr() - or else subsequent shell output will also
105not work. ("yes" has been downgraded to NOEXEC, since hush signal handling
106does not have this problem - which makes "yes" to not exit on ^C (bug).
107But stray EINTRs can be seen in any NOFORK under ash, until ash is fixed).
73 108
74NOFORK applets give the most of speed advantage, but are trickiest 109NOFORK applets give the most of speed advantage, but are trickiest
75to implement. In order to minimize amount of bugs and maintenance, 110to implement. In order to minimize amount of bugs and maintenance,
@@ -79,6 +114,8 @@ frequently executed from shell/find/xargs, particularly in shell
79script loops. Applets which mess with signal handlers, termios etc 114script loops. Applets which mess with signal handlers, termios etc
80are probably not worth the effort. 115are probably not worth the effort.
81 116
117Applets which must be interruptible by ^C in shells can not be NOFORKs.
118
82Any NOFORK applet is also a NOEXEC applet. 119Any NOFORK applet is also a NOEXEC applet.
83 120
84 121
@@ -91,11 +128,11 @@ API to call NOFORK applets is two functions:
91 128
92First one is directly used by shells if FEATURE_SH_NOFORK=y. 129First one is directly used by shells if FEATURE_SH_NOFORK=y.
93Second one is used by many applets, but main users are xargs and find. 130Second one is used by many applets, but main users are xargs and find.
94It itself calls run_nofork_applet(), if argv[0] turned out to be a name 131It itself calls run_nofork_applet(), if argv[0] is a name
95of a NOFORK applet. 132of a NOFORK applet.
96 133
97run_nofork_applet() saves/inits/restores option parsing, xfunc_error_retval, 134run_nofork_applet() saves/inits/restores option parsing, xfunc_error_retval,
98applet_name. Thus, for example, caller does not need to worry about 135logmode, applet_name. Thus, for example, caller does not need to worry about
99option_mask32 getting trashed. 136option_mask32 getting trashed.
100 137
101 138
@@ -104,22 +141,3 @@ option_mask32 getting trashed.
104It's the same trusty spawn_and_wait(argv). If FEATURE_PREFER_APPLETS=y, 141It's the same trusty spawn_and_wait(argv). If FEATURE_PREFER_APPLETS=y,
105it does NOEXEC trick. It resets xfunc_error_retval = 1 and 142it does NOEXEC trick. It resets xfunc_error_retval = 1 and
106logmode = LOGMODE_STDIO in the child. 143logmode = LOGMODE_STDIO in the child.
107
108
109 Relevant CONFIG options
110
111FEATURE_PREFER_APPLETS
112 BB_EXECVP(cmd, argv) will try to exec /proc/self/exe
113 if command's name matches some applet name;
114 spawn_and_wait(argv) will do NOFORK/NOEXEC tricks
115
116//TODO: the above two things probably should have separate options?
117
118FEATURE_SH_STANDALONE
119 shells will try to exec /proc/self/exe if command's name matches
120 some applet name; shells will do NOEXEC trick on NOEXEC applets
121
122//TODO: split (same as for PREFER_APPLETS)
123
124FEATURE_SH_NOFORK
125 shells will do NOFORK trick on NOFORK applets
diff --git a/e2fsprogs/chattr.c b/e2fsprogs/chattr.c
index 72327d728..76a5253b6 100644
--- a/e2fsprogs/chattr.c
+++ b/e2fsprogs/chattr.c
@@ -15,14 +15,17 @@
15//config: help 15//config: help
16//config: chattr changes the file attributes on a second extended file system. 16//config: chattr changes the file attributes on a second extended file system.
17 17
18//applet:IF_CHATTR(APPLET(chattr, BB_DIR_BIN, BB_SUID_DROP)) 18//applet:IF_CHATTR(APPLET_NOEXEC(chattr, chattr, BB_DIR_BIN, BB_SUID_DROP, chattr))
19 19
20//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o 20//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
21 21
22//usage:#define chattr_trivial_usage 22//usage:#define chattr_trivial_usage
23//usage: "[-R] [-+=AacDdijsStTu] [-v VERSION] [FILE]..." 23//usage: "[-R] [-v VERSION] [-+=AacDdijsStTu] FILE..."
24//usage:#define chattr_full_usage "\n\n" 24//usage:#define chattr_full_usage "\n\n"
25//usage: "Change ext2 file attributes\n" 25//usage: "Change ext2 file attributes\n"
26//usage: "\n -R Recurse"
27//usage: "\n -v VER Set version/generation number"
28//-V, -f accepted but ignored
26//usage: "\nModifiers:" 29//usage: "\nModifiers:"
27//usage: "\n -,+,= Remove/add/set attributes" 30//usage: "\n -,+,= Remove/add/set attributes"
28//usage: "\nAttributes:" 31//usage: "\nAttributes:"
@@ -37,8 +40,6 @@
37//usage: "\n S Write synchronously" 40//usage: "\n S Write synchronously"
38//usage: "\n t Disable tail-merging of partial blocks with other files" 41//usage: "\n t Disable tail-merging of partial blocks with other files"
39//usage: "\n u Allow file to be undeleted" 42//usage: "\n u Allow file to be undeleted"
40//usage: "\n -R Recurse"
41//usage: "\n -v VER Set version/generation number"
42 43
43#include "libbb.h" 44#include "libbb.h"
44#include "e2fs_lib.h" 45#include "e2fs_lib.h"
@@ -52,7 +53,7 @@ struct globals {
52 unsigned long version; 53 unsigned long version;
53 unsigned long af; 54 unsigned long af;
54 unsigned long rf; 55 unsigned long rf;
55 smallint flags; 56 int flags;
56 smallint recursive; 57 smallint recursive;
57}; 58};
58 59
@@ -64,10 +65,11 @@ static unsigned long get_flag(char c)
64 bb_show_usage(); 65 bb_show_usage();
65} 66}
66 67
67static int decode_arg(const char *arg, struct globals *gp) 68static char** decode_arg(char **argv, struct globals *gp)
68{ 69{
69 unsigned long *fl; 70 unsigned long *fl;
70 char opt = *arg++; 71 const char *arg = *argv;
72 char opt = *arg;
71 73
72 fl = &gp->af; 74 fl = &gp->af;
73 if (opt == '-') { 75 if (opt == '-') {
@@ -75,15 +77,43 @@ static int decode_arg(const char *arg, struct globals *gp)
75 fl = &gp->rf; 77 fl = &gp->rf;
76 } else if (opt == '+') { 78 } else if (opt == '+') {
77 gp->flags |= OPT_ADD; 79 gp->flags |= OPT_ADD;
78 } else if (opt == '=') { 80 } else { /* if (opt == '=') */
79 gp->flags |= OPT_SET; 81 gp->flags |= OPT_SET;
80 } else 82 }
81 return 0;
82 83
83 while (*arg) 84 while (*++arg) {
84 *fl |= get_flag(*arg++); 85 if (opt == '-') {
86//e2fsprogs-1.43.1 accepts:
87// "-RRR", "-RRRv VER" and even "-ARRRva VER" and "-vvv V1 V2 V3"
88// but not "-vVER".
89// IOW: options are parsed as part of "remove attrs" strings,
90// if "v" is seen, next argv[] is VER, even if more opts/attrs follow in this argv[]!
91 if (*arg == 'R') {
92 gp->recursive = 1;
93 continue;
94 }
95 if (*arg == 'V') {
96 /*"verbose and print program version" (nop for now) */;
97 continue;
98 }
99 if (*arg == 'f') {
100 /*"suppress most error messages" (nop) */;
101 continue;
102 }
103 if (*arg == 'v') {
104 if (!*++argv)
105 bb_show_usage();
106 gp->version = xatoul(*argv);
107 gp->flags |= OPT_SET_VER;
108 continue;
109 }
110//TODO: "-p PROJECT_NUM" ?
111 /* not a known option, try as an attribute */
112 }
113 *fl |= get_flag(*arg);
114 }
85 115
86 return 1; 116 return argv;
87} 117}
88 118
89static void change_attributes(const char *name, struct globals *gp); 119static void change_attributes(const char *name, struct globals *gp);
@@ -133,7 +163,7 @@ static void change_attributes(const char *name, struct globals *gp)
133 fsflags &= ~gp->rf; 163 fsflags &= ~gp->rf;
134 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */ 164 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
135 fsflags |= gp->af; 165 fsflags |= gp->af;
136 /* What is this? And why it's not done for SET case? */ 166// What is this? And why it's not done for SET case?
137 if (!S_ISDIR(st.st_mode)) 167 if (!S_ISDIR(st.st_mode))
138 fsflags &= ~EXT2_DIRSYNC_FL; 168 fsflags &= ~EXT2_DIRSYNC_FL;
139 } 169 }
@@ -149,36 +179,22 @@ int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
149int chattr_main(int argc UNUSED_PARAM, char **argv) 179int chattr_main(int argc UNUSED_PARAM, char **argv)
150{ 180{
151 struct globals g; 181 struct globals g;
152 char *arg;
153 182
154 memset(&g, 0, sizeof(g)); 183 memset(&g, 0, sizeof(g));
155 184
156 /* parse the args */ 185 /* parse the args */
157 while ((arg = *++argv)) { 186 for (;;) {
158 /* take care of -R and -v <version> */ 187 char *arg = *++argv;
159 if (arg[0] == '-' 188 if (!arg)
160 && (arg[1] == 'R' || arg[1] == 'v') 189 bb_show_usage();
161 && !arg[2] 190 if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=')
162 ) {
163 if (arg[1] == 'R') {
164 g.recursive = 1;
165 continue;
166 }
167 /* arg[1] == 'v' */
168 if (!*++argv)
169 bb_show_usage();
170 g.version = xatoul(*argv);
171 g.flags |= OPT_SET_VER;
172 continue;
173 }
174
175 if (!decode_arg(arg, &g))
176 break; 191 break;
192
193 argv = decode_arg(argv, &g);
177 } 194 }
195 /* note: on loop exit, remaining argv[] is never empty */
178 196
179 /* run sanity checks on all the arguments given us */ 197 /* run sanity checks on all the arguments given us */
180 if (!*argv)
181 bb_show_usage();
182 if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM))) 198 if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
183 bb_error_msg_and_die("= is incompatible with - and +"); 199 bb_error_msg_and_die("= is incompatible with - and +");
184 if (g.rf & g.af) 200 if (g.rf & g.af)
diff --git a/e2fsprogs/e2fs_lib.h b/e2fsprogs/e2fs_lib.h
index f2ae56f43..ae28c353b 100644
--- a/e2fsprogs/e2fs_lib.h
+++ b/e2fsprogs/e2fs_lib.h
@@ -26,7 +26,7 @@ int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_f
26#define fgetflags(name, flags) fgetsetflags(name, flags, 0) 26#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
27#define fsetflags(name, flags) fgetsetflags(name, NULL, flags) 27#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
28 28
29/* Must be 1 for compatibility with `int long_format'. */ 29/* Must be 1 for compatibility with 'int long_format'. */
30#define PFOPT_LONG 1 30#define PFOPT_LONG 1
31/* Print file attributes on an ext2 file system */ 31/* Print file attributes on an ext2 file system */
32void print_e2flags(FILE *f, unsigned long flags, unsigned options); 32void print_e2flags(FILE *f, unsigned long flags, unsigned options);
diff --git a/e2fsprogs/lsattr.c b/e2fsprogs/lsattr.c
index 756d26832..56c1187c1 100644
--- a/e2fsprogs/lsattr.c
+++ b/e2fsprogs/lsattr.c
@@ -16,7 +16,8 @@
16//config: help 16//config: help
17//config: lsattr lists the file attributes on a second extended file system. 17//config: lsattr lists the file attributes on a second extended file system.
18 18
19//applet:IF_LSATTR(APPLET(lsattr, BB_DIR_BIN, BB_SUID_DROP)) 19//applet:IF_LSATTR(APPLET_NOEXEC(lsattr, lsattr, BB_DIR_BIN, BB_SUID_DROP, lsattr))
20/* ls is NOEXEC, so we should be too! ;) */
20 21
21//kbuild:lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o 22//kbuild:lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o
22 23
diff --git a/e2fsprogs/tune2fs.c b/e2fsprogs/tune2fs.c
index 95411db5f..a1caf011c 100644
--- a/e2fsprogs/tune2fs.c
+++ b/e2fsprogs/tune2fs.c
@@ -13,7 +13,7 @@
13//config: tune2fs allows the system administrator to adjust various tunable 13//config: tune2fs allows the system administrator to adjust various tunable
14//config: filesystem parameters on Linux ext2/ext3 filesystems. 14//config: filesystem parameters on Linux ext2/ext3 filesystems.
15 15
16//applet:IF_TUNE2FS(APPLET(tune2fs, BB_DIR_SBIN, BB_SUID_DROP)) 16//applet:IF_TUNE2FS(APPLET_NOEXEC(tune2fs, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, tune2fs))
17 17
18//TODO alias to "tune2fs -L LABEL": //applet:IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, e2label)) 18//TODO alias to "tune2fs -L LABEL": //applet:IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, e2label))
19 19
@@ -71,8 +71,7 @@ int tune2fs_main(int argc UNUSED_PARAM, char **argv)
71 struct ext2_super_block *sb; 71 struct ext2_super_block *sb;
72 int fd; 72 int fd;
73 73
74 opt_complementary = "=1"; 74 opts = getopt32(argv, "^" "L:c:i:C:" "\0" "=1", &label, &str_c, &str_i, &str_C);
75 opts = getopt32(argv, "L:c:i:C:", &label, &str_c, &str_i, &str_C);
76 if (!opts) 75 if (!opts)
77 bb_show_usage(); 76 bb_show_usage();
78 argv += optind; // argv[0] -- device 77 argv += optind; // argv[0] -- device
diff --git a/editors/awk.c b/editors/awk.c
index 56688d72c..2fc7d6102 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -70,7 +70,11 @@
70#endif 70#endif
71 71
72 72
73#define OPTSTR_AWK \ 73/* "+": stop on first non-option:
74 * $ awk 'BEGIN { for(i=1; i<ARGC; ++i) { print i ": " ARGV[i] }}' -argz
75 * 1: -argz
76 */
77#define OPTSTR_AWK "+" \
74 "F:v:*f:*" \ 78 "F:v:*f:*" \
75 IF_FEATURE_AWK_GNU_EXTENSIONS("e:*") \ 79 IF_FEATURE_AWK_GNU_EXTENSIONS("e:*") \
76 "W:" 80 "W:"
@@ -230,7 +234,7 @@ typedef struct tsplitter_s {
230 */ 234 */
231#define TC_LENGTH (1 << 20) 235#define TC_LENGTH (1 << 20)
232#define TC_GETLINE (1 << 21) 236#define TC_GETLINE (1 << 21)
233#define TC_FUNCDECL (1 << 22) /* `function' `func' */ 237#define TC_FUNCDECL (1 << 22) /* 'function' 'func' */
234#define TC_BEGIN (1 << 23) 238#define TC_BEGIN (1 << 23)
235#define TC_END (1 << 24) 239#define TC_END (1 << 24)
236#define TC_EOF (1 << 25) 240#define TC_EOF (1 << 25)
diff --git a/editors/cmp.c b/editors/cmp.c
index f53d9603c..ec86c0ce2 100644
--- a/editors/cmp.c
+++ b/editors/cmp.c
@@ -36,7 +36,7 @@ static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT"u, line %u
36// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT"u %o %o\n" 36// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT"u %o %o\n"
37static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n"; 37static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n";
38 38
39static const char opt_chars[] ALIGN1 = "sl"; 39#define OPT_STR "sl"
40#define CMP_OPT_s (1<<0) 40#define CMP_OPT_s (1<<0)
41#define CMP_OPT_l (1<<1) 41#define CMP_OPT_l (1<<1)
42 42
@@ -52,11 +52,13 @@ int cmp_main(int argc UNUSED_PARAM, char **argv)
52 unsigned opt; 52 unsigned opt;
53 int retval = 0; 53 int retval = 0;
54 54
55 opt_complementary = "-1" 55 opt = getopt32(argv, "^"
56 OPT_STR
57 "\0" "-1"
56 IF_DESKTOP(":?4") 58 IF_DESKTOP(":?4")
57 IF_NOT_DESKTOP(":?2") 59 IF_NOT_DESKTOP(":?2")
58 ":l--s:s--l"; 60 ":l--s:s--l"
59 opt = getopt32(argv, opt_chars); 61 );
60 argv += optind; 62 argv += optind;
61 63
62 filename1 = *argv; 64 filename1 = *argv;
diff --git a/editors/diff.c b/editors/diff.c
index 62f558944..1ff399ffb 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -988,6 +988,11 @@ static const char diff_longopts[] ALIGN1 =
988 "starting-file\0" Required_argument "S" 988 "starting-file\0" Required_argument "S"
989 "minimal\0" No_argument "d" 989 "minimal\0" No_argument "d"
990 ; 990 ;
991# define GETOPT32 getopt32long
992# define LONGOPTS ,diff_longopts
993#else
994# define GETOPT32 getopt32
995# define LONGOPTS
991#endif 996#endif
992 997
993int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 998int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -1000,11 +1005,8 @@ int diff_main(int argc UNUSED_PARAM, char **argv)
1000 INIT_G(); 1005 INIT_G();
1001 1006
1002 /* exactly 2 params; collect multiple -L <label>; -U N */ 1007 /* exactly 2 params; collect multiple -L <label>; -U N */
1003 opt_complementary = "=2"; 1008 GETOPT32(argv, "^" "abdiL:*NqrsS:tTU:+wupBE" "\0" "=2"
1004#if ENABLE_FEATURE_DIFF_LONG_OPTIONS 1009 LONGOPTS,
1005 applet_long_options = diff_longopts;
1006#endif
1007 getopt32(argv, "abdiL:*NqrsS:tTU:+wupBE",
1008 &L_arg, &s_start, &opt_U_context); 1010 &L_arg, &s_start, &opt_U_context);
1009 argv += optind; 1011 argv += optind;
1010 while (L_arg) 1012 while (L_arg)
diff --git a/editors/ed.c b/editors/ed.c
index 7f21ded92..05797692c 100644
--- a/editors/ed.c
+++ b/editors/ed.c
@@ -360,7 +360,7 @@ static void addLines(int num)
360 * 0 on ctrl-C, 360 * 0 on ctrl-C,
361 * >0 length of input string, including terminating '\n' 361 * >0 length of input string, including terminating '\n'
362 */ 362 */
363 len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); 363 len = read_line_input(NULL, "", buf, sizeof(buf));
364 if (len <= 0) { 364 if (len <= 0) {
365 /* Previously, ctrl-C was exiting to shell. 365 /* Previously, ctrl-C was exiting to shell.
366 * Now we exit to ed prompt. Is in important? */ 366 * Now we exit to ed prompt. Is in important? */
@@ -789,7 +789,7 @@ static void doCommands(void)
789 * 0 on ctrl-C, 789 * 0 on ctrl-C,
790 * >0 length of input string, including terminating '\n' 790 * >0 length of input string, including terminating '\n'
791 */ 791 */
792 len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); 792 len = read_line_input(NULL, ": ", buf, sizeof(buf));
793 if (len <= 0) 793 if (len <= 0)
794 return; 794 return;
795 while (len && isspace(buf[--len])) 795 while (len && isspace(buf[--len]))
@@ -892,7 +892,7 @@ static void doCommands(void)
892 } 892 }
893 if (!dirty) 893 if (!dirty)
894 return; 894 return;
895 len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1); 895 len = read_line_input(NULL, "Really quit? ", buf, 16);
896 /* read error/EOF - no way to continue */ 896 /* read error/EOF - no way to continue */
897 if (len < 0) 897 if (len < 0)
898 return; 898 return;
diff --git a/editors/patch_bbox.c b/editors/patch_bbox.c
index aae7b7987..8e09ef488 100644
--- a/editors/patch_bbox.c
+++ b/editors/patch_bbox.c
@@ -111,10 +111,9 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
111 "no-backup-if-mismatch\0" No_argument "\xff" /*ignored*/ 111 "no-backup-if-mismatch\0" No_argument "\xff" /*ignored*/
112# endif 112# endif
113 ; 113 ;
114 applet_long_options = patch_longopts;
115#endif 114#endif
116 /* -f,-E,-g are ignored */ 115 /* -f,-E,-g are ignored */
117 opt = getopt32(argv, "p:i:RN""fEg:", &p, &i, NULL); 116 opt = getopt32long(argv, "p:i:RN""fEg:", patch_longopts, &p, &i, NULL);
118 if (opt & OPT_R) 117 if (opt & OPT_R)
119 plus = '-'; 118 plus = '-';
120 patch_level = xatoi(p); /* can be negative! */ 119 patch_level = xatoi(p); /* can be negative! */
diff --git a/editors/sed.c b/editors/sed.c
index e10078b7c..d6e25aedc 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -68,8 +68,8 @@
68//applet:IF_SED(APPLET(sed, BB_DIR_BIN, BB_SUID_DROP)) 68//applet:IF_SED(APPLET(sed, BB_DIR_BIN, BB_SUID_DROP))
69 69
70//usage:#define sed_trivial_usage 70//usage:#define sed_trivial_usage
71//usage: "[-inrE] [-f FILE]... [-e CMD]... [FILE]...\n" 71//usage: "[-i[SFX]] [-nrE] [-f FILE]... [-e CMD]... [FILE]...\n"
72//usage: "or: sed [-inrE] CMD [FILE]..." 72//usage: "or: sed [-i[SFX]] [-nrE] CMD [FILE]..."
73//usage:#define sed_full_usage "\n\n" 73//usage:#define sed_full_usage "\n\n"
74//usage: " -e CMD Add CMD to sed commands to be executed" 74//usage: " -e CMD Add CMD to sed commands to be executed"
75//usage: "\n -f FILE Add FILE contents to sed commands to be executed" 75//usage: "\n -f FILE Add FILE contents to sed commands to be executed"
@@ -1512,21 +1512,21 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1512 /* do normal option parsing */ 1512 /* do normal option parsing */
1513 opt_e = opt_f = NULL; 1513 opt_e = opt_f = NULL;
1514 opt_i = NULL; 1514 opt_i = NULL;
1515 opt_complementary = "nn"; /* count -n */
1516
1517 IF_LONG_OPTS(applet_long_options = sed_longopts);
1518
1519 /* -i must be first, to match OPT_in_place definition */ 1515 /* -i must be first, to match OPT_in_place definition */
1520 /* -E is a synonym of -r: 1516 /* -E is a synonym of -r:
1521 * GNU sed 4.2.1 mentions it in neither --help 1517 * GNU sed 4.2.1 mentions it in neither --help
1522 * nor manpage, but does recognize it. 1518 * nor manpage, but does recognize it.
1523 */ 1519 */
1524 opt = getopt32(argv, "i::rEne:*f:*", &opt_i, &opt_e, &opt_f, 1520 opt = getopt32long(argv, "^"
1525 &G.be_quiet); /* counter for -n */ 1521 "i::rEne:*f:*"
1522 "\0" "nn"/*count -n*/,
1523 sed_longopts,
1524 &opt_i, &opt_e, &opt_f,
1525 &G.be_quiet); /* counter for -n */
1526 //argc -= optind; 1526 //argc -= optind;
1527 argv += optind; 1527 argv += optind;
1528 if (opt & OPT_in_place) { // -i 1528 if (opt & OPT_in_place) { // -i
1529 atexit(cleanup_outname); 1529 die_func = cleanup_outname;
1530 } 1530 }
1531 if (opt & (2|4)) 1531 if (opt & (2|4))
1532 G.regex_type |= REG_EXTENDED; // -r or -E 1532 G.regex_type |= REG_EXTENDED; // -r or -E
diff --git a/findutils/find.c b/findutils/find.c
index 69baf065d..5857a3f44 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -15,7 +15,7 @@
15 * # find file.txt -exec 'echo {}' '{} {}' ';' 15 * # find file.txt -exec 'echo {}' '{} {}' ';'
16 * find: echo file.txt: No such file or directory 16 * find: echo file.txt: No such file or directory
17 * # find file.txt -exec 'echo' '{} {}' '; ' 17 * # find file.txt -exec 'echo' '{} {}' '; '
18 * find: missing argument to `-exec' 18 * find: missing argument to '-exec'
19 * # find file.txt -exec 'echo {}' '{} {}' ';' junk 19 * # find file.txt -exec 'echo {}' '{} {}' ';' junk
20 * find: paths must precede expression 20 * find: paths must precede expression
21 * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';' 21 * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';'
diff --git a/findutils/grep.c b/findutils/grep.c
index 568ab12c2..7c5f73d2f 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -107,6 +107,7 @@
107//usage:#define fgrep_trivial_usage NOUSAGE_STR 107//usage:#define fgrep_trivial_usage NOUSAGE_STR
108//usage:#define fgrep_full_usage "" 108//usage:#define fgrep_full_usage ""
109 109
110/* -e,-f are lists; -m,-A,-B,-C have numeric param */
110#define OPTSTR_GREP \ 111#define OPTSTR_GREP \
111 "lnqvscFiHhe:*f:*Lorm:+wx" \ 112 "lnqvscFiHhe:*f:*Lorm:+wx" \
112 IF_FEATURE_GREP_CONTEXT("A:+B:+C:+") \ 113 IF_FEATURE_GREP_CONTEXT("A:+B:+C:+") \
@@ -686,11 +687,9 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
686 687
687 /* do normal option parsing */ 688 /* do normal option parsing */
688#if ENABLE_FEATURE_GREP_CONTEXT 689#if ENABLE_FEATURE_GREP_CONTEXT
689 /* -H unsets -h; -C unsets -A,-B; -e,-f are lists; 690 /* -H unsets -h; -C unsets -A,-B */
690 * -m,-A,-B,-C have numeric param */
691 opt_complementary = "H-h:C-AB";
692 opts = getopt32(argv, 691 opts = getopt32(argv,
693 OPTSTR_GREP, 692 "^" OPTSTR_GREP "\0" "H-h:C-AB",
694 &pattern_head, &fopt, &max_matches, 693 &pattern_head, &fopt, &max_matches,
695 &lines_after, &lines_before, &Copt); 694 &lines_after, &lines_before, &Copt);
696 695
@@ -716,9 +715,7 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
716 } 715 }
717#else 716#else
718 /* with auto sanity checks */ 717 /* with auto sanity checks */
719 /* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */ 718 getopt32(argv, "^" OPTSTR_GREP "\0" "H-h:c-n:q-n:l-n:", // why trailing ":"?
720 opt_complementary = "H-h:c-n:q-n:l-n:";
721 getopt32(argv, OPTSTR_GREP,
722 &pattern_head, &fopt, &max_matches); 719 &pattern_head, &fopt, &max_matches);
723#endif 720#endif
724 invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */ 721 invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
diff --git a/findutils/xargs.c b/findutils/xargs.c
index e5384d14c..97afef039 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -508,13 +508,8 @@ int xargs_main(int argc, char **argv)
508 508
509 INIT_G(); 509 INIT_G();
510 510
511#if ENABLE_DESKTOP && ENABLE_LONG_OPTS 511 opt = getopt32long(argv, OPTION_STR,
512 /* For example, Fedora's build system uses --no-run-if-empty */ 512 "no-run-if-empty\0" No_argument "r",
513 applet_long_options =
514 "no-run-if-empty\0" No_argument "r"
515 ;
516#endif
517 opt = getopt32(argv, OPTION_STR,
518 &max_args, &max_chars, &G.eof_str, &G.eof_str 513 &max_args, &max_chars, &G.eof_str, &G.eof_str
519 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str) 514 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
520 ); 515 );
diff --git a/include/bb_archive.h b/include/bb_archive.h
index c118fa7ec..acb3c3cbd 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -74,9 +74,6 @@ typedef struct archive_handle_t {
74 /* Currently processed file's header */ 74 /* Currently processed file's header */
75 file_header_t *file_header; 75 file_header_t *file_header;
76 76
77 /* List of symlink placeholders */
78 llist_t *symlink_placeholders;
79
80 /* Process the header component, e.g. tar -t */ 77 /* Process the header component, e.g. tar -t */
81 void FAST_FUNC (*action_header)(const file_header_t *); 78 void FAST_FUNC (*action_header)(const file_header_t *);
82 79
@@ -210,6 +207,7 @@ void seek_by_jump(int fd, off_t amount) FAST_FUNC;
210void seek_by_read(int fd, off_t amount) FAST_FUNC; 207void seek_by_read(int fd, off_t amount) FAST_FUNC;
211 208
212const char *strip_unsafe_prefix(const char *str) FAST_FUNC; 209const char *strip_unsafe_prefix(const char *str) FAST_FUNC;
210int unsafe_symlink_target(const char *target) FAST_FUNC;
213 211
214void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; 212void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC;
215const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; 213const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC;
diff --git a/include/libbb.h b/include/libbb.h
index 61ceb6085..6073cbf4a 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -365,7 +365,7 @@ unsigned long long monotonic_ms(void) FAST_FUNC;
365unsigned monotonic_sec(void) FAST_FUNC; 365unsigned monotonic_sec(void) FAST_FUNC;
366 366
367extern void chomp(char *s) FAST_FUNC; 367extern void chomp(char *s) FAST_FUNC;
368extern void trim(char *s) FAST_FUNC; 368extern char *trim(char *s) FAST_FUNC;
369extern char *skip_whitespace(const char *) FAST_FUNC; 369extern char *skip_whitespace(const char *) FAST_FUNC;
370extern char *skip_non_whitespace(const char *) FAST_FUNC; 370extern char *skip_non_whitespace(const char *) FAST_FUNC;
371extern char *skip_dev_pfx(const char *tty_name) FAST_FUNC; 371extern char *skip_dev_pfx(const char *tty_name) FAST_FUNC;
@@ -886,7 +886,7 @@ unsigned bb_clk_tck(void) FAST_FUNC;
886 886
887#if SEAMLESS_COMPRESSION 887#if SEAMLESS_COMPRESSION
888/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ 888/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */
889extern int setup_unzip_on_fd(int fd, int fail_if_not_compressed) FAST_FUNC; 889int setup_unzip_on_fd(int fd, int fail_if_not_compressed) FAST_FUNC;
890/* Autodetects .gz etc */ 890/* Autodetects .gz etc */
891extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC; 891extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC;
892extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; 892extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
@@ -895,6 +895,8 @@ extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
895# define open_zipped(fname, fail_if_not_compressed) open((fname), O_RDONLY); 895# define open_zipped(fname, fail_if_not_compressed) open((fname), O_RDONLY);
896# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p)) 896# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p))
897#endif 897#endif
898/* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */
899void setup_lzma_on_fd(int fd) FAST_FUNC;
898 900
899extern ssize_t safe_write(int fd, const void *buf, size_t count) FAST_FUNC; 901extern ssize_t safe_write(int fd, const void *buf, size_t count) FAST_FUNC;
900// NB: will return short write on error, not -1, 902// NB: will return short write on error, not -1,
@@ -1138,9 +1140,15 @@ int wait_for_exitstatus(pid_t pid) FAST_FUNC;
1138int spawn_and_wait(char **argv) FAST_FUNC; 1140int spawn_and_wait(char **argv) FAST_FUNC;
1139/* Does NOT check that applet is NOFORK, just blindly runs it */ 1141/* Does NOT check that applet is NOFORK, just blindly runs it */
1140int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; 1142int run_nofork_applet(int applet_no, char **argv) FAST_FUNC;
1143void run_noexec_applet_and_exit(int a, const char *name, char **argv) NORETURN FAST_FUNC;
1141#ifndef BUILD_INDIVIDUAL 1144#ifndef BUILD_INDIVIDUAL
1142extern int find_applet_by_name(const char *name) FAST_FUNC; 1145int find_applet_by_name(const char *name) FAST_FUNC;
1143extern void run_applet_no_and_exit(int a, const char *name, char **argv) NORETURN FAST_FUNC; 1146void run_applet_no_and_exit(int a, const char *name, char **argv) NORETURN FAST_FUNC;
1147#endif
1148#if defined(__linux__)
1149void set_task_comm(const char *comm) FAST_FUNC;
1150#else
1151# define set_task_comm(name) ((void)0)
1144#endif 1152#endif
1145#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_NOFORK 1153#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_NOFORK
1146extern int long_running_applet(int applet_no) FAST_FUNC; 1154extern int long_running_applet(int applet_no) FAST_FUNC;
@@ -1198,21 +1206,26 @@ enum {
1198#endif 1206#endif
1199void bb_daemonize_or_rexec(int flags, char **argv) FAST_FUNC; 1207void bb_daemonize_or_rexec(int flags, char **argv) FAST_FUNC;
1200void bb_sanitize_stdio(void) FAST_FUNC; 1208void bb_sanitize_stdio(void) FAST_FUNC;
1209#define bb_daemon_helper(arg) bb_daemonize_or_rexec((arg) | DAEMON_ONLY_SANITIZE, NULL)
1201/* Clear dangerous stuff, set PATH. Return 1 if was run by different user. */ 1210/* Clear dangerous stuff, set PATH. Return 1 if was run by different user. */
1202int sanitize_env_if_suid(void) FAST_FUNC; 1211int sanitize_env_if_suid(void) FAST_FUNC;
1203 1212
1204 1213
1214/* For top, ps. Some argv[i] are replaced by malloced "-opt" strings */
1215void make_all_argv_opts(char **argv) FAST_FUNC;
1205char* single_argv(char **argv) FAST_FUNC; 1216char* single_argv(char **argv) FAST_FUNC;
1206extern const char *const bb_argv_dash[]; /* "-", NULL */ 1217extern const char *const bb_argv_dash[]; /* { "-", NULL } */
1207extern const char *opt_complementary;
1208#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
1209#define No_argument "\0"
1210#define Required_argument "\001"
1211#define Optional_argument "\002"
1212extern const char *applet_long_options;
1213#endif
1214extern uint32_t option_mask32; 1218extern uint32_t option_mask32;
1215extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC; 1219uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC;
1220# define No_argument "\0"
1221# define Required_argument "\001"
1222# define Optional_argument "\002"
1223#if ENABLE_LONG_OPTS
1224uint32_t getopt32long(char **argv, const char *optstring, const char *longopts, ...) FAST_FUNC;
1225#else
1226#define getopt32long(argv,optstring,longopts,...) \
1227 getopt32(argv,optstring,##__VA_ARGS__)
1228#endif
1216/* BSD-derived getopt() functions require that optind be set to 1 in 1229/* BSD-derived getopt() functions require that optind be set to 1 in
1217 * order to reset getopt() state. This used to be generally accepted 1230 * order to reset getopt() state. This used to be generally accepted
1218 * way of resetting getopt(). However, glibc's getopt() 1231 * way of resetting getopt(). However, glibc's getopt()
@@ -1429,6 +1442,11 @@ enum {
1429 // keep a copy of current line 1442 // keep a copy of current line
1430 PARSE_KEEP_COPY = 0x00200000 * ENABLE_FEATURE_CROND_D, 1443 PARSE_KEEP_COPY = 0x00200000 * ENABLE_FEATURE_CROND_D,
1431 PARSE_EOL_COMMENTS = 0x00400000, // comments are recognized even if they aren't the first char 1444 PARSE_EOL_COMMENTS = 0x00400000, // comments are recognized even if they aren't the first char
1445 PARSE_ALT_COMMENTS = 0x00800000, // delim[0] and delim[1] are two different allowed comment chars
1446 // (so far, delim[0] will only work as comment char for full-line comment)
1447 // (IOW: it works as if PARSE_EOL_COMMENTS is not set. sysctl applet is okay with this)
1448 PARSE_WS_COMMENTS = 0x01000000, // comments are recognized even if there is whitespace before
1449 // ("line start><space><tab><space>#comment" is also comment, not only "line start>#comment")
1432 // NORMAL is: 1450 // NORMAL is:
1433 // * remove leading and trailing delimiters and collapse 1451 // * remove leading and trailing delimiters and collapse
1434 // multiple delimiters into one 1452 // multiple delimiters into one
@@ -1484,6 +1502,21 @@ extern void run_shell(const char *shell, int loginshell, const char **args) NORE
1484 */ 1502 */
1485const char *get_shell_name(void) FAST_FUNC; 1503const char *get_shell_name(void) FAST_FUNC;
1486 1504
1505unsigned cap_name_to_number(const char *cap) FAST_FUNC;
1506void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC;
1507void drop_capability(int cap_ordinal) FAST_FUNC;
1508/* Structures inside "struct caps" are Linux-specific and libcap-specific: */
1509#define DEFINE_STRUCT_CAPS \
1510struct caps { \
1511 struct __user_cap_header_struct header; \
1512 unsigned u32s; \
1513 struct __user_cap_data_struct data[2]; \
1514}
1515void getcaps(void *caps) FAST_FUNC;
1516
1517unsigned cap_name_to_number(const char *name) FAST_FUNC;
1518void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC;
1519
1487#if ENABLE_SELINUX 1520#if ENABLE_SELINUX
1488extern void renew_current_security_context(void) FAST_FUNC; 1521extern void renew_current_security_context(void) FAST_FUNC;
1489extern void set_current_security_context(security_context_t sid) FAST_FUNC; 1522extern void set_current_security_context(security_context_t sid) FAST_FUNC;
@@ -1668,9 +1701,9 @@ enum {
1668 * buffer[0] is used as a counter of buffered chars and must be 0 1701 * buffer[0] is used as a counter of buffered chars and must be 0
1669 * on first call. 1702 * on first call.
1670 * timeout: 1703 * timeout:
1671 * -2: do not poll for input; 1704 * -2: do not poll(-1) for input - read() it, return on EAGAIN at once
1672 * -1: poll(-1) (i.e. block); 1705 * -1: poll(-1) (i.e. block even on NONBLOCKed fd)
1673 * >=0: poll for TIMEOUT milliseconds, return -1/EAGAIN on timeout 1706 * >=0: poll() for TIMEOUT milliseconds, return -1/EAGAIN on timeout
1674 */ 1707 */
1675int64_t read_key(int fd, char *buffer, int timeout) FAST_FUNC; 1708int64_t read_key(int fd, char *buffer, int timeout) FAST_FUNC;
1676void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC; 1709void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC;
@@ -1686,6 +1719,7 @@ unsigned size_from_HISTFILESIZE(const char *hp) FAST_FUNC;
1686# endif 1719# endif
1687typedef struct line_input_t { 1720typedef struct line_input_t {
1688 int flags; 1721 int flags;
1722 int timeout;
1689 const char *path_lookup; 1723 const char *path_lookup;
1690# if MAX_HISTORY 1724# if MAX_HISTORY
1691 int cnt_history; 1725 int cnt_history;
@@ -1721,7 +1755,7 @@ line_input_t *new_line_input_t(int flags) FAST_FUNC;
1721 * 0 on ctrl-C (the line entered is still returned in 'command'), 1755 * 0 on ctrl-C (the line entered is still returned in 'command'),
1722 * >0 length of input string, including terminating '\n' 1756 * >0 length of input string, including terminating '\n'
1723 */ 1757 */
1724int read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) FAST_FUNC; 1758int read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize) FAST_FUNC;
1725void show_history(const line_input_t *st) FAST_FUNC; 1759void show_history(const line_input_t *st) FAST_FUNC;
1726# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 1760# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1727void save_history(line_input_t *st); 1761void save_history(line_input_t *st);
@@ -1729,7 +1763,7 @@ void save_history(line_input_t *st);
1729#else 1763#else
1730#define MAX_HISTORY 0 1764#define MAX_HISTORY 0
1731int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC; 1765int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC;
1732#define read_line_input(state, prompt, command, maxsize, timeout) \ 1766#define read_line_input(state, prompt, command, maxsize) \
1733 read_line_input(prompt, command, maxsize) 1767 read_line_input(prompt, command, maxsize)
1734#endif 1768#endif
1735 1769
diff --git a/include/platform.h b/include/platform.h
index 749169b0c..cc22f4fc9 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -277,6 +277,11 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING;
277#define put_unaligned_le32(val, buf) move_to_unaligned32(buf, SWAP_LE32(val)) 277#define put_unaligned_le32(val, buf) move_to_unaligned32(buf, SWAP_LE32(val))
278#define put_unaligned_be32(val, buf) move_to_unaligned32(buf, SWAP_BE32(val)) 278#define put_unaligned_be32(val, buf) move_to_unaligned32(buf, SWAP_BE32(val))
279 279
280/* unxz needs an aligned fixed-endian accessor.
281 * (however, the compiler does not realize it's aligned, the cast is still necessary)
282 */
283#define get_le32(u32p) ({ uint32_t v = *(bb__aliased_uint32_t*)(u32p); SWAP_LE32(v); })
284
280 285
281/* ---- Size-saving "small" ints (arch-dependent) ----------- */ 286/* ---- Size-saving "small" ints (arch-dependent) ----------- */
282 287
diff --git a/klibc-utils/Config.src b/klibc-utils/Config.src
new file mode 100644
index 000000000..fe7cb1315
--- /dev/null
+++ b/klibc-utils/Config.src
@@ -0,0 +1,10 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "klibc-utils"
7
8INSERT
9
10endmenu
diff --git a/klibc-utils/Kbuild.src b/klibc-utils/Kbuild.src
new file mode 100644
index 000000000..6b4fb7470
--- /dev/null
+++ b/klibc-utils/Kbuild.src
@@ -0,0 +1,9 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
4#
5# Licensed under GPLv2, see file LICENSE in this source tree.
6
7lib-y:=
8
9INSERT
diff --git a/klibc-utils/minips.c b/klibc-utils/minips.c
new file mode 100644
index 000000000..6003bf57b
--- /dev/null
+++ b/klibc-utils/minips.c
@@ -0,0 +1,12 @@
1/*
2 * Copyright (c) 2017 Denys Vlasenko <vda.linux@googlemail.com>
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6//config:config MINIPS
7//config: bool "minips (11 kb)"
8//config: default n # for god's sake, just use "ps" name in your scripts
9//config: help
10//config: Alias to "ps".
11
12/* applet and kbuild hooks are in ps.c */
diff --git a/klibc-utils/nuke.c b/klibc-utils/nuke.c
new file mode 100644
index 000000000..a5d2f8d70
--- /dev/null
+++ b/klibc-utils/nuke.c
@@ -0,0 +1,46 @@
1/*
2 * Copyright (c) 2017 Denys Vlasenko <vda.linux@googlemail.com>
3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 */
6//config:config NUKE
7//config: bool "nuke"
8//config: default y
9//config: help
10//config: Alias to "rm -rf".
11
12//applet:IF_NUKE(APPLET_NOEXEC(nuke, nuke, BB_DIR_BIN, BB_SUID_DROP, nuke))
13
14//kbuild:lib-$(CONFIG_NUKE) += nuke.o
15
16//usage:#define nuke_trivial_usage
17//usage: "DIR..."
18//usage:#define nuke_full_usage "\n\n"
19//usage: "Remove DIRs"
20
21#include "libbb.h"
22
23/* This is a NOEXEC applet. Be very careful! */
24
25int nuke_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
26int nuke_main(int argc UNUSED_PARAM, char **argv)
27{
28// klibc-utils do not check opts, will try to delete "-dir" args
29 //opt = getopt32(argv, "");
30 //argv += optind;
31
32 while (*++argv) {
33#if 0
34// klibc-utils do not check this, will happily operate on ".."
35 const char *base = bb_get_last_path_component_strip(*argv);
36 if (DOT_OR_DOTDOT(base)) {
37 bb_error_msg("can't remove '.' or '..'");
38 continue;
39 }
40#endif
41 remove_file(*argv, FILEUTILS_FORCE | FILEUTILS_RECUR);
42 }
43
44// klibc-utils do not indicate errors
45 return EXIT_SUCCESS;
46}
diff --git a/klibc-utils/resume.c b/klibc-utils/resume.c
new file mode 100644
index 000000000..de142f350
--- /dev/null
+++ b/klibc-utils/resume.c
@@ -0,0 +1,115 @@
1/*
2 * Copyright (c) 2017 Denys Vlasenko <vda.linux@googlemail.com>
3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 */
6//config:config RESUME
7//config: bool "resume"
8//config: default y
9//config: help
10//config: Resume from saved "suspend-to-disk" image
11
12//applet:IF_RESUME(APPLET_NOEXEC(resume, resume, BB_DIR_BIN, BB_SUID_DROP, resume))
13
14//kbuild:lib-$(CONFIG_RESUME) += resume.o
15
16#include "libbb.h"
17
18/* This is a NOEXEC applet. Be very careful! */
19
20/* name_to_dev_t() in klibc-utils supports extended device name formats,
21 * apart from the usual case where /dev/NAME already exists.
22 *
23 * - device number in hexadecimal represents itself (in dev_t layout).
24 * - device number in major:minor decimal represents itself.
25 * - if block device (or partition) with this name is found in sysfs.
26 * - if /dev/ prefix is not given, it is assumed.
27 *
28 * klibc-utils also recognizes these, but they don't work
29 * for "resume" tool purposes (thus we don't support them (yet?)):
30 * - /dev/nfs
31 * - /dev/ram (alias to /dev/ram0)
32 * - /dev/mtd
33 */
34static dev_t name_to_dev_t(const char *devname)
35{
36 char devfile[sizeof(int)*3 * 2 + 4];
37 char *sysname;
38 unsigned major_num, minor_num;
39 struct stat st;
40 int r;
41
42 if (strncmp(devname, "/dev/", 5) != 0) {
43 char *cptr;
44
45 cptr = strchr(devname, ':');
46 if (cptr) {
47 /* Colon-separated decimal device number? */
48 *cptr = '\0';
49 major_num = bb_strtou(devname, NULL, 10);
50 if (!errno)
51 minor_num = bb_strtou(cptr + 1, NULL, 10);
52 *cptr = ':';
53 if (!errno)
54 return makedev(major_num, minor_num);
55 } else {
56 /* Hexadecimal device number? */
57 dev_t res = (dev_t) bb_strtoul(devname, NULL, 16);
58 if (!errno)
59 return res;
60 }
61
62 devname = xasprintf("/dev/%s", devname);
63 }
64 /* Now devname is always "/dev/FOO" */
65
66 if (stat(devname, &st) == 0 && S_ISBLK(st.st_mode))
67 return st.st_rdev;
68
69 /* Full blockdevs as well as partitions may be visible
70 * in /sys/class/block/ even if /dev is not populated.
71 */
72 sysname = xasprintf("/sys/class/block/%s/dev", devname + 5);
73 r = open_read_close(sysname, devfile, sizeof(devfile) - 1);
74 //free(sysname);
75 if (r > 0) {
76 devfile[r] = '\0';
77 if (sscanf(devfile, "%u:%u", &major_num, &minor_num) == 2) {
78 return makedev(major_num, minor_num);
79 }
80 }
81
82 return (dev_t) 0;
83}
84
85//usage:#define resume_trivial_usage
86//usage: "BLOCKDEV [OFFSET]"
87//usage:#define resume_full_usage "\n"
88//usage: "\n""Restore system state from 'suspend-to-disk' data in BLOCKDEV"
89
90int resume_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
91int resume_main(int argc UNUSED_PARAM, char **argv)
92{
93 unsigned long long ofs;
94 dev_t resume_device;
95 char *s;
96 int fd;
97
98 argv++;
99 if (!argv[0])
100 bb_show_usage();
101
102 resume_device = name_to_dev_t(argv[0]);
103 if (major(resume_device) == 0) {
104 bb_error_msg_and_die("invalid resume device: %s", argv[0]);
105 }
106 ofs = (argv[1] ? xstrtoull(argv[1], 0) : 0);
107
108 fd = xopen("/sys/power/resume", O_WRONLY);
109 s = xasprintf("%u:%u:%llu", major(resume_device), minor(resume_device), ofs);
110
111 xwrite_str(fd, s);
112 /* if write() returns, resume did not succeed */
113
114 return EXIT_FAILURE; /* klibc-utils exits -1 aka 255 */
115}
diff --git a/klibc-utils/run-init.c b/klibc-utils/run-init.c
new file mode 100644
index 000000000..a70d1bfbf
--- /dev/null
+++ b/klibc-utils/run-init.c
@@ -0,0 +1,27 @@
1/*
2 * run-init implementation for busybox
3 *
4 * Copyright (c) 2017 Denys Vlasenko <vda.linux@gmail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8//config:config RUN_INIT
9//config: bool "run-init"
10//config: default y
11//config: select PLATFORM_LINUX
12//config: help
13//config: The run-init utility is used from initramfs to select a new
14//config: root device. Under initramfs, you have to use this instead of
15//config: pivot_root.
16//config:
17//config: Booting with initramfs extracts a gzipped cpio archive into rootfs
18//config: (which is a variant of ramfs/tmpfs). Because rootfs can't be moved
19//config: or unmounted, pivot_root will not work from initramfs. Instead,
20//config: run-init deletes everything out of rootfs (including itself),
21//config: does a mount --move that overmounts rootfs with the new root, and
22//config: then execs the specified init program.
23//config:
24//config: util-linux has a similar tool, switch-root.
25//config: run-init differs by also having a "-d CAPS_TO_DROP" option.
26
27/* applet and kbuild hooks are in switch_root.c */
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 401475f18..6330b6f8b 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -34,14 +34,6 @@
34# include <malloc.h> /* for mallopt */ 34# include <malloc.h> /* for mallopt */
35#endif 35#endif
36 36
37#include <sys/prctl.h>
38#ifndef PR_SET_NAME
39#define PR_SET_NAME 15
40#endif
41#ifndef PR_GET_NAME
42#define PR_GET_NAME 16
43#endif
44
45/* Declare <applet>_main() */ 37/* Declare <applet>_main() */
46#define PROTOTYPES 38#define PROTOTYPES
47#include "applets.h" 39#include "applets.h"
@@ -985,8 +977,6 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **ar
985{ 977{
986 int argc = string_array_len(argv); 978 int argc = string_array_len(argv);
987 979
988 /* Reinit some shared global data */
989 xfunc_error_retval = EXIT_FAILURE;
990 /* 980 /*
991 * We do not use argv[0]: do not want to repeat massaging of 981 * We do not use argv[0]: do not want to repeat massaging of
992 * "-/sbin/halt" -> "halt", for example. 982 * "-/sbin/halt" -> "halt", for example.
@@ -1163,15 +1153,14 @@ int main(int argc UNUSED_PARAM, char **argv)
1163 } 1153 }
1164 applet_name = bb_basename(applet_name); 1154 applet_name = bb_basename(applet_name);
1165 1155
1166# if defined(__linux__)
1167 /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */ 1156 /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */
1168 if (ENABLE_FEATURE_SH_STANDALONE 1157 if (ENABLE_FEATURE_SH_STANDALONE
1169 || ENABLE_FEATURE_PREFER_APPLETS 1158 || ENABLE_FEATURE_PREFER_APPLETS
1170 || !BB_MMU 1159 || !BB_MMU
1171 ) { 1160 ) {
1172 prctl(PR_SET_NAME, (long)applet_name, 0, 0, 0); 1161 if (NUM_APPLETS > 1)
1162 set_task_comm(applet_name);
1173 } 1163 }
1174# endif
1175 1164
1176 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ 1165 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
1177 run_applet_and_exit(applet_name, argv); 1166 run_applet_and_exit(applet_name, argv);
diff --git a/libbb/bb_pwd.c b/libbb/bb_pwd.c
index 4829b723a..dca0a150b 100644
--- a/libbb/bb_pwd.c
+++ b/libbb/bb_pwd.c
@@ -31,9 +31,9 @@ struct group* FAST_FUNC xgetgrnam(const char *name)
31 return gr; 31 return gr;
32} 32}
33 33
34
35struct passwd* FAST_FUNC xgetpwuid(uid_t uid) 34struct passwd* FAST_FUNC xgetpwuid(uid_t uid)
36{ 35{
36 /* Note: used in nofork applets (whoami), be careful not to leak anything */
37 struct passwd *pw = getpwuid(uid); 37 struct passwd *pw = getpwuid(uid);
38 if (!pw) 38 if (!pw)
39 bb_error_msg_and_die("unknown uid %u", (unsigned)uid); 39 bb_error_msg_and_die("unknown uid %u", (unsigned)uid);
@@ -50,6 +50,7 @@ struct group* FAST_FUNC xgetgrgid(gid_t gid)
50 50
51char* FAST_FUNC xuid2uname(uid_t uid) 51char* FAST_FUNC xuid2uname(uid_t uid)
52{ 52{
53 /* Note: used in nofork applets (whoami), be careful not to leak anything */
53 struct passwd *pw = xgetpwuid(uid); 54 struct passwd *pw = xgetpwuid(uid);
54 return pw->pw_name; 55 return pw->pw_name;
55} 56}
diff --git a/libbb/capability.c b/libbb/capability.c
new file mode 100644
index 000000000..f60062bfc
--- /dev/null
+++ b/libbb/capability.c
@@ -0,0 +1,126 @@
1/*
2 * Copyright (C) 2017 by <assafgordon@gmail.com>
3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 */
6//kbuild:lib-$(CONFIG_PLATFORM_LINUX) += capability.o
7
8#include <linux/capability.h>
9// #include <sys/capability.h>
10// This header is in libcap, but the functions are in libc.
11// Comment in the header says this above capset/capget:
12/* system calls - look to libc for function to system call mapping */
13extern int capset(cap_user_header_t header, cap_user_data_t data);
14extern int capget(cap_user_header_t header, const cap_user_data_t data);
15// so for bbox, let's just repeat the declarations.
16// This way, libcap needs not be installed in build environment.
17#include "libbb.h"
18
19static const char *const capabilities[] = {
20 "chown",
21 "dac_override",
22 "dac_read_search",
23 "fowner",
24 "fsetid",
25 "kill",
26 "setgid",
27 "setuid",
28 "setpcap",
29 "linux_immutable",
30 "net_bind_service",
31 "net_broadcast",
32 "net_admin",
33 "net_raw",
34 "ipc_lock",
35 "ipc_owner",
36 "sys_module",
37 "sys_rawio",
38 "sys_chroot",
39 "sys_ptrace",
40 "sys_pacct",
41 "sys_admin",
42 "sys_boot",
43 "sys_nice",
44 "sys_resource",
45 "sys_time",
46 "sys_tty_config",
47 "mknod",
48 "lease",
49 "audit_write",
50 "audit_control",
51 "setfcap",
52 "mac_override",
53 "mac_admin",
54 "syslog",
55 "wake_alarm",
56 "block_suspend",
57 "audit_read",
58};
59
60unsigned FAST_FUNC cap_name_to_number(const char *cap)
61{
62 unsigned i, n;
63
64 if ((sscanf(cap, "cap_%u", &n)) == 1) {
65 i = n;
66 goto found;
67 }
68 for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
69 if (strcasecmp(capabilities[i], cap) != 0)
70 goto found;
71 }
72 bb_error_msg_and_die("unknown capability '%s'", cap);
73
74 found:
75 if (!cap_valid(i))
76 bb_error_msg_and_die("unknown capability '%s'", cap);
77 return i;
78}
79
80void FAST_FUNC printf_cap(const char *pfx, unsigned cap_no)
81{
82 if (cap_no < ARRAY_SIZE(capabilities)) {
83 printf("%s%s", pfx, capabilities[cap_no]);
84 return;
85 }
86 printf("%scap_%u", pfx, cap_no);
87}
88
89DEFINE_STRUCT_CAPS;
90
91void FAST_FUNC getcaps(void *arg)
92{
93 static const uint8_t versions[] = {
94 _LINUX_CAPABILITY_U32S_3, /* = 2 (fits into byte) */
95 _LINUX_CAPABILITY_U32S_2, /* = 2 */
96 _LINUX_CAPABILITY_U32S_1, /* = 1 */
97 };
98 int i;
99 struct caps *caps = arg;
100
101 caps->header.pid = 0;
102 for (i = 0; i < ARRAY_SIZE(versions); i++) {
103 caps->header.version = versions[i];
104 if (capget(&caps->header, NULL) == 0)
105 goto got_it;
106 }
107 bb_simple_perror_msg_and_die("capget");
108 got_it:
109
110 switch (caps->header.version) {
111 case _LINUX_CAPABILITY_VERSION_1:
112 caps->u32s = _LINUX_CAPABILITY_U32S_1;
113 break;
114 case _LINUX_CAPABILITY_VERSION_2:
115 caps->u32s = _LINUX_CAPABILITY_U32S_2;
116 break;
117 case _LINUX_CAPABILITY_VERSION_3:
118 caps->u32s = _LINUX_CAPABILITY_U32S_3;
119 break;
120 default:
121 bb_error_msg_and_die("unsupported capability version");
122 }
123
124 if (capget(&caps->header, caps->data) != 0)
125 bb_simple_perror_msg_and_die("capget");
126}
diff --git a/libbb/change_identity.c b/libbb/change_identity.c
index d48d86326..431f72c8c 100644
--- a/libbb/change_identity.c
+++ b/libbb/change_identity.c
@@ -15,7 +15,7 @@
15 * may be used to endorse or promote products derived from this software 15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission. 16 * without specific prior written permission.
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND 18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ''AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE 21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index cb6d12359..5372d8680 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -374,7 +374,10 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
374 int r = symlink(lpath, dest); 374 int r = symlink(lpath, dest);
375 free(lpath); 375 free(lpath);
376 if (r < 0) { 376 if (r < 0) {
377 bb_perror_msg("can't create symlink '%s'", dest); 377 /* shared message */
378 bb_perror_msg("can't create %slink '%s' to '%s'",
379 "sym", dest, lpath
380 );
378 return -1; 381 return -1;
379 } 382 }
380 if (flags & FILEUTILS_PRESERVE_STATUS) 383 if (flags & FILEUTILS_PRESERVE_STATUS)
diff --git a/libbb/correct_password.c b/libbb/correct_password.c
index f4635a5bc..51928f68d 100644
--- a/libbb/correct_password.c
+++ b/libbb/correct_password.c
@@ -15,7 +15,7 @@
15 * may be used to endorse or promote products derived from this software 15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission. 16 * without specific prior written permission.
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND 18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ''AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE 21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
diff --git a/libbb/dump.c b/libbb/dump.c
index 211a1ed9e..e23b71294 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -828,7 +828,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
828 * may be used to endorse or promote products derived from this software 828 * may be used to endorse or promote products derived from this software
829 * without specific prior written permission. 829 * without specific prior written permission.
830 * 830 *
831 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 831 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
832 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 832 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
833 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 833 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
834 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 834 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/libbb/get_console.c b/libbb/get_console.c
index 9b6407bd0..96b339ca7 100644
--- a/libbb/get_console.c
+++ b/libbb/get_console.c
@@ -64,7 +64,6 @@ int FAST_FUNC get_console_fd_or_die(void)
64 } 64 }
65 65
66 bb_error_msg_and_die("can't open console"); 66 bb_error_msg_and_die("can't open console");
67 /*return fd; - total failure */
68} 67}
69 68
70/* From <linux/vt.h> */ 69/* From <linux/vt.h> */
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 80f4cc060..f778c6e89 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -6,12 +6,13 @@
6 * 6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */ 8 */
9 9#if ENABLE_LONG_OPTS
10#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
11# include <getopt.h> 10# include <getopt.h>
12#endif 11#endif
13#include "libbb.h" 12#include "libbb.h"
14 13
14//kbuild:lib-y += getopt32.o
15
15/* Documentation 16/* Documentation
16 17
17uint32_t 18uint32_t
@@ -95,20 +96,23 @@ getopt32(char **argv, const char *applet_opts, ...)
95 env -i ls -d / 96 env -i ls -d /
96 Here we want env to process just the '-i', not the '-d'. 97 Here we want env to process just the '-i', not the '-d'.
97 98
98 "!" Report bad option, missing required options, 99 "!" Report bad options, missing required options,
99 inconsistent options with all-ones return value (instead of abort). 100 inconsistent options with all-ones return value (instead of abort).
100 101
101const char *applet_long_options 102 "^" options string is "^optchars""\0""opt_complementary".
103
104uint32_t
105getopt32long(char **argv, const char *applet_opts, const char *logopts...)
102 106
103 This struct allows you to define long options: 107 This allows you to define long options:
104 108
105 static const char applet_longopts[] ALIGN1 = 109 static const char applet_longopts[] ALIGN1 =
106 //"name\0" has_arg val 110 //"name\0" has_arg val
107 "verbose\0" No_argument "v" 111 "verbose\0" No_argument "v"
108 ; 112 ;
109 applet_long_options = applet_longopts; 113 opt = getopt32long(argv, applet_opts, applet_longopts, ...);
110 114
111 The last member of struct option (val) typically is set to 115 The last element (val) typically is set to
112 matching short option from applet_opts. If there is no matching 116 matching short option from applet_opts. If there is no matching
113 char in applet_opts, then: 117 char in applet_opts, then:
114 - return bit has next position after short options 118 - return bit has next position after short options
@@ -119,7 +123,7 @@ const char *applet_long_options
119 config process and not a required feature. The current standard 123 config process and not a required feature. The current standard
120 is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS. 124 is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
121 125
122const char *opt_complementary 126opt_complementary - option modifiers.
123 127
124 ":" The colon (":") is used to separate groups of two or more chars 128 ":" The colon (":") is used to separate groups of two or more chars
125 and/or groups of chars and special characters (stating some 129 and/or groups of chars and special characters (stating some
@@ -130,8 +134,7 @@ const char *opt_complementary
130 Their flags will be turned on if the main option is found even 134 Their flags will be turned on if the main option is found even
131 if they are not specified on the command line. For example: 135 if they are not specified on the command line. For example:
132 136
133 opt_complementary = "abc"; 137 flags = getopt32(argv, "^abcd""\0""abc")
134 flags = getopt32(argv, "abcd")
135 138
136 If getopt() finds "-a" on the command line, then 139 If getopt() finds "-a" on the command line, then
137 getopt32's return value will be as if "-a -b -c" were 140 getopt32's return value will be as if "-a -b -c" were
@@ -144,8 +147,7 @@ const char *opt_complementary
144 if w is given more than once, it is "unlimited" 147 if w is given more than once, it is "unlimited"
145 148
146 int w_counter = 0; // must be initialized! 149 int w_counter = 0; // must be initialized!
147 opt_complementary = "ww"; 150 getopt32(argv, "^w""\0""ww", &w_counter);
148 getopt32(argv, "w", &w_counter);
149 if (w_counter) 151 if (w_counter)
150 width = (w_counter == 1) ? 132 : INT_MAX; 152 width = (w_counter == 1) ? 132 : INT_MAX;
151 else 153 else
@@ -160,8 +162,9 @@ const char *opt_complementary
160 162
161 llist_t *my_b = NULL; 163 llist_t *my_b = NULL;
162 int verbose_level = 0; 164 int verbose_level = 0;
163 opt_complementary = "vv:b-c:c-b"; 165 f = getopt32(argv, "^vb:*c"
164 f = getopt32(argv, "vb:*c", &my_b, &verbose_level); 166 "\0""vv:b-c:c-b"
167 , &my_b, &verbose_level);
165 if (f & 2) // -c after -b unsets -b flag 168 if (f & 2) // -c after -b unsets -b flag
166 while (my_b) dosomething_with(llist_pop(&my_b)); 169 while (my_b) dosomething_with(llist_pop(&my_b));
167 if (my_b) // but llist is stored if -b is specified 170 if (my_b) // but llist is stored if -b is specified
@@ -170,31 +173,6 @@ const char *opt_complementary
170 173
171Special characters: 174Special characters:
172 175
173 "-" A group consisting of just a dash forces all arguments
174 to be treated as options, even if they have no leading dashes.
175 Next char in this case can't be a digit (0-9), use ':' or end of line.
176 Example:
177
178 opt_complementary = "-:w-x:x-w"; // "-w-x:x-w" would also work,
179 getopt32(argv, "wx"); // but is less readable
180
181 This makes it possible to use options without a dash (./program w x)
182 as well as with a dash (./program -x).
183
184 NB: getopt32() will leak a small amount of memory if you use
185 this option! Do not use it if there is a possibility of recursive
186 getopt32() calls.
187
188 "--" A double dash at the beginning of opt_complementary means the
189 argv[1] string should always be treated as options, even if it isn't
190 prefixed with a "-". This is useful for special syntax in applets
191 such as "ar" and "tar":
192 tar xvf foo.tar
193
194 NB: getopt32() will leak a small amount of memory if you use
195 this option! Do not use it if there is a possibility of recursive
196 getopt32() calls.
197
198 "-N" A dash as the first char in a opt_complementary group followed 176 "-N" A dash as the first char in a opt_complementary group followed
199 by a single digit (0-9) means that at least N non-option 177 by a single digit (0-9) means that at least N non-option
200 arguments must be present on the command line 178 arguments must be present on the command line
@@ -222,7 +200,7 @@ Special characters:
222 getopt32 finds -s, then -d is unset or if it finds -d 200 getopt32 finds -s, then -d is unset or if it finds -d
223 then -s is unset. (Note: busybox implements the GNU 201 then -s is unset. (Note: busybox implements the GNU
224 "--max-depth" option as "-d".) To obtain this behavior, you 202 "--max-depth" option as "-d".) To obtain this behavior, you
225 set opt_complementary = "s-d:d-s". Only one flag value is 203 set opt_complementary to "s-d:d-s". Only one flag value is
226 added to getopt32's return value depending on the 204 added to getopt32's return value depending on the
227 position of the options on the command line. If one of the 205 position of the options on the command line. If one of the
228 two options requires an argument pointer (":" in applet_opts 206 two options requires an argument pointer (":" in applet_opts
@@ -230,8 +208,7 @@ Special characters:
230 208
231 char *smax_print_depth; 209 char *smax_print_depth;
232 210
233 opt_complementary = "s-d:d-s:x-x"; 211 opt = getopt32(argv, "^sd:x""\0""s-d:d-s:x-x", &smax_print_depth);
234 opt = getopt32(argv, "sd:x", &smax_print_depth);
235 212
236 if (opt & 2) 213 if (opt & 2)
237 max_print_depth = atoi(smax_print_depth); 214 max_print_depth = atoi(smax_print_depth);
@@ -247,7 +224,7 @@ Special characters:
247 The cut applet must have only one type of list specified, so 224 The cut applet must have only one type of list specified, so
248 -b, -c and -f are mutually exclusive and should raise an error 225 -b, -c and -f are mutually exclusive and should raise an error
249 if specified together. In this case you must set 226 if specified together. In this case you must set
250 opt_complementary = "b--cf:c--bf:f--bc". If two of the 227 opt_complementary to "b--cf:c--bf:f--bc". If two of the
251 mutually exclusive options are found, getopt32 will call 228 mutually exclusive options are found, getopt32 will call
252 bb_show_usage() and die. 229 bb_show_usage() and die.
253 230
@@ -259,8 +236,7 @@ Special characters:
259 with xatoi_positive() - allowed range is 0..INT_MAX. 236 with xatoi_positive() - allowed range is 0..INT_MAX.
260 237
261 int param; // "unsigned param;" will also work 238 int param; // "unsigned param;" will also work
262 opt_complementary = "p+"; 239 getopt32(argv, "^p:""\0""p+", &param);
263 getopt32(argv, "p:", &param);
264 240
265 "o::" A double colon after a char in opt_complementary means that the 241 "o::" A double colon after a char in opt_complementary means that the
266 option can occur multiple times. Each occurrence will be saved as 242 option can occur multiple times. Each occurrence will be saved as
@@ -275,8 +251,7 @@ Special characters:
275 (this pointer must be initializated to NULL if the list is empty 251 (this pointer must be initializated to NULL if the list is empty
276 as required by llist_add_to_end(llist_t **old_head, char *new_item).) 252 as required by llist_add_to_end(llist_t **old_head, char *new_item).)
277 253
278 opt_complementary = "e::"; 254 getopt32(argv, "^e:""\0""e::", &patterns);
279 getopt32(argv, "e:", &patterns);
280 255
281 $ grep -e user -e root /etc/passwd 256 $ grep -e user -e root /etc/passwd
282 root:x:0:0:root:/root:/bin/bash 257 root:x:0:0:root:/root:/bin/bash
@@ -294,8 +269,7 @@ Special characters:
294 For example from "id" applet: 269 For example from "id" applet:
295 270
296 // Don't allow -n -r -rn -ug -rug -nug -rnug 271 // Don't allow -n -r -rn -ug -rug -nug -rnug
297 opt_complementary = "r?ug:n?ug:u--g:g--u"; 272 flags = getopt32(argv, "^rnug""\0""r?ug:n?ug:u--g:g--u");
298 flags = getopt32(argv, "rnug");
299 273
300 This example allowed only: 274 This example allowed only:
301 $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng 275 $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
@@ -306,8 +280,7 @@ Special characters:
306 For example from "start-stop-daemon" applet: 280 For example from "start-stop-daemon" applet:
307 281
308 // Don't allow -KS -SK, but -S or -K is required 282 // Don't allow -KS -SK, but -S or -K is required
309 opt_complementary = "K:S:K--S:S--K"; 283 flags = getopt32(argv, "^KS...""\0""K:S:K--S:S--K");
310 flags = getopt32(argv, "KS...);
311 284
312 285
313 Don't forget to use ':'. For example, "?322-22-23X-x-a" 286 Don't forget to use ':'. For example, "?322-22-23X-x-a"
@@ -322,8 +295,6 @@ Special characters:
322 295
323const char *const bb_argv_dash[] = { "-", NULL }; 296const char *const bb_argv_dash[] = { "-", NULL };
324 297
325const char *opt_complementary;
326
327enum { 298enum {
328 PARAM_STRING, 299 PARAM_STRING,
329 PARAM_LIST, 300 PARAM_LIST,
@@ -341,58 +312,63 @@ typedef struct {
341 int *counter; 312 int *counter;
342} t_complementary; 313} t_complementary;
343 314
344/* You can set applet_long_options for parse called long options */ 315uint32_t option_mask32;
345#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG 316
317#if ENABLE_LONG_OPTS
346static const struct option bb_null_long_options[1] = { 318static const struct option bb_null_long_options[1] = {
347 { 0, 0, 0, 0 } 319 { 0, 0, 0, 0 }
348}; 320};
349const char *applet_long_options; 321#else
322#define vgetopt32(argv,applet_opts,applet_long_options,p) \
323 vgetopt32(argv,applet_opts,p)
350#endif 324#endif
351 325
352uint32_t option_mask32; 326/* Please keep getopt32 free from xmalloc */
353 327
354uint32_t FAST_FUNC 328static uint32_t
355getopt32(char **argv, const char *applet_opts, ...) 329vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, va_list p)
356{ 330{
357 int argc; 331 int argc;
358 unsigned flags = 0; 332 unsigned flags = 0;
359 unsigned requires = 0; 333 unsigned requires = 0;
334 unsigned len;
360 t_complementary complementary[33]; /* last stays zero-filled */ 335 t_complementary complementary[33]; /* last stays zero-filled */
361 char first_char; 336 char dont_die_flag;
362 int c; 337 int c;
363 const unsigned char *s; 338 const unsigned char *s;
339 const char *opt_complementary;
364 t_complementary *on_off; 340 t_complementary *on_off;
365 va_list p; 341#if ENABLE_LONG_OPTS
366#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
367 const struct option *l_o; 342 const struct option *l_o;
368 struct option *long_options = (struct option *) &bb_null_long_options; 343 struct option *long_options = (struct option *) &bb_null_long_options;
369#endif 344#endif
370 unsigned trigger; 345 unsigned trigger;
371 char **pargv;
372 int min_arg = 0; 346 int min_arg = 0;
373 int max_arg = -1; 347 int max_arg = -1;
374
375#define SHOW_USAGE_IF_ERROR 1
376#define ALL_ARGV_IS_OPTS 2
377#define FIRST_ARGV_IS_OPT 4
378
379 int spec_flgs = 0; 348 int spec_flgs = 0;
380 349
381 /* skip 0: some applets cheat: they do not actually HAVE argv[0] */ 350#define SHOW_USAGE_IF_ERROR 1
382 argc = 1 + string_array_len(argv + 1);
383
384 va_start(p, applet_opts);
385 351
386 on_off = complementary; 352 on_off = complementary;
387 memset(on_off, 0, sizeof(complementary)); 353 memset(on_off, 0, sizeof(complementary));
388 354
389 applet_opts = strcpy(alloca(strlen(applet_opts) + 1), applet_opts); 355 len = strlen(applet_opts);
390 356
391 /* skip bbox extension */ 357 /* skip bbox extension */
392 first_char = applet_opts[0]; 358 opt_complementary = NULL;
393 if (first_char == '!') 359 if (applet_opts[0] == '^') {
360 applet_opts++;
361 /* point it past terminating NUL */
362 opt_complementary = applet_opts + len;
363 }
364
365 /* skip another bbox extension */
366 dont_die_flag = applet_opts[0];
367 if (dont_die_flag == '!')
394 applet_opts++; 368 applet_opts++;
395 369
370 applet_opts = strcpy(alloca(len + 1), applet_opts);
371
396 /* skip GNU extension */ 372 /* skip GNU extension */
397 s = (const unsigned char *)applet_opts; 373 s = (const unsigned char *)applet_opts;
398 if (*s == '+' || *s == '-') 374 if (*s == '+' || *s == '-')
@@ -419,7 +395,7 @@ getopt32(char **argv, const char *applet_opts, ...)
419 c++; 395 c++;
420 } 396 }
421 397
422#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG 398#if ENABLE_LONG_OPTS
423 if (applet_long_options) { 399 if (applet_long_options) {
424 const char *optstr; 400 const char *optstr;
425 unsigned i, count; 401 unsigned i, count;
@@ -458,14 +434,11 @@ getopt32(char **argv, const char *applet_opts, ...)
458 c++; 434 c++;
459 next_long: ; 435 next_long: ;
460 } 436 }
461 /* Make it unnecessary to clear applet_long_options
462 * by hand after each call to getopt32
463 */
464 applet_long_options = NULL;
465 } 437 }
466#endif /* ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG */ 438#endif /* ENABLE_LONG_OPTS */
467 439
468 for (s = (const unsigned char *)opt_complementary; s && *s; s++) { 440 s = (const unsigned char *)opt_complementary;
441 if (s) for (; *s; s++) {
469 t_complementary *pair; 442 t_complementary *pair;
470 unsigned *pair_switch; 443 unsigned *pair_switch;
471 444
@@ -482,13 +455,7 @@ getopt32(char **argv, const char *applet_opts, ...)
482 continue; 455 continue;
483 } 456 }
484 if (*s == '-') { 457 if (*s == '-') {
485 if (c < '0' || c > '9') { 458 if (c >= '0' && c <= '9') {
486 if (c == '-') {
487 spec_flgs |= FIRST_ARGV_IS_OPT;
488 s++;
489 } else
490 spec_flgs |= ALL_ARGV_IS_OPTS;
491 } else {
492 min_arg = c - '0'; 459 min_arg = c - '0';
493 s++; 460 s++;
494 } 461 }
@@ -548,26 +515,6 @@ getopt32(char **argv, const char *applet_opts, ...)
548 } 515 }
549 s--; 516 s--;
550 } 517 }
551 opt_complementary = NULL;
552 va_end(p);
553
554 if (spec_flgs & (FIRST_ARGV_IS_OPT | ALL_ARGV_IS_OPTS)) {
555 pargv = argv + 1;
556 while (*pargv) {
557 if (pargv[0][0] != '-' && pargv[0][0] != '\0') {
558 /* Can't use alloca: opts with params will
559 * return pointers to stack!
560 * NB: we leak these allocations... */
561 char *pp = xmalloc(strlen(*pargv) + 2);
562 *pp = '-';
563 strcpy(pp + 1, *pargv);
564 *pargv = pp;
565 }
566 if (!(spec_flgs & ALL_ARGV_IS_OPTS))
567 break;
568 pargv++;
569 }
570 }
571 518
572 /* In case getopt32 was already called: 519 /* In case getopt32 was already called:
573 * reset the libc getopt() function, which keeps internal state. 520 * reset the libc getopt() function, which keeps internal state.
@@ -576,11 +523,14 @@ getopt32(char **argv, const char *applet_opts, ...)
576 */ 523 */
577 GETOPT_RESET(); 524 GETOPT_RESET();
578 525
526 /* skip 0: some applets cheat: they do not actually HAVE argv[0] */
527 argc = 1 + string_array_len(argv + 1);
528
579 /* Note: just "getopt() <= 0" will not work well for 529 /* Note: just "getopt() <= 0" will not work well for
580 * "fake" short options, like this one: 530 * "fake" short options, like this one:
581 * wget $'-\203' "Test: test" http://kernel.org/ 531 * wget $'-\203' "Test: test" http://kernel.org/
582 * (supposed to act as --header, but doesn't) */ 532 * (supposed to act as --header, but doesn't) */
583#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG 533#if ENABLE_LONG_OPTS
584 while ((c = getopt_long(argc, argv, applet_opts, 534 while ((c = getopt_long(argc, argv, applet_opts,
585 long_options, NULL)) != -1) { 535 long_options, NULL)) != -1) {
586#else 536#else
@@ -637,7 +587,33 @@ getopt32(char **argv, const char *applet_opts, ...)
637 return flags; 587 return flags;
638 588
639 error: 589 error:
640 if (first_char != '!') 590 if (dont_die_flag != '!')
641 bb_show_usage(); 591 bb_show_usage();
642 return (int32_t)-1; 592 return (int32_t)-1;
643} 593}
594
595uint32_t FAST_FUNC
596getopt32(char **argv, const char *applet_opts, ...)
597{
598 uint32_t opt;
599 va_list p;
600
601 va_start(p, applet_opts);
602 opt = vgetopt32(argv, applet_opts, NULL, p);
603 va_end(p);
604 return opt;
605}
606
607#if ENABLE_LONG_OPTS
608uint32_t FAST_FUNC
609getopt32long(char **argv, const char *applet_opts, const char *longopts, ...)
610{
611 uint32_t opt;
612 va_list p;
613
614 va_start(p, longopts);
615 opt = vgetopt32(argv, applet_opts, longopts, p);
616 va_end(p);
617 return opt;
618}
619#endif
diff --git a/libbb/getopt_allopts.c b/libbb/getopt_allopts.c
new file mode 100644
index 000000000..a67d2b70e
--- /dev/null
+++ b/libbb/getopt_allopts.c
@@ -0,0 +1,27 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2017 Denys Vlasenko
4 *
5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6 */
7#include "libbb.h"
8
9//kbuild:lib-y += getopt_allopts.o
10
11void FAST_FUNC make_all_argv_opts(char **argv)
12{
13 /* Note: we skip argv[0] */
14 while (*++argv) {
15 char *p;
16
17 if (argv[0][0] == '-')
18 continue;
19 /* Neither top nor ps care if "" arg turns into "-" */
20 /*if (argv[0][0] == '\0')
21 continue;*/
22 p = xmalloc(strlen(*argv) + 2);
23 *p = '-';
24 strcpy(p + 1, *argv);
25 *argv = p;
26 }
27}
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 051a39b2e..c2b0a3842 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -1320,6 +1320,7 @@ line_input_t* FAST_FUNC new_line_input_t(int flags)
1320{ 1320{
1321 line_input_t *n = xzalloc(sizeof(*n)); 1321 line_input_t *n = xzalloc(sizeof(*n));
1322 n->flags = flags; 1322 n->flags = flags;
1323 n->timeout = -1;
1323#if MAX_HISTORY > 0 1324#if MAX_HISTORY > 0
1324 n->max_history = MAX_HISTORY; 1325 n->max_history = MAX_HISTORY;
1325#endif 1326#endif
@@ -2192,7 +2193,7 @@ enum {
2192 * Backspace deletes last matched char. 2193 * Backspace deletes last matched char.
2193 * Control keys exit search and return to normal editing (at current history line). 2194 * Control keys exit search and return to normal editing (at current history line).
2194 */ 2195 */
2195static int32_t reverse_i_search(void) 2196static int32_t reverse_i_search(int timeout)
2196{ 2197{
2197 char match_buf[128]; /* for user input */ 2198 char match_buf[128]; /* for user input */
2198 char read_key_buffer[KEYCODE_BUFFER_SIZE]; 2199 char read_key_buffer[KEYCODE_BUFFER_SIZE];
@@ -2214,8 +2215,8 @@ static int32_t reverse_i_search(void)
2214 int h; 2215 int h;
2215 unsigned match_buf_len = strlen(match_buf); 2216 unsigned match_buf_len = strlen(match_buf);
2216 2217
2217//FIXME: correct timeout? 2218//FIXME: correct timeout? (i.e. count it down?)
2218 ic = lineedit_read_key(read_key_buffer, -1); 2219 ic = lineedit_read_key(read_key_buffer, timeout);
2219 2220
2220 switch (ic) { 2221 switch (ic) {
2221 case CTRL('R'): /* searching for the next match */ 2222 case CTRL('R'): /* searching for the next match */
@@ -2318,9 +2319,10 @@ static int32_t reverse_i_search(void)
2318 * (in both cases the cursor remains on the input line, '\n' is not printed) 2319 * (in both cases the cursor remains on the input line, '\n' is not printed)
2319 * >0 length of input string, including terminating '\n' 2320 * >0 length of input string, including terminating '\n'
2320 */ 2321 */
2321int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) 2322int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize)
2322{ 2323{
2323 int len; 2324 int len;
2325 int timeout;
2324#if ENABLE_FEATURE_TAB_COMPLETION 2326#if ENABLE_FEATURE_TAB_COMPLETION
2325 smallint lastWasTab = 0; 2327 smallint lastWasTab = 0;
2326#endif 2328#endif
@@ -2366,8 +2368,15 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2366 maxsize = MAX_LINELEN; 2368 maxsize = MAX_LINELEN;
2367 S.maxsize = maxsize; 2369 S.maxsize = maxsize;
2368 2370
2369 /* With zero flags, no other fields are ever used */ 2371 timeout = -1;
2370 state = st ? st : (line_input_t*) &const_int_0; 2372 /* Make state->flags == 0 if st is NULL.
2373 * With zeroed flags, no other fields are ever referenced.
2374 */
2375 state = (line_input_t*) &const_int_0;
2376 if (st) {
2377 state = st;
2378 timeout = st->timeout;
2379 }
2371#if MAX_HISTORY > 0 2380#if MAX_HISTORY > 0
2372# if ENABLE_FEATURE_EDITING_SAVEHISTORY 2381# if ENABLE_FEATURE_EDITING_SAVEHISTORY
2373 if (state->hist_file) 2382 if (state->hist_file)
@@ -2584,7 +2593,7 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2584 } 2593 }
2585#if ENABLE_FEATURE_REVERSE_SEARCH 2594#if ENABLE_FEATURE_REVERSE_SEARCH
2586 case CTRL('R'): 2595 case CTRL('R'):
2587 ic = ic_raw = reverse_i_search(); 2596 ic = ic_raw = reverse_i_search(timeout);
2588 goto again; 2597 goto again;
2589#endif 2598#endif
2590 2599
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
index 307ae2cd2..8701b010c 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -42,8 +42,9 @@ int parse_main(int argc UNUSED_PARAM, char **argv)
42 int mintokens = 0, ntokens = 128; 42 int mintokens = 0, ntokens = 128;
43 unsigned noout; 43 unsigned noout;
44 44
45 opt_complementary = "-1"; 45 noout = 1 & getopt32(argv, "^" "xn:+m:+d:f:+" "\0" "-1",
46 noout = 1 & getopt32(argv, "xn:+m:+d:f:+", &ntokens, &mintokens, &delims, &flags); 46 &ntokens, &mintokens, &delims, &flags
47 );
47 //argc -= optind; 48 //argc -= optind;
48 argv += optind; 49 argv += optind;
49 50
@@ -161,13 +162,18 @@ mintokens > 0 make config_read() print error message if less than mintokens
161#undef config_read 162#undef config_read
162int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims) 163int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
163{ 164{
164 char *line; 165 char *line, *p;
165 int ntokens, mintokens; 166 int ntokens, mintokens;
166 int t; 167 int t;
168 char alt_comment_ch;
167 169
168 if (!parser) 170 if (!parser)
169 return 0; 171 return 0;
170 172
173 alt_comment_ch = '\0';
174 if (flags & PARSE_ALT_COMMENTS)
175 alt_comment_ch = *delims++;
176
171 ntokens = (uint8_t)flags; 177 ntokens = (uint8_t)flags;
172 mintokens = (uint8_t)(flags >> 8); 178 mintokens = (uint8_t)(flags >> 8);
173 179
@@ -184,7 +190,10 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
184 if (flags & PARSE_TRIM) 190 if (flags & PARSE_TRIM)
185 line += strspn(line, delims + 1); 191 line += strspn(line, delims + 1);
186 192
187 if (line[0] == '\0' || line[0] == delims[0]) 193 p = line;
194 if (flags & PARSE_WS_COMMENTS)
195 p = skip_whitespace(p);
196 if (p[0] == '\0' || p[0] == delims[0] || p[0] == alt_comment_ch)
188 goto again; 197 goto again;
189 198
190 if (flags & PARSE_KEEP_COPY) { 199 if (flags & PARSE_KEEP_COPY) {
@@ -201,10 +210,10 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
201 /* Combine remaining arguments? */ 210 /* Combine remaining arguments? */
202 if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) { 211 if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) {
203 /* Vanilla token, find next delimiter */ 212 /* Vanilla token, find next delimiter */
204 line += strcspn(line, delims[0] ? delims : delims + 1); 213 line += strcspn(line, (delims[0] && (flags & PARSE_EOL_COMMENTS)) ? delims : delims + 1);
205 } else { 214 } else {
206 /* Combining, find comment char if any */ 215 /* Combining, find comment char if any */
207 line = strchrnul(line, PARSE_EOL_COMMENTS ? delims[0] : '\0'); 216 line = strchrnul(line, (flags & PARSE_EOL_COMMENTS) ? delims[0] : '\0');
208 217
209 /* Trim any extra delimiters from the end */ 218 /* Trim any extra delimiters from the end */
210 if (flags & PARSE_TRIM) { 219 if (flags & PARSE_TRIM) {
@@ -214,10 +223,10 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
214 } 223 }
215 224
216 /* Token not terminated? */ 225 /* Token not terminated? */
217 if (*line == delims[0]) 226 if ((flags & PARSE_EOL_COMMENTS) && *line == delims[0])
218 *line = '\0'; 227 *line = '\0'; /* ends with comment char: this line is done */
219 else if (*line != '\0') 228 else if (*line != '\0')
220 *line++ = '\0'; 229 *line++ = '\0'; /* token is done, continue parsing line */
221 230
222#if 0 /* unused so far */ 231#if 0 /* unused so far */
223 if (flags & PARSE_ESCAPE) { 232 if (flags & PARSE_ESCAPE) {
diff --git a/libbb/progress.c b/libbb/progress.c
index 3c2f01667..64e6529ac 100644
--- a/libbb/progress.c
+++ b/libbb/progress.c
@@ -25,7 +25,7 @@
25 * may be used to endorse or promote products derived from this software 25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission. 26 * without specific prior written permission.
27 * 27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/libbb/pw_encrypt_des.c b/libbb/pw_encrypt_des.c
index c8e02ddff..19a9ab15b 100644
--- a/libbb/pw_encrypt_des.c
+++ b/libbb/pw_encrypt_des.c
@@ -24,7 +24,7 @@
24 * may be used to endorse or promote products derived from this software 24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission. 25 * without specific prior written permission.
26 * 26 *
27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
diff --git a/libbb/run_shell.c b/libbb/run_shell.c
index b6b9360e8..3bb58bb6f 100644
--- a/libbb/run_shell.c
+++ b/libbb/run_shell.c
@@ -15,7 +15,7 @@
15 * may be used to endorse or promote products derived from this software 15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission. 16 * without specific prior written permission.
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND 18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ''AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE 21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
diff --git a/libbb/setup_environment.c b/libbb/setup_environment.c
index 944ac5538..7ac22cd92 100644
--- a/libbb/setup_environment.c
+++ b/libbb/setup_environment.c
@@ -15,7 +15,7 @@
15 * may be used to endorse or promote products derived from this software 15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission. 16 * without specific prior written permission.
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND 18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ''AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE 21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
diff --git a/libbb/trim.c b/libbb/trim.c
index 16cb4fbb0..e47fec74e 100644
--- a/libbb/trim.c
+++ b/libbb/trim.c
@@ -10,9 +10,10 @@
10 10
11#include "libbb.h" 11#include "libbb.h"
12 12
13void FAST_FUNC trim(char *s) 13char* FAST_FUNC trim(char *s)
14{ 14{
15 size_t len = strlen(s); 15 size_t len = strlen(s);
16 size_t old = len;
16 17
17 /* trim trailing whitespace */ 18 /* trim trailing whitespace */
18 while (len && isspace(s[len-1])) 19 while (len && isspace(s[len-1]))
@@ -26,5 +27,12 @@ void FAST_FUNC trim(char *s)
26 memmove(s, nws, len); 27 memmove(s, nws, len);
27 } 28 }
28 } 29 }
29 s[len] = '\0'; 30
31 s += len;
32 /* If it was a "const char*" which does not need trimming,
33 * avoid superfluous store */
34 if (old != len)
35 *s = '\0';
36
37 return s;
30} 38}
diff --git a/libbb/ubi.c b/libbb/ubi.c
index 34595d797..a90016acf 100644
--- a/libbb/ubi.c
+++ b/libbb/ubi.c
@@ -35,6 +35,7 @@ int FAST_FUNC ubi_get_volid_by_name(unsigned ubi_devnum, const char *vol_name)
35 if (open_read_close(fname, buf, sizeof(buf)) <= 0) 35 if (open_read_close(fname, buf, sizeof(buf)) <= 0)
36 continue; 36 continue;
37 37
38 buf[UBI_MAX_VOLUME_NAME] = '\0';
38 strchrnul(buf, '\n')[0] = '\0'; 39 strchrnul(buf, '\n')[0] = '\0';
39 if (strcmp(vol_name, buf) == 0) 40 if (strcmp(vol_name, buf) == 0)
40 return i; 41 return i;
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index 4b3ed5a3b..eca2fabf5 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -14,78 +14,46 @@
14 * 14 *
15 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 15 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
16 */ 16 */
17#include <sys/prctl.h>
18#ifndef PR_SET_NAME
19#define PR_SET_NAME 15
20#endif
21#ifndef PR_GET_NAME
22#define PR_GET_NAME 16
23#endif
17 24
18#include "busybox.h" /* uses applet tables */ 25#include "busybox.h" /* uses applet tables */
19#include "NUM_APPLETS.h" 26#include "NUM_APPLETS.h"
20 27
21#if !ENABLE_PLATFORM_MINGW32 28#define NOFORK_SUPPORT ((NUM_APPLETS > 1) && (ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_NOFORK))
22/* This does a fork/exec in one call, using vfork(). Returns PID of new child, 29#define NOEXEC_SUPPORT ((NUM_APPLETS > 1) && (ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE))
23 * -1 for failure. Runs argv[0], searching path if that has no / in it. */
24pid_t FAST_FUNC spawn(char **argv)
25{
26 /* Compiler should not optimize stores here */
27 volatile int failed;
28 pid_t pid;
29
30 fflush_all();
31
32 /* Be nice to nommu machines. */
33 failed = 0;
34 pid = vfork();
35 if (pid < 0) /* error */
36 return pid;
37 if (!pid) { /* child */
38 /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
39 BB_EXECVP(argv[0], argv);
40
41 /* We are (maybe) sharing a stack with blocked parent,
42 * let parent know we failed and then exit to unblock parent
43 * (but don't run atexit() stuff, which would screw up parent.)
44 */
45 failed = errno;
46 /* mount, for example, does not want the message */
47 /*bb_perror_msg("can't execute '%s'", argv[0]);*/
48 _exit(111);
49 }
50 /* parent */
51 /* Unfortunately, this is not reliable: according to standards
52 * vfork() can be equivalent to fork() and we won't see value
53 * of 'failed'.
54 * Interested party can wait on pid and learn exit code.
55 * If 111 - then it (most probably) failed to exec */
56 if (failed) {
57 safe_waitpid(pid, NULL, 0); /* prevent zombie */
58 errno = failed;
59 return -1;
60 }
61 return pid;
62}
63#endif
64 30
65/* Die with an error message if we can't spawn a child process. */ 31#if defined(__linux__) && (NUM_APPLETS > 1)
66pid_t FAST_FUNC xspawn(char **argv) 32void FAST_FUNC set_task_comm(const char *comm)
67{ 33{
68 pid_t pid = spawn(argv); 34 /* okay if too long (truncates) */
69 if (pid < 0) 35 prctl(PR_SET_NAME, (long)comm, 0, 0, 0);
70 bb_simple_perror_msg_and_die(*argv);
71 return pid;
72} 36}
37#endif
73 38
74#if ENABLE_FEATURE_PREFER_APPLETS \ 39/*
75 || ENABLE_FEATURE_SH_NOFORK 40 * NOFORK/NOEXEC support
41 */
42#if NOFORK_SUPPORT
76static jmp_buf die_jmp; 43static jmp_buf die_jmp;
77static void jump(void) 44static void jump(void)
78{ 45{
79 /* Special case. We arrive here if NOFORK applet 46 /* Special case. We arrive here if NOFORK applet
80 * calls xfunc, which then decides to die. 47 * calls xfunc, which then decides to die.
81 * We don't die, but jump instead back to caller. 48 * We don't die, but instead jump back to caller.
82 * NOFORK applets still cannot carelessly call xfuncs: 49 * NOFORK applets still cannot carelessly call xfuncs:
83 * p = xmalloc(10); 50 * p = xmalloc(10);
84 * q = xmalloc(10); // BUG! if this dies, we leak p! 51 * q = xmalloc(10); // BUG! if this dies, we leak p!
85 */ 52 */
86 /* | 0x100 allows to pass zero exitcode (longjmp can't pass 0). 53 /* | 0x100 allows to pass zero exitcode (longjmp can't pass 0).
87 * This works because exitcodes are bytes, 54 * This works because exitcodes are bytes,
88 * run_nofork_applet() ensures that by "& 0xff" */ 55 * run_nofork_applet() ensures that by "& 0xff"
56 */
89 longjmp(die_jmp, xfunc_error_retval | 0x100); 57 longjmp(die_jmp, xfunc_error_retval | 0x100);
90} 58}
91 59
@@ -94,6 +62,7 @@ struct nofork_save_area {
94 void (*die_func)(void); 62 void (*die_func)(void);
95 const char *applet_name; 63 const char *applet_name;
96 uint32_t option_mask32; 64 uint32_t option_mask32;
65 smallint logmode;
97 uint8_t xfunc_error_retval; 66 uint8_t xfunc_error_retval;
98}; 67};
99static void save_nofork_data(struct nofork_save_area *save) 68static void save_nofork_data(struct nofork_save_area *save)
@@ -102,6 +71,7 @@ static void save_nofork_data(struct nofork_save_area *save)
102 save->die_func = die_func; 71 save->die_func = die_func;
103 save->applet_name = applet_name; 72 save->applet_name = applet_name;
104 save->option_mask32 = option_mask32; 73 save->option_mask32 = option_mask32;
74 save->logmode = logmode;
105 save->xfunc_error_retval = xfunc_error_retval; 75 save->xfunc_error_retval = xfunc_error_retval;
106} 76}
107static void restore_nofork_data(struct nofork_save_area *save) 77static void restore_nofork_data(struct nofork_save_area *save)
@@ -110,6 +80,7 @@ static void restore_nofork_data(struct nofork_save_area *save)
110 die_func = save->die_func; 80 die_func = save->die_func;
111 applet_name = save->applet_name; 81 applet_name = save->applet_name;
112 option_mask32 = save->option_mask32; 82 option_mask32 = save->option_mask32;
83 logmode = save->logmode;
113 xfunc_error_retval = save->xfunc_error_retval; 84 xfunc_error_retval = save->xfunc_error_retval;
114} 85}
115 86
@@ -120,16 +91,15 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
120 91
121 save_nofork_data(&old); 92 save_nofork_data(&old);
122 93
94 logmode = LOGMODE_STDIO;
123 xfunc_error_retval = EXIT_FAILURE; 95 xfunc_error_retval = EXIT_FAILURE;
124 96 /* In case getopt() was already called:
125 /* In case getopt() or getopt32() was already called:
126 * reset the libc getopt() function, which keeps internal state. 97 * reset the libc getopt() function, which keeps internal state.
98 * (getopt32() does it itself, but getopt() doesn't (and can't))
127 */ 99 */
128 GETOPT_RESET(); 100 GETOPT_RESET();
129 101
130 argc = 1; 102 argc = string_array_len(argv);
131 while (argv[argc])
132 argc++;
133 103
134 /* If xfunc "dies" in NOFORK applet, die_func longjmp's here instead */ 104 /* If xfunc "dies" in NOFORK applet, die_func longjmp's here instead */
135 die_func = jump; 105 die_func = jump;
@@ -142,19 +112,97 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
142 applet_name = tmp_argv[0]; 112 applet_name = tmp_argv[0];
143 /* Finally we can call NOFORK applet's main() */ 113 /* Finally we can call NOFORK applet's main() */
144 rc = applet_main[applet_no](argc, tmp_argv); 114 rc = applet_main[applet_no](argc, tmp_argv);
115 /* Important for shells: `which CMD` was failing */
116 fflush_all();
145 } else { 117 } else {
146 /* xfunc died in NOFORK applet */ 118 /* xfunc died in NOFORK applet */
147 } 119 }
148 120
149 /* Restoring some globals */ 121 /* Restoring some globals */
150 restore_nofork_data(&old); 122 restore_nofork_data(&old);
151
152 /* Other globals can be simply reset to defaults */ 123 /* Other globals can be simply reset to defaults */
153 GETOPT_RESET(); 124 GETOPT_RESET();
154 125
155 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ 126 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
156} 127}
157#endif /* FEATURE_PREFER_APPLETS || FEATURE_SH_NOFORK */ 128#endif
129
130#if NOEXEC_SUPPORT
131void FAST_FUNC run_noexec_applet_and_exit(int a, const char *name, char **argv)
132{
133 /* reset some state and run without execing */
134 /* msg_eol = "\n"; - no caller needs this reinited yet */
135 logmode = LOGMODE_STDIO;
136 xfunc_error_retval = EXIT_FAILURE;
137 die_func = NULL;
138 GETOPT_RESET();
139
140//TODO: think pidof, pgrep, pkill!
141//set_task_comm() makes our pidof find NOEXECs (e.g. "yes >/dev/null"),
142//but one from procps-ng-3.3.10 needs more!
143//Rewrite /proc/PID/cmdline? (need to save argv0 and length at init for this to work!)
144 set_task_comm(name);
145 /* applet_name is set by this function: */
146 run_applet_no_and_exit(a, name, argv);
147}
148#endif
149
150/*
151 * Higher-level code, hiding optional NOFORK/NOEXEC trickery.
152 */
153
154#if !ENABLE_PLATFORM_MINGW32
155/* This does a fork/exec in one call, using vfork(). Returns PID of new child,
156 * -1 for failure. Runs argv[0], searching path if that has no / in it. */
157pid_t FAST_FUNC spawn(char **argv)
158{
159 /* Compiler should not optimize stores here */
160 volatile int failed;
161 pid_t pid;
162
163 fflush_all();
164
165 /* Be nice to nommu machines. */
166 failed = 0;
167 pid = vfork();
168 if (pid < 0) /* error */
169 return pid;
170 if (!pid) { /* child */
171 /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
172 BB_EXECVP(argv[0], argv);
173
174 /* We are (maybe) sharing a stack with blocked parent,
175 * let parent know we failed and then exit to unblock parent
176 * (but don't run atexit() stuff, which would screw up parent.)
177 */
178 failed = errno;
179 /* mount, for example, does not want the message */
180 /*bb_perror_msg("can't execute '%s'", argv[0]);*/
181 _exit(111);
182 }
183 /* parent */
184 /* Unfortunately, this is not reliable: according to standards
185 * vfork() can be equivalent to fork() and we won't see value
186 * of 'failed'.
187 * Interested party can wait on pid and learn exit code.
188 * If 111 - then it (most probably) failed to exec */
189 if (failed) {
190 safe_waitpid(pid, NULL, 0); /* prevent zombie */
191 errno = failed;
192 return -1;
193 }
194 return pid;
195}
196#endif
197
198/* Die with an error message if we can't spawn a child process. */
199pid_t FAST_FUNC xspawn(char **argv)
200{
201 pid_t pid = spawn(argv);
202 if (pid < 0)
203 bb_simple_perror_msg_and_die(*argv);
204 return pid;
205}
158 206
159int FAST_FUNC spawn_and_wait(char **argv) 207int FAST_FUNC spawn_and_wait(char **argv)
160{ 208{
@@ -174,21 +222,12 @@ int FAST_FUNC spawn_and_wait(char **argv)
174 return wait4pid(rc); 222 return wait4pid(rc);
175 223
176 /* child */ 224 /* child */
177 /* reset some state and run without execing */ 225 run_noexec_applet_and_exit(a, argv[0], argv);
178
179 /* msg_eol = "\n"; - no caller needs this reinited yet */
180 logmode = LOGMODE_STDIO;
181 /* die_func = NULL; - needed if the caller is a shell,
182 * init, or a NOFORK applet. But none of those call us
183 * as of yet (and that should probably always stay true).
184 */
185 /* xfunc_error_retval and applet_name are init by: */
186 run_applet_no_and_exit(a, argv[0], argv);
187 } 226 }
188# endif 227# endif
189# endif 228# endif
190 } 229 }
191#endif /* FEATURE_PREFER_APPLETS */ 230#endif
192 rc = spawn(argv); 231 rc = spawn(argv);
193 return wait4pid(rc); 232 return wait4pid(rc);
194} 233}
@@ -209,6 +248,9 @@ pid_t FAST_FUNC fork_or_rexec(char **argv)
209 /* Maybe we are already re-execed and come here again? */ 248 /* Maybe we are already re-execed and come here again? */
210 if (re_execed) 249 if (re_execed)
211 return 0; 250 return 0;
251
252 /* fflush_all(); ? - so far all callers had no buffered output to flush */
253
212 pid = xvfork(); 254 pid = xvfork();
213 if (pid) /* parent */ 255 if (pid) /* parent */
214 return pid; 256 return pid;
@@ -245,8 +287,11 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
245 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */ 287 fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
246 288
247 if (!(flags & DAEMON_ONLY_SANITIZE)) { 289 if (!(flags & DAEMON_ONLY_SANITIZE)) {
290
291 /* fflush_all(); - add it in fork_or_rexec() if necessary */
292
248 if (fork_or_rexec(argv)) 293 if (fork_or_rexec(argv))
249 exit(EXIT_SUCCESS); /* parent */ 294 _exit(EXIT_SUCCESS); /* parent */
250 /* if daemonizing, detach from stdio & ctty */ 295 /* if daemonizing, detach from stdio & ctty */
251 setsid(); 296 setsid();
252 dup2(fd, 0); 297 dup2(fd, 0);
@@ -258,7 +303,7 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
258 * Prevent this: stop being a session leader. 303 * Prevent this: stop being a session leader.
259 */ 304 */
260 if (fork_or_rexec(argv)) 305 if (fork_or_rexec(argv))
261 exit(EXIT_SUCCESS); /* parent */ 306 _exit(EXIT_SUCCESS); /* parent */
262 } 307 }
263 } 308 }
264 while (fd > 2) { 309 while (fd > 2) {
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 98d3531d6..1b3a1667b 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -313,7 +313,7 @@ int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp)
313 313
314int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags) 314int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags)
315{ 315{
316//TODO: lineedit, microcom and less might be adapted to use this too: 316//TODO: lineedit, microcom, slattach, less might be adapted to use this too:
317// grep for "tcsetattr" 317// grep for "tcsetattr"
318 318
319 struct termios newterm; 319 struct termios newterm;
diff --git a/libpwdgrp/uidgid_get.c b/libpwdgrp/uidgid_get.c
index 1199f23f9..283ac78fc 100644
--- a/libpwdgrp/uidgid_get.c
+++ b/libpwdgrp/uidgid_get.c
@@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
13 3. The name of the author may not be used to endorse or promote products 13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission. 14 derived from this software without specific prior written permission.
15 15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
diff --git a/loginutils/add-remove-shell.c b/loginutils/add-remove-shell.c
index 750b44bd6..6d03de254 100644
--- a/loginutils/add-remove-shell.c
+++ b/loginutils/add-remove-shell.c
@@ -19,9 +19,9 @@
19//config: help 19//config: help
20//config: Remove shells from /etc/shells. 20//config: Remove shells from /etc/shells.
21 21
22// APPLET_ODDNAME:name main location suid_type help 22// APPLET_NOEXEC:name main location suid_type help
23//applet:IF_ADD_SHELL( APPLET_ODDNAME(add-shell , add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, add_shell )) 23//applet:IF_ADD_SHELL( APPLET_NOEXEC(add-shell , add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, add_shell ))
24//applet:IF_REMOVE_SHELL(APPLET_ODDNAME(remove-shell, add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, remove_shell)) 24//applet:IF_REMOVE_SHELL(APPLET_NOEXEC(remove-shell, add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, remove_shell))
25 25
26//kbuild:lib-$(CONFIG_ADD_SHELL) += add-remove-shell.o 26//kbuild:lib-$(CONFIG_ADD_SHELL) += add-remove-shell.o
27//kbuild:lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o 27//kbuild:lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o
@@ -64,6 +64,7 @@ int add_remove_shell_main(int argc UNUSED_PARAM, char **argv)
64 if (orig_fp) 64 if (orig_fp)
65 xfstat(fileno(orig_fp), &sb, orig_fn); 65 xfstat(fileno(orig_fp), &sb, orig_fn);
66 66
67
67 new_fn = xasprintf("%s.tmp", orig_fn); 68 new_fn = xasprintf("%s.tmp", orig_fn);
68 /* 69 /*
69 * O_TRUNC or O_EXCL? At the first glance, O_EXCL looks better, 70 * O_TRUNC or O_EXCL? At the first glance, O_EXCL looks better,
diff --git a/loginutils/addgroup.c b/loginutils/addgroup.c
index b197fc149..adef2328d 100644
--- a/loginutils/addgroup.c
+++ b/loginutils/addgroup.c
@@ -12,14 +12,10 @@
12//config:config ADDGROUP 12//config:config ADDGROUP
13//config: bool "addgroup (8.2 kb)" 13//config: bool "addgroup (8.2 kb)"
14//config: default y 14//config: default y
15//config: select LONG_OPTS
15//config: help 16//config: help
16//config: Utility for creating a new group account. 17//config: Utility for creating a new group account.
17//config: 18//config:
18//config:config FEATURE_ADDGROUP_LONG_OPTIONS
19//config: bool "Enable long options"
20//config: default y
21//config: depends on ADDGROUP && LONG_OPTS
22//config:
23//config:config FEATURE_ADDUSER_TO_GROUP 19//config:config FEATURE_ADDUSER_TO_GROUP
24//config: bool "Support adding users to groups" 20//config: bool "Support adding users to groups"
25//config: default y 21//config: default y
@@ -29,7 +25,7 @@
29//config: addgroup will add an existing user to an 25//config: addgroup will add an existing user to an
30//config: existing group. 26//config: existing group.
31 27
32//applet:IF_ADDGROUP(APPLET(addgroup, BB_DIR_USR_SBIN, BB_SUID_DROP)) 28//applet:IF_ADDGROUP(APPLET_NOEXEC(addgroup, addgroup, BB_DIR_USR_SBIN, BB_SUID_DROP, addgroup))
33 29
34//kbuild:lib-$(CONFIG_ADDGROUP) += addgroup.o 30//kbuild:lib-$(CONFIG_ADDGROUP) += addgroup.o
35 31
@@ -131,12 +127,11 @@ static void new_group(char *group, gid_t gid)
131#endif 127#endif
132} 128}
133 129
134#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS 130//FIXME: upstream addgroup has no short options! NOT COMPATIBLE!
135static const char addgroup_longopts[] ALIGN1 = 131static const char addgroup_longopts[] ALIGN1 =
136 "gid\0" Required_argument "g" 132 "gid\0" Required_argument "g"
137 "system\0" No_argument "S" 133 "system\0" No_argument "S"
138 ; 134 ;
139#endif
140 135
141/* 136/*
142 * addgroup will take a login_name as its first parameter. 137 * addgroup will take a login_name as its first parameter.
@@ -155,16 +150,14 @@ int addgroup_main(int argc UNUSED_PARAM, char **argv)
155 if (geteuid()) { 150 if (geteuid()) {
156 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); 151 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
157 } 152 }
158#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS
159 applet_long_options = addgroup_longopts;
160#endif
161 /* Syntax: 153 /* Syntax:
162 * addgroup group 154 * addgroup group
163 * addgroup -g num group 155 * addgroup --gid num group
164 * addgroup user group 156 * addgroup user group
165 * Check for min, max and missing args */ 157 * Check for min, max and missing args */
166 opt_complementary = "-1:?2"; 158 opts = getopt32long(argv, "^" "g:S" "\0" "-1:?2", addgroup_longopts,
167 opts = getopt32(argv, "g:S", &gid); 159 &gid
160 );
168 /* move past the commandline options */ 161 /* move past the commandline options */
169 argv += optind; 162 argv += optind;
170 //argc -= optind; 163 //argc -= optind;
diff --git a/loginutils/adduser.c b/loginutils/adduser.c
index ef18278ac..b2b5be5b3 100644
--- a/loginutils/adduser.c
+++ b/loginutils/adduser.c
@@ -10,14 +10,10 @@
10//config:config ADDUSER 10//config:config ADDUSER
11//config: bool "adduser (15 kb)" 11//config: bool "adduser (15 kb)"
12//config: default y 12//config: default y
13//config: select LONG_OPTS
13//config: help 14//config: help
14//config: Utility for creating a new user account. 15//config: Utility for creating a new user account.
15//config: 16//config:
16//config:config FEATURE_ADDUSER_LONG_OPTIONS
17//config: bool "Enable long options"
18//config: default y
19//config: depends on ADDUSER && LONG_OPTS
20//config:
21//config:config FEATURE_CHECK_NAMES 17//config:config FEATURE_CHECK_NAMES
22//config: bool "Enable sanity check on user/group names in adduser and addgroup" 18//config: bool "Enable sanity check on user/group names in adduser and addgroup"
23//config: default n 19//config: default n
@@ -53,7 +49,7 @@
53//config: help 49//config: help
54//config: Last valid system uid or gid for adduser and addgroup 50//config: Last valid system uid or gid for adduser and addgroup
55 51
56//applet:IF_ADDUSER(APPLET(adduser, BB_DIR_USR_SBIN, BB_SUID_DROP)) 52//applet:IF_ADDUSER(APPLET_NOEXEC(adduser, adduser, BB_DIR_USR_SBIN, BB_SUID_DROP, adduser))
57 53
58//kbuild:lib-$(CONFIG_ADDUSER) += adduser.o 54//kbuild:lib-$(CONFIG_ADDUSER) += adduser.o
59 55
@@ -148,15 +144,7 @@ static int addgroup_wrapper(struct passwd *p, const char *group_name)
148 /* Add user to his own group with the first free gid 144 /* Add user to his own group with the first free gid
149 * found in passwd_study. 145 * found in passwd_study.
150 */ 146 */
151#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP
152 /* We try to use --gid, not -g, because "standard" addgroup
153 * has no short option -g, it has only long --gid.
154 */
155 argv[1] = (char*)"--gid"; 147 argv[1] = (char*)"--gid";
156#else
157 /* Breaks if system in fact does NOT use busybox addgroup */
158 argv[1] = (char*)"-g";
159#endif
160 argv[2] = utoa(p->pw_gid); 148 argv[2] = utoa(p->pw_gid);
161 argv[3] = (char*)"--"; 149 argv[3] = (char*)"--";
162 argv[4] = p->pw_name; 150 argv[4] = p->pw_name;
@@ -174,7 +162,7 @@ static void passwd_wrapper(const char *login_name)
174 bb_error_msg_and_die("can't execute passwd, you must set password manually"); 162 bb_error_msg_and_die("can't execute passwd, you must set password manually");
175} 163}
176 164
177#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS 165//FIXME: upstream adduser has no short options! NOT COMPATIBLE!
178static const char adduser_longopts[] ALIGN1 = 166static const char adduser_longopts[] ALIGN1 =
179 "home\0" Required_argument "h" 167 "home\0" Required_argument "h"
180 "gecos\0" Required_argument "g" 168 "gecos\0" Required_argument "g"
@@ -187,7 +175,6 @@ static const char adduser_longopts[] ALIGN1 =
187 "uid\0" Required_argument "u" 175 "uid\0" Required_argument "u"
188 "skel\0" Required_argument "k" 176 "skel\0" Required_argument "k"
189 ; 177 ;
190#endif
191 178
192/* 179/*
193 * adduser will take a login_name as its first parameter. 180 * adduser will take a login_name as its first parameter.
@@ -204,10 +191,6 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
204 char *uid; 191 char *uid;
205 const char *skel = "/etc/skel"; 192 const char *skel = "/etc/skel";
206 193
207#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
208 applet_long_options = adduser_longopts;
209#endif
210
211 /* got root? */ 194 /* got root? */
212 if (geteuid()) { 195 if (geteuid()) {
213 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); 196 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
@@ -218,10 +201,15 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
218 pw.pw_shell = (char *)get_shell_name(); 201 pw.pw_shell = (char *)get_shell_name();
219 pw.pw_dir = NULL; 202 pw.pw_dir = NULL;
220 203
221 /* at least one and at most two non-option args */ 204 opts = getopt32long(argv, "^"
222 /* disable interactive passwd for system accounts */ 205 "h:g:s:G:DSHu:k:"
223 opt_complementary = "-1:?2:SD"; 206 /* at least one and at most two non-option args */
224 opts = getopt32(argv, "h:g:s:G:DSHu:k:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid, &skel); 207 /* disable interactive passwd for system accounts */
208 "\0" "-1:?2:SD",
209 adduser_longopts,
210 &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell,
211 &usegroup, &uid, &skel
212 );
225 if (opts & OPT_UID) 213 if (opts & OPT_UID)
226 pw.pw_uid = xatou_range(uid, 0, CONFIG_LAST_ID); 214 pw.pw_uid = xatou_range(uid, 0, CONFIG_LAST_ID);
227 215
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c
index e390da647..652e4f127 100644
--- a/loginutils/chpasswd.c
+++ b/loginutils/chpasswd.c
@@ -61,9 +61,10 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
61 if (getuid() != 0) 61 if (getuid() != 0)
62 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); 62 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
63 63
64 opt_complementary = "m--ec:e--mc:c--em"; 64 opt = getopt32long(argv, "^" "emc:" "\0" "m--ec:e--mc:c--em",
65 IF_LONG_OPTS(applet_long_options = chpasswd_longopts;) 65 chpasswd_longopts,
66 opt = getopt32(argv, "emc:", &algo); 66 &algo
67 );
67 68
68 while ((name = xmalloc_fgetline(stdin)) != NULL) { 69 while ((name = xmalloc_fgetline(stdin)) != NULL) {
69 char *free_me; 70 char *free_me;
diff --git a/loginutils/cryptpw.c b/loginutils/cryptpw.c
index f8906c59a..76138a61f 100644
--- a/loginutils/cryptpw.c
+++ b/loginutils/cryptpw.c
@@ -24,9 +24,9 @@
24//config: using the given salt. Debian has this utility under mkpasswd 24//config: using the given salt. Debian has this utility under mkpasswd
25//config: name. Busybox provides mkpasswd as an alias for cryptpw. 25//config: name. Busybox provides mkpasswd as an alias for cryptpw.
26 26
27//applet:IF_CRYPTPW(APPLET(cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP)) 27//applet:IF_CRYPTPW( APPLET_NOEXEC(cryptpw, cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP, cryptpw))
28// APPLET_ODDNAME:name main location suid_type help 28// APPLET_NOEXEC:name main location suid_type help
29//applet:IF_MKPASSWD(APPLET_ODDNAME(mkpasswd, cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP, cryptpw)) 29//applet:IF_MKPASSWD(APPLET_NOEXEC(mkpasswd, cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP, cryptpw))
30 30
31//kbuild:lib-$(CONFIG_CRYPTPW) += cryptpw.o 31//kbuild:lib-$(CONFIG_CRYPTPW) += cryptpw.o
32//kbuild:lib-$(CONFIG_MKPASSWD) += cryptpw.o 32//kbuild:lib-$(CONFIG_MKPASSWD) += cryptpw.o
@@ -106,14 +106,15 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
106 "salt\0" Required_argument "S" 106 "salt\0" Required_argument "S"
107 "method\0" Required_argument "m" 107 "method\0" Required_argument "m"
108 ; 108 ;
109 applet_long_options = mkpasswd_longopts;
110#endif 109#endif
111 fd = STDIN_FILENO; 110 fd = STDIN_FILENO;
112 opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; 111 opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
113 opt_S = NULL; 112 opt_S = NULL;
114 /* at most two non-option arguments; -P NUM */ 113 /* at most two non-option arguments; -P NUM */
115 opt_complementary = "?2"; 114 getopt32long(argv, "^" "sP:+S:m:a:" "\0" "?2",
116 getopt32(argv, "sP:+S:m:a:", &fd, &opt_S, &opt_m, &opt_m); 115 mkpasswd_longopts,
116 &fd, &opt_S, &opt_m, &opt_m
117 );
117 argv += optind; 118 argv += optind;
118 119
119 /* have no idea how to handle -s... */ 120 /* have no idea how to handle -s... */
diff --git a/loginutils/deluser.c b/loginutils/deluser.c
index 3b6bd952d..5ee22d4f1 100644
--- a/loginutils/deluser.c
+++ b/loginutils/deluser.c
@@ -28,8 +28,9 @@
28//config: If called with two non-option arguments, deluser 28//config: If called with two non-option arguments, deluser
29//config: or delgroup will remove an user from a specified group. 29//config: or delgroup will remove an user from a specified group.
30 30
31//applet:IF_DELUSER(APPLET(deluser, BB_DIR_USR_SBIN, BB_SUID_DROP)) 31// APPLET_NOEXEC:name main location suid_type help
32//applet:IF_DELGROUP(APPLET_ODDNAME(delgroup, deluser, BB_DIR_USR_SBIN, BB_SUID_DROP, delgroup)) 32//applet:IF_DELUSER( APPLET_NOEXEC(deluser, deluser, BB_DIR_USR_SBIN, BB_SUID_DROP, deluser))
33//applet:IF_DELGROUP(APPLET_NOEXEC(delgroup, deluser, BB_DIR_USR_SBIN, BB_SUID_DROP, delgroup))
33 34
34//kbuild:lib-$(CONFIG_DELUSER) += deluser.o 35//kbuild:lib-$(CONFIG_DELUSER) += deluser.o
35//kbuild:lib-$(CONFIG_DELGROUP) += deluser.o 36//kbuild:lib-$(CONFIG_DELGROUP) += deluser.o
@@ -67,9 +68,8 @@ int deluser_main(int argc, char **argv)
67#else 68#else
68 int opt_delhome = 0; 69 int opt_delhome = 0;
69 if (do_deluser) { 70 if (do_deluser) {
70 applet_long_options = 71 opt_delhome = getopt32long(argv, "",
71 "remove-home\0" No_argument "\xff"; 72 "remove-home\0" No_argument "\xff");
72 opt_delhome = getopt32(argv, "");
73 argv += opt_delhome; 73 argv += opt_delhome;
74 argc -= opt_delhome; 74 argc -= opt_delhome;
75 } 75 }
diff --git a/loginutils/getty.c b/loginutils/getty.c
index fd5116d08..23e92bc77 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -131,7 +131,7 @@ struct globals {
131//usage: "\n" 131//usage: "\n"
132//usage: "\nBAUD_RATE of 0 leaves it unchanged" 132//usage: "\nBAUD_RATE of 0 leaves it unchanged"
133 133
134static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:+wn"; 134#define OPT_STR "I:LH:f:hil:mt:+wn"
135#define F_INITSTRING (1 << 0) /* -I */ 135#define F_INITSTRING (1 << 0) /* -I */
136#define F_LOCAL (1 << 1) /* -L */ 136#define F_LOCAL (1 << 1) /* -L */
137#define F_FAKEHOST (1 << 2) /* -H */ 137#define F_FAKEHOST (1 << 2) /* -H */
@@ -179,8 +179,7 @@ static void parse_args(char **argv)
179 char *ts; 179 char *ts;
180 int flags; 180 int flags;
181 181
182 opt_complementary = "-2"; /* at least 2 args; -t N */ 182 flags = getopt32(argv, "^" OPT_STR "\0" "-2"/* at least 2 args*/,
183 flags = getopt32(argv, opt_string,
184 &G.initstring, &G.fakehost, &G.issue, 183 &G.initstring, &G.fakehost, &G.issue,
185 &G.login, &G.timeout 184 &G.login, &G.timeout
186 ); 185 );
diff --git a/loginutils/login.c b/loginutils/login.c
index 381468d81..fcdb9592c 100644
--- a/loginutils/login.c
+++ b/loginutils/login.c
@@ -350,8 +350,8 @@ int login_main(int argc UNUSED_PARAM, char **argv)
350 /* Mandatory paranoia for suid applet: 350 /* Mandatory paranoia for suid applet:
351 * ensure that fd# 0,1,2 are opened (at least to /dev/null) 351 * ensure that fd# 0,1,2 are opened (at least to /dev/null)
352 * and any extra open fd's are closed. 352 * and any extra open fd's are closed.
353 * (The name of the function is misleading. Not daemonizing here.) */ 353 */
354 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL); 354 bb_daemon_helper(DAEMON_CLOSE_EXTRA_FDS);
355 355
356 username[0] = '\0'; 356 username[0] = '\0';
357 opt = getopt32(argv, "f:h:p", &opt_user, &opt_host); 357 opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c
index d5a463cac..27ea5dff0 100644
--- a/loginutils/sulogin.c
+++ b/loginutils/sulogin.c
@@ -12,7 +12,7 @@
12//config: sulogin is invoked when the system goes into single user 12//config: sulogin is invoked when the system goes into single user
13//config: mode (this is done through an entry in inittab). 13//config: mode (this is done through an entry in inittab).
14 14
15//applet:IF_SULOGIN(APPLET(sulogin, BB_DIR_SBIN, BB_SUID_DROP)) 15//applet:IF_SULOGIN(APPLET_NOEXEC(sulogin, sulogin, BB_DIR_SBIN, BB_SUID_DROP, sulogin))
16 16
17//kbuild:lib-$(CONFIG_SULOGIN) += sulogin.o 17//kbuild:lib-$(CONFIG_SULOGIN) += sulogin.o
18 18
@@ -34,7 +34,7 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
34 34
35 /* Note: sulogin is not a suid app. It is meant to be run by init 35 /* Note: sulogin is not a suid app. It is meant to be run by init
36 * for single user / emergency mode. init starts it as root. 36 * for single user / emergency mode. init starts it as root.
37 * Normal users (potentially malisious ones) can only run it under 37 * Normal users (potentially malicious ones) can only run it under
38 * their UID, therefore no paranoia here is warranted: 38 * their UID, therefore no paranoia here is warranted:
39 * $LD_LIBRARY_PATH in env, TTY = /dev/sda 39 * $LD_LIBRARY_PATH in env, TTY = /dev/sda
40 * are no more dangerous here than in e.g. cp applet. 40 * are no more dangerous here than in e.g. cp applet.
diff --git a/loginutils/vlock.c b/loginutils/vlock.c
index bf46d085c..9e319fe61 100644
--- a/loginutils/vlock.c
+++ b/loginutils/vlock.c
@@ -66,8 +66,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
66 struct passwd *pw; 66 struct passwd *pw;
67 67
68 pw = xgetpwuid(getuid()); 68 pw = xgetpwuid(getuid());
69 opt_complementary = "=0"; /* no params! */ 69 getopt32(argv, "^" "a" "\0" "=0"/* no args!*/);
70 getopt32(argv, "a");
71 70
72 /* Ignore some signals so that we don't get killed by them */ 71 /* Ignore some signals so that we don't get killed by them */
73 bb_signals(0 72 bb_signals(0
diff --git a/mailutils/mail.c b/mailutils/mail.c
index a7e43c0d1..eceb89071 100644
--- a/mailutils/mail.c
+++ b/mailutils/mail.c
@@ -6,23 +6,15 @@
6 * 6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree. 7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */ 8 */
9#include <sys/prctl.h>
9#include "libbb.h" 10#include "libbb.h"
10#include "mail.h" 11#include "mail.h"
11 12
12static void kill_helper(void)
13{
14 if (G.helper_pid > 0) {
15 kill(G.helper_pid, SIGTERM);
16 G.helper_pid = 0;
17 }
18}
19
20// generic signal handler 13// generic signal handler
21static void signal_handler(int signo) 14static void signal_handler(int signo)
22{ 15{
23#define err signo 16#define err signo
24 if (SIGALRM == signo) { 17 if (SIGALRM == signo) {
25 kill_helper();
26 bb_error_msg_and_die("timed out"); 18 bb_error_msg_and_die("timed out");
27 } 19 }
28 20
@@ -66,16 +58,15 @@ void FAST_FUNC launch_helper(const char **argv)
66 // child stdout [1] -> parent stdin [0] 58 // child stdout [1] -> parent stdin [0]
67 59
68 if (!G.helper_pid) { 60 if (!G.helper_pid) {
69 // child: try to execute connection helper 61 // child
62 // if parent dies, get SIGTERM
63 prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
64 // try to execute connection helper
70 // NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec 65 // NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
71 BB_EXECVP_or_die((char**)argv); 66 BB_EXECVP_or_die((char**)argv);
72 } 67 }
73 68
74 // parent 69 // parent goes on
75 // check whether child is alive
76 //redundant:signal_handler(SIGCHLD);
77 // child seems OK -> parent goes on
78 atexit(kill_helper);
79} 70}
80 71
81char* FAST_FUNC send_mail_command(const char *fmt, const char *param) 72char* FAST_FUNC send_mail_command(const char *fmt, const char *param)
diff --git a/mailutils/popmaildir.c b/mailutils/popmaildir.c
index 1695a9bb8..5756eaa76 100644
--- a/mailutils/popmaildir.c
+++ b/mailutils/popmaildir.c
@@ -125,9 +125,9 @@ int popmaildir_main(int argc UNUSED_PARAM, char **argv)
125 INIT_G(); 125 INIT_G();
126 126
127 // parse options 127 // parse options
128 opt_complementary = "-1:dd"; 128 opts = getopt32(argv, "^"
129 opts = getopt32(argv, 129 "bdmVcasTkt:+" "R:+Z:L:+H:+" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:")
130 "bdmVcasTkt:+" "R:+Z:L:+H:+" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"), 130 "\0" "-1:dd",
131 &timeout, NULL, NULL, NULL, &opt_nlines 131 &timeout, NULL, NULL, NULL, &opt_nlines
132 IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same 132 IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same
133 ); 133 );
diff --git a/mailutils/reformime.c b/mailutils/reformime.c
index 6a0254803..321729e0a 100644
--- a/mailutils/reformime.c
+++ b/mailutils/reformime.c
@@ -280,9 +280,9 @@ int reformime_main(int argc UNUSED_PARAM, char **argv)
280 280
281 // parse options 281 // parse options
282 // N.B. only -x and -X are supported so far 282 // N.B. only -x and -X are supported so far
283 opt_complementary = "x--X:X--x"; 283 opts = getopt32(argv, "^"
284 opts = getopt32(argv, 284 "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:*h:o:O:")
285 "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:*h:o:O:"), 285 "\0" "x--X:X--x",
286 &opt_prefix 286 &opt_prefix
287 IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL) 287 IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
288 ); 288 );
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index 346de2712..f440e6319 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -166,9 +166,8 @@ static char *angle_address(char *str)
166{ 166{
167 char *s, *e; 167 char *s, *e;
168 168
169 trim(str); 169 e = trim(str);
170 e = last_char_is(str, '>'); 170 if (e != str && e[-1] == '>') {
171 if (e) {
172 s = strrchr(str, '<'); 171 s = strrchr(str, '<');
173 if (s) { 172 if (s) {
174 *e = '\0'; 173 *e = '\0';
@@ -257,13 +256,17 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
257 G.fp0 = xfdopen_for_read(3); 256 G.fp0 = xfdopen_for_read(3);
258 257
259 // parse options 258 // parse options
260 // -v is a counter, -H and -S are mutually exclusive, -a is a list
261 opt_complementary = "vv:H--S:S--H";
262 // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect 259 // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect
263 // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility, 260 // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility,
264 // it is still under development. 261 // it is still under development.
265 opts = getopt32(argv, "tf:o:iw:+H:S:a:*:v", &opt_from, NULL, 262 opts = getopt32(argv, "^"
266 &timeout, &opt_connect, &opt_connect, &list, &verbose); 263 "tf:o:iw:+H:S:a:*:v"
264 "\0"
265 // -v is a counter, -H and -S are mutually exclusive, -a is a list
266 "vv:H--S:S--H",
267 &opt_from, NULL,
268 &timeout, &opt_connect, &opt_connect, &list, &verbose
269 );
267 //argc -= optind; 270 //argc -= optind;
268 argv += optind; 271 argv += optind;
269 272
diff --git a/miscutils/adjtimex.c b/miscutils/adjtimex.c
index 04ba5636f..c1718e909 100644
--- a/miscutils/adjtimex.c
+++ b/miscutils/adjtimex.c
@@ -1,6 +1,6 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables. 3 * adjtimex.c - read, and possibly modify, the Linux kernel 'timex' variables.
4 * 4 *
5 * Originally written: October 1997 5 * Originally written: October 1997
6 * Last hack: March 2001 6 * Last hack: March 2001
@@ -18,7 +18,7 @@
18//config: Adjtimex reads and optionally sets adjustment parameters for 18//config: Adjtimex reads and optionally sets adjustment parameters for
19//config: the Linux clock adjustment algorithm. 19//config: the Linux clock adjustment algorithm.
20 20
21//applet:IF_ADJTIMEX(APPLET(adjtimex, BB_DIR_SBIN, BB_SUID_DROP)) 21//applet:IF_ADJTIMEX(APPLET_NOFORK(adjtimex, adjtimex, BB_DIR_SBIN, BB_SUID_DROP, adjtimex))
22 22
23//kbuild:lib-$(CONFIG_ADJTIMEX) += adjtimex.o 23//kbuild:lib-$(CONFIG_ADJTIMEX) += adjtimex.o
24 24
@@ -90,13 +90,15 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv)
90 unsigned opt; 90 unsigned opt;
91 char *opt_o, *opt_f, *opt_p, *opt_t; 91 char *opt_o, *opt_f, *opt_p, *opt_t;
92 struct timex txc; 92 struct timex txc;
93 int i, ret; 93 int ret;
94 const char *descript; 94 const char *descript;
95 95
96 opt_complementary = "=0"; /* no valid non-option parameters */ 96 memset(&txc, 0, sizeof(txc));
97 opt = getopt32(argv, "qo:f:p:t:", 97
98 &opt_o, &opt_f, &opt_p, &opt_t); 98 opt = getopt32(argv, "^" "qo:f:p:t:"
99 txc.modes = 0; 99 "\0" "=0"/*no valid non-option args*/,
100 &opt_o, &opt_f, &opt_p, &opt_t
101 );
100 //if (opt & 0x1) // -q 102 //if (opt & 0x1) // -q
101 if (opt & 0x2) { // -o 103 if (opt & 0x2) { // -o
102 txc.offset = xatol(opt_o); 104 txc.offset = xatol(opt_o);
@@ -115,15 +117,19 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv)
115 txc.modes |= ADJ_TICK; 117 txc.modes |= ADJ_TICK;
116 } 118 }
117 119
118 ret = adjtimex(&txc); 120 /* It's NOFORK applet because the code is very simple:
121 * just some printf. No opens, no allocs.
122 * If you need to make it more complex, feel free to downgrade to NOEXEC
123 */
119 124
120 if (ret < 0) { 125 ret = adjtimex(&txc);
126 if (ret < 0)
121 bb_perror_nomsg_and_die(); 127 bb_perror_nomsg_and_die();
122 }
123 128
124 if (!(opt & OPT_quiet)) { 129 if (!(opt & OPT_quiet)) {
125 const char *sep; 130 const char *sep;
126 const char *name; 131 const char *name;
132 int i;
127 133
128 printf( 134 printf(
129 " mode: %d\n" 135 " mode: %d\n"
@@ -132,8 +138,9 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv)
132 " maxerror: %ld\n" 138 " maxerror: %ld\n"
133 " esterror: %ld\n" 139 " esterror: %ld\n"
134 " status: %d (", 140 " status: %d (",
135 txc.modes, txc.offset, txc.freq, txc.maxerror, 141 txc.modes, txc.offset, txc.freq, txc.maxerror,
136 txc.esterror, txc.status); 142 txc.esterror, txc.status
143 );
137 144
138 /* representative output of next code fragment: 145 /* representative output of next code fragment:
139 * "PLL | PPSTIME" 146 * "PLL | PPSTIME"
@@ -159,9 +166,11 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv)
159 " time.tv_sec: %ld\n" 166 " time.tv_sec: %ld\n"
160 " time.tv_usec: %ld\n" 167 " time.tv_usec: %ld\n"
161 " return value: %d (%s)\n", 168 " return value: %d (%s)\n",
162 txc.constant, 169 txc.constant,
163 txc.precision, txc.tolerance, txc.tick, 170 txc.precision, txc.tolerance, txc.tick,
164 (long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript); 171 (long)txc.time.tv_sec, (long)txc.time.tv_usec,
172 ret, descript
173 );
165 } 174 }
166 175
167 return 0; 176 return 0;
diff --git a/miscutils/chat.c b/miscutils/chat.c
index 216a899a0..1446a040c 100644
--- a/miscutils/chat.c
+++ b/miscutils/chat.c
@@ -82,8 +82,8 @@
82//usage: "EXPECT [SEND [EXPECT [SEND...]]]" 82//usage: "EXPECT [SEND [EXPECT [SEND...]]]"
83//usage:#define chat_full_usage "\n\n" 83//usage:#define chat_full_usage "\n\n"
84//usage: "Useful for interacting with a modem connected to stdin/stdout.\n" 84//usage: "Useful for interacting with a modem connected to stdin/stdout.\n"
85//usage: "A script consists of one or more \"expect-send\" pairs of strings,\n" 85//usage: "A script consists of \"expect-send\" argument pairs.\n"
86//usage: "each pair is a pair of arguments. Example:\n" 86//usage: "Example:\n"
87//usage: "chat '' ATZ OK ATD123456 CONNECT '' ogin: pppuser word: ppppass '~'" 87//usage: "chat '' ATZ OK ATD123456 CONNECT '' ogin: pppuser word: ppppass '~'"
88 88
89#include "libbb.h" 89#include "libbb.h"
diff --git a/miscutils/conspy.c b/miscutils/conspy.c
index 47b9e7207..a0e0d4e4b 100644
--- a/miscutils/conspy.c
+++ b/miscutils/conspy.c
@@ -367,7 +367,7 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
367 unsigned ttynum; 367 unsigned ttynum;
368 int poll_timeout_ms; 368 int poll_timeout_ms;
369#if ENABLE_LONG_OPTS 369#if ENABLE_LONG_OPTS
370 static const char getopt_longopts[] ALIGN1 = 370 static const char conspy_longopts[] ALIGN1 =
371 "viewonly\0" No_argument "v" 371 "viewonly\0" No_argument "v"
372 "createdevice\0" No_argument "c" 372 "createdevice\0" No_argument "c"
373 "neverquit\0" No_argument "Q" 373 "neverquit\0" No_argument "Q"
@@ -377,8 +377,6 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
377 "follow\0" No_argument "f" 377 "follow\0" No_argument "f"
378 "framebuffer\0" No_argument "F" 378 "framebuffer\0" No_argument "F"
379 ; 379 ;
380
381 applet_long_options = getopt_longopts;
382#endif 380#endif
383#define keybuf bb_common_bufsiz1 381#define keybuf bb_common_bufsiz1
384 setup_common_bufsiz(); 382 setup_common_bufsiz();
@@ -387,7 +385,7 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
387 strcpy(G.vcsa_name, DEV_VCSA); 385 strcpy(G.vcsa_name, DEV_VCSA);
388 386
389 // numeric params 387 // numeric params
390 opts = getopt32(argv, "vcQsndfFx:+y:+", &G.x, &G.y); 388 opts = getopt32long(argv, "vcQsndfFx:+y:+", conspy_longopts, &G.x, &G.y);
391 argv += optind; 389 argv += optind;
392 ttynum = 0; 390 ttynum = 0;
393 if (argv[0]) { 391 if (argv[0]) {
diff --git a/miscutils/crond.c b/miscutils/crond.c
index 48e429976..f6580a9d4 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -1021,13 +1021,17 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
1021 1021
1022 INIT_G(); 1022 INIT_G();
1023 1023
1024 /* "-b after -f is ignored", and so on for every pair a-b */ 1024 opts = getopt32(argv, "^"
1025 opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") 1025 "l:L:fbSc:" IF_FEATURE_CROND_D("d:")
1026 "\0"
1027 /* "-b after -f is ignored", and so on for every pair a-b */
1028 "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l")
1026 /* -l and -d have numeric param */ 1029 /* -l and -d have numeric param */
1027 ":l+" IF_FEATURE_CROND_D(":d+"); 1030 ":l+" IF_FEATURE_CROND_D(":d+")
1028 opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), 1031 ,
1029 &G.log_level, &G.log_filename, &G.crontab_dir_name 1032 &G.log_level, &G.log_filename, &G.crontab_dir_name
1030 IF_FEATURE_CROND_D(,&G.log_level)); 1033 IF_FEATURE_CROND_D(,&G.log_level)
1034 );
1031 /* both -d N and -l N set the same variable: G.log_level */ 1035 /* both -d N and -l N set the same variable: G.log_level */
1032 1036
1033 if (!(opts & OPT_f)) { 1037 if (!(opts & OPT_f)) {
diff --git a/miscutils/crontab.c b/miscutils/crontab.c
index 804cb57f2..4787fa08f 100644
--- a/miscutils/crontab.c
+++ b/miscutils/crontab.c
@@ -99,8 +99,9 @@ int crontab_main(int argc UNUSED_PARAM, char **argv)
99 OPT_ler = OPT_l + OPT_e + OPT_r, 99 OPT_ler = OPT_l + OPT_e + OPT_r,
100 }; 100 };
101 101
102 opt_complementary = "?1:dr"; /* max one argument; -d implies -r */ 102 opt_ler = getopt32(argv, "^" "u:c:lerd" "\0" "?1:dr"/*max one arg; -d implies -r*/,
103 opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir); 103 &user_name, &crontab_dir
104 );
104 argv += optind; 105 argv += optind;
105 106
106 if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */ 107 if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */
diff --git a/miscutils/flash_eraseall.c b/miscutils/flash_eraseall.c
index af9ebea24..a6ce41f27 100644
--- a/miscutils/flash_eraseall.c
+++ b/miscutils/flash_eraseall.c
@@ -17,6 +17,7 @@
17//config: This utility is used to erase the whole MTD device. 17//config: This utility is used to erase the whole MTD device.
18 18
19//applet:IF_FLASH_ERASEALL(APPLET(flash_eraseall, BB_DIR_USR_SBIN, BB_SUID_DROP)) 19//applet:IF_FLASH_ERASEALL(APPLET(flash_eraseall, BB_DIR_USR_SBIN, BB_SUID_DROP))
20/* not NOEXEC: if flash operation stalls, use less memory in "hung" process */
20 21
21//kbuild:lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o 22//kbuild:lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o
22 23
@@ -81,8 +82,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
81 unsigned int flags; 82 unsigned int flags;
82 char *mtd_name; 83 char *mtd_name;
83 84
84 opt_complementary = "=1"; 85 flags = getopt32(argv, "^" "jNq" "\0" "=1");
85 flags = getopt32(argv, "jNq");
86 86
87 mtd_name = argv[optind]; 87 mtd_name = argv[optind];
88 fd = xopen(mtd_name, O_RDWR); 88 fd = xopen(mtd_name, O_RDWR);
diff --git a/miscutils/flash_lock_unlock.c b/miscutils/flash_lock_unlock.c
index 374eed5f6..6f2c049f4 100644
--- a/miscutils/flash_lock_unlock.c
+++ b/miscutils/flash_lock_unlock.c
@@ -20,6 +20,7 @@
20// APPLET_ODDNAME:name main location suid_type help 20// APPLET_ODDNAME:name main location suid_type help
21//applet:IF_FLASH_LOCK( APPLET_ODDNAME(flash_lock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_lock)) 21//applet:IF_FLASH_LOCK( APPLET_ODDNAME(flash_lock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_lock))
22//applet:IF_FLASH_UNLOCK(APPLET_ODDNAME(flash_unlock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_unlock)) 22//applet:IF_FLASH_UNLOCK(APPLET_ODDNAME(flash_unlock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_unlock))
23/* not NOEXEC: if flash operation stalls, use less memory in "hung" process */
23 24
24//kbuild:lib-$(CONFIG_FLASH_LOCK) += flash_lock_unlock.o 25//kbuild:lib-$(CONFIG_FLASH_LOCK) += flash_lock_unlock.o
25//kbuild:lib-$(CONFIG_FLASH_UNLOCK) += flash_lock_unlock.o 26//kbuild:lib-$(CONFIG_FLASH_UNLOCK) += flash_lock_unlock.o
diff --git a/miscutils/flashcp.c b/miscutils/flashcp.c
index d4ac62df4..858cee194 100644
--- a/miscutils/flashcp.c
+++ b/miscutils/flashcp.c
@@ -14,6 +14,7 @@
14//config: This utility is used to copy images into a MTD device. 14//config: This utility is used to copy images into a MTD device.
15 15
16//applet:IF_FLASHCP(APPLET(flashcp, BB_DIR_USR_SBIN, BB_SUID_DROP)) 16//applet:IF_FLASHCP(APPLET(flashcp, BB_DIR_USR_SBIN, BB_SUID_DROP))
17/* not NOEXEC: if flash operation stalls, use less memory in "hung" process */
17 18
18//kbuild:lib-$(CONFIG_FLASHCP) += flashcp.o 19//kbuild:lib-$(CONFIG_FLASHCP) += flashcp.o
19 20
@@ -68,8 +69,7 @@ int flashcp_main(int argc UNUSED_PARAM, char **argv)
68 RESERVE_CONFIG_UBUFFER(buf, BUFSIZE); 69 RESERVE_CONFIG_UBUFFER(buf, BUFSIZE);
69 RESERVE_CONFIG_UBUFFER(buf2, BUFSIZE); 70 RESERVE_CONFIG_UBUFFER(buf2, BUFSIZE);
70 71
71 opt_complementary = "=2"; /* exactly 2 non-option args: file, dev */ 72 /*opts =*/ getopt32(argv, "^" "v" "\0" "=2"/*exactly 2 non-option args: file,dev*/);
72 /*opts =*/ getopt32(argv, "v");
73 argv += optind; 73 argv += optind;
74// filename = *argv++; 74// filename = *argv++;
75// devicename = *argv; 75// devicename = *argv;
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c
index ca2580e92..30f606e8e 100644
--- a/miscutils/i2c_tools.c
+++ b/miscutils/i2c_tools.c
@@ -42,6 +42,7 @@
42//applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP)) 42//applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP))
43//applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP)) 43//applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP))
44//applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP)) 44//applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP))
45/* not NOEXEC: if hw operation stalls, use less memory in "hung" process */
45 46
46//kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o 47//kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o
47//kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o 48//kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o
@@ -454,14 +455,12 @@ int i2cget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
454int i2cget_main(int argc UNUSED_PARAM, char **argv) 455int i2cget_main(int argc UNUSED_PARAM, char **argv)
455{ 456{
456 const unsigned opt_f = (1 << 0), opt_y = (1 << 1); 457 const unsigned opt_f = (1 << 0), opt_y = (1 << 1);
457 const char *const optstr = "fy";
458 458
459 int bus_num, bus_addr, data_addr = -1, status; 459 int bus_num, bus_addr, data_addr = -1, status;
460 int mode = I2C_SMBUS_BYTE, pec = 0, fd; 460 int mode = I2C_SMBUS_BYTE, pec = 0, fd;
461 unsigned opts; 461 unsigned opts;
462 462
463 opt_complementary = "-2:?4"; /* from 2 to 4 args */ 463 opts = getopt32(argv, "^" "fy" "\0" "-2:?4"/*from 2 to 4 args*/);
464 opts = getopt32(argv, optstr);
465 argv += optind; 464 argv += optind;
466 465
467 bus_num = i2c_bus_lookup(argv[0]); 466 bus_num = i2c_bus_lookup(argv[0]);
@@ -543,7 +542,6 @@ int i2cset_main(int argc, char **argv)
543{ 542{
544 const unsigned opt_f = (1 << 0), opt_y = (1 << 1), 543 const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
545 opt_m = (1 << 2), opt_r = (1 << 3); 544 opt_m = (1 << 2), opt_r = (1 << 3);
546 const char *const optstr = "fym:r";
547 545
548 int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0; 546 int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0;
549 int val, blen = 0, mask = 0, fd, status; 547 int val, blen = 0, mask = 0, fd, status;
@@ -551,8 +549,7 @@ int i2cset_main(int argc, char **argv)
551 char *opt_m_arg = NULL; 549 char *opt_m_arg = NULL;
552 unsigned opts; 550 unsigned opts;
553 551
554 opt_complementary = "-3"; /* from 3 to ? args */ 552 opts = getopt32(argv, "^" "fym:r" "\0" "-3"/*from 3 to ? args*/, &opt_m_arg);
555 opts = getopt32(argv, optstr, &opt_m_arg);
556 argv += optind; 553 argv += optind;
557 argc -= optind; 554 argc -= optind;
558 555
@@ -904,7 +901,6 @@ int i2cdump_main(int argc UNUSED_PARAM, char **argv)
904{ 901{
905 const unsigned opt_f = (1 << 0), opt_y = (1 << 1), 902 const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
906 opt_r = (1 << 2); 903 opt_r = (1 << 2);
907 const char *const optstr = "fyr:";
908 904
909 int bus_num, bus_addr, mode = I2C_SMBUS_BYTE_DATA, even = 0, pec = 0; 905 int bus_num, bus_addr, mode = I2C_SMBUS_BYTE_DATA, even = 0, pec = 0;
910 unsigned first = 0x00, last = 0xff, opts; 906 unsigned first = 0x00, last = 0xff, opts;
@@ -912,8 +908,11 @@ int i2cdump_main(int argc UNUSED_PARAM, char **argv)
912 char *opt_r_str, *dash; 908 char *opt_r_str, *dash;
913 int fd, res; 909 int fd, res;
914 910
915 opt_complementary = "-2:?3"; /* from 2 to 3 args */ 911 opts = getopt32(argv, "^"
916 opts = getopt32(argv, optstr, &opt_r_str); 912 "fyr:"
913 "\0" "-2:?3" /* from 2 to 3 args */,
914 &opt_r_str
915 );
917 argv += optind; 916 argv += optind;
918 917
919 bus_num = i2c_bus_lookup(argv[0]); 918 bus_num = i2c_bus_lookup(argv[0]);
@@ -1207,15 +1206,16 @@ int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
1207 const unsigned opt_y = (1 << 0), opt_a = (1 << 1), 1206 const unsigned opt_y = (1 << 0), opt_a = (1 << 1),
1208 opt_q = (1 << 2), opt_r = (1 << 3), 1207 opt_q = (1 << 2), opt_r = (1 << 3),
1209 opt_F = (1 << 4), opt_l = (1 << 5); 1208 opt_F = (1 << 4), opt_l = (1 << 5);
1210 const char *const optstr = "yaqrFl";
1211 1209
1212 int fd, bus_num, i, j, mode = I2CDETECT_MODE_AUTO, status, cmd; 1210 int fd, bus_num, i, j, mode = I2CDETECT_MODE_AUTO, status, cmd;
1213 unsigned first = 0x03, last = 0x77, opts; 1211 unsigned first = 0x03, last = 0x77, opts;
1214 unsigned long funcs; 1212 unsigned long funcs;
1215 1213
1216 opt_complementary = "q--r:r--q:" /* mutually exclusive */ 1214 opts = getopt32(argv, "^"
1217 "?3"; /* up to 3 args */ 1215 "yaqrFl"
1218 opts = getopt32(argv, optstr); 1216 "\0"
1217 "q--r:r--q:"/*mutually exclusive*/ "?3"/*up to 3 args*/
1218 );
1219 argv += optind; 1219 argv += optind;
1220 1220
1221 if (opts & opt_l) 1221 if (opts & opt_l)
diff --git a/miscutils/lsscsi.c b/miscutils/lsscsi.c
index b69ff1eef..d7cd51056 100644
--- a/miscutils/lsscsi.c
+++ b/miscutils/lsscsi.c
@@ -16,7 +16,7 @@
16//config: 16//config:
17//config: This version uses sysfs (/sys/bus/scsi/devices) only. 17//config: This version uses sysfs (/sys/bus/scsi/devices) only.
18 18
19//applet:IF_LSSCSI(APPLET(lsscsi, BB_DIR_USR_BIN, BB_SUID_DROP)) 19//applet:IF_LSSCSI(APPLET_NOEXEC(lsscsi, lsscsi, BB_DIR_USR_BIN, BB_SUID_DROP, lsscsi))
20 20
21//kbuild:lib-$(CONFIG_LSSCSI) += lsscsi.o 21//kbuild:lib-$(CONFIG_LSSCSI) += lsscsi.o
22 22
@@ -37,9 +37,8 @@ static char *get_line(const char *filename, char *buf, unsigned *bufsize_p)
37 if (sz < 0) 37 if (sz < 0)
38 sz = 0; 38 sz = 0;
39 buf[sz] = '\0'; 39 buf[sz] = '\0';
40 trim(buf);
41 40
42 sz = strlen(buf) + 1; 41 sz = (trim(buf) - buf) + 1;
43 bufsize -= sz; 42 bufsize -= sz;
44 buf += sz; 43 buf += sz;
45 buf[0] = '\0'; 44 buf[0] = '\0';
diff --git a/miscutils/makedevs.c b/miscutils/makedevs.c
index c2f86df01..80975c652 100644
--- a/miscutils/makedevs.c
+++ b/miscutils/makedevs.c
@@ -38,7 +38,7 @@
38//config: 38//config:
39//config:endchoice 39//config:endchoice
40 40
41//applet:IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP)) 41//applet:IF_MAKEDEVS(APPLET_NOEXEC(makedevs, makedevs, BB_DIR_SBIN, BB_SUID_DROP, makedevs))
42 42
43//kbuild:lib-$(CONFIG_MAKEDEVS) += makedevs.o 43//kbuild:lib-$(CONFIG_MAKEDEVS) += makedevs.o
44 44
@@ -183,8 +183,7 @@ int makedevs_main(int argc UNUSED_PARAM, char **argv)
183 char *line = (char *)"-"; 183 char *line = (char *)"-";
184 int ret = EXIT_SUCCESS; 184 int ret = EXIT_SUCCESS;
185 185
186 opt_complementary = "=1"; /* exactly one param */ 186 getopt32(argv, "^" "d:" "\0" "=1", &line);
187 getopt32(argv, "d:", &line);
188 argv += optind; 187 argv += optind;
189 188
190 xchdir(*argv); /* ensure root dir exists */ 189 xchdir(*argv); /* ensure root dir exists */
diff --git a/miscutils/man.c b/miscutils/man.c
index f68784767..68a75c6d7 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -257,8 +257,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
257 257
258 INIT_G(); 258 INIT_G();
259 259
260 opt_complementary = "-1"; /* at least one argument */ 260 opt = getopt32(argv, "^+" "aw" "\0" "-1"/*at least one arg*/);
261 opt = getopt32(argv, "+aw");
262 argv += optind; 261 argv += optind;
263 262
264 sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); 263 sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9");
diff --git a/miscutils/microcom.c b/miscutils/microcom.c
index 14b9f3baf..b87f3273f 100644
--- a/miscutils/microcom.c
+++ b/miscutils/microcom.c
@@ -74,7 +74,9 @@ int microcom_main(int argc UNUSED_PARAM, char **argv)
74 unsigned opts; 74 unsigned opts;
75 75
76 // fetch options 76 // fetch options
77 opts = getopt32(argv, "Xs:+d:+t:+", &speed, &delay, &timeout); 77 opts = getopt32(argv, "^" "Xs:+d:+t:+" "\0" "=1",
78 &speed, &delay, &timeout
79 );
78// argc -= optind; 80// argc -= optind;
79 argv += optind; 81 argv += optind;
80 82
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c
index 14b1ed056..80a005821 100644
--- a/miscutils/nandwrite.c
+++ b/miscutils/nandwrite.c
@@ -123,15 +123,12 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv)
123 const char *opt_s = "0", *opt_f = "-", *opt_l, *opt_bb; 123 const char *opt_s = "0", *opt_f = "-", *opt_l, *opt_bb;
124 124
125 if (IS_NANDDUMP) { 125 if (IS_NANDDUMP) {
126 opt_complementary = "=1"; 126 opts = getopt32long(argv, "^" "ons:f:l:" "\0" "=1",
127#if ENABLE_LONG_OPTS 127 "bb\0" Required_argument "\xff", /* no short equivalent */
128 applet_long_options = 128 &opt_s, &opt_f, &opt_l, &opt_bb
129 "bb\0" Required_argument "\xff"; /* no short equivalent */ 129 );
130#endif
131 opts = getopt32(argv, "ons:f:l:", &opt_s, &opt_f, &opt_l, &opt_bb);
132 } else { /* nandwrite */ 130 } else { /* nandwrite */
133 opt_complementary = "-1:?2"; 131 opts = getopt32(argv, "^" "pns:" "\0" "-1:?2", &opt_s);
134 opts = getopt32(argv, "pns:", &opt_s);
135 } 132 }
136 argv += optind; 133 argv += optind;
137 134
diff --git a/miscutils/partprobe.c b/miscutils/partprobe.c
index 2c12a7d20..d1ae27348 100644
--- a/miscutils/partprobe.c
+++ b/miscutils/partprobe.c
@@ -11,7 +11,7 @@
11//config: help 11//config: help
12//config: Ask kernel to rescan partition table. 12//config: Ask kernel to rescan partition table.
13 13
14//applet:IF_PARTPROBE(APPLET(partprobe, BB_DIR_USR_SBIN, BB_SUID_DROP)) 14//applet:IF_PARTPROBE(APPLET_NOEXEC(partprobe, partprobe, BB_DIR_USR_SBIN, BB_SUID_DROP, partprobe))
15 15
16//kbuild:lib-$(CONFIG_PARTPROBE) += partprobe.o 16//kbuild:lib-$(CONFIG_PARTPROBE) += partprobe.o
17 17
diff --git a/miscutils/raidautorun.c b/miscutils/raidautorun.c
index ecedf9ce2..caf6e0821 100644
--- a/miscutils/raidautorun.c
+++ b/miscutils/raidautorun.c
@@ -15,7 +15,7 @@
15//config: raidautorun tells the kernel md driver to 15//config: raidautorun tells the kernel md driver to
16//config: search and start RAID arrays. 16//config: search and start RAID arrays.
17 17
18//applet:IF_RAIDAUTORUN(APPLET(raidautorun, BB_DIR_SBIN, BB_SUID_DROP)) 18//applet:IF_RAIDAUTORUN(APPLET_NOEXEC(raidautorun, raidautorun, BB_DIR_SBIN, BB_SUID_DROP, raidautorun))
19 19
20//kbuild:lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o 20//kbuild:lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o
21 21
diff --git a/miscutils/runlevel.c b/miscutils/runlevel.c
index 6b4742255..0b2098564 100644
--- a/miscutils/runlevel.c
+++ b/miscutils/runlevel.c
@@ -21,7 +21,7 @@
21//config: This applet uses utmp but does not rely on busybox supporing 21//config: This applet uses utmp but does not rely on busybox supporing
22//config: utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc. 22//config: utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc.
23 23
24//applet:IF_RUNLEVEL(APPLET(runlevel, BB_DIR_SBIN, BB_SUID_DROP)) 24//applet:IF_RUNLEVEL(APPLET_NOEXEC(runlevel, runlevel, BB_DIR_SBIN, BB_SUID_DROP, runlevel))
25 25
26//kbuild:lib-$(CONFIG_RUNLEVEL) += runlevel.o 26//kbuild:lib-$(CONFIG_RUNLEVEL) += runlevel.o
27 27
diff --git a/miscutils/setserial.c b/miscutils/setserial.c
index 28a1bef18..f217c3beb 100644
--- a/miscutils/setserial.c
+++ b/miscutils/setserial.c
@@ -15,7 +15,7 @@
15//config: help 15//config: help
16//config: Retrieve or set Linux serial port. 16//config: Retrieve or set Linux serial port.
17 17
18//applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP)) 18//applet:IF_SETSERIAL(APPLET_NOEXEC(setserial, setserial, BB_DIR_BIN, BB_SUID_DROP, setserial))
19 19
20//kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o 20//kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o
21 21
@@ -210,35 +210,35 @@ struct serial_struct {
210#endif 210#endif
211 211
212//usage:#define setserial_trivial_usage 212//usage:#define setserial_trivial_usage
213//usage: "[-gabGvzV] DEVICE [PARAMETER [ARG]]..." 213//usage: "[-abGvz] { DEVICE [PARAMETER [ARG]]... | -g DEVICE... }"
214//usage:#define setserial_full_usage "\n\n" 214//usage:#define setserial_full_usage "\n\n"
215//usage: "Request or set Linux serial port information\n" 215//usage: "Print or set serial port parameters"
216//usage: "\n" 216//usage: "\n"
217//usage: " -g Interpret parameters as list of devices for reporting\n" 217//usage: "\n"" -a Print all"
218//usage: " -a Print all available information\n" 218//usage: "\n"" -b Print summary"
219//usage: " -b Print summary information\n" 219//usage: "\n"" -G Print as setserial PARAMETERs"
220//usage: " -G Print in form which can be fed back\n" 220//usage: "\n"" -v Verbose"
221//usage: " to setserial as command line parameters\n" 221//usage: "\n"" -z Zero out serial flags before setting"
222//usage: " -z Zero out serial flags before setting\n" 222//usage: "\n"" -g All args are device names"
223//usage: " -v Verbose\n" 223//usage: "\n"
224//usage: "\n" 224//usage: "\n""PARAMETERs: (* = takes ARG, ^ = can be turned off by preceding ^)"
225//usage: "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n" 225//usage: "\n"" *port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,"
226//usage: " *port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,\n" 226//usage: "\n"" ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,"
227//usage: " ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n" 227//usage: "\n"" ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,"
228//usage: " ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n" 228//usage: "\n"" spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust"
229//usage: " spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n" 229//usage: "\n""ARG for uart:"
230//usage: "\n" 230//usage: "\n"" unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,"
231//usage: "UART types:\n" 231//usage: "\n"" 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,"
232//usage: " unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n" 232//usage: "\n"" U6_16550A"
233//usage: " 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n" 233
234//usage: " U6_16550A" 234// option string is "bGavzgq". "q" is accepted but ignored.
235
236#define OPT_PRINT_SUMMARY (1 << 0) 235#define OPT_PRINT_SUMMARY (1 << 0)
237#define OPT_PRINT_FEDBACK (1 << 1) 236#define OPT_PRINT_FEDBACK (1 << 1)
238#define OPT_PRINT_ALL (1 << 2) 237#define OPT_PRINT_ALL (1 << 2)
239#define OPT_VERBOSE (1 << 3) 238#define OPT_VERBOSE (1 << 3)
240#define OPT_ZERO (1 << 4) 239#define OPT_ZERO (1 << 4)
241#define OPT_GET (1 << 5) 240#define OPT_LIST_OF_DEVS (1 << 5)
241/*#define OPT_QUIET (1 << 6)*/
242 242
243#define OPT_MODE_MASK \ 243#define OPT_MODE_MASK \
244 (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK) 244 (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
@@ -362,7 +362,7 @@ static bool cmd_is_flag(int cmd)
362 return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST); 362 return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST);
363} 363}
364 364
365static bool cmd_need_arg(int cmd) 365static bool cmd_needs_arg(int cmd)
366{ 366{
367 return (cmd >= CMD_PORT && cmd <= CMD_WAIT); 367 return (cmd >= CMD_PORT && cmd <= CMD_WAIT);
368} 368}
@@ -652,11 +652,9 @@ static int find_cmd(const char *cmd)
652static void serial_set(char **arg, int opts) 652static void serial_set(char **arg, int opts)
653{ 653{
654 struct serial_struct serinfo; 654 struct serial_struct serinfo;
655 int cmd;
656 const char *word;
657 int fd; 655 int fd;
658 656
659 fd = serial_open(*arg++, /*quiet:*/ false); 657 fd = serial_open(*arg, /*quiet:*/ false);
660 if (fd < 0) 658 if (fd < 0)
661 exit(201); 659 exit(201);
662 660
@@ -665,17 +663,20 @@ static void serial_set(char **arg, int opts)
665 if (opts & OPT_ZERO) 663 if (opts & OPT_ZERO)
666 serinfo.flags = 0; 664 serinfo.flags = 0;
667 665
668 while (*arg) { 666 while (*++arg) {
667 const char *word;
669 int invert; 668 int invert;
669 int cmd;
670 670
671 word = *arg++; 671 word = *arg;
672 invert = (*word == '^'); 672 invert = (word[0] == '^');
673 word += invert; 673 word += invert;
674 674
675 cmd = find_cmd(word); 675 cmd = find_cmd(word);
676 676
677 if (*arg == NULL && cmd_need_arg(cmd)) 677 if (cmd_needs_arg(cmd))
678 bb_error_msg_and_die(bb_msg_requires_arg, word); 678 if (*++arg == NULL)
679 bb_error_msg_and_die(bb_msg_requires_arg, word);
679 680
680 if (invert && !cmd_is_flag(cmd)) 681 if (invert && !cmd_is_flag(cmd))
681 bb_error_msg_and_die("can't invert %s", word); 682 bb_error_msg_and_die("can't invert %s", word);
@@ -705,25 +706,25 @@ static void serial_set(char **arg, int opts)
705 serinfo.flags |= setbits[cmd]; 706 serinfo.flags |= setbits[cmd];
706 break; 707 break;
707 case CMD_PORT: 708 case CMD_PORT:
708 serinfo.port = get_numeric(*arg++); 709 serinfo.port = get_numeric(*arg);
709 break; 710 break;
710 case CMD_IRQ: 711 case CMD_IRQ:
711 serinfo.irq = get_numeric(*arg++); 712 serinfo.irq = get_numeric(*arg);
712 break; 713 break;
713 case CMD_DIVISOR: 714 case CMD_DIVISOR:
714 serinfo.custom_divisor = get_numeric(*arg++); 715 serinfo.custom_divisor = get_numeric(*arg);
715 break; 716 break;
716 case CMD_UART: 717 case CMD_UART:
717 serinfo.type = get_uart(*arg++); 718 serinfo.type = get_uart(*arg);
718 break; 719 break;
719 case CMD_BASE: 720 case CMD_BASE:
720 serinfo.baud_base = get_numeric(*arg++); 721 serinfo.baud_base = get_numeric(*arg);
721 break; 722 break;
722 case CMD_DELAY: 723 case CMD_DELAY:
723 serinfo.close_delay = get_numeric(*arg++); 724 serinfo.close_delay = get_numeric(*arg);
724 break; 725 break;
725 case CMD_WAIT: 726 case CMD_WAIT:
726 serinfo.closing_wait = get_wait(*arg++); 727 serinfo.closing_wait = get_wait(*arg);
727 break; 728 break;
728 case CMD_AUTOCONFIG: 729 case CMD_AUTOCONFIG:
729 serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo); 730 serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo);
@@ -741,22 +742,22 @@ int setserial_main(int argc UNUSED_PARAM, char **argv)
741{ 742{
742 int opts; 743 int opts;
743 744
744 opt_complementary = "-1:b-aG:G-ab:a-bG"; 745 opts = getopt32(argv, "^" "bGavzgq" "\0" "-1:b-aG:G-ab:a-bG");
745 opts = getopt32(argv, "bGavzg");
746 argv += optind; 746 argv += optind;
747 747
748 if (!argv[1]) /* one arg only? */ 748 if (!argv[1]) /* one arg only? (nothing to change?) */
749 opts |= OPT_GET; 749 opts |= OPT_LIST_OF_DEVS; /* force display */
750 750
751 if (!(opts & OPT_GET)) { 751 if (!(opts & OPT_LIST_OF_DEVS)) {
752 serial_set(argv, opts); 752 serial_set(argv, opts);
753 argv[1] = NULL; 753 argv[1] = NULL;
754 } 754 }
755 755
756 if (opts & (OPT_VERBOSE | OPT_GET)) { 756 /* -v effect: "after setting params, do not be silent, show them" */
757 if (opts & (OPT_VERBOSE | OPT_LIST_OF_DEVS)) {
757 do { 758 do {
758 serial_get(*argv++, opts & OPT_MODE_MASK); 759 serial_get(*argv, opts & OPT_MODE_MASK);
759 } while (*argv); 760 } while (*++argv);
760 } 761 }
761 762
762 return EXIT_SUCCESS; 763 return EXIT_SUCCESS;
diff --git a/miscutils/time.c b/miscutils/time.c
index 60fc11f6e..65dbcdcf3 100644
--- a/miscutils/time.c
+++ b/miscutils/time.c
@@ -127,13 +127,13 @@ static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
127 127
128/* summarize: Report on the system use of a command. 128/* summarize: Report on the system use of a command.
129 129
130 Print the FMT argument except that `%' sequences 130 Print the FMT argument except that '%' sequences
131 have special meaning, and `\n' and `\t' are translated into 131 have special meaning, and '\n' and '\t' are translated into
132 newline and tab, respectively, and `\\' is translated into `\'. 132 newline and tab, respectively, and '\\' is translated into '\'.
133 133
134 The character following a `%' can be: 134 The character following a '%' can be:
135 (* means the tcsh time builtin also recognizes it) 135 (* means the tcsh time builtin also recognizes it)
136 % == a literal `%' 136 % == a literal '%'
137 C == command name and arguments 137 C == command name and arguments
138* D == average unshared data size in K (ru_idrss+ru_isrss) 138* D == average unshared data size in K (ru_idrss+ru_isrss)
139* E == elapsed real (wall clock) time in [hour:]min:sec 139* E == elapsed real (wall clock) time in [hour:]min:sec
@@ -430,9 +430,10 @@ int time_main(int argc UNUSED_PARAM, char **argv)
430 OPT_f = (1 << 4), 430 OPT_f = (1 << 4),
431 }; 431 };
432 432
433 opt_complementary = "-1"; /* at least one arg */
434 /* "+": stop on first non-option */ 433 /* "+": stop on first non-option */
435 opt = getopt32(argv, "+vpao:f:", &output_filename, &output_format); 434 opt = getopt32(argv, "^+" "vpao:f:" "\0" "-1"/*at least one arg*/,
435 &output_filename, &output_format
436 );
436 argv += optind; 437 argv += optind;
437 if (opt & OPT_v) 438 if (opt & OPT_v)
438 output_format = long_format; 439 output_format = long_format;
diff --git a/miscutils/ttysize.c b/miscutils/ttysize.c
index 7f6a84308..2c2d4ec33 100644
--- a/miscutils/ttysize.c
+++ b/miscutils/ttysize.c
@@ -18,7 +18,7 @@
18//config: error, but returns default 80x24. 18//config: error, but returns default 80x24.
19//config: Usage in shell scripts: width=`ttysize w`. 19//config: Usage in shell scripts: width=`ttysize w`.
20 20
21//applet:IF_TTYSIZE(APPLET(ttysize, BB_DIR_USR_BIN, BB_SUID_DROP)) 21//applet:IF_TTYSIZE(APPLET_NOFORK(ttysize, ttysize, BB_DIR_USR_BIN, BB_SUID_DROP, ttysize))
22 22
23//kbuild:lib-$(CONFIG_TTYSIZE) += ttysize.o 23//kbuild:lib-$(CONFIG_TTYSIZE) += ttysize.o
24 24
diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c
index c6ba22adf..d142d1144 100644
--- a/miscutils/ubi_tools.c
+++ b/miscutils/ubi_tools.c
@@ -52,6 +52,7 @@
52//applet:IF_UBIRMVOL( APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) 52//applet:IF_UBIRMVOL( APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol))
53//applet:IF_UBIRSVOL( APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) 53//applet:IF_UBIRSVOL( APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol))
54//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol)) 54//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol))
55/* not NOEXEC: if flash operation stalls, use less memory in "hung" process */
55 56
56//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o 57//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o
57//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o 58//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o
@@ -67,23 +68,32 @@
67#endif 68#endif
68#include <mtd/ubi-user.h> 69#include <mtd/ubi-user.h>
69 70
70#define do_attach (ENABLE_UBIATTACH && applet_name[3] == 'a') 71#define UBI_APPLET_CNT (0 \
71#define do_detach (ENABLE_UBIDETACH && applet_name[3] == 'd') 72 + ENABLE_UBIATTACH \
72#define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') 73 + ENABLE_UBIDETACH \
73#define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') 74 + ENABLE_UBIMKVOL \
74#define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') 75 + ENABLE_UBIRMVOL \
75#define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u') 76 + ENABLE_UBIRSVOL \
76 77 + ENABLE_UBIUPDATEVOL \
77static unsigned get_num_from_file(const char *path, unsigned max, const char *errmsg) 78 )
79
80#define do_attach (ENABLE_UBIATTACH && (UBI_APPLET_CNT == 1 || applet_name[4] == 't'))
81#define do_detach (ENABLE_UBIDETACH && (UBI_APPLET_CNT == 1 || applet_name[4] == 'e'))
82#define do_mkvol (ENABLE_UBIMKVOL && (UBI_APPLET_CNT == 1 || applet_name[4] == 'k'))
83#define do_rmvol (ENABLE_UBIRMVOL && (UBI_APPLET_CNT == 1 || applet_name[4] == 'm'))
84#define do_rsvol (ENABLE_UBIRSVOL && (UBI_APPLET_CNT == 1 || applet_name[4] == 's'))
85#define do_update (ENABLE_UBIUPDATEVOL && (UBI_APPLET_CNT == 1 || applet_name[4] == 'p'))
86
87static unsigned get_num_from_file(const char *path, unsigned max)
78{ 88{
79 char buf[sizeof(long long)*3]; 89 char buf[sizeof(long long)*3];
80 unsigned long long num; 90 unsigned long long num;
81 91
82 if (open_read_close(path, buf, sizeof(buf)) < 0) 92 if (open_read_close(path, buf, sizeof(buf)) < 0)
83 bb_perror_msg_and_die(errmsg, path); 93 bb_perror_msg_and_die("can't open '%s'", path);
84 /* It can be \n terminated, xatoull won't work well */ 94 /* It can be \n terminated, xatoull won't work well */
85 if (sscanf(buf, "%llu", &num) != 1 || num > max) 95 if (sscanf(buf, "%llu", &num) != 1 || num > max)
86 bb_error_msg_and_die(errmsg, path); 96 bb_error_msg_and_die("number in '%s' is malformed or too large", path);
87 return num; 97 return num;
88} 98}
89 99
@@ -135,20 +145,17 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
135#define OPTION_a (1 << 5) 145#define OPTION_a (1 << 5)
136#define OPTION_t (1 << 6) 146#define OPTION_t (1 << 6)
137 if (do_mkvol) { 147 if (do_mkvol) {
138 opt_complementary = "-1"; 148 opts = getopt32(argv, "^" "md:+n:+N:s:a:+t:O:+" "\0" "-1",
139 opts = getopt32(argv, "md:+n:+N:s:a:+t:O:+",
140 &dev_num, &vol_id, 149 &dev_num, &vol_id,
141 &vol_name, &size_bytes_str, &alignment, &type, 150 &vol_name, &size_bytes_str, &alignment, &type,
142 &vid_hdr_offset 151 &vid_hdr_offset
143 ); 152 );
144 } else 153 } else
145 if (do_update) { 154 if (do_update) {
146 opt_complementary = "-1"; 155 opts = getopt32(argv, "^" "s:at" "\0" "-1", &size_bytes_str);
147 opts = getopt32(argv, "s:at", &size_bytes_str);
148 opts *= OPTION_s; 156 opts *= OPTION_s;
149 } else { 157 } else {
150 opt_complementary = "-1"; 158 opts = getopt32(argv, "^" "m:+d:+n:+N:s:a:+t:" "\0" "-1",
151 opts = getopt32(argv, "m:+d:+n:+N:s:a:+t:",
152 &mtd_num, &dev_num, &vol_id, 159 &mtd_num, &dev_num, &vol_id,
153 &vol_name, &size_bytes_str, &alignment, &type 160 &vol_name, &size_bytes_str, &alignment, &type
154 ); 161 );
@@ -217,10 +224,10 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
217 p = path_sys_class_ubi_ubi + sprintf(path_sys_class_ubi_ubi, "%u/", num); 224 p = path_sys_class_ubi_ubi + sprintf(path_sys_class_ubi_ubi, "%u/", num);
218 225
219 strcpy(p, "avail_eraseblocks"); 226 strcpy(p, "avail_eraseblocks");
220 leb_avail = get_num_from_file(path, UINT_MAX, "Can't get available eraseblocks from '%s'"); 227 leb_avail = get_num_from_file(path, UINT_MAX);
221 228
222 strcpy(p, "eraseblock_size"); 229 strcpy(p, "eraseblock_size");
223 leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get eraseblock size from '%s'"); 230 leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK);
224 231
225 size_bytes = leb_avail * (unsigned long long)leb_size; 232 size_bytes = leb_avail * (unsigned long long)leb_size;
226 //if (size_bytes <= 0) 233 //if (size_bytes <= 0)
@@ -232,16 +239,19 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
232 if (!(opts & OPTION_N)) 239 if (!(opts & OPTION_N))
233 bb_error_msg_and_die("name not specified"); 240 bb_error_msg_and_die("name not specified");
234 241
242 /* the structure is memset(0) above */
235 mkvol_req.vol_id = vol_id; 243 mkvol_req.vol_id = vol_id;
236 mkvol_req.vol_type = UBI_DYNAMIC_VOLUME; 244 mkvol_req.vol_type = UBI_DYNAMIC_VOLUME;
237 if ((opts & OPTION_t) && type[0] == 's') 245 if ((opts & OPTION_t) && type[0] == 's')
238 mkvol_req.vol_type = UBI_STATIC_VOLUME; 246 mkvol_req.vol_type = UBI_STATIC_VOLUME;
239 mkvol_req.alignment = alignment; 247 mkvol_req.alignment = alignment;
240 mkvol_req.bytes = size_bytes; /* signed int64_t */ 248 mkvol_req.bytes = size_bytes; /* signed int64_t */
241 strncpy(mkvol_req.name, vol_name, UBI_MAX_VOLUME_NAME); 249 /* strnlen avoids overflow of 16-bit field (paranoia) */
242 mkvol_req.name_len = strlen(vol_name); 250 mkvol_req.name_len = strnlen(vol_name, UBI_MAX_VOLUME_NAME+1);
243 if (mkvol_req.name_len > UBI_MAX_VOLUME_NAME) 251 if (mkvol_req.name_len > UBI_MAX_VOLUME_NAME)
244 bb_error_msg_and_die("volume name too long: '%s'", vol_name); 252 bb_error_msg_and_die("volume name too long: '%s'", vol_name);
253 /* this is safe: .name[] is UBI_MAX_VOLUME_NAME+1 bytes */
254 strcpy(mkvol_req.name, vol_name);
245 255
246 xioctl(fd, UBI_IOCMKVOL, &mkvol_req); 256 xioctl(fd, UBI_IOCMKVOL, &mkvol_req);
247 } else 257 } else
@@ -289,7 +299,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
289 } else 299 } else
290 300
291//usage:#define ubiupdatevol_trivial_usage 301//usage:#define ubiupdatevol_trivial_usage
292//usage: "[-t | [-s SIZE] IMG_FILE] UBI_DEVICE" 302//usage: "-t UBI_DEVICE | [-s SIZE] UBI_DEVICE IMG_FILE"
293//usage:#define ubiupdatevol_full_usage "\n\n" 303//usage:#define ubiupdatevol_full_usage "\n\n"
294//usage: "Update UBI volume\n" 304//usage: "Update UBI volume\n"
295//usage: "\n -t Truncate to zero size" 305//usage: "\n -t Truncate to zero size"
@@ -304,24 +314,25 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
304 xioctl(fd, UBI_IOCVOLUP, &bytes64); 314 xioctl(fd, UBI_IOCVOLUP, &bytes64);
305 } 315 }
306 else { 316 else {
307 struct stat st;
308 unsigned ubinum, volnum; 317 unsigned ubinum, volnum;
309 unsigned leb_size; 318 unsigned leb_size;
310 ssize_t len; 319 char *buf;
311 char *input_data;
312 320
313 /* Assume that device is in normal format. */ 321 /* Assume that device is in normal format. */
314 /* Removes need for scanning sysfs tree as full libubi does. */ 322 /* Removes need for scanning sysfs tree as full libubi does. */
315 if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2) 323 if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2)
316 bb_error_msg_and_die("wrong format of UBI device name"); 324 bb_error_msg_and_die("UBI device name '%s' is not /dev/ubiN_M", ubi_ctrl);
317 325
318 sprintf(path_sys_class_ubi_ubi, "%u_%u/usable_eb_size", ubinum, volnum); 326 sprintf(path_sys_class_ubi_ubi, "%u_%u/usable_eb_size", ubinum, volnum);
319 leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get usable eraseblock size from '%s'"); 327 leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK);
320 328
321 if (!(opts & OPTION_s)) { 329 if (!*argv)
322 if (!*argv) 330 bb_show_usage();
323 bb_show_usage(); 331 if (NOT_LONE_DASH(*argv)) /* mtd-utils supports "-" as stdin */
324 xmove_fd(xopen(*argv, O_RDONLY), STDIN_FILENO); 332 xmove_fd(xopen(*argv, O_RDONLY), STDIN_FILENO);
333
334 if (!(opts & OPTION_s)) {
335 struct stat st;
325 xfstat(STDIN_FILENO, &st, *argv); 336 xfstat(STDIN_FILENO, &st, *argv);
326 size_bytes = st.st_size; 337 size_bytes = st.st_size;
327 } 338 }
@@ -330,12 +341,24 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
330 /* this ioctl expects signed int64_t* parameter */ 341 /* this ioctl expects signed int64_t* parameter */
331 xioctl(fd, UBI_IOCVOLUP, &bytes64); 342 xioctl(fd, UBI_IOCVOLUP, &bytes64);
332 343
333 input_data = xmalloc(leb_size); 344 /* can't use bb_copyfd_exact_size(): copy in blocks of exactly leb_size */
334 while ((len = full_read(STDIN_FILENO, input_data, leb_size)) > 0) { 345 buf = xmalloc(leb_size);
335 xwrite(fd, input_data, len); 346 while (size_bytes != 0) {
347 int len = full_read(STDIN_FILENO, buf, leb_size);
348 if (len <= 0) {
349 if (len < 0)
350 bb_perror_msg_and_die("read error from '%s'", *argv);
351 break;
352 }
353 if ((unsigned)len > size_bytes) {
354 /* for this case: "ubiupdatevol -s 1024000 $UBIDEV /dev/urandom" */
355 len = size_bytes;
356 }
357 xwrite(fd, buf, len);
358 size_bytes -= len;
336 } 359 }
337 if (len < 0) 360 if (ENABLE_FEATURE_CLEAN_UP)
338 bb_perror_msg_and_die("UBI volume update failed"); 361 free(buf);
339 } 362 }
340 } 363 }
341 364
diff --git a/miscutils/ubirename.c b/miscutils/ubirename.c
index 786c4b9fa..ecc8fe137 100644
--- a/miscutils/ubirename.c
+++ b/miscutils/ubirename.c
@@ -14,6 +14,7 @@
14//config: Utility to rename UBI volumes 14//config: Utility to rename UBI volumes
15 15
16//applet:IF_UBIRENAME(APPLET(ubirename, BB_DIR_USR_SBIN, BB_SUID_DROP)) 16//applet:IF_UBIRENAME(APPLET(ubirename, BB_DIR_USR_SBIN, BB_SUID_DROP))
17/* not NOEXEC: if flash operation stalls, use less memory in "hung" process */
17 18
18//kbuild:lib-$(CONFIG_UBIRENAME) += ubirename.o 19//kbuild:lib-$(CONFIG_UBIRENAME) += ubirename.o
19 20
@@ -80,9 +81,12 @@ int ubirename_main(int argc, char **argv)
80 argv += 2; 81 argv += 2;
81 while (argv[0]) { 82 while (argv[0]) {
82 rnvol->ents[n].vol_id = ubi_get_volid_by_name(ubi_devnum, argv[0]); 83 rnvol->ents[n].vol_id = ubi_get_volid_by_name(ubi_devnum, argv[0]);
83 rnvol->ents[n].name_len = strlen(argv[1]); 84
85 /* strnlen avoids overflow of 16-bit field (paranoia) */
86 rnvol->ents[n].name_len = strnlen(argv[1], sizeof(rnvol->ents[n].name));
84 if (rnvol->ents[n].name_len >= sizeof(rnvol->ents[n].name)) 87 if (rnvol->ents[n].name_len >= sizeof(rnvol->ents[n].name))
85 bb_error_msg_and_die("new name '%s' is too long", argv[1]); 88 bb_error_msg_and_die("new name '%s' is too long", argv[1]);
89
86 strcpy(rnvol->ents[n].name, argv[1]); 90 strcpy(rnvol->ents[n].name, argv[1]);
87 n++; 91 n++;
88 argv += 2; 92 argv += 2;
diff --git a/miscutils/watchdog.c b/miscutils/watchdog.c
index ec06bcb51..392d05646 100644
--- a/miscutils/watchdog.c
+++ b/miscutils/watchdog.c
@@ -101,8 +101,9 @@ int watchdog_main(int argc UNUSED_PARAM, char **argv)
101 char *st_arg; 101 char *st_arg;
102 char *ht_arg; 102 char *ht_arg;
103 103
104 opt_complementary = "=1"; /* must have exactly 1 argument */ 104 opts = getopt32(argv, "^" "Ft:T:" "\0" "=1"/*must have exactly 1 arg*/,
105 opts = getopt32(argv, "Ft:T:", &st_arg, &ht_arg); 105 &st_arg, &ht_arg
106 );
106 107
107 /* We need to daemonize *before* opening the watchdog as many drivers 108 /* We need to daemonize *before* opening the watchdog as many drivers
108 * will only allow one process at a time to do so. Since daemonizing 109 * will only allow one process at a time to do so. Since daemonizing
diff --git a/modutils/insmod.c b/modutils/insmod.c
index b8ede8a81..4dc0b6608 100644
--- a/modutils/insmod.c
+++ b/modutils/insmod.c
@@ -13,7 +13,7 @@
13//config: help 13//config: help
14//config: insmod is used to load specified modules in the running kernel. 14//config: insmod is used to load specified modules in the running kernel.
15 15
16//applet:IF_INSMOD(IF_NOT_MODPROBE_SMALL(APPLET(insmod, BB_DIR_SBIN, BB_SUID_DROP))) 16//applet:IF_INSMOD(IF_NOT_MODPROBE_SMALL(APPLET_NOEXEC(insmod, insmod, BB_DIR_SBIN, BB_SUID_DROP, insmod)))
17 17
18//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y) 18//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
19//kbuild:lib-$(CONFIG_INSMOD) += insmod.o modutils.o 19//kbuild:lib-$(CONFIG_INSMOD) += insmod.o modutils.o
diff --git a/modutils/lsmod.c b/modutils/lsmod.c
index 4bf8f8481..84860cd79 100644
--- a/modutils/lsmod.c
+++ b/modutils/lsmod.c
@@ -23,7 +23,7 @@
23//config: the format of module-init-tools for Linux kernel 2.6. 23//config: the format of module-init-tools for Linux kernel 2.6.
24//config: Increases size somewhat. 24//config: Increases size somewhat.
25 25
26//applet:IF_LSMOD(IF_NOT_MODPROBE_SMALL(APPLET(lsmod, BB_DIR_SBIN, BB_SUID_DROP))) 26//applet:IF_LSMOD(IF_NOT_MODPROBE_SMALL(APPLET_NOEXEC(lsmod, lsmod, BB_DIR_SBIN, BB_SUID_DROP, lsmod)))
27 27
28//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y) 28//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
29//kbuild:lib-$(CONFIG_LSMOD) += lsmod.o modutils.o 29//kbuild:lib-$(CONFIG_LSMOD) += lsmod.o modutils.o
diff --git a/modutils/modinfo.c b/modutils/modinfo.c
index 371c93991..3f91622a9 100644
--- a/modutils/modinfo.c
+++ b/modutils/modinfo.c
@@ -12,7 +12,7 @@
12//config: help 12//config: help
13//config: Show information about a Linux Kernel module 13//config: Show information about a Linux Kernel module
14 14
15//applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP)) 15//applet:IF_MODINFO(APPLET_NOEXEC(modinfo, modinfo, BB_DIR_SBIN, BB_SUID_DROP, modinfo))
16 16
17//kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o 17//kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o
18 18
@@ -148,8 +148,7 @@ int modinfo_main(int argc UNUSED_PARAM, char **argv)
148 unsigned i; 148 unsigned i;
149 149
150 field = NULL; 150 field = NULL;
151 opt_complementary = "-1"; /* minimum one param */ 151 opts = getopt32(argv, "^" "0F:nadlp" "\0" "-1"/*minimum one arg*/, &field);
152 opts = getopt32(argv, "0F:nadlp", &field);
153 /* If no field selected, show all */ 152 /* If no field selected, show all */
154 if (!(opts & (OPT_TAGS|OPT_F))) 153 if (!(opts & (OPT_TAGS|OPT_F)))
155 option_mask32 |= OPT_TAGS; 154 option_mask32 |= OPT_TAGS;
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 053a7df89..a94b0b9a6 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -11,12 +11,15 @@
11/* modprobe-small configs are defined in Config.src to ensure better 11/* modprobe-small configs are defined in Config.src to ensure better
12 * "make config" order */ 12 * "make config" order */
13 13
14//applet:IF_LSMOD( IF_MODPROBE_SMALL(APPLET(lsmod, BB_DIR_SBIN, BB_SUID_DROP))) 14//applet:IF_LSMOD( IF_MODPROBE_SMALL(APPLET_NOEXEC( lsmod, lsmod, BB_DIR_SBIN, BB_SUID_DROP, lsmod )))
15//applet:IF_MODPROBE(IF_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP))) 15//applet:IF_MODPROBE(IF_MODPROBE_SMALL(APPLET_NOEXEC( modprobe, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)))
16// APPLET_ODDNAME:name main location suid_type help 16// APPLET_ODDNAME:name main location suid_type help
17//applet:IF_DEPMOD(IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, depmod))) 17//applet:IF_DEPMOD( IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, depmod )))
18//applet:IF_INSMOD(IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, insmod))) 18//applet:IF_INSMOD( IF_MODPROBE_SMALL(APPLET_NOEXEC( insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, insmod )))
19//applet:IF_RMMOD( IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, rmmod))) 19//applet:IF_RMMOD( IF_MODPROBE_SMALL(APPLET_NOEXEC( rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, rmmod )))
20/* noexec speeds up boot with many modules loaded (need SH_STANDALONE=y) */
21/* I measured about ~5 times faster insmod */
22/* depmod is not noexec, it runs longer and benefits from memory trimming via exec */
20 23
21//kbuild:lib-$(CONFIG_MODPROBE_SMALL) += modprobe-small.o 24//kbuild:lib-$(CONFIG_MODPROBE_SMALL) += modprobe-small.o
22 25
@@ -985,10 +988,9 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
985 988
986#if ENABLE_MODPROBE || ENABLE_INSMOD || ENABLE_RMMOD 989#if ENABLE_MODPROBE || ENABLE_INSMOD || ENABLE_RMMOD
987 /* modprobe, insmod, rmmod require at least one argument */ 990 /* modprobe, insmod, rmmod require at least one argument */
988 opt_complementary = "-1";
989 /* only -q (quiet) and -r (rmmod), 991 /* only -q (quiet) and -r (rmmod),
990 * the rest are accepted and ignored (compat) */ 992 * the rest are accepted and ignored (compat) */
991 getopt32(argv, "qrfsvwb"); 993 getopt32(argv, "^" "qrfsvwb" "\0" "-1");
992 argv += optind; 994 argv += optind;
993 995
994 if (is_modprobe) { 996 if (is_modprobe) {
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index 1a7db09f2..59f6d54f3 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -26,7 +26,7 @@
26//config: hardware autodetection scripts to load modules like evdev, frame 26//config: hardware autodetection scripts to load modules like evdev, frame
27//config: buffer drivers etc. 27//config: buffer drivers etc.
28 28
29//applet:IF_MODPROBE(IF_NOT_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP))) 29//applet:IF_MODPROBE(IF_NOT_MODPROBE_SMALL(APPLET_NOEXEC(modprobe, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe)))
30 30
31//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y) 31//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
32//kbuild:lib-$(CONFIG_MODPROBE) += modprobe.o modutils.o 32//kbuild:lib-$(CONFIG_MODPROBE) += modprobe.o modutils.o
@@ -133,7 +133,7 @@
133 */ 133 */
134#define MODPROBE_OPTS "alrDb" 134#define MODPROBE_OPTS "alrDb"
135/* -a and -D _are_ in fact compatible */ 135/* -a and -D _are_ in fact compatible */
136#define MODPROBE_COMPLEMENTARY ("q-v:v-q:l--arD:r--alD:a--lr:D--rl") 136#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--arD:r--alD:a--lr:D--rl"
137//#define MODPROBE_OPTS "acd:lnrt:C:b" 137//#define MODPROBE_OPTS "acd:lnrt:C:b"
138//#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--acr:a--lr:r--al" 138//#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--acr:a--lr:r--al"
139enum { 139enum {
@@ -566,9 +566,10 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
566 566
567 INIT_G(); 567 INIT_G();
568 568
569 IF_LONG_OPTS(applet_long_options = modprobe_longopts;) 569 opt = getopt32long(argv, "^" INSMOD_OPTS MODPROBE_OPTS "\0" MODPROBE_COMPLEMENTARY,
570 opt_complementary = MODPROBE_COMPLEMENTARY; 570 modprobe_longopts
571 opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS); 571 INSMOD_ARGS
572 );
572 argv += optind; 573 argv += optind;
573 574
574 /* Goto modules location */ 575 /* Goto modules location */
diff --git a/modutils/modutils-24.c b/modutils/modutils-24.c
index 9ce91351d..1a30dd87c 100644
--- a/modutils/modutils-24.c
+++ b/modutils/modutils-24.c
@@ -2269,8 +2269,8 @@ static int add_symbols_from(struct obj_file *f,
2269 2269
2270#ifdef SYMBOL_PREFIX 2270#ifdef SYMBOL_PREFIX
2271 /* Prepend SYMBOL_PREFIX to the symbol's name (the 2271 /* Prepend SYMBOL_PREFIX to the symbol's name (the
2272 kernel exports `C names', but module object files 2272 kernel exports 'C names', but module object files
2273 reference `linker names'). */ 2273 reference 'linker names'). */
2274 size_t extra = sizeof SYMBOL_PREFIX; 2274 size_t extra = sizeof SYMBOL_PREFIX;
2275 size_t name_size = strlen(name) + extra; 2275 size_t name_size = strlen(name) + extra;
2276 if (name_size > name_alloced_size) { 2276 if (name_size > name_alloced_size) {
diff --git a/modutils/rmmod.c b/modutils/rmmod.c
index d60e49413..df50e58af 100644
--- a/modutils/rmmod.c
+++ b/modutils/rmmod.c
@@ -14,7 +14,7 @@
14//config: help 14//config: help
15//config: rmmod is used to unload specified modules from the kernel. 15//config: rmmod is used to unload specified modules from the kernel.
16 16
17//applet:IF_RMMOD(IF_NOT_MODPROBE_SMALL(APPLET(rmmod, BB_DIR_SBIN, BB_SUID_DROP))) 17//applet:IF_RMMOD(IF_NOT_MODPROBE_SMALL(APPLET_NOEXEC(rmmod, rmmod, BB_DIR_SBIN, BB_SUID_DROP, rmmod)))
18 18
19//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y) 19//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
20//kbuild:lib-$(CONFIG_RMMOD) += rmmod.o modutils.o 20//kbuild:lib-$(CONFIG_RMMOD) += rmmod.o modutils.o
diff --git a/networking/arping.c b/networking/arping.c
index 71672957e..f9967d81e 100644
--- a/networking/arping.c
+++ b/networking/arping.c
@@ -295,8 +295,8 @@ int arping_main(int argc UNUSED_PARAM, char **argv)
295 295
296 sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0); 296 sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0);
297 297
298 // Drop suid root privileges 298 // If you ever change BB_SUID_DROP to BB_SUID_REQUIRE,
299 // Need to remove SUID_NEVER from applets.h for this to work 299 // drop suid root privileges here:
300 //xsetuid(getuid()); 300 //xsetuid(getuid());
301 301
302 { 302 {
@@ -306,9 +306,9 @@ int arping_main(int argc UNUSED_PARAM, char **argv)
306 /* Dad also sets quit_on_reply. 306 /* Dad also sets quit_on_reply.
307 * Advert also sets unsolicited. 307 * Advert also sets unsolicited.
308 */ 308 */
309 opt_complementary = "=1:Df:AU"; 309 opt = getopt32(argv, "^" "DUAqfbc:+w:I:s:" "\0" "=1:Df:AU",
310 opt = getopt32(argv, "DUAqfbc:+w:I:s:", 310 &count, &str_timeout, &device, &source
311 &count, &str_timeout, &device, &source); 311 );
312 if (opt & 0x80) /* -w: timeout */ 312 if (opt & 0x80) /* -w: timeout */
313 timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000; 313 timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
314 //if (opt & 0x200) /* -s: source */ 314 //if (opt & 0x200) /* -s: source */
diff --git a/networking/brctl.c b/networking/brctl.c
index 690791e4c..5d5f0af30 100644
--- a/networking/brctl.c
+++ b/networking/brctl.c
@@ -39,7 +39,7 @@
39//config: Add support for option which prints the current config: 39//config: Add support for option which prints the current config:
40//config: show 40//config: show
41 41
42//applet:IF_BRCTL(APPLET(brctl, BB_DIR_USR_SBIN, BB_SUID_DROP)) 42//applet:IF_BRCTL(APPLET_NOEXEC(brctl, brctl, BB_DIR_USR_SBIN, BB_SUID_DROP, brctl))
43 43
44//kbuild:lib-$(CONFIG_BRCTL) += brctl.o 44//kbuild:lib-$(CONFIG_BRCTL) += brctl.o
45 45
diff --git a/networking/ether-wake.c b/networking/ether-wake.c
index 52522e76d..6677f07d5 100644
--- a/networking/ether-wake.c
+++ b/networking/ether-wake.c
@@ -212,8 +212,7 @@ int ether_wake_main(int argc UNUSED_PARAM, char **argv)
212 struct whereto_t whereto; /* who to wake up */ 212 struct whereto_t whereto; /* who to wake up */
213 213
214 /* handle misc user options */ 214 /* handle misc user options */
215 opt_complementary = "=1"; 215 flags = getopt32(argv, "^" "bi:p:" "\0" "=1", &ifname, &pass);
216 flags = getopt32(argv, "bi:p:", &ifname, &pass);
217 if (flags & 4) /* -p */ 216 if (flags & 4) /* -p */
218 wol_passwd_sz = get_wol_pw(pass, wol_passwd); 217 wol_passwd_sz = get_wol_pw(pass, wol_passwd);
219 flags &= 1; /* we further interested only in -b [bcast] flag */ 218 flags &= 1; /* we further interested only in -b [bcast] flag */
diff --git a/networking/ftpd.c b/networking/ftpd.c
index c562c2886..8af5acac2 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -1174,17 +1174,20 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
1174 abs_timeout = 1 * 60 * 60; 1174 abs_timeout = 1 * 60 * 60;
1175 verbose_S = 0; 1175 verbose_S = 0;
1176 G.timeout = 2 * 60; 1176 G.timeout = 2 * 60;
1177 opt_complementary = "vv:SS";
1178#if BB_MMU 1177#if BB_MMU
1179 opts = getopt32(argv, "vS" 1178 opts = getopt32(argv, "^" "vS"
1180 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:"), 1179 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1180 "\0" "vv:SS",
1181 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,) 1181 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1182 &G.verbose, &verbose_S); 1182 &G.verbose, &verbose_S
1183 );
1183#else 1184#else
1184 opts = getopt32(argv, "l1AvS" 1185 opts = getopt32(argv, "^" "l1AvS"
1185 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:"), 1186 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1187 "\0" "vv:SS",
1186 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,) 1188 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1187 &G.verbose, &verbose_S); 1189 &G.verbose, &verbose_S
1190 );
1188 if (opts & (OPT_l|OPT_1)) { 1191 if (opts & (OPT_l|OPT_1)) {
1189 /* Our secret backdoor to ls */ 1192 /* Our secret backdoor to ls */
1190 if (fchdir(3) != 0) 1193 if (fchdir(3) != 0)
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index cb0a96b59..029587aa2 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -39,37 +39,20 @@
39//usage: "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE" 39//usage: "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE"
40//usage:#define ftpget_full_usage "\n\n" 40//usage:#define ftpget_full_usage "\n\n"
41//usage: "Download a file via FTP\n" 41//usage: "Download a file via FTP\n"
42//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
43//usage: "\n -c,--continue Continue previous transfer"
44//usage: "\n -v,--verbose Verbose"
45//usage: "\n -u,--username USER Username"
46//usage: "\n -p,--password PASS Password"
47//usage: "\n -P,--port NUM Port"
48//usage: )
49//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
50//usage: "\n -c Continue previous transfer" 42//usage: "\n -c Continue previous transfer"
51//usage: "\n -v Verbose" 43//usage: "\n -v Verbose"
52//usage: "\n -u USER Username" 44//usage: "\n -u USER Username"
53//usage: "\n -p PASS Password" 45//usage: "\n -p PASS Password"
54//usage: "\n -P NUM Port" 46//usage: "\n -P NUM Port"
55//usage: )
56//usage: 47//usage:
57//usage:#define ftpput_trivial_usage 48//usage:#define ftpput_trivial_usage
58//usage: "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE" 49//usage: "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE"
59//usage:#define ftpput_full_usage "\n\n" 50//usage:#define ftpput_full_usage "\n\n"
60//usage: "Upload a file to a FTP server\n" 51//usage: "Upload a file to a FTP server\n"
61//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
62//usage: "\n -v,--verbose Verbose"
63//usage: "\n -u,--username USER Username"
64//usage: "\n -p,--password PASS Password"
65//usage: "\n -P,--port NUM Port"
66//usage: )
67//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
68//usage: "\n -v Verbose" 52//usage: "\n -v Verbose"
69//usage: "\n -u USER Username" 53//usage: "\n -u USER Username"
70//usage: "\n -p PASS Password" 54//usage: "\n -p PASS Password"
71//usage: "\n -P NUM Port number" 55//usage: "\n -P NUM Port number"
72//usage: )
73 56
74#include "libbb.h" 57#include "libbb.h"
75#include "common_bufsiz.h" 58#include "common_bufsiz.h"
@@ -367,12 +350,15 @@ int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
367 /* 350 /*
368 * Decipher the command line 351 * Decipher the command line
369 */ 352 */
353 /* must have 2 to 3 params; -v and -c count */
354#define OPTSTRING "^cvu:p:P:" "\0" "-2:?3:vv:cc"
370#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS 355#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
371 applet_long_options = ftpgetput_longopts; 356 getopt32long(argv, OPTSTRING, ftpgetput_longopts,
357#else
358 getopt32(argv, OPTSTRING,
372#endif 359#endif
373 opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */ 360 &user, &password, &port, &verbose_flag, &do_continue
374 getopt32(argv, "cvu:p:P:", &user, &password, &port, 361 );
375 &verbose_flag, &do_continue);
376 argv += optind; 362 argv += optind;
377 363
378 /* We want to do exactly _one_ DNS lookup, since some 364 /* We want to do exactly _one_ DNS lookup, since some
diff --git a/networking/hostname.c b/networking/hostname.c
index 4b305d2b6..d87f6562f 100644
--- a/networking/hostname.c
+++ b/networking/hostname.c
@@ -22,9 +22,9 @@
22//config: help 22//config: help
23//config: Alias to "hostname -d". 23//config: Alias to "hostname -d".
24 24
25// APPLET_ODDNAME:name main location suid_type help 25// APPLET_NOEXEC:name main location suid_type help
26//applet:IF_DNSDOMAINNAME(APPLET_ODDNAME(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname)) 26//applet:IF_DNSDOMAINNAME(APPLET_NOEXEC(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname))
27//applet:IF_HOSTNAME(APPLET(hostname, BB_DIR_BIN, BB_SUID_DROP)) 27//applet:IF_HOSTNAME( APPLET_NOEXEC(hostname, hostname, BB_DIR_BIN, BB_SUID_DROP, hostname ))
28 28
29//kbuild: lib-$(CONFIG_HOSTNAME) += hostname.o 29//kbuild: lib-$(CONFIG_HOSTNAME) += hostname.o
30//kbuild: lib-$(CONFIG_DNSDOMAINNAME) += hostname.o 30//kbuild: lib-$(CONFIG_DNSDOMAINNAME) += hostname.o
@@ -114,7 +114,7 @@ static void do_sethostname(char *s, int isfile)
114 * { bbox: not supported } 114 * { bbox: not supported }
115 * -F, --file filename 115 * -F, --file filename
116 * Read the host name from the specified file. Comments (lines 116 * Read the host name from the specified file. Comments (lines
117 * starting with a `#') are ignored. 117 * starting with a '#') are ignored.
118 */ 118 */
119int hostname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 119int hostname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
120int hostname_main(int argc UNUSED_PARAM, char **argv) 120int hostname_main(int argc UNUSED_PARAM, char **argv)
@@ -132,8 +132,9 @@ int hostname_main(int argc UNUSED_PARAM, char **argv)
132 char *buf; 132 char *buf;
133 char *hostname_str; 133 char *hostname_str;
134 134
135#if ENABLE_LONG_OPTS 135 /* dnsdomainname from net-tools 1.60, hostname 1.100 (2001-04-14),
136 applet_long_options = 136 * supports hostname's options too (not just -v as manpage says) */
137 opts = getopt32(argv, "dfisF:v", &hostname_str,
137 "domain\0" No_argument "d" 138 "domain\0" No_argument "d"
138 "fqdn\0" No_argument "f" 139 "fqdn\0" No_argument "f"
139 //Enable if seen in active use in some distro: 140 //Enable if seen in active use in some distro:
@@ -142,12 +143,7 @@ int hostname_main(int argc UNUSED_PARAM, char **argv)
142 // "short\0" No_argument "s" 143 // "short\0" No_argument "s"
143 // "verbose\0" No_argument "v" 144 // "verbose\0" No_argument "v"
144 "file\0" No_argument "F" 145 "file\0" No_argument "F"
145 ; 146 );
146
147#endif
148 /* dnsdomainname from net-tools 1.60, hostname 1.100 (2001-04-14),
149 * supports hostname's options too (not just -v as manpage says) */
150 opts = getopt32(argv, "dfisF:v", &hostname_str);
151 argv += optind; 147 argv += optind;
152 buf = safe_gethostname(); 148 buf = safe_gethostname();
153 if (ENABLE_DNSDOMAINNAME) { 149 if (ENABLE_DNSDOMAINNAME) {
diff --git a/networking/httpd.c b/networking/httpd.c
index 079145757..9369de824 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -2636,17 +2636,19 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2636#endif 2636#endif
2637 2637
2638 home_httpd = xrealloc_getcwd_or_warn(NULL); 2638 home_httpd = xrealloc_getcwd_or_warn(NULL);
2639 /* -v counts, -i implies -f */
2640 opt_complementary = "vv:if";
2641 /* We do not "absolutize" path given by -h (home) opt. 2639 /* We do not "absolutize" path given by -h (home) opt.
2642 * If user gives relative path in -h, 2640 * If user gives relative path in -h,
2643 * $SCRIPT_FILENAME will not be set. */ 2641 * $SCRIPT_FILENAME will not be set. */
2644 opt = getopt32(argv, "c:d:h:" 2642 opt = getopt32(argv, "^"
2643 "c:d:h:"
2645 IF_FEATURE_HTTPD_ENCODE_URL_STR("e:") 2644 IF_FEATURE_HTTPD_ENCODE_URL_STR("e:")
2646 IF_FEATURE_HTTPD_BASIC_AUTH("r:") 2645 IF_FEATURE_HTTPD_BASIC_AUTH("r:")
2647 IF_FEATURE_HTTPD_AUTH_MD5("m:") 2646 IF_FEATURE_HTTPD_AUTH_MD5("m:")
2648 IF_FEATURE_HTTPD_SETUID("u:") 2647 IF_FEATURE_HTTPD_SETUID("u:")
2649 "p:ifv", 2648 "p:ifv"
2649 "\0"
2650 /* -v counts, -i implies -f */
2651 "vv:if",
2650 &opt_c_configFile, &url_for_decode, &home_httpd 2652 &opt_c_configFile, &url_for_decode, &home_httpd
2651 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) 2653 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
2652 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) 2654 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
diff --git a/networking/ifenslave.c b/networking/ifenslave.c
index 26e5e8cf3..5e769b61d 100644
--- a/networking/ifenslave.c
+++ b/networking/ifenslave.c
@@ -105,7 +105,7 @@
105//config: Userspace application to bind several interfaces 105//config: Userspace application to bind several interfaces
106//config: to a logical interface (use with kernel bonding driver). 106//config: to a logical interface (use with kernel bonding driver).
107 107
108//applet:IF_IFENSLAVE(APPLET(ifenslave, BB_DIR_SBIN, BB_SUID_DROP)) 108//applet:IF_IFENSLAVE(APPLET_NOEXEC(ifenslave, ifenslave, BB_DIR_SBIN, BB_SUID_DROP, ifenslave))
109 109
110//kbuild:lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o 110//kbuild:lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o
111 111
@@ -113,10 +113,10 @@
113//usage: "[-cdf] MASTER_IFACE SLAVE_IFACE..." 113//usage: "[-cdf] MASTER_IFACE SLAVE_IFACE..."
114//usage:#define ifenslave_full_usage "\n\n" 114//usage:#define ifenslave_full_usage "\n\n"
115//usage: "Configure network interfaces for parallel routing\n" 115//usage: "Configure network interfaces for parallel routing\n"
116//usage: "\n -c,--change-active Change active slave" 116//usage: "\n -c Change active slave"
117//usage: "\n -d,--detach Remove slave interface from bonding device" 117//usage: "\n -d Remove slave interface from bonding device"
118//usage: "\n -f,--force Force, even if interface is not Ethernet" 118//usage: "\n -f Force, even if interface is not Ethernet"
119/* //usage: "\n -r,--receive-slave Create a receive-only slave" */ 119/* //usage: "\n -r Create a receive-only slave" */
120//usage: 120//usage:
121//usage:#define ifenslave_example_usage 121//usage:#define ifenslave_example_usage
122//usage: "To create a bond device, simply follow these three steps:\n" 122//usage: "To create a bond device, simply follow these three steps:\n"
@@ -493,19 +493,15 @@ int ifenslave_main(int argc UNUSED_PARAM, char **argv)
493 OPT_d = (1 << 1), 493 OPT_d = (1 << 1),
494 OPT_f = (1 << 2), 494 OPT_f = (1 << 2),
495 }; 495 };
496#if ENABLE_LONG_OPTS 496
497 static const char ifenslave_longopts[] ALIGN1 = 497 INIT_G();
498
499 opt = getopt32long(argv, "cdfa",
498 "change-active\0" No_argument "c" 500 "change-active\0" No_argument "c"
499 "detach\0" No_argument "d" 501 "detach\0" No_argument "d"
500 "force\0" No_argument "f" 502 "force\0" No_argument "f"
501 /* "all-interfaces\0" No_argument "a" */ 503 /* "all-interfaces\0" No_argument "a" */
502 ; 504 );
503
504 applet_long_options = ifenslave_longopts;
505#endif
506 INIT_G();
507
508 opt = getopt32(argv, "cdfa");
509 argv += optind; 505 argv += optind;
510 if (opt & (opt-1)) /* Only one option can be given */ 506 if (opt & (opt-1)) /* Only one option can be given */
511 bb_show_usage(); 507 bb_show_usage();
diff --git a/networking/inetd.c b/networking/inetd.c
index 91545d0a3..67984accb 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -217,7 +217,6 @@
217//config: bool "Support RPC services" 217//config: bool "Support RPC services"
218//config: default n # very rarely used, and needs Sun RPC support in libc 218//config: default n # very rarely used, and needs Sun RPC support in libc
219//config: depends on INETD 219//config: depends on INETD
220//config: select FEATURE_HAVE_RPC
221//config: help 220//config: help
222//config: Support Sun-RPC based services 221//config: Support Sun-RPC based services
223 222
diff --git a/networking/ipcalc.c b/networking/ipcalc.c
index 9888a6ff2..cdae8eea8 100644
--- a/networking/ipcalc.c
+++ b/networking/ipcalc.c
@@ -31,7 +31,7 @@
31//config: Adds the options hostname, prefix and silent to the output of 31//config: Adds the options hostname, prefix and silent to the output of
32//config: "ipcalc". 32//config: "ipcalc".
33 33
34//applet:IF_IPCALC(APPLET(ipcalc, BB_DIR_BIN, BB_SUID_DROP)) 34//applet:IF_IPCALC(APPLET_NOEXEC(ipcalc, ipcalc, BB_DIR_BIN, BB_SUID_DROP, ipcalc))
35 35
36//kbuild:lib-$(CONFIG_IPCALC) += ipcalc.o 36//kbuild:lib-$(CONFIG_IPCALC) += ipcalc.o
37 37
@@ -39,26 +39,14 @@
39//usage: "[OPTIONS] ADDRESS" 39//usage: "[OPTIONS] ADDRESS"
40//usage: IF_FEATURE_IPCALC_FANCY("[/PREFIX]") " [NETMASK]" 40//usage: IF_FEATURE_IPCALC_FANCY("[/PREFIX]") " [NETMASK]"
41//usage:#define ipcalc_full_usage "\n\n" 41//usage:#define ipcalc_full_usage "\n\n"
42//usage: "Calculate IP network settings from a IP address\n" 42//usage: "Calculate and display network settings from IP address\n"
43//usage: IF_FEATURE_IPCALC_LONG_OPTIONS( 43//usage: "\n -b Broadcast address"
44//usage: "\n -b,--broadcast Display calculated broadcast address" 44//usage: "\n -n Network address"
45//usage: "\n -n,--network Display calculated network address" 45//usage: "\n -m Default netmask for IP"
46//usage: "\n -m,--netmask Display default netmask for IP"
47//usage: IF_FEATURE_IPCALC_FANCY( 46//usage: IF_FEATURE_IPCALC_FANCY(
48//usage: "\n -p,--prefix Display the prefix for IP/NETMASK" 47//usage: "\n -p Prefix for IP/NETMASK"
49//usage: "\n -h,--hostname Display first resolved host name" 48//usage: "\n -h Resolved host name"
50//usage: "\n -s,--silent Don't ever display error messages" 49//usage: "\n -s No error messages"
51//usage: )
52//usage: )
53//usage: IF_NOT_FEATURE_IPCALC_LONG_OPTIONS(
54//usage: "\n -b Display calculated broadcast address"
55//usage: "\n -n Display calculated network address"
56//usage: "\n -m Display default netmask for IP"
57//usage: IF_FEATURE_IPCALC_FANCY(
58//usage: "\n -p Display the prefix for IP/NETMASK"
59//usage: "\n -h Display first resolved host name"
60//usage: "\n -s Don't ever display error messages"
61//usage: )
62//usage: ) 50//usage: )
63 51
64#include "libbb.h" 52#include "libbb.h"
@@ -120,6 +108,11 @@ int get_prefix(unsigned long netmask);
120 "silent\0" No_argument "s" // don’t ever display error messages 108 "silent\0" No_argument "s" // don’t ever display error messages
121# endif 109# endif
122 ; 110 ;
111# define GETOPT32 getopt32long
112# define LONGOPTS ,ipcalc_longopts
113#else
114# define GETOPT32 getopt32
115# define LONGOPTS
123#endif 116#endif
124 117
125int ipcalc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 118int ipcalc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -137,11 +130,11 @@ int ipcalc_main(int argc UNUSED_PARAM, char **argv)
137#define ipaddr (s_ipaddr.s_addr) 130#define ipaddr (s_ipaddr.s_addr)
138 char *ipstr; 131 char *ipstr;
139 132
140#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS 133 opt = GETOPT32(argv, "^"
141 applet_long_options = ipcalc_longopts; 134 "mbn" IF_FEATURE_IPCALC_FANCY("phs")
142#endif 135 "\0" "-1:?2"/*min 1, max 2 args*/
143 opt_complementary = "-1:?2"; /* minimum 1 arg, maximum 2 args */ 136 LONGOPTS
144 opt = getopt32(argv, "mbn" IF_FEATURE_IPCALC_FANCY("phs")); 137 );
145 argv += optind; 138 argv += optind;
146 if (opt & SILENT) 139 if (opt & SILENT)
147 logmode = LOGMODE_NONE; /* suppress error_msg() output */ 140 logmode = LOGMODE_NONE; /* suppress error_msg() output */
diff --git a/networking/nameif.c b/networking/nameif.c
index 31ee98a39..1f2695495 100644
--- a/networking/nameif.c
+++ b/networking/nameif.c
@@ -40,7 +40,7 @@
40//config: new_interface_name mac=00:80:C8:38:91:B5 40//config: new_interface_name mac=00:80:C8:38:91:B5
41//config: new_interface_name 00:80:C8:38:91:B5 41//config: new_interface_name 00:80:C8:38:91:B5
42 42
43//applet:IF_NAMEIF(APPLET(nameif, BB_DIR_SBIN, BB_SUID_DROP)) 43//applet:IF_NAMEIF(APPLET_NOEXEC(nameif, nameif, BB_DIR_SBIN, BB_SUID_DROP, nameif))
44 44
45//kbuild:lib-$(CONFIG_NAMEIF) += nameif.o 45//kbuild:lib-$(CONFIG_NAMEIF) += nameif.o
46 46
diff --git a/networking/nbd-client.c b/networking/nbd-client.c
index a5e25e6aa..cf1857231 100644
--- a/networking/nbd-client.c
+++ b/networking/nbd-client.c
@@ -7,7 +7,7 @@
7#include <netinet/tcp.h> 7#include <netinet/tcp.h>
8#include <linux/fs.h> 8#include <linux/fs.h>
9 9
10//applet:IF_NBDCLIENT(APPLET_ODDNAME(nbd-client, nbdclient, BB_DIR_USR_SBIN, BB_SUID_DROP, nbdclient)) 10//applet:IF_NBDCLIENT(APPLET_NOEXEC(nbd-client, nbdclient, BB_DIR_USR_SBIN, BB_SUID_DROP, nbdclient))
11 11
12//kbuild:lib-$(CONFIG_NBDCLIENT) += nbd-client.o 12//kbuild:lib-$(CONFIG_NBDCLIENT) += nbd-client.o
13 13
@@ -43,7 +43,7 @@
43//blocksizes other than 1024 without patches 43//blocksizes other than 1024 without patches
44 44
45int nbdclient_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 45int nbdclient_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
46int nbdclient_main(int argc, char **argv) 46int nbdclient_main(int argc UNUSED_PARAM, char **argv)
47{ 47{
48 unsigned long timeout = 0; 48 unsigned long timeout = 0;
49#if BB_MMU 49#if BB_MMU
@@ -61,7 +61,7 @@ int nbdclient_main(int argc, char **argv)
61 BUILD_BUG_ON(offsetof(struct nbd_header_t, data) != 8+8+8+4); 61 BUILD_BUG_ON(offsetof(struct nbd_header_t, data) != 8+8+8+4);
62 62
63 // Parse command line stuff (just a stub now) 63 // Parse command line stuff (just a stub now)
64 if (argc != 4) 64 if (!argv[1] || !argv[2] || !argv[3] || argv[4])
65 bb_show_usage(); 65 bb_show_usage();
66 66
67#if !BB_MMU 67#if !BB_MMU
diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c
index 3db784982..64098648a 100644
--- a/networking/nc_bloaty.c
+++ b/networking/nc_bloaty.c
@@ -787,11 +787,15 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
787 e_found: 787 e_found:
788 788
789 // -g -G -t -r deleted, unimplemented -a deleted too 789 // -g -G -t -r deleted, unimplemented -a deleted too
790 opt_complementary = "?2:vv:ll"; /* max 2 params; -v and -l are counters; -w N */ 790 getopt32(argv, "^"
791 getopt32(argv, "np:s:uvw:+" IF_NC_SERVER("lk") 791 "np:s:uvw:+"/* -w N */ IF_NC_SERVER("lk")
792 IF_NC_EXTRA("i:o:z"), 792 IF_NC_EXTRA("i:o:z")
793 &str_p, &str_s, &o_wait 793 "\0"
794 IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l)); 794 "?2:vv:ll", /* max 2 params; -v and -l are counters */
795 &str_p, &str_s, &o_wait
796 IF_NC_EXTRA(, &str_i, &str_o)
797 , &o_verbose IF_NC_SERVER(, &cnt_l)
798 );
795 argv += optind; 799 argv += optind;
796#if ENABLE_NC_EXTRA 800#if ENABLE_NC_EXTRA
797 if (option_mask32 & OPT_i) /* line-interval time */ 801 if (option_mask32 & OPT_i) /* line-interval time */
diff --git a/networking/ntpd.c b/networking/ntpd.c
index f21f9513d..25fa44389 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -2230,15 +2230,16 @@ static NOINLINE void ntp_init(char **argv)
2230 2230
2231 /* Parse options */ 2231 /* Parse options */
2232 peers = NULL; 2232 peers = NULL;
2233 opt_complementary = "dd:wn" /* -d: counter; -p: list; -w implies -n */ 2233 opts = getopt32(argv, "^"
2234 IF_FEATURE_NTPD_SERVER(":Il"); /* -I implies -l */
2235 opts = getopt32(argv,
2236 "nqNx" /* compat */ 2234 "nqNx" /* compat */
2237 "wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */ 2235 "wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
2238 IF_FEATURE_NTPD_SERVER("I:") /* compat */ 2236 IF_FEATURE_NTPD_SERVER("I:") /* compat */
2239 "d" /* compat */ 2237 "d" /* compat */
2240 "46aAbgL", /* compat, ignored */ 2238 "46aAbgL", /* compat, ignored */
2241 &peers, &G.script_name, 2239 "\0"
2240 "dd:wn" /* -d: counter; -p: list; -w implies -n */
2241 IF_FEATURE_NTPD_SERVER(":Il") /* -I implies -l */
2242 , &peers, &G.script_name,
2242#if ENABLE_FEATURE_NTPD_SERVER 2243#if ENABLE_FEATURE_NTPD_SERVER
2243 &G.if_name, 2244 &G.if_name,
2244#endif 2245#endif
diff --git a/networking/ping.c b/networking/ping.c
index 506e7b11b..7460e4414 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -340,7 +340,8 @@ static int common_ping_main(sa_family_t af, char **argv)
340 340
341/* Full(er) version */ 341/* Full(er) version */
342 342
343#define OPT_STRING ("qvc:+s:t:+w:+W:+I:np:4" IF_PING6("6")) 343/* -c NUM, -t NUM, -w NUM, -W NUM */
344#define OPT_STRING "qvc:+s:t:+w:+W:+I:np:4"IF_PING6("6")
344enum { 345enum {
345 OPT_QUIET = 1 << 0, 346 OPT_QUIET = 1 << 0,
346 OPT_VERBOSE = 1 << 1, 347 OPT_VERBOSE = 1 << 1,
@@ -863,9 +864,12 @@ static int common_ping_main(int opt, char **argv)
863 864
864 INIT_G(); 865 INIT_G();
865 866
866 /* exactly one argument needed; -v and -q don't mix; -c NUM, -t NUM, -w NUM, -W NUM */ 867 opt |= getopt32(argv, "^"
867 opt_complementary = "=1:q--v:v--q"; 868 OPT_STRING
868 opt |= getopt32(argv, OPT_STRING, &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p); 869 /* exactly one arg; -v and -q don't mix */
870 "\0" "=1:q--v:v--q",
871 &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p
872 );
869 if (opt & OPT_s) 873 if (opt & OPT_s)
870 datalen = xatou16(str_s); // -s 874 datalen = xatou16(str_s); // -s
871 if (opt & OPT_I) { // -I 875 if (opt & OPT_I) { // -I
@@ -953,7 +957,7 @@ int ping6_main(int argc UNUSED_PARAM, char **argv)
953 * may be used to endorse or promote products derived from this software 957 * may be used to endorse or promote products derived from this software
954 * without specific prior written permission. 958 * without specific prior written permission.
955 * 959 *
956 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 960 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
957 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 961 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
958 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 962 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
959 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 963 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/networking/pscan.c b/networking/pscan.c
index 17985d2c8..95b0a937d 100644
--- a/networking/pscan.c
+++ b/networking/pscan.c
@@ -75,8 +75,11 @@ int pscan_main(int argc UNUSED_PARAM, char **argv)
75 unsigned rtt_4; 75 unsigned rtt_4;
76 unsigned start, diff; 76 unsigned start, diff;
77 77
78 opt_complementary = "=1"; /* exactly one non-option */ 78 opt = getopt32(argv, "^"
79 opt = getopt32(argv, "cbp:P:t:T:", &opt_min_port, &opt_max_port, &opt_timeout, &opt_min_rtt); 79 "cbp:P:t:T:"
80 "\0" "=1", /* exactly one non-option */
81 &opt_min_port, &opt_max_port, &opt_timeout, &opt_min_rtt
82 );
80 argv += optind; 83 argv += optind;
81 max_port = xatou_range(opt_max_port, 1, 65535); 84 max_port = xatou_range(opt_max_port, 1, 65535);
82 port = xatou_range(opt_min_port, 1, max_port); 85 port = xatou_range(opt_min_port, 1, max_port);
diff --git a/networking/slattach.c b/networking/slattach.c
index 71b5bf427..e0a388926 100644
--- a/networking/slattach.c
+++ b/networking/slattach.c
@@ -17,23 +17,23 @@
17//config: default y 17//config: default y
18//config: select PLATFORM_LINUX 18//config: select PLATFORM_LINUX
19//config: help 19//config: help
20//config: slattach is a small utility to attach network interfaces to serial 20//config: slattach configures serial line as SLIP network interface.
21//config: lines.
22 21
23//applet:IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP)) 22//applet:IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP))
23/* shouldn't be NOEXEC: may sleep indefinitely */
24 24
25//kbuild:lib-$(CONFIG_SLATTACH) += slattach.o 25//kbuild:lib-$(CONFIG_SLATTACH) += slattach.o
26 26
27//usage:#define slattach_trivial_usage 27//usage:#define slattach_trivial_usage
28//usage: "[-cehmLF] [-s SPEED] [-p PROTOCOL] DEVICE" 28//usage: "[-ehmLF] [-c SCRIPT] [-s BAUD] [-p PROTOCOL] SERIAL_DEVICE"
29//usage:#define slattach_full_usage "\n\n" 29//usage:#define slattach_full_usage "\n\n"
30//usage: "Attach network interface(s) to serial line(s)\n" 30//usage: "Configure serial line as SLIP network interface\n"
31//usage: "\n -p PROT Set protocol (slip, cslip, slip6, clisp6 or adaptive)" 31//usage: "\n -p PROT Protocol: slip, cslip (default), slip6, clisp6, adaptive"
32//usage: "\n -s SPD Set line speed" 32//usage: "\n -s BAUD Line speed"
33//usage: "\n -e Exit after initializing device" 33//usage: "\n -e Exit after initialization"
34//usage: "\n -h Exit when the carrier is lost" 34//usage: "\n -h Exit if carrier is lost (else never exits)"
35//usage: "\n -c PROG Run PROG when the line is hung up" 35//usage: "\n -c PROG Run PROG on carrier loss"
36//usage: "\n -m Do NOT initialize the line in raw 8 bits mode" 36//usage: "\n -m Do NOT set raw 8bit mode"
37//usage: "\n -L Enable 3-wire operation" 37//usage: "\n -L Enable 3-wire operation"
38//usage: "\n -F Disable RTS/CTS flow control" 38//usage: "\n -F Disable RTS/CTS flow control"
39 39
@@ -42,103 +42,53 @@
42#include "libiproute/utils.h" /* invarg_1_to_2() */ 42#include "libiproute/utils.h" /* invarg_1_to_2() */
43 43
44struct globals { 44struct globals {
45 int handle;
46 int saved_disc; 45 int saved_disc;
47 struct termios saved_state; 46 struct termios saved_state;
48} FIX_ALIASING; 47} FIX_ALIASING;
49#define G (*(struct globals*)bb_common_bufsiz1) 48#define G (*(struct globals*)bb_common_bufsiz1)
50#define handle (G.handle )
51#define saved_disc (G.saved_disc )
52#define saved_state (G.saved_state )
53#define INIT_G() do { setup_common_bufsiz(); } while (0) 49#define INIT_G() do { setup_common_bufsiz(); } while (0)
54 50
51#define serial_fd 3
55 52
56/* 53static int tcsetattr_serial_or_warn(struct termios *state)
57 * Save tty state and line discipline
58 *
59 * It is fine here to bail out on errors, since we haven modified anything yet
60 */
61static void save_state(void)
62{
63 /* Save line status */
64 if (tcgetattr(handle, &saved_state) < 0)
65 bb_perror_msg_and_die("get state");
66
67 /* Save line discipline */
68 xioctl(handle, TIOCGETD, &saved_disc);
69}
70
71static int set_termios_state_or_warn(struct termios *state)
72{ 54{
73 int ret; 55 int ret;
74 56
75 ret = tcsetattr(handle, TCSANOW, state); 57 ret = tcsetattr(serial_fd, TCSANOW, state);
76 if (ret < 0) { 58 if (ret != 0) {
77 bb_perror_msg("set state"); 59 bb_perror_msg("tcsetattr");
78 return 1; /* used as exitcode */ 60 return 1; /* used as exitcode */
79 } 61 }
80 return 0; 62 return ret; /* 0 */
81} 63}
82 64
83/*
84 * Restore state and line discipline for ALL managed ttys
85 *
86 * Restoring ALL managed ttys is the only way to have a single
87 * hangup delay.
88 *
89 * Go on after errors: we want to restore as many controlled ttys
90 * as possible.
91 */
92static void restore_state_and_exit(int exitcode) NORETURN; 65static void restore_state_and_exit(int exitcode) NORETURN;
93static void restore_state_and_exit(int exitcode) 66static void restore_state_and_exit(int exitcode)
94{ 67{
95 struct termios state; 68 struct termios state;
96 69
97 /* Restore line discipline */ 70 /* Restore line discipline */
98 if (ioctl_or_warn(handle, TIOCSETD, &saved_disc) < 0) { 71 if (ioctl_or_warn(serial_fd, TIOCSETD, &G.saved_disc)) {
99 exitcode = 1; 72 exitcode = 1;
100 } 73 }
101 74
102 /* Hangup */ 75 /* Hangup */
103 memcpy(&state, &saved_state, sizeof(state)); 76 memcpy(&state, &G.saved_state, sizeof(state));
104 cfsetispeed(&state, B0); 77 cfsetispeed(&state, B0);
105 cfsetospeed(&state, B0); 78 cfsetospeed(&state, B0);
106 if (set_termios_state_or_warn(&state)) 79 exitcode |= tcsetattr_serial_or_warn(&state);
107 exitcode = 1;
108 sleep(1); 80 sleep(1);
109 81
110 /* Restore line status */ 82 /* Restore line status */
111 if (set_termios_state_or_warn(&saved_state)) 83 if (tcsetattr_serial_or_warn(&G.saved_state))
112 exit(EXIT_FAILURE); 84 exit(EXIT_FAILURE);
85
113 if (ENABLE_FEATURE_CLEAN_UP) 86 if (ENABLE_FEATURE_CLEAN_UP)
114 close(handle); 87 close(serial_fd);
115 88
116 exit(exitcode); 89 exit(exitcode);
117} 90}
118 91
119/*
120 * Set tty state, line discipline and encapsulation
121 */
122static void set_state(struct termios *state, int encap)
123{
124 int disc;
125
126 /* Set line status */
127 if (set_termios_state_or_warn(state))
128 goto bad;
129 /* Set line discliple (N_SLIP always) */
130 disc = N_SLIP;
131 if (ioctl_or_warn(handle, TIOCSETD, &disc) < 0) {
132 goto bad;
133 }
134
135 /* Set encapsulation (SLIP, CSLIP, etc) */
136 if (ioctl_or_warn(handle, SIOCSIFENCAP, &encap) < 0) {
137 bad:
138 restore_state_and_exit(EXIT_FAILURE);
139 }
140}
141
142static void sig_handler(int signo UNUSED_PARAM) 92static void sig_handler(int signo UNUSED_PARAM)
143{ 93{
144 restore_state_and_exit(EXIT_SUCCESS); 94 restore_state_and_exit(EXIT_SUCCESS);
@@ -155,13 +105,14 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
155 "cslip6\0" /* 3 */ 105 "cslip6\0" /* 3 */
156 "adaptive\0" /* 8 */ 106 "adaptive\0" /* 8 */
157 ; 107 ;
108 static const int int_N_SLIP = N_SLIP;
158 109
159 int i, encap, opt; 110 int encap, opt, fd;
160 struct termios state; 111 struct termios state;
161 const char *proto = "cslip"; 112 const char *proto = "cslip";
162 const char *extcmd; /* Command to execute after hangup */ 113 const char *extcmd; /* Command to execute after hangup */
163 const char *baud_str; 114 const char *baud_str;
164 int baud_code = -1; /* Line baud rate (system code) */ 115 int baud_code = baud_code; /* for compiler */
165 116
166 enum { 117 enum {
167 OPT_p_proto = 1 << 0, 118 OPT_p_proto = 1 << 0,
@@ -177,15 +128,13 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
177 INIT_G(); 128 INIT_G();
178 129
179 /* Parse command line options */ 130 /* Parse command line options */
180 opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd); 131 opt = getopt32(argv, "^" "p:s:c:ehmLF" "\0" "=1",
132 &proto, &baud_str, &extcmd
133 );
181 /*argc -= optind;*/ 134 /*argc -= optind;*/
182 argv += optind; 135 argv += optind;
183 136
184 if (!*argv)
185 bb_show_usage();
186
187 encap = index_in_strings(proto_names, proto); 137 encap = index_in_strings(proto_names, proto);
188
189 if (encap < 0) 138 if (encap < 0)
190 invarg_1_to_2(proto, "protocol"); 139 invarg_1_to_2(proto, "protocol");
191 if (encap > 3) 140 if (encap > 3)
@@ -198,6 +147,22 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
198 invarg_1_to_2(baud_str, "baud rate"); 147 invarg_1_to_2(baud_str, "baud rate");
199 } 148 }
200 149
150 /* Open tty */
151 fd = open(*argv, O_RDWR | O_NDELAY);
152 if (fd < 0) {
153 char *buf = concat_path_file("/dev", *argv);
154 fd = xopen(buf, O_RDWR | O_NDELAY);
155 /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */
156 free(buf);
157 }
158 xmove_fd(fd, serial_fd);
159
160 /* Save current tty state */
161 if (tcgetattr(serial_fd, &G.saved_state) != 0)
162 bb_perror_msg_and_die("tcgetattr");
163 /* Save line discipline */
164 xioctl(serial_fd, TIOCGETD, &G.saved_disc);
165
201 /* Trap signals in order to restore tty states upon exit */ 166 /* Trap signals in order to restore tty states upon exit */
202 if (!(opt & OPT_e_quit)) { 167 if (!(opt & OPT_e_quit)) {
203 bb_signals(0 168 bb_signals(0
@@ -208,43 +173,37 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
208 , sig_handler); 173 , sig_handler);
209 } 174 }
210 175
211 /* Open tty */
212 handle = open(*argv, O_RDWR | O_NDELAY);
213 if (handle < 0) {
214 char *buf = concat_path_file("/dev", *argv);
215 handle = xopen(buf, O_RDWR | O_NDELAY);
216 /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */
217 free(buf);
218 }
219
220 /* Save current tty state */
221 save_state();
222
223 /* Configure tty */ 176 /* Configure tty */
224 memcpy(&state, &saved_state, sizeof(state)); 177 memcpy(&state, &G.saved_state, sizeof(state));
225 if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */ 178 if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */
226 memset(&state.c_cc, 0, sizeof(state.c_cc)); 179 memset(&state.c_cc, 0, sizeof(state.c_cc));
227 state.c_cc[VMIN] = 1; 180 state.c_cc[VMIN] = 1;
228 state.c_iflag = IGNBRK | IGNPAR; 181 state.c_iflag = IGNBRK | IGNPAR;
229 state.c_oflag = 0; 182 /*state.c_oflag = 0;*/
230 state.c_lflag = 0; 183 /*state.c_lflag = 0;*/
231 state.c_cflag = CS8 | HUPCL | CREAD 184 state.c_cflag = CS8 | HUPCL | CREAD
232 | ((opt & OPT_L_local) ? CLOCAL : 0) 185 | ((opt & OPT_L_local) ? CLOCAL : 0)
233 | ((opt & OPT_F_noflow) ? 0 : CRTSCTS); 186 | ((opt & OPT_F_noflow) ? 0 : CRTSCTS);
234 cfsetispeed(&state, cfgetispeed(&saved_state)); 187 cfsetispeed(&state, cfgetispeed(&G.saved_state));
235 cfsetospeed(&state, cfgetospeed(&saved_state)); 188 cfsetospeed(&state, cfgetospeed(&G.saved_state));
236 } 189 }
237
238 if (opt & OPT_s_baud) { 190 if (opt & OPT_s_baud) {
239 cfsetispeed(&state, baud_code); 191 cfsetispeed(&state, baud_code);
240 cfsetospeed(&state, baud_code); 192 cfsetospeed(&state, baud_code);
241 } 193 }
242 194 /* Set line status */
243 set_state(&state, encap); 195 if (tcsetattr_serial_or_warn(&state))
196 goto bad;
197 /* Set line disclipline (N_SLIP always) */
198 if (ioctl_or_warn(serial_fd, TIOCSETD, (void*)&int_N_SLIP))
199 goto bad;
200 /* Set encapsulation (SLIP, CSLIP, etc) */
201 if (ioctl_or_warn(serial_fd, SIOCSIFENCAP, &encap))
202 goto bad;
244 203
245 /* Exit now if option -e was passed */ 204 /* Exit now if option -e was passed */
246 if (opt & OPT_e_quit) 205 if (opt & OPT_e_quit)
247 return 0; 206 return EXIT_SUCCESS;
248 207
249 /* If we're not requested to watch, just keep descriptor open 208 /* If we're not requested to watch, just keep descriptor open
250 * until we are killed */ 209 * until we are killed */
@@ -254,17 +213,20 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
254 213
255 /* Watch line for hangup */ 214 /* Watch line for hangup */
256 while (1) { 215 while (1) {
257 if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR)) 216 int modem_stat;
258 goto no_carrier; 217 if (ioctl(serial_fd, TIOCMGET, &modem_stat))
218 break;
219 if (!(modem_stat & TIOCM_CAR))
220 break;
259 sleep(15); 221 sleep(15);
260 } 222 }
261 223
262 no_carrier:
263
264 /* Execute command on hangup */ 224 /* Execute command on hangup */
265 if (opt & OPT_c_extcmd) 225 if (opt & OPT_c_extcmd)
266 system(extcmd); 226 system(extcmd);
267 227
268 /* Restore states and exit */ 228 /* Restore states and exit */
269 restore_state_and_exit(EXIT_SUCCESS); 229 restore_state_and_exit(EXIT_SUCCESS);
230 bad:
231 restore_state_and_exit(EXIT_FAILURE);
270} 232}
diff --git a/networking/tcpudp.c b/networking/tcpudp.c
index 270325164..d4c69e0f7 100644
--- a/networking/tcpudp.c
+++ b/networking/tcpudp.c
@@ -269,10 +269,11 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
269 269
270 tcp = (applet_name[0] == 't'); 270 tcp = (applet_name[0] == 't');
271 271
272 /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */
273 opt_complementary = "-3:i--i:ph:vv";
274#ifdef SSLSVD 272#ifdef SSLSVD
275 opts = getopt32(argv, "+c:+C:i:x:u:l:Eb:+hpt:vU:/:Z:K:", 273 opts = getopt32(argv, "^+"
274 "c:+C:i:x:u:l:Eb:+hpt:vU:/:Z:K:" /* -c NUM, -b NUM */
275 /* 3+ args, -i at most once, -p implies -h, -v is a counter */
276 "\0" "-3:i--i:ph:vv",
276 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, 277 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
277 &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose 278 &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose
278 ); 279 );
diff --git a/networking/telnet.c b/networking/telnet.c
index 9f191f7ad..1f8a44466 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -44,7 +44,7 @@
44//config: Setting this option will forward the USER environment variable to the 44//config: Setting this option will forward the USER environment variable to the
45//config: remote host you are connecting to. This is useful when you need to 45//config: remote host you are connecting to. This is useful when you need to
46//config: log into a machine without telling the username (autologin). This 46//config: log into a machine without telling the username (autologin). This
47//config: option enables `-a' and `-l USER' arguments. 47//config: option enables '-a' and '-l USER' options.
48//config: 48//config:
49//config:config FEATURE_TELNET_WIDTH 49//config:config FEATURE_TELNET_WIDTH
50//config: bool "Enable window size autodetection" 50//config: bool "Enable window size autodetection"
@@ -643,8 +643,10 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
643 } 643 }
644 644
645#if ENABLE_FEATURE_TELNET_AUTOLOGIN 645#if ENABLE_FEATURE_TELNET_AUTOLOGIN
646 if (1 & getopt32(argv, "al:", &G.autologin)) 646 if (1 == getopt32(argv, "al:", &G.autologin)) {
647 /* Only -a without -l USER picks $USER from envvar */
647 G.autologin = getenv("USER"); 648 G.autologin = getenv("USER");
649 }
648 argv += optind; 650 argv += optind;
649#else 651#else
650 argv++; 652 argv++;
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 16c572e8d..a6bafa21d 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -659,13 +659,15 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
659#endif 659#endif
660 INIT_G(); 660 INIT_G();
661 661
662 /* -w NUM, and implies -F. -w and -i don't mix */
663 IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:i--w:w--i";)
664 /* Even if !STANDALONE, we accept (and ignore) -i, thus people 662 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
665 * don't need to guess whether it's ok to pass -i to us */ 663 * don't need to guess whether it's ok to pass -i to us */
666 opt = getopt32(argv, "f:l:Ki" 664 opt = getopt32(argv, "^"
665 "f:l:Ki"
667 IF_FEATURE_TELNETD_STANDALONE("p:b:F") 666 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
668 IF_FEATURE_TELNETD_INETD_WAIT("Sw:+"), 667 IF_FEATURE_TELNETD_INETD_WAIT("Sw:+") /* -w NUM */
668 "\0"
669 /* -w implies -F. -w and -i don't mix */
670 IF_FEATURE_TELNETD_INETD_WAIT("wF:i--w:w--i"),
669 &G.issuefile, &G.loginpath 671 &G.issuefile, &G.loginpath
670 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr) 672 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
671 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger) 673 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
diff --git a/networking/tftp.c b/networking/tftp.c
index 5baa80448..73a9829aa 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -761,15 +761,16 @@ int tftp_main(int argc UNUSED_PARAM, char **argv)
761 761
762 INIT_G(); 762 INIT_G();
763 763
764 /* -p or -g is mandatory, and they are mutually exclusive */ 764 IF_GETPUT(opt =) getopt32(argv, "^"
765 opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:")
766 IF_GETPUT("g--p:p--g:");
767
768 IF_GETPUT(opt =) getopt32(argv,
769 IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p") 765 IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p")
770 "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:"), 766 "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:")
767 "\0"
768 /* -p or -g is mandatory, and they are mutually exclusive */
769 IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:")
770 IF_GETPUT("g--p:p--g:"),
771 &local_file, &remote_file 771 &local_file, &remote_file
772 IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str)); 772 IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str)
773 );
773 argv += optind; 774 argv += optind;
774 775
775# if ENABLE_FEATURE_TFTP_BLOCKSIZE 776# if ENABLE_FEATURE_TFTP_BLOCKSIZE
diff --git a/networking/traceroute.c b/networking/traceroute.c
index a958a2c6c..8b6247482 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -12,12 +12,12 @@
12 * this paragraph in its entirety in the documentation or other materials 12 * this paragraph in its entirety in the documentation or other materials
13 * provided with the distribution, and (3) all advertising materials mentioning 13 * provided with the distribution, and (3) all advertising materials mentioning
14 * features or use of this software display the following acknowledgement: 14 * features or use of this software display the following acknowledgement:
15 * ``This product includes software developed by the University of California, 15 * ''This product includes software developed by the University of California,
16 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 16 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
17 * the University nor the names of its contributors may be used to endorse 17 * the University nor the names of its contributors may be used to endorse
18 * or promote products derived from this software without specific prior 18 * or promote products derived from this software without specific prior
19 * written permission. 19 * written permission.
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 20 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
21 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 21 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 */ 23 */
@@ -833,9 +833,9 @@ common_traceroute_main(int op, char **argv)
833 833
834 INIT_G(); 834 INIT_G();
835 835
836 /* minimum 1 arg */ 836 op |= getopt32(argv, "^"
837 opt_complementary = "-1:x-x"; 837 OPT_STRING
838 op |= getopt32(argv, OPT_STRING 838 "\0" "-1:x-x" /* minimum 1 arg */
839 , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str 839 , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str
840 , &source, &waittime_str, &pausemsecs_str, &first_ttl_str 840 , &source, &waittime_str, &pausemsecs_str, &first_ttl_str
841 ); 841 );
diff --git a/networking/tunctl.c b/networking/tunctl.c
index 0a26ff7fb..f2dc645a1 100644
--- a/networking/tunctl.c
+++ b/networking/tunctl.c
@@ -24,7 +24,7 @@
24//config: Allow to specify owner and group of newly created interface. 24//config: Allow to specify owner and group of newly created interface.
25//config: 340 bytes of pure bloat. Say no here. 25//config: 340 bytes of pure bloat. Say no here.
26 26
27//applet:IF_TUNCTL(APPLET(tunctl, BB_DIR_SBIN, BB_SUID_DROP)) 27//applet:IF_TUNCTL(APPLET_NOEXEC(tunctl, tunctl, BB_DIR_SBIN, BB_SUID_DROP, tunctl))
28 28
29//kbuild:lib-$(CONFIG_TUNCTL) += tunctl.o 29//kbuild:lib-$(CONFIG_TUNCTL) += tunctl.o
30 30
@@ -83,10 +83,13 @@ int tunctl_main(int argc UNUSED_PARAM, char **argv)
83#endif 83#endif
84 }; 84 };
85 85
86 opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d 86 opts = getopt32(argv, "^"
87 opts = getopt32(argv, "f:t:d:" IF_FEATURE_TUNCTL_UG("u:g:b"), 87 "f:t:d:" IF_FEATURE_TUNCTL_UG("u:g:b")
88 "\0"
89 "=0:t--d:d--t", // no arguments; t ^ d
88 &opt_device, &opt_name, &opt_name 90 &opt_device, &opt_name, &opt_name
89 IF_FEATURE_TUNCTL_UG(, &opt_user, &opt_group)); 91 IF_FEATURE_TUNCTL_UG(, &opt_user, &opt_group)
92 );
90 93
91 // select device 94 // select device
92 memset(&ifr, 0, sizeof(ifr)); 95 memset(&ifr, 0, sizeof(ifr));
@@ -153,9 +156,12 @@ int tunctl_main(int argc UNUSED_PARAM, char **argv)
153 OPT_d = 1 << 2, // delete named interface 156 OPT_d = 1 << 2, // delete named interface
154 }; 157 };
155 158
156 opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d 159 opts = getopt32(argv, "^"
157 opts = getopt32(argv, "f:t:d:u:g:b", // u, g, b accepted and ignored 160 "f:t:d:u:g:b" // u, g, b accepted and ignored
158 &opt_device, &opt_name, &opt_name, NULL, NULL); 161 "\0"
162 "=0:t--d:d--t", // no arguments; t ^ d
163 &opt_device, &opt_name, &opt_name, NULL, NULL
164 );
159 165
160 // set interface name 166 // set interface name
161 memset(&ifr, 0, sizeof(ifr)); 167 memset(&ifr, 0, sizeof(ifr));
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 43081efca..849ca1388 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -1101,14 +1101,15 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1101 client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT; 1101 client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
1102 1102
1103 /* Parse command line */ 1103 /* Parse command line */
1104 /* O,x: list; -T,-t,-A take numeric param */ 1104 opt = getopt32long(argv, "^"
1105 IF_UDHCP_VERBOSE(opt_complementary = "vv";) 1105 /* O,x: list; -T,-t,-A take numeric param */
1106 IF_LONG_OPTS(applet_long_options = udhcpc6_longopts;) 1106 "i:np:qRr:s:T:+t:+SA:+O:*ox:*f"
1107 opt = getopt32(argv, "i:np:qRr:s:T:+t:+SA:+O:*ox:*f"
1108 USE_FOR_MMU("b") 1107 USE_FOR_MMU("b")
1109 ///IF_FEATURE_UDHCPC_ARPING("a") 1108 ///IF_FEATURE_UDHCPC_ARPING("a")
1110 IF_FEATURE_UDHCP_PORT("P:") 1109 IF_FEATURE_UDHCP_PORT("P:")
1111 "v" 1110 "v"
1111 "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */
1112 , udhcpc6_longopts
1112 , &client_config.interface, &client_config.pidfile, &str_r /* i,p */ 1113 , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
1113 , &client_config.script /* s */ 1114 , &client_config.script /* s */
1114 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ 1115 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index ccf04993d..55e0400b9 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -1295,16 +1295,18 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1295 str_V = "udhcp "BB_VER; 1295 str_V = "udhcp "BB_VER;
1296 1296
1297 /* Parse command line */ 1297 /* Parse command line */
1298 /* O,x: list; -T,-t,-A take numeric param */ 1298 opt = getopt32long(argv, "^"
1299 IF_UDHCP_VERBOSE(opt_complementary = "vv";) 1299 /* O,x: list; -T,-t,-A take numeric param */
1300 IF_LONG_OPTS(applet_long_options = udhcpc_longopts;) 1300 "CV:H:h:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB"
1301 opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB"
1302 USE_FOR_MMU("b") 1301 USE_FOR_MMU("b")
1303 IF_FEATURE_UDHCPC_ARPING("a::") 1302 IF_FEATURE_UDHCPC_ARPING("a::")
1304 IF_FEATURE_UDHCP_PORT("P:") 1303 IF_FEATURE_UDHCP_PORT("P:")
1305 "v" 1304 "v"
1305 "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */
1306 , udhcpc_longopts
1306 , &str_V, &str_h, &str_h, &str_F 1307 , &str_V, &str_h, &str_h, &str_F
1307 , &client_config.interface, &client_config.pidfile, &str_r /* i,p */ 1308 , &client_config.interface, &client_config.pidfile /* i,p */
1309 , &str_r /* r */
1308 , &client_config.script /* s */ 1310 , &client_config.script /* s */
1309 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ 1311 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
1310 , &list_O 1312 , &list_O
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 3a5fc2db7..05ddc8649 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -814,11 +814,12 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
814 IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;) 814 IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
815 IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;) 815 IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
816 816
817 opt = getopt32(argv, "^"
818 "fSI:va:"IF_FEATURE_UDHCP_PORT("P:")
819 "\0"
817#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 820#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
818 opt_complementary = "vv"; 821 "vv"
819#endif 822#endif
820 opt = getopt32(argv, "fSI:va:"
821 IF_FEATURE_UDHCP_PORT("P:")
822 , &str_I 823 , &str_I
823 , &str_a 824 , &str_a
824 IF_FEATURE_UDHCP_PORT(, &str_P) 825 IF_FEATURE_UDHCP_PORT(, &str_P)
diff --git a/networking/udhcp/dumpleases.c b/networking/udhcp/dumpleases.c
index dce9084b3..70d2d1434 100644
--- a/networking/udhcp/dumpleases.c
+++ b/networking/udhcp/dumpleases.c
@@ -2,7 +2,7 @@
2/* 2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */ 4 */
5//applet:IF_DUMPLEASES(APPLET(dumpleases, BB_DIR_USR_BIN, BB_SUID_DROP)) 5//applet:IF_DUMPLEASES(APPLET_NOEXEC(dumpleases, dumpleases, BB_DIR_USR_BIN, BB_SUID_DROP, dumpleases))
6 6
7//kbuild:lib-$(CONFIG_DUMPLEASES) += dumpleases.o 7//kbuild:lib-$(CONFIG_DUMPLEASES) += dumpleases.o
8 8
@@ -51,12 +51,15 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
51 "decimal\0" No_argument "d" 51 "decimal\0" No_argument "d"
52 ; 52 ;
53 53
54 applet_long_options = dumpleases_longopts;
55#endif 54#endif
56 init_unicode(); 55 init_unicode();
57 56
58 opt_complementary = "=0:a--r:r--a"; 57 opt = getopt32long(argv, "^"
59 opt = getopt32(argv, "arf:d", &file); 58 "arf:d"
59 "\0" "=0:a--r:r--a",
60 dumpleases_longopts,
61 &file
62 );
60 63
61 fd = xopen(file, O_RDONLY); 64 fd = xopen(file, O_RDONLY);
62 65
diff --git a/networking/vconfig.c b/networking/vconfig.c
index e6e2872bf..62a483865 100644
--- a/networking/vconfig.c
+++ b/networking/vconfig.c
@@ -16,7 +16,7 @@
16//config: help 16//config: help
17//config: Creates, removes, and configures VLAN interfaces 17//config: Creates, removes, and configures VLAN interfaces
18 18
19//applet:IF_VCONFIG(APPLET(vconfig, BB_DIR_SBIN, BB_SUID_DROP)) 19//applet:IF_VCONFIG(APPLET_NOEXEC(vconfig, vconfig, BB_DIR_SBIN, BB_SUID_DROP, vconfig))
20 20
21//kbuild:lib-$(CONFIG_VCONFIG) += vconfig.o 21//kbuild:lib-$(CONFIG_VCONFIG) += vconfig.o
22 22
diff --git a/networking/wget.c b/networking/wget.c
index ab9bc1836..0001ddcba 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -1371,6 +1371,11 @@ IF_DESKTOP( "no-clobber\0" No_argument "\xf0")
1371IF_DESKTOP( "no-host-directories\0" No_argument "\xf0") 1371IF_DESKTOP( "no-host-directories\0" No_argument "\xf0")
1372IF_DESKTOP( "no-parent\0" No_argument "\xf0") 1372IF_DESKTOP( "no-parent\0" No_argument "\xf0")
1373 ; 1373 ;
1374# define GETOPT32 getopt32long
1375# define LONGOPTS ,wget_longopts
1376#else
1377# define GETOPT32 getopt32
1378# define LONGOPTS
1374#endif 1379#endif
1375 1380
1376#if ENABLE_FEATURE_WGET_LONG_OPTIONS 1381#if ENABLE_FEATURE_WGET_LONG_OPTIONS
@@ -1387,11 +1392,9 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0")
1387 G.user_agent = "Wget"; /* "User-Agent" header field */ 1392 G.user_agent = "Wget"; /* "User-Agent" header field */
1388 1393
1389#if ENABLE_FEATURE_WGET_LONG_OPTIONS 1394#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1390 applet_long_options = wget_longopts;
1391#endif 1395#endif
1392 opt_complementary = "-1" /* at least one URL */ 1396 GETOPT32(argv, "^"
1393 IF_FEATURE_WGET_LONG_OPTIONS(":\xff::"); /* --header is a list */ 1397 "cqSO:P:Y:U:T:+"
1394 getopt32(argv, "cqSO:P:Y:U:T:+"
1395 /*ignored:*/ "t:" 1398 /*ignored:*/ "t:"
1396 /*ignored:*/ "n::" 1399 /*ignored:*/ "n::"
1397 /* wget has exactly four -n<letter> opts, all of which we can ignore: 1400 /* wget has exactly four -n<letter> opts, all of which we can ignore:
@@ -1402,6 +1405,10 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0")
1402 * "n::" above says that we accept -n[ARG]. 1405 * "n::" above says that we accept -n[ARG].
1403 * Specifying "n:" would be a bug: "-n ARG" would eat ARG! 1406 * Specifying "n:" would be a bug: "-n ARG" would eat ARG!
1404 */ 1407 */
1408 "\0"
1409 "-1" /* at least one URL */
1410 IF_FEATURE_WGET_LONG_OPTIONS(":\xff::") /* --header is a list */
1411 LONGOPTS
1405 , &G.fname_out, &G.dir_prefix, 1412 , &G.fname_out, &G.dir_prefix,
1406 &G.proxy_flag, &G.user_agent, 1413 &G.proxy_flag, &G.user_agent,
1407 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL), 1414 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
diff --git a/networking/whois.c b/networking/whois.c
index 0cb7e5411..fd1cdf43e 100644
--- a/networking/whois.c
+++ b/networking/whois.c
@@ -167,8 +167,7 @@ int whois_main(int argc UNUSED_PARAM, char **argv)
167 int port = 43; 167 int port = 43;
168 const char *host = "whois.iana.org"; 168 const char *host = "whois.iana.org";
169 169
170 opt_complementary = "-1"; 170 getopt32(argv, "^" "ih:p:+" "\0" "-1", &host, &port);
171 getopt32(argv, "ih:p:+", &host, &port);
172 argv += optind; 171 argv += optind;
173 172
174 do { 173 do {
diff --git a/networking/zcip.c b/networking/zcip.c
index 94174a165..55440285f 100644
--- a/networking/zcip.c
+++ b/networking/zcip.c
@@ -253,8 +253,9 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
253#define QUIT (opts & 2) 253#define QUIT (opts & 2)
254 // Parse commandline: prog [options] ifname script 254 // Parse commandline: prog [options] ifname script
255 // exactly 2 args; -v accumulates and implies -f 255 // exactly 2 args; -v accumulates and implies -f
256 opt_complementary = "=2:vv:vf"; 256 opts = getopt32(argv, "^" "fqr:l:v" "\0" "=2:vv:vf",
257 opts = getopt32(argv, "fqr:l:v", &r_opt, &l_opt, &verbose); 257 &r_opt, &l_opt, &verbose
258 );
258#if !BB_MMU 259#if !BB_MMU
259 // on NOMMU reexec early (or else we will rerun things twice) 260 // on NOMMU reexec early (or else we will rerun things twice)
260 if (!FOREGROUND) 261 if (!FOREGROUND)
diff --git a/printutils/lpd.c b/printutils/lpd.c
index 3fdba5d2b..662d3a224 100644
--- a/printutils/lpd.c
+++ b/printutils/lpd.c
@@ -198,9 +198,8 @@ int lpd_main(int argc UNUSED_PARAM, char *argv[])
198 q = p; // next line 198 q = p; // next line
199 } 199 }
200 // helper should not talk over network. 200 // helper should not talk over network.
201 // this call reopens stdio fds to "/dev/null" 201 // this call reopens stdio fds to "/dev/null".
202 // (no daemonization is done) 202 bb_daemon_helper(DAEMON_DEVNULL_STDIO);
203 bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
204 BB_EXECVP_or_die(argv); 203 BB_EXECVP_or_die(argv);
205 } 204 }
206 205
diff --git a/procps/free.c b/procps/free.c
index 618664e08..b57e4a322 100644
--- a/procps/free.c
+++ b/procps/free.c
@@ -15,7 +15,7 @@
15//config: memory in the system, as well as the buffers used by the kernel. 15//config: memory in the system, as well as the buffers used by the kernel.
16//config: The shared memory column should be ignored; it is obsolete. 16//config: The shared memory column should be ignored; it is obsolete.
17 17
18//applet:IF_FREE(APPLET(free, BB_DIR_USR_BIN, BB_SUID_DROP)) 18//applet:IF_FREE(APPLET_NOEXEC(free, free, BB_DIR_USR_BIN, BB_SUID_DROP, free))
19 19
20//kbuild:lib-$(CONFIG_FREE) += free.o 20//kbuild:lib-$(CONFIG_FREE) += free.o
21 21
@@ -47,7 +47,10 @@ struct globals {
47#endif 47#endif
48} FIX_ALIASING; 48} FIX_ALIASING;
49#define G (*(struct globals*)bb_common_bufsiz1) 49#define G (*(struct globals*)bb_common_bufsiz1)
50#define INIT_G() do { setup_common_bufsiz(); } while (0) 50#define INIT_G() do { \
51 setup_common_bufsiz(); \
52 /* NB: noexec applet - globals not zeroed */ \
53} while (0)
51 54
52 55
53static unsigned long long scale(unsigned long d) 56static unsigned long long scale(unsigned long d)
diff --git a/procps/fuser.c b/procps/fuser.c
index 2585a4203..418f57b57 100644
--- a/procps/fuser.c
+++ b/procps/fuser.c
@@ -299,8 +299,7 @@ int fuser_main(int argc UNUSED_PARAM, char **argv)
299 break; 299 break;
300 } 300 }
301 301
302 opt_complementary = "-1"; /* at least one param */ 302 getopt32(argv, "^" OPTION_STRING "\0" "-1"/*at least one arg*/);
303 getopt32(argv, OPTION_STRING);
304 argv += optind; 303 argv += optind;
305 304
306 pp = argv; 305 pp = argv;
diff --git a/procps/iostat.c b/procps/iostat.c
index 6a39c324f..050625f57 100644
--- a/procps/iostat.c
+++ b/procps/iostat.c
@@ -418,8 +418,7 @@ int iostat_main(int argc UNUSED_PARAM, char **argv)
418 418
419 /* Parse and process arguments */ 419 /* Parse and process arguments */
420 /* -k and -m are mutually exclusive */ 420 /* -k and -m are mutually exclusive */
421 opt_complementary = "k--m:m--k"; 421 opt = getopt32(argv, "^" "cdtzkm" "\0" "k--m:m--k");
422 opt = getopt32(argv, "cdtzkm");
423 if (!(opt & (OPT_c + OPT_d))) 422 if (!(opt & (OPT_c + OPT_d)))
424 /* Default is -cd */ 423 /* Default is -cd */
425 opt |= OPT_c + OPT_d; 424 opt |= OPT_c + OPT_d;
diff --git a/procps/kill.c b/procps/kill.c
index 09beefb2d..0ddae2f70 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -32,10 +32,10 @@
32//config: in its own session, so it won't kill the shell that is running 32//config: in its own session, so it won't kill the shell that is running
33//config: the script it was called from. 33//config: the script it was called from.
34 34
35//applet:IF_KILL(APPLET(kill, BB_DIR_BIN, BB_SUID_DROP)) 35//applet:IF_KILL( APPLET_NOFORK(kill, kill, BB_DIR_BIN, BB_SUID_DROP, kill))
36// APPLET_ODDNAME:name main location suid_type help 36// APPLET_NOFORK:name main location suid_type help
37//applet:IF_KILLALL( APPLET_ODDNAME(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall)) 37//applet:IF_KILLALL( APPLET_NOFORK(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall))
38//applet:IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_SBIN, BB_SUID_DROP, killall5)) 38//applet:IF_KILLALL5(APPLET_NOFORK(killall5, kill, BB_DIR_USR_SBIN, BB_SUID_DROP, killall5))
39 39
40//kbuild:lib-$(CONFIG_KILL) += kill.o 40//kbuild:lib-$(CONFIG_KILL) += kill.o
41//kbuild:lib-$(CONFIG_KILLALL) += kill.o 41//kbuild:lib-$(CONFIG_KILLALL) += kill.o
@@ -87,7 +87,7 @@
87 * + we can't use xfunc here 87 * + we can't use xfunc here
88 * + we can't use applet_name 88 * + we can't use applet_name
89 * + we can't use bb_show_usage 89 * + we can't use bb_show_usage
90 * (Above doesn't apply for killall[5] cases) 90 * (doesn't apply for killall[5], still should be careful b/c NOFORK)
91 * 91 *
92 * kill %n gets translated into kill ' -<process group>' by shell (note space!) 92 * kill %n gets translated into kill ' -<process group>' by shell (note space!)
93 * This is needed to avoid collision with kill -9 ... syntax 93 * This is needed to avoid collision with kill -9 ... syntax
diff --git a/procps/mpstat.c b/procps/mpstat.c
index 05a3f3ff3..c6279f9d8 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -8,6 +8,7 @@
8 */ 8 */
9 9
10//applet:IF_MPSTAT(APPLET(mpstat, BB_DIR_BIN, BB_SUID_DROP)) 10//applet:IF_MPSTAT(APPLET(mpstat, BB_DIR_BIN, BB_SUID_DROP))
11/* shouldn't be noexec: "mpstat INTERVAL" runs indefinitely */
11 12
12//kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o 13//kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o
13 14
diff --git a/procps/pgrep.c b/procps/pgrep.c
index a3ca9e295..a16a6e959 100644
--- a/procps/pgrep.c
+++ b/procps/pgrep.c
@@ -18,9 +18,13 @@
18//config: help 18//config: help
19//config: Send signals to processes by name. 19//config: Send signals to processes by name.
20 20
21//applet:IF_PGREP(APPLET(pgrep, BB_DIR_USR_BIN, BB_SUID_DROP)) 21//applet:IF_PGREP(APPLET_ODDNAME(pgrep, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pgrep))
22// APPLET_ODDNAME:name main location suid_type help 22// APPLET_ODDNAME:name main location suid_type help
23//applet:IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill)) 23//applet:IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill))
24/* can't be noexec: can find _itself_ under wrong name, since after fork only,
25 * /proc/PID/cmdline and comm are wrong! Can fix comm (prctl(PR_SET_NAME)),
26 * but cmdline?
27 */
24 28
25//kbuild:lib-$(CONFIG_PGREP) += pgrep.o 29//kbuild:lib-$(CONFIG_PGREP) += pgrep.o
26//kbuild:lib-$(CONFIG_PKILL) += pgrep.o 30//kbuild:lib-$(CONFIG_PKILL) += pgrep.o
diff --git a/procps/pidof.c b/procps/pidof.c
index 41247a02c..98d7949f8 100644
--- a/procps/pidof.c
+++ b/procps/pidof.c
@@ -30,6 +30,10 @@
30//config: of the pidof, in other words the calling shell or shell script. 30//config: of the pidof, in other words the calling shell or shell script.
31 31
32//applet:IF_PIDOF(APPLET(pidof, BB_DIR_BIN, BB_SUID_DROP)) 32//applet:IF_PIDOF(APPLET(pidof, BB_DIR_BIN, BB_SUID_DROP))
33/* can't be noexec: can find _itself_ under wrong name, since after fork only,
34 * /proc/PID/cmdline and comm are wrong! Can fix comm (prctl(PR_SET_NAME)),
35 * but cmdline?
36 */
33 37
34//kbuild:lib-$(CONFIG_PIDOF) += pidof.o 38//kbuild:lib-$(CONFIG_PIDOF) += pidof.o
35 39
diff --git a/procps/pmap.c b/procps/pmap.c
index c8f728897..5c2d1ad59 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -18,7 +18,7 @@
18//kbuild:lib-$(CONFIG_PMAP) += pmap.o 18//kbuild:lib-$(CONFIG_PMAP) += pmap.o
19 19
20//usage:#define pmap_trivial_usage 20//usage:#define pmap_trivial_usage
21//usage: "[-xq] PID" 21//usage: "[-xq] PID..."
22//usage:#define pmap_full_usage "\n\n" 22//usage:#define pmap_full_usage "\n\n"
23//usage: "Display process memory usage" 23//usage: "Display process memory usage"
24//usage: "\n" 24//usage: "\n"
@@ -96,7 +96,7 @@ int pmap_main(int argc UNUSED_PARAM, char **argv)
96 unsigned opts; 96 unsigned opts;
97 int ret; 97 int ret;
98 98
99 opts = getopt32(argv, "xq"); 99 opts = getopt32(argv, "^" "xq" "\0" "-1"); /* min one arg */
100 argv += optind; 100 argv += optind;
101 101
102 ret = 0; 102 ret = 0;
diff --git a/procps/powertop.c b/procps/powertop.c
index ebd659bdb..5d522bf91 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -8,11 +8,6 @@
8 * 8 *
9 * Licensed under GPLv2, see file LICENSE in this source tree. 9 * Licensed under GPLv2, see file LICENSE in this source tree.
10 */ 10 */
11
12//applet:IF_POWERTOP(APPLET(powertop, BB_DIR_USR_SBIN, BB_SUID_DROP))
13
14//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
15
16//config:config POWERTOP 11//config:config POWERTOP
17//config: bool "powertop (9.1 kb)" 12//config: bool "powertop (9.1 kb)"
18//config: default y 13//config: default y
@@ -27,6 +22,10 @@
27//config: Without this, powertop will only refresh display every 10 seconds. 22//config: Without this, powertop will only refresh display every 10 seconds.
28//config: No keyboard commands will work, only ^C to terminate. 23//config: No keyboard commands will work, only ^C to terminate.
29 24
25//applet:IF_POWERTOP(APPLET(powertop, BB_DIR_USR_SBIN, BB_SUID_DROP))
26
27//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
28
30// XXX This should be configurable 29// XXX This should be configurable
31#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1 30#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
32 31
@@ -718,7 +717,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
718 set_termios_to_raw(STDIN_FILENO, &G.init_settings, TERMIOS_CLEAR_ISIG); 717 set_termios_to_raw(STDIN_FILENO, &G.init_settings, TERMIOS_CLEAR_ISIG);
719 bb_signals(BB_FATAL_SIGS, sig_handler); 718 bb_signals(BB_FATAL_SIGS, sig_handler);
720 /* So we don't forget to reset term settings */ 719 /* So we don't forget to reset term settings */
721 atexit(reset_term); 720 die_func = reset_term;
722#endif 721#endif
723 722
724 /* Collect initial data */ 723 /* Collect initial data */
@@ -855,6 +854,9 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
855 } /* for (;;) */ 854 } /* for (;;) */
856 855
857 bb_putchar('\n'); 856 bb_putchar('\n');
857#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
858 reset_term();
859#endif
858 860
859 return EXIT_SUCCESS; 861 return EXIT_SUCCESS;
860} 862}
diff --git a/procps/ps.c b/procps/ps.c
index 3e83a3e03..742d1715e 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -15,26 +15,26 @@
15//config: ps gives a snapshot of the current processes. 15//config: ps gives a snapshot of the current processes.
16//config: 16//config:
17//config:config FEATURE_PS_WIDE 17//config:config FEATURE_PS_WIDE
18//config: bool "Enable wide output option (-w)" 18//config: bool "Enable wide output (-w)"
19//config: default y 19//config: default y
20//config: depends on PS && !DESKTOP 20//config: depends on (PS || MINIPS) && !DESKTOP
21//config: help 21//config: help
22//config: Support argument 'w' for wide output. 22//config: Support argument 'w' for wide output.
23//config: If given once, 132 chars are printed, and if given more 23//config: If given once, 132 chars are printed, and if given more
24//config: than once, the length is unlimited. 24//config: than once, the length is unlimited.
25//config: 25//config:
26//config:config FEATURE_PS_LONG 26//config:config FEATURE_PS_LONG
27//config: bool "Enable long output option (-l)" 27//config: bool "Enable long output (-l)"
28//config: default y 28//config: default y
29//config: depends on PS && !DESKTOP 29//config: depends on (PS || MINIPS) && !DESKTOP
30//config: help 30//config: help
31//config: Support argument 'l' for long output. 31//config: Support argument 'l' for long output.
32//config: Adds fields PPID, RSS, START, TIME & TTY 32//config: Adds fields PPID, RSS, START, TIME & TTY
33//config: 33//config:
34//config:config FEATURE_PS_TIME 34//config:config FEATURE_PS_TIME
35//config: bool "Support -o time and -o etime output specifiers" 35//config: bool "Enable -o time and -o etime specifiers"
36//config: default y 36//config: default y
37//config: depends on PS && DESKTOP 37//config: depends on (PS || MINIPS) && DESKTOP
38//config: select PLATFORM_LINUX 38//config: select PLATFORM_LINUX
39//config: 39//config:
40//config:config FEATURE_PS_UNUSUAL_SYSTEMS 40//config:config FEATURE_PS_UNUSUAL_SYSTEMS
@@ -46,13 +46,16 @@
46//config: (if you are on Linux 2.4.0+ and use ELF, you don't need this) 46//config: (if you are on Linux 2.4.0+ and use ELF, you don't need this)
47//config: 47//config:
48//config:config FEATURE_PS_ADDITIONAL_COLUMNS 48//config:config FEATURE_PS_ADDITIONAL_COLUMNS
49//config: bool "Support -o rgroup, -o ruser, -o nice specifiers" 49//config: bool "Enable -o rgroup, -o ruser, -o nice specifiers"
50//config: default y 50//config: default y
51//config: depends on PS && DESKTOP 51//config: depends on (PS || MINIPS) && DESKTOP
52 52
53//applet:IF_PS(APPLET(ps, BB_DIR_BIN, BB_SUID_DROP)) 53// APPLET_NOEXEC:name main location suid_type help
54//applet:IF_PS( APPLET_NOEXEC(ps, ps, BB_DIR_BIN, BB_SUID_DROP, ps))
55//applet:IF_MINIPS(APPLET_NOEXEC(minips, ps, BB_DIR_BIN, BB_SUID_DROP, ps))
54 56
55//kbuild:lib-$(CONFIG_PS) += ps.o 57//kbuild:lib-$(CONFIG_PS) += ps.o
58//kbuild:lib-$(CONFIG_MINIPS) += ps.o
56 59
57//usage:#if ENABLE_DESKTOP 60//usage:#if ENABLE_DESKTOP
58//usage: 61//usage:
@@ -144,12 +147,6 @@ static unsigned long get_uptime(void)
144#endif 147#endif
145 148
146#if ENABLE_DESKTOP 149#if ENABLE_DESKTOP
147
148#include <sys/times.h> /* for times() */
149#ifndef AT_CLKTCK
150# define AT_CLKTCK 17
151#endif
152
153/* TODO: 150/* TODO:
154 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html 151 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
155 * specifies (for XSI-conformant systems) following default columns 152 * specifies (for XSI-conformant systems) following default columns
@@ -186,7 +183,9 @@ struct globals {
186 char *buffer; 183 char *buffer;
187 unsigned terminal_width; 184 unsigned terminal_width;
188#if ENABLE_FEATURE_PS_TIME 185#if ENABLE_FEATURE_PS_TIME
186# if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS || !defined(__linux__)
189 unsigned kernel_HZ; 187 unsigned kernel_HZ;
188# endif
190 unsigned long seconds_since_boot; 189 unsigned long seconds_since_boot;
191#endif 190#endif
192} FIX_ALIASING; 191} FIX_ALIASING;
@@ -197,91 +196,15 @@ struct globals {
197#define need_flags (G.need_flags ) 196#define need_flags (G.need_flags )
198#define buffer (G.buffer ) 197#define buffer (G.buffer )
199#define terminal_width (G.terminal_width ) 198#define terminal_width (G.terminal_width )
200#define kernel_HZ (G.kernel_HZ )
201#define INIT_G() do { setup_common_bufsiz(); } while (0) 199#define INIT_G() do { setup_common_bufsiz(); } while (0)
202 200
203#if ENABLE_FEATURE_PS_TIME 201#if ENABLE_FEATURE_PS_TIME
204/* for ELF executables, notes are pushed before environment and args */ 202# if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS || !defined(__linux__)
205static uintptr_t find_elf_note(uintptr_t findme) 203# define get_kernel_HZ() (G.kernel_HZ)
206{ 204# else
207 uintptr_t *ep = (uintptr_t *) environ; 205 /* non-ancient Linux standardized on 100 for "times" freq */
208 206# define get_kernel_HZ() ((unsigned)100)
209 while (*ep++) 207# endif
210 continue;
211 while (*ep) {
212 if (ep[0] == findme) {
213 return ep[1];
214 }
215 ep += 2;
216 }
217 return -1;
218}
219
220#if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS
221static unsigned get_HZ_by_waiting(void)
222{
223 struct timeval tv1, tv2;
224 unsigned t1, t2, r, hz;
225 unsigned cnt = cnt; /* for compiler */
226 int diff;
227
228 r = 0;
229
230 /* Wait for times() to reach new tick */
231 t1 = times(NULL);
232 do {
233 t2 = times(NULL);
234 } while (t2 == t1);
235 gettimeofday(&tv2, NULL);
236
237 do {
238 t1 = t2;
239 tv1.tv_usec = tv2.tv_usec;
240
241 /* Wait exactly one times() tick */
242 do {
243 t2 = times(NULL);
244 } while (t2 == t1);
245 gettimeofday(&tv2, NULL);
246
247 /* Calculate ticks per sec, rounding up to even */
248 diff = tv2.tv_usec - tv1.tv_usec;
249 if (diff <= 0) diff += 1000000;
250 hz = 1000000u / (unsigned)diff;
251 hz = (hz+1) & ~1;
252
253 /* Count how many same hz values we saw */
254 if (r != hz) {
255 r = hz;
256 cnt = 0;
257 }
258 cnt++;
259 } while (cnt < 3); /* exit if saw 3 same values */
260
261 return r;
262}
263#else
264static inline unsigned get_HZ_by_waiting(void)
265{
266 /* Better method? */
267 return 100;
268}
269#endif
270
271static unsigned get_kernel_HZ(void)
272{
273 if (kernel_HZ)
274 return kernel_HZ;
275
276 /* Works for ELF only, Linux 2.4.0+ */
277 kernel_HZ = find_elf_note(AT_CLKTCK);
278 if (kernel_HZ == (unsigned)-1)
279 kernel_HZ = get_HZ_by_waiting();
280
281 G.seconds_since_boot = get_uptime();
282
283 return kernel_HZ;
284}
285#endif 208#endif
286 209
287/* Print value to buf, max size+1 chars (including trailing '\0') */ 210/* Print value to buf, max size+1 chars (including trailing '\0') */
@@ -379,52 +302,71 @@ static void func_tty(char *buf, int size, const procps_status_t *ps)
379#endif 302#endif
380 303
381#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 304#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
382
383static void func_rgroup(char *buf, int size, const procps_status_t *ps) 305static void func_rgroup(char *buf, int size, const procps_status_t *ps)
384{ 306{
385 safe_strncpy(buf, get_cached_groupname(ps->rgid), size+1); 307 safe_strncpy(buf, get_cached_groupname(ps->rgid), size+1);
386} 308}
387
388static void func_ruser(char *buf, int size, const procps_status_t *ps) 309static void func_ruser(char *buf, int size, const procps_status_t *ps)
389{ 310{
390 safe_strncpy(buf, get_cached_username(ps->ruid), size+1); 311 safe_strncpy(buf, get_cached_username(ps->ruid), size+1);
391} 312}
392
393static void func_nice(char *buf, int size, const procps_status_t *ps) 313static void func_nice(char *buf, int size, const procps_status_t *ps)
394{ 314{
395 sprintf(buf, "%*d", size, ps->niceness); 315 sprintf(buf, "%*d", size, ps->niceness);
396} 316}
397
398#endif 317#endif
399 318
400#if ENABLE_FEATURE_PS_TIME 319#if ENABLE_FEATURE_PS_TIME
320static void format_time(char *buf, int size, unsigned long tt)
321{
322 unsigned ff;
401 323
324 /* Used to show "14453:50" if tt is large. Ugly.
325 * procps-ng 3.3.10 uses "[[dd-]hh:]mm:ss" format.
326 * TODO: switch to that?
327 */
328
329 /* Formatting for 5-char TIME column.
330 * NB: "size" is not always 5: ELAPSED is wider (7),
331 * not taking advantage of that (yet?).
332 */
333 ff = tt % 60;
334 tt /= 60;
335 if (tt < 60) {
336 snprintf(buf, size+1, "%2u:%02u", (unsigned)tt, ff);
337 return;
338 }
339 ff = tt % 60;
340 tt /= 60;
341 if (tt < 24) {
342 snprintf(buf, size+1, "%2uh%02u", (unsigned)tt, ff);
343 return;
344 }
345 ff = tt % 24;
346 tt /= 24;
347 if (tt < 100) {
348 snprintf(buf, size+1, "%2ud%02u", (unsigned)tt, ff);
349 return;
350 }
351 snprintf(buf, size+1, "%4lud", tt);
352}
402static void func_etime(char *buf, int size, const procps_status_t *ps) 353static void func_etime(char *buf, int size, const procps_status_t *ps)
403{ 354{
404 /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */ 355 /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */
405 unsigned long mm; 356 unsigned long mm;
406 unsigned ss;
407 357
408 mm = ps->start_time / get_kernel_HZ(); 358 mm = ps->start_time / get_kernel_HZ();
409 /* must be after get_kernel_HZ()! */
410 mm = G.seconds_since_boot - mm; 359 mm = G.seconds_since_boot - mm;
411 ss = mm % 60; 360 format_time(buf, size, mm);
412 mm /= 60;
413 snprintf(buf, size+1, "%3lu:%02u", mm, ss);
414} 361}
415
416static void func_time(char *buf, int size, const procps_status_t *ps) 362static void func_time(char *buf, int size, const procps_status_t *ps)
417{ 363{
418 /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */ 364 /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */
419 unsigned long mm; 365 unsigned long mm;
420 unsigned ss;
421 366
422 mm = (ps->utime + ps->stime) / get_kernel_HZ(); 367 mm = (ps->utime + ps->stime) / get_kernel_HZ();
423 ss = mm % 60; 368 format_time(buf, size, mm);
424 mm /= 60;
425 snprintf(buf, size+1, "%3lu:%02u", mm, ss);
426} 369}
427
428#endif 370#endif
429 371
430#if ENABLE_SELINUX 372#if ENABLE_SELINUX
@@ -465,7 +407,7 @@ static const ps_out_t out_spec[] = {
465// { 5 , "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ }, 407// { 5 , "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ },
466#endif 408#endif
467#if ENABLE_FEATURE_PS_TIME 409#if ENABLE_FEATURE_PS_TIME
468 { 6 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, 410 { 5 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME },
469#endif 411#endif
470#if !ENABLE_PLATFORM_MINGW32 412#if !ENABLE_PLATFORM_MINGW32
471 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, 413 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY },
@@ -641,6 +583,12 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
641 }; 583 };
642 584
643 INIT_G(); 585 INIT_G();
586#if ENABLE_FEATURE_PS_TIME
587 G.seconds_since_boot = get_uptime();
588# if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS || !defined(__linux__)
589 G.kernel_HZ = bb_clk_tck(); /* this is sysconf(_SC_CLK_TCK) */
590# endif
591#endif
644 592
645 // POSIX: 593 // POSIX:
646 // -a Write information for all processes associated with terminals 594 // -a Write information for all processes associated with terminals
@@ -731,9 +679,12 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
731# if ENABLE_FEATURE_PS_WIDE 679# if ENABLE_FEATURE_PS_WIDE
732 /* -w is a bit complicated */ 680 /* -w is a bit complicated */
733 int w_count = 0; 681 int w_count = 0;
734 opt_complementary = "-:ww"; 682 make_all_argv_opts(argv);
735 opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l") 683 opts = getopt32(argv, "^"
736 "w", &w_count); 684 IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l")"w"
685 "\0" "ww",
686 &w_count
687 );
737 /* if w is given once, GNU ps sets the width to 132, 688 /* if w is given once, GNU ps sets the width to 132,
738 * if w is given more than once, it is "unlimited" 689 * if w is given more than once, it is "unlimited"
739 */ 690 */
@@ -747,7 +698,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
747 } 698 }
748# else 699# else
749 /* -w is not supported, only -Z and/or -T */ 700 /* -w is not supported, only -Z and/or -T */
750 opt_complementary = "-"; 701 make_all_argv_opts(argv);
751 opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l")); 702 opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l"));
752# endif 703# endif
753 704
diff --git a/procps/pstree.c b/procps/pstree.c
index 212cda23c..4fda1c21c 100644
--- a/procps/pstree.c
+++ b/procps/pstree.c
@@ -9,14 +9,13 @@
9 * 9 *
10 * Licensed under GPLv2, see file LICENSE in this source tree. 10 * Licensed under GPLv2, see file LICENSE in this source tree.
11 */ 11 */
12
13//config:config PSTREE 12//config:config PSTREE
14//config: bool "pstree (9.4 kb)" 13//config: bool "pstree (9.4 kb)"
15//config: default y 14//config: default y
16//config: help 15//config: help
17//config: Display a tree of processes. 16//config: Display a tree of processes.
18 17
19//applet:IF_PSTREE(APPLET(pstree, BB_DIR_USR_BIN, BB_SUID_DROP)) 18//applet:IF_PSTREE(APPLET_NOEXEC(pstree, pstree, BB_DIR_USR_BIN, BB_SUID_DROP, pstree))
20 19
21//kbuild:lib-$(CONFIG_PSTREE) += pstree.o 20//kbuild:lib-$(CONFIG_PSTREE) += pstree.o
22 21
@@ -387,8 +386,7 @@ int pstree_main(int argc UNUSED_PARAM, char **argv)
387 386
388 G.output_width = get_terminal_width(0); 387 G.output_width = get_terminal_width(0);
389 388
390 opt_complementary = "?1"; 389 getopt32(argv, "^" "p" "\0" "?1");
391 getopt32(argv, "p");
392 argv += optind; 390 argv += optind;
393 391
394 if (argv[0]) { 392 if (argv[0]) {
diff --git a/procps/pwdx.c b/procps/pwdx.c
index dac238950..c72cf804a 100644
--- a/procps/pwdx.c
+++ b/procps/pwdx.c
@@ -14,7 +14,7 @@
14//config: help 14//config: help
15//config: Report current working directory of a process 15//config: Report current working directory of a process
16 16
17//applet:IF_PWDX(APPLET(pwdx, BB_DIR_USR_BIN, BB_SUID_DROP)) 17//applet:IF_PWDX(APPLET_NOFORK(pwdx, pwdx, BB_DIR_USR_BIN, BB_SUID_DROP, pwdx))
18 18
19//kbuild:lib-$(CONFIG_PWDX) += pwdx.o 19//kbuild:lib-$(CONFIG_PWDX) += pwdx.o
20 20
@@ -28,8 +28,7 @@
28int pwdx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 28int pwdx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
29int pwdx_main(int argc UNUSED_PARAM, char **argv) 29int pwdx_main(int argc UNUSED_PARAM, char **argv)
30{ 30{
31 opt_complementary = "-1"; 31 getopt32(argv, "^" "" "\0" "-1");
32 getopt32(argv, "");
33 argv += optind; 32 argv += optind;
34 33
35 do { 34 do {
@@ -50,6 +49,7 @@ int pwdx_main(int argc UNUSED_PARAM, char **argv)
50 49
51 sprintf(buf, "/proc/%u/cwd", pid); 50 sprintf(buf, "/proc/%u/cwd", pid);
52 51
52 /* NOFORK: only one alloc is allowed; must free */
53 s = xmalloc_readlink(buf); 53 s = xmalloc_readlink(buf);
54 // "pwdx /proc/1" says "/proc/1: DIR", not "1: DIR" 54 // "pwdx /proc/1" says "/proc/1: DIR", not "1: DIR"
55 printf("%s: %s\n", *argv, s ? s : strerror(errno == ENOENT ? ESRCH : errno)); 55 printf("%s: %s\n", *argv, s ? s : strerror(errno == ENOENT ? ESRCH : errno));
diff --git a/procps/sysctl.c b/procps/sysctl.c
index b17f5e896..827e09cce 100644
--- a/procps/sysctl.c
+++ b/procps/sysctl.c
@@ -16,22 +16,22 @@
16//config: help 16//config: help
17//config: Configure kernel parameters at runtime. 17//config: Configure kernel parameters at runtime.
18 18
19//applet:IF_BB_SYSCTL(APPLET(sysctl, BB_DIR_SBIN, BB_SUID_DROP)) 19//applet:IF_BB_SYSCTL(APPLET_NOEXEC(sysctl, sysctl, BB_DIR_SBIN, BB_SUID_DROP, sysctl))
20 20
21//kbuild:lib-$(CONFIG_BB_SYSCTL) += sysctl.o 21//kbuild:lib-$(CONFIG_BB_SYSCTL) += sysctl.o
22 22
23//usage:#define sysctl_trivial_usage 23//usage:#define sysctl_trivial_usage
24//usage: "[OPTIONS] [KEY[=VALUE]]..." 24//usage: "-p [-enq] [FILE...] / [-enqaw] [KEY[=VALUE]]..."
25//usage:#define sysctl_full_usage "\n\n" 25//usage:#define sysctl_full_usage "\n\n"
26//usage: "Show/set kernel parameters\n" 26//usage: "Show/set kernel parameters\n"
27//usage: "\n -p Set values from FILEs (default /etc/sysctl.conf)"
27//usage: "\n -e Don't warn about unknown keys" 28//usage: "\n -e Don't warn about unknown keys"
28//usage: "\n -n Don't show key names" 29//usage: "\n -n Don't show key names"
30//usage: "\n -q Quiet"
29//usage: "\n -a Show all values" 31//usage: "\n -a Show all values"
30/* Same as -a, no need to show it */ 32/* Same as -a, no need to show it */
31/* //usage: "\n -A Show all values in table form" */ 33/* //usage: "\n -A Show all values in table form" */
32//usage: "\n -w Set values" 34//usage: "\n -w Set values"
33//usage: "\n -p FILE Set values from FILE (default /etc/sysctl.conf)"
34//usage: "\n -q Set values silently"
35//usage: 35//usage:
36//usage:#define sysctl_example_usage 36//usage:#define sysctl_example_usage
37//usage: "sysctl [-n] [-e] variable...\n" 37//usage: "sysctl [-n] [-e] variable...\n"
@@ -48,7 +48,7 @@ enum {
48 FLAG_TABLE_FORMAT = 1 << 2, /* not implemented */ 48 FLAG_TABLE_FORMAT = 1 << 2, /* not implemented */
49 FLAG_SHOW_ALL = 1 << 3, 49 FLAG_SHOW_ALL = 1 << 3,
50 FLAG_PRELOAD_FILE = 1 << 4, 50 FLAG_PRELOAD_FILE = 1 << 4,
51/* TODO: procps 3.2.8 seems to not require -w for KEY=VAL to work: */ 51 /* NB: procps 3.2.8 does not require -w for KEY=VAL to work, it only rejects non-KEY=VAL form */
52 FLAG_WRITE = 1 << 5, 52 FLAG_WRITE = 1 << 5,
53 FLAG_QUIET = 1 << 6, 53 FLAG_QUIET = 1 << 6,
54}; 54};
@@ -104,6 +104,7 @@ static int sysctl_act_on_setting(char *setting)
104 int fd, retval = EXIT_SUCCESS; 104 int fd, retval = EXIT_SUCCESS;
105 char *cptr, *outname; 105 char *cptr, *outname;
106 char *value = value; /* for compiler */ 106 char *value = value; /* for compiler */
107 bool writing = (option_mask32 & FLAG_WRITE);
107 108
108 outname = xstrdup(setting); 109 outname = xstrdup(setting);
109 110
@@ -114,8 +115,10 @@ static int sysctl_act_on_setting(char *setting)
114 cptr++; 115 cptr++;
115 } 116 }
116 117
117 if (option_mask32 & FLAG_WRITE) { 118 cptr = strchr(setting, '=');
118 cptr = strchr(setting, '='); 119 if (cptr)
120 writing = 1;
121 if (writing) {
119 if (cptr == NULL) { 122 if (cptr == NULL) {
120 bb_error_msg("error: '%s' must be of the form name=value", 123 bb_error_msg("error: '%s' must be of the form name=value",
121 outname); 124 outname);
@@ -147,7 +150,7 @@ static int sysctl_act_on_setting(char *setting)
147 break; 150 break;
148 default: 151 default:
149 bb_perror_msg("error %sing key '%s'", 152 bb_perror_msg("error %sing key '%s'",
150 option_mask32 & FLAG_WRITE ? 153 writing ?
151 "sett" : "read", 154 "sett" : "read",
152 outname); 155 outname);
153 break; 156 break;
@@ -156,7 +159,7 @@ static int sysctl_act_on_setting(char *setting)
156 goto end; 159 goto end;
157 } 160 }
158 161
159 if (option_mask32 & FLAG_WRITE) { 162 if (writing) {
160//TODO: procps 3.2.7 writes "value\n", note trailing "\n" 163//TODO: procps 3.2.7 writes "value\n", note trailing "\n"
161 xwrite_str(fd, value); 164 xwrite_str(fd, value);
162 close(fd); 165 close(fd);
@@ -238,23 +241,33 @@ static int sysctl_handle_preload_file(const char *filename)
238{ 241{
239 char *token[2]; 242 char *token[2];
240 parser_t *parser; 243 parser_t *parser;
244 int parse_flags;
241 245
242 parser = config_open(filename); 246 parser = config_open(filename);
243 /* Must do it _after_ config_open(): */ 247 /* Must do it _after_ config_open(): */
244 xchdir("/proc/sys"); 248 xchdir("/proc/sys");
245 /* xchroot("/proc/sys") - if you are paranoid */
246 249
247//TODO: ';' is comment char too 250 parse_flags = 0;
248//TODO: comment may be only at line start. "var=1 #abc" - "1 #abc" is the value 251 parse_flags &= ~PARSE_COLLAPSE; // NO (var==val is not var=val) - treat consecutive delimiters as one
249// (but _whitespace_ from ends should be trimmed first (and we do it right)) 252 parse_flags &= ~PARSE_TRIM; // NO - trim leading and trailing delimiters
250//TODO: "var==1" is mishandled (must use "=1" as a value, but uses "1") 253 parse_flags |= PARSE_GREEDY; // YES - last token takes entire remainder of the line
251// can it be fixed by removing PARSE_COLLAPSE bit? 254 parse_flags &= ~PARSE_MIN_DIE; // NO - die if < min tokens found
252 while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { 255 parse_flags &= ~PARSE_EOL_COMMENTS; // NO (only first char) - comments are recognized even if not first char
256 parse_flags |= PARSE_ALT_COMMENTS;// YES - two comment chars: ';' and '#'
257 /* <space><tab><space>#comment is also comment, not strictly 1st char only */
258 parse_flags |= PARSE_WS_COMMENTS; // YES - comments are recognized even if there is whitespace before
259 while (config_read(parser, token, 2, 2, ";#=", parse_flags)) {
253 char *tp; 260 char *tp;
261
262 trim(token[1]);
263 tp = trim(token[0]);
254 sysctl_dots_to_slashes(token[0]); 264 sysctl_dots_to_slashes(token[0]);
255 tp = xasprintf("%s=%s", token[0], token[1]); 265 /* ^^^converted in-place. tp still points to NUL */
256 sysctl_act_recursive(tp); 266 /* now, add "=TOKEN1" */
257 free(tp); 267 *tp++ = '=';
268 overlapping_strcpy(tp, token[1]);
269
270 sysctl_act_on_setting(token[0]);
258 } 271 }
259 if (ENABLE_FEATURE_CLEAN_UP) 272 if (ENABLE_FEATURE_CLEAN_UP)
260 config_close(parser); 273 config_close(parser);
@@ -273,12 +286,19 @@ int sysctl_main(int argc UNUSED_PARAM, char **argv)
273 option_mask32 = opt; 286 option_mask32 = opt;
274 287
275 if (opt & FLAG_PRELOAD_FILE) { 288 if (opt & FLAG_PRELOAD_FILE) {
289 int cur_dir_fd;
276 option_mask32 |= FLAG_WRITE; 290 option_mask32 |= FLAG_WRITE;
277 /* xchdir("/proc/sys") is inside */ 291 if (!*argv)
278 return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf"); 292 *--argv = (char*)"/etc/sysctl.conf";
293 cur_dir_fd = xopen(".", O_RDONLY | O_DIRECTORY);
294 do {
295 /* xchdir("/proc/sys") is inside */
296 sysctl_handle_preload_file(*argv);
297 xfchdir(cur_dir_fd); /* files can be relative, must restore cwd */
298 } while (*++argv);
299 return 0; /* procps-ng 3.3.10 does not flag parse errors */
279 } 300 }
280 xchdir("/proc/sys"); 301 xchdir("/proc/sys");
281 /* xchroot("/proc/sys") - if you are paranoid */
282 if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) { 302 if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) {
283 return sysctl_act_recursive("."); 303 return sysctl_act_recursive(".");
284 } 304 }
diff --git a/procps/top.c b/procps/top.c
index 015d1ab74..f97ded5d6 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -116,7 +116,6 @@
116//kbuild:lib-$(CONFIG_TOP) += top.o 116//kbuild:lib-$(CONFIG_TOP) += top.o
117 117
118#include "libbb.h" 118#include "libbb.h"
119#include "common_bufsiz.h"
120 119
121 120
122typedef struct top_status_t { 121typedef struct top_status_t {
@@ -154,6 +153,8 @@ typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
154 153
155enum { SORT_DEPTH = 3 }; 154enum { SORT_DEPTH = 3 };
156 155
156/* Screens wider than this are unlikely */
157enum { LINE_BUF_SIZE = 512 - 64 };
157 158
158struct globals { 159struct globals {
159 top_status_t *top; 160 top_status_t *top;
@@ -192,10 +193,9 @@ struct globals {
192#if ENABLE_FEATURE_TOP_INTERACTIVE 193#if ENABLE_FEATURE_TOP_INTERACTIVE
193 char kbd_input[KEYCODE_BUFFER_SIZE]; 194 char kbd_input[KEYCODE_BUFFER_SIZE];
194#endif 195#endif
195 char line_buf[80]; 196 char line_buf[LINE_BUF_SIZE];
196}; //FIX_ALIASING; - large code growth 197};
197enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) }; 198#define G (*ptr_to_globals)
198#define G (*(struct globals*)bb_common_bufsiz1)
199#define top (G.top ) 199#define top (G.top )
200#define ntop (G.ntop ) 200#define ntop (G.ntop )
201#define sort_field (G.sort_field ) 201#define sort_field (G.sort_field )
@@ -213,8 +213,7 @@ enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
213#define total_pcpu (G.total_pcpu ) 213#define total_pcpu (G.total_pcpu )
214#define line_buf (G.line_buf ) 214#define line_buf (G.line_buf )
215#define INIT_G() do { \ 215#define INIT_G() do { \
216 setup_common_bufsiz(); \ 216 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
217 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
218 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ 217 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
219} while (0) 218} while (0)
220 219
@@ -1110,15 +1109,14 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1110#endif 1109#endif
1111 1110
1112 /* all args are options; -n NUM */ 1111 /* all args are options; -n NUM */
1113 opt_complementary = "-"; /* options can be specified w/o dash */ 1112 make_all_argv_opts(argv); /* options can be specified w/o dash */
1114 col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations); 1113 col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations);
1115#if ENABLE_FEATURE_TOPMEM 1114#if ENABLE_FEATURE_TOPMEM
1116 if (col & OPT_m) /* -m (busybox specific) */ 1115 if (col & OPT_m) /* -m (busybox specific) */
1117 scan_mask = TOPMEM_MASK; 1116 scan_mask = TOPMEM_MASK;
1118#endif 1117#endif
1119 if (col & OPT_d) { 1118 if (col & OPT_d) {
1120 /* work around for "-d 1" -> "-d -1" done by getopt32 1119 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1121 * (opt_complementary == "-" does this) */
1122 if (str_interval[0] == '-') 1120 if (str_interval[0] == '-')
1123 str_interval++; 1121 str_interval++;
1124 /* Need to limit it to not overflow poll timeout */ 1122 /* Need to limit it to not overflow poll timeout */
@@ -1148,6 +1146,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1148 else { 1146 else {
1149 /* Turn on unbuffered input; turn off echoing, ^C ^Z etc */ 1147 /* Turn on unbuffered input; turn off echoing, ^C ^Z etc */
1150 set_termios_to_raw(STDIN_FILENO, &initial_settings, TERMIOS_CLEAR_ISIG); 1148 set_termios_to_raw(STDIN_FILENO, &initial_settings, TERMIOS_CLEAR_ISIG);
1149 die_func = reset_term;
1151 } 1150 }
1152 1151
1153 bb_signals(BB_FATAL_SIGS, sig_catcher); 1152 bb_signals(BB_FATAL_SIGS, sig_catcher);
diff --git a/procps/uptime.c b/procps/uptime.c
index 24b2b39df..b0ee8391b 100644
--- a/procps/uptime.c
+++ b/procps/uptime.c
@@ -27,7 +27,7 @@
27//config: help 27//config: help
28//config: Display the number of users currently logged on. 28//config: Display the number of users currently logged on.
29 29
30//applet:IF_UPTIME(APPLET(uptime, BB_DIR_USR_BIN, BB_SUID_DROP)) 30//applet:IF_UPTIME(APPLET_NOEXEC(uptime, uptime, BB_DIR_USR_BIN, BB_SUID_DROP, uptime))
31 31
32//kbuild:lib-$(CONFIG_UPTIME) += uptime.o 32//kbuild:lib-$(CONFIG_UPTIME) += uptime.o
33 33
diff --git a/procps/watch.c b/procps/watch.c
index 2bb7cca90..6fc9f7db7 100644
--- a/procps/watch.c
+++ b/procps/watch.c
@@ -62,9 +62,9 @@ int watch_main(int argc UNUSED_PARAM, char **argv)
62 xopen("/dev/null", O_RDONLY); 62 xopen("/dev/null", O_RDONLY);
63#endif 63#endif
64 64
65 opt_complementary = "-1"; // at least one param; -n NUM 65 // "+": stop at first non-option (procps 3.x only); -n NUM
66 // "+": stop at first non-option (procps 3.x only) 66 // at least one param
67 opt = getopt32(argv, "+dtn:+", &period); 67 opt = getopt32(argv, "^+" "dtn:+" "\0" "-1", &period);
68 argv += optind; 68 argv += optind;
69 69
70 // watch from both procps 2.x and 3.x does concatenation. Example: 70 // watch from both procps 2.x and 3.x does concatenation. Example:
diff --git a/runit/chpst.c b/runit/chpst.c
index 3a2f9e616..3ecb85cba 100644
--- a/runit/chpst.c
+++ b/runit/chpst.c
@@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
13 3. The name of the author may not be used to endorse or promote products 13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission. 14 derived from this software without specific prior written permission.
15 15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
@@ -59,12 +59,12 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59//config: help 59//config: help
60//config: Sets soft resource limits as specified by options 60//config: Sets soft resource limits as specified by options
61 61
62//applet:IF_CHPST(APPLET(chpst, BB_DIR_USR_BIN, BB_SUID_DROP)) 62//applet:IF_CHPST( APPLET_NOEXEC(chpst, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, chpst))
63// APPLET_ODDNAME:name main location suid_type help 63// APPLET_NOEXEC:name main location suid_type help
64//applet:IF_ENVDIR( APPLET_ODDNAME(envdir, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envdir)) 64//applet:IF_ENVDIR( APPLET_NOEXEC(envdir, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envdir))
65//applet:IF_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envuidgid)) 65//applet:IF_ENVUIDGID(APPLET_NOEXEC(envuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envuidgid))
66//applet:IF_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, setuidgid)) 66//applet:IF_SETUIDGID(APPLET_NOEXEC(setuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, setuidgid))
67//applet:IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, softlimit)) 67//applet:IF_SOFTLIMIT(APPLET_NOEXEC(softlimit, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, softlimit))
68 68
69//kbuild:lib-$(CONFIG_CHPST) += chpst.o 69//kbuild:lib-$(CONFIG_CHPST) += chpst.o
70//kbuild:lib-$(CONFIG_ENVDIR) += chpst.o 70//kbuild:lib-$(CONFIG_ENVDIR) += chpst.o
@@ -301,9 +301,10 @@ int chpst_main(int argc UNUSED_PARAM, char **argv)
301 // FIXME: can we live with int-sized limits? 301 // FIXME: can we live with int-sized limits?
302 // can we live with 40000 days? 302 // can we live with 40000 days?
303 // if yes -> getopt converts strings to numbers for us 303 // if yes -> getopt converts strings to numbers for us
304 opt_complementary = "-1"; 304 opt = getopt32(argv, "^+"
305 opt = getopt32(argv, "+a:+c:+d:+f:+l:+m:+o:+p:+r:+s:+t:+u:U:e:" 305 "a:+c:+d:+f:+l:+m:+o:+p:+r:+s:+t:+u:U:e:"
306 IF_CHPST("/:n:vP012"), 306 IF_CHPST("/:n:vP012")
307 "\0" "-1",
307 &limita, &limitc, &limitd, &limitf, &limitl, 308 &limita, &limitc, &limitd, &limitf, &limitl,
308 &limitm, &limito, &limitp, &limitr, &limits, &limitt, 309 &limitm, &limito, &limitp, &limitr, &limits, &limitt,
309 &set_user, &set_user, &env_dir 310 &set_user, &set_user, &env_dir
diff --git a/runit/runit_lib.h b/runit/runit_lib.h
index c36ea4ca5..c54561616 100644
--- a/runit/runit_lib.h
+++ b/runit/runit_lib.h
@@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
13 3. The name of the author may not be used to endorse or promote products 13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission. 14 derived from this software without specific prior written permission.
15 15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
diff --git a/runit/runsv.c b/runit/runsv.c
index ad7d82cb8..a67280b4b 100644
--- a/runit/runsv.c
+++ b/runit/runsv.c
@@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
13 3. The name of the author may not be used to endorse or promote products 13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission. 14 derived from this software without specific prior written permission.
15 15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
diff --git a/runit/runsvdir.c b/runit/runsvdir.c
index b4f5c303b..11ab40abf 100644
--- a/runit/runsvdir.c
+++ b/runit/runsvdir.c
@@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
13 3. The name of the author may not be used to endorse or promote products 13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission. 14 derived from this software without specific prior written permission.
15 15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
@@ -248,10 +248,9 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
248 248
249 INIT_G(); 249 INIT_G();
250 250
251 opt_complementary = "-1";
252 opt_s_argv[0] = NULL; 251 opt_s_argv[0] = NULL;
253 opt_s_argv[2] = NULL; 252 opt_s_argv[2] = NULL;
254 getopt32(argv, "Ps:", &opt_s_argv[0]); 253 getopt32(argv, "^" "Ps:" "\0" "-1", &opt_s_argv[0]);
255 argv += optind; 254 argv += optind;
256 255
257 i_am_init = (getpid() == 1); 256 i_am_init = (getpid() == 1);
diff --git a/runit/sv.c b/runit/sv.c
index 2d5b466bf..1d0809be8 100644
--- a/runit/sv.c
+++ b/runit/sv.c
@@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
13 3. The name of the author may not be used to endorse or promote products 13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission. 14 derived from this software without specific prior written permission.
15 15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
@@ -175,8 +175,8 @@ Exit Codes
175//config: svc controls the state of services monitored by the runsv supervisor. 175//config: svc controls the state of services monitored by the runsv supervisor.
176//config: It is comaptible with daemontools command with the same name. 176//config: It is comaptible with daemontools command with the same name.
177 177
178//applet:IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP)) 178//applet:IF_SV( APPLET_NOEXEC(sv, sv, BB_DIR_USR_BIN, BB_SUID_DROP, sv ))
179//applet:IF_SVC(APPLET(svc, BB_DIR_USR_BIN, BB_SUID_DROP)) 179//applet:IF_SVC(APPLET_NOEXEC(svc, svc, BB_DIR_USR_BIN, BB_SUID_DROP, svc))
180 180
181//kbuild:lib-$(CONFIG_SV) += sv.o 181//kbuild:lib-$(CONFIG_SV) += sv.o
182//kbuild:lib-$(CONFIG_SVC) += sv.o 182//kbuild:lib-$(CONFIG_SVC) += sv.o
@@ -193,7 +193,7 @@ struct globals {
193/* "Bernstein" time format: unix + 0x400000000000000aULL */ 193/* "Bernstein" time format: unix + 0x400000000000000aULL */
194 uint64_t tstart, tnow; 194 uint64_t tstart, tnow;
195 svstatus_t svstatus; 195 svstatus_t svstatus;
196 unsigned islog; 196 smallint islog;
197} FIX_ALIASING; 197} FIX_ALIASING;
198#define G (*(struct globals*)bb_common_bufsiz1) 198#define G (*(struct globals*)bb_common_bufsiz1)
199#define acts (G.acts ) 199#define acts (G.acts )
@@ -203,7 +203,11 @@ struct globals {
203#define tnow (G.tnow ) 203#define tnow (G.tnow )
204#define svstatus (G.svstatus ) 204#define svstatus (G.svstatus )
205#define islog (G.islog ) 205#define islog (G.islog )
206#define INIT_G() do { setup_common_bufsiz(); } while (0) 206#define INIT_G() do { \
207 setup_common_bufsiz(); \
208 /* need to zero out, svc calls sv() repeatedly */ \
209 memset(&G, 0, sizeof(G)); \
210} while (0)
207 211
208 212
209#define str_equal(s,t) (strcmp((s), (t)) == 0) 213#define str_equal(s,t) (strcmp((s), (t)) == 0)
@@ -502,8 +506,9 @@ static int sv(char **argv)
502 x = getenv("SVWAIT"); 506 x = getenv("SVWAIT");
503 if (x) waitsec = xatou(x); 507 if (x) waitsec = xatou(x);
504 508
505 opt_complementary = "vv"; /* -w N, -v is a counter */ 509 getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
506 getopt32(argv, "w:+v", &waitsec, &verbose); 510 &waitsec, &verbose
511 );
507 argv += optind; 512 argv += optind;
508 action = *argv++; 513 action = *argv++;
509 if (!action || !*argv) bb_show_usage(); 514 if (!action || !*argv) bb_show_usage();
@@ -701,8 +706,6 @@ int svc_main(int argc UNUSED_PARAM, char **argv)
701 const char *optstring; 706 const char *optstring;
702 unsigned opts; 707 unsigned opts;
703 708
704 INIT_G();
705
706 optstring = "udopchaitkx"; 709 optstring = "udopchaitkx";
707 opts = getopt32(argv, optstring); 710 opts = getopt32(argv, optstring);
708 argv += optind; 711 argv += optind;
@@ -718,15 +721,16 @@ int svc_main(int argc UNUSED_PARAM, char **argv)
718 argv[1] = command; 721 argv[1] = command;
719 command[1] = '\0'; 722 command[1] = '\0';
720 723
721 /* getopt32() was already called:
722 * reset the libc getopt() function, which keeps internal state.
723 */
724 GETOPT_RESET();
725
726 do { 724 do {
727 if (opts & 1) { 725 if (opts & 1) {
728 int r; 726 int r;
727
729 command[0] = *optstring; 728 command[0] = *optstring;
729
730 /* getopt() was already called by getopt32():
731 * reset the libc getopt() function's internal state.
732 */
733 GETOPT_RESET();
730 r = sv(argv); 734 r = sv(argv);
731 if (r) 735 if (r)
732 return 1; 736 return 1;
diff --git a/runit/svlogd.c b/runit/svlogd.c
index 8dbf67106..739483356 100644
--- a/runit/svlogd.c
+++ b/runit/svlogd.c
@@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
13 3. The name of the author may not be used to endorse or promote products 13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission. 14 derived from this software without specific prior written permission.
15 15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
@@ -1037,9 +1037,10 @@ int svlogd_main(int argc, char **argv)
1037 1037
1038 INIT_G(); 1038 INIT_G();
1039 1039
1040 opt_complementary = "tt:vv"; 1040 opt = getopt32(argv, "^"
1041 opt = getopt32(argv, "r:R:l:b:tv", 1041 "r:R:l:b:tv" "\0" "tt:vv",
1042 &r, &replace, &l, &b, &timestamp, &verbose); 1042 &r, &replace, &l, &b, &timestamp, &verbose
1043 );
1043 if (opt & 1) { // -r 1044 if (opt & 1) { // -r
1044 repl = r[0]; 1045 repl = r[0];
1045 if (!repl || r[1]) 1046 if (!repl || r[1])
diff --git a/scripts/Makefile.IMA b/scripts/Makefile.IMA
index 0eced2982..f155108d7 100644
--- a/scripts/Makefile.IMA
+++ b/scripts/Makefile.IMA
@@ -115,6 +115,9 @@ lib-y:=
115include debianutils/Kbuild 115include debianutils/Kbuild
116lib-all-y += $(patsubst %,debianutils/%,$(sort $(lib-y))) 116lib-all-y += $(patsubst %,debianutils/%,$(sort $(lib-y)))
117lib-y:= 117lib-y:=
118include klibc-utils/Kbuild
119lib-all-y += $(patsubst %,klibc-utils/%,$(sort $(lib-y)))
120lib-y:=
118include runit/Kbuild 121include runit/Kbuild
119lib-all-y += $(patsubst %,runit/%,$(sort $(lib-y))) 122lib-all-y += $(patsubst %,runit/%,$(sort $(lib-y)))
120lib-y:= 123lib-y:=
diff --git a/scripts/echo.c b/scripts/echo.c
index cb207ae05..8c6b409d3 100644
--- a/scripts/echo.c
+++ b/scripts/echo.c
@@ -214,7 +214,7 @@ int main(int argc, char **argv)
214 * may be used to endorse or promote products derived from this software 214 * may be used to endorse or promote products derived from this software
215 * without specific prior written permission. 215 * without specific prior written permission.
216 * 216 *
217 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 217 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
218 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 219 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
220 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 220 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/selinux/chcon.c b/selinux/chcon.c
index ae87fb554..3ddb2dd46 100644
--- a/selinux/chcon.c
+++ b/selinux/chcon.c
@@ -13,11 +13,6 @@
13//config: depends on SELINUX 13//config: depends on SELINUX
14//config: help 14//config: help
15//config: Enable support to change the security context of file. 15//config: Enable support to change the security context of file.
16//config:
17//config:config FEATURE_CHCON_LONG_OPTIONS
18//config: bool "Enable long options"
19//config: default y
20//config: depends on CHCON && LONG_OPTS
21 16
22//applet:IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP)) 17//applet:IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP))
23 18
@@ -26,34 +21,24 @@
26//usage:#define chcon_trivial_usage 21//usage:#define chcon_trivial_usage
27//usage: "[OPTIONS] CONTEXT FILE..." 22//usage: "[OPTIONS] CONTEXT FILE..."
28//usage: "\n chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..." 23//usage: "\n chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..."
29//usage: IF_FEATURE_CHCON_LONG_OPTIONS( 24//usage: IF_LONG_OPTS(
30//usage: "\n chcon [OPTIONS] --reference=RFILE FILE..." 25//usage: "\n chcon [OPTIONS] --reference=RFILE FILE..."
31//usage: ) 26//usage: )
27//usage:
32//usage:#define chcon_full_usage "\n\n" 28//usage:#define chcon_full_usage "\n\n"
33//usage: "Change the security context of each FILE to CONTEXT\n" 29//usage: "Change the security context of each FILE to CONTEXT\n"
34//usage: IF_FEATURE_CHCON_LONG_OPTIONS(
35//usage: "\n -v,--verbose Verbose"
36//usage: "\n -c,--changes Report changes made"
37//usage: "\n -h,--no-dereference Affect symlinks instead of their targets"
38//usage: "\n -f,--silent,--quiet Suppress most error messages"
39//usage: "\n --reference RFILE Use RFILE's group instead of using a CONTEXT value"
40//usage: "\n -u,--user USER Set user/role/type/range in the target"
41//usage: "\n -r,--role ROLE security context"
42//usage: "\n -t,--type TYPE"
43//usage: "\n -l,--range RANGE"
44//usage: "\n -R,--recursive Recurse"
45//usage: )
46//usage: IF_NOT_FEATURE_CHCON_LONG_OPTIONS(
47//usage: "\n -v Verbose" 30//usage: "\n -v Verbose"
48//usage: "\n -c Report changes made" 31//usage: "\n -c Report changes made"
49//usage: "\n -h Affect symlinks instead of their targets" 32//usage: "\n -h Affect symlinks instead of their targets"
50//usage: "\n -f Suppress most error messages" 33//usage: "\n -f Suppress most error messages"
34//usage: IF_LONG_OPTS(
35//usage: "\n --reference RFILE Use RFILE's group instead of using a CONTEXT value"
36//usage: )
51//usage: "\n -u USER Set user/role/type/range in the target security context" 37//usage: "\n -u USER Set user/role/type/range in the target security context"
52//usage: "\n -r ROLE" 38//usage: "\n -r ROLE"
53//usage: "\n -t TYPE" 39//usage: "\n -t TYPE"
54//usage: "\n -l RNG" 40//usage: "\n -l RNG"
55//usage: "\n -R Recurse" 41//usage: "\n -R Recurse"
56//usage: )
57 42
58#include <selinux/context.h> 43#include <selinux/context.h>
59 44
@@ -68,7 +53,7 @@
68#define OPT_TYPE (1<<6) /* 't' */ 53#define OPT_TYPE (1<<6) /* 't' */
69#define OPT_RANGE (1<<7) /* 'l' */ 54#define OPT_RANGE (1<<7) /* 'l' */
70#define OPT_VERBOSE (1<<8) /* 'v' */ 55#define OPT_VERBOSE (1<<8) /* 'v' */
71#define OPT_REFERENCE ((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS) 56#define OPT_REFERENCE ((1<<9) * ENABLE_LONG_OPTS)
72#define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE) 57#define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE)
73 58
74static char *user = NULL; 59static char *user = NULL;
@@ -157,7 +142,7 @@ skip:
157 return rc; 142 return rc;
158} 143}
159 144
160#if ENABLE_FEATURE_CHCON_LONG_OPTIONS 145#if ENABLE_LONG_OPTS
161static const char chcon_longopts[] ALIGN1 = 146static const char chcon_longopts[] ALIGN1 =
162 "recursive\0" No_argument "R" 147 "recursive\0" No_argument "R"
163 "changes\0" No_argument "c" 148 "changes\0" No_argument "c"
@@ -180,20 +165,21 @@ int chcon_main(int argc UNUSED_PARAM, char **argv)
180 char *fname; 165 char *fname;
181 int i, errors = 0; 166 int i, errors = 0;
182 167
183#if ENABLE_FEATURE_CHCON_LONG_OPTIONS 168 getopt32long(argv, "^"
184 applet_long_options = chcon_longopts; 169 "Rchfu:r:t:l:v"
185#endif 170 "\0"
186 opt_complementary = "-1" /* at least 1 param */ 171 "-1" /* at least 1 arg */
187 ":?" /* error if exclusivity constraints are violated */ 172 ":?" /* error if exclusivity constraints are violated */
188#if ENABLE_FEATURE_CHCON_LONG_OPTIONS 173#if ENABLE_LONG_OPTS
189 ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff" 174 ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
190#endif 175#endif
191 ":f--v:v--f"; /* 'verbose' and 'quiet' are exclusive */ 176 ":f--v:v--f" /* 'verbose' and 'quiet' are exclusive */
192 getopt32(argv, "Rchfu:r:t:l:v", 177 , chcon_longopts,
193 &user, &role, &type, &range, &reference_file); 178 &user, &role, &type, &range, &reference_file
179 );
194 argv += optind; 180 argv += optind;
195 181
196#if ENABLE_FEATURE_CHCON_LONG_OPTIONS 182#if ENABLE_LONG_OPTS
197 if (option_mask32 & OPT_REFERENCE) { 183 if (option_mask32 & OPT_REFERENCE) {
198 /* FIXME: lgetfilecon() should be used when '-h' is specified. 184 /* FIXME: lgetfilecon() should be used when '-h' is specified.
199 * But current implementation follows the original one. */ 185 * But current implementation follows the original one. */
diff --git a/selinux/matchpathcon.c b/selinux/matchpathcon.c
index 3388d0857..e57120d3b 100644
--- a/selinux/matchpathcon.c
+++ b/selinux/matchpathcon.c
@@ -58,9 +58,13 @@ int matchpathcon_main(int argc UNUSED_PARAM, char **argv)
58 unsigned opts; 58 unsigned opts;
59 char *fcontext, *prefix, *path; 59 char *fcontext, *prefix, *path;
60 60
61 opt_complementary = "-1" /* at least one param reqd */ 61 opts = getopt32(argv, "^"
62 ":?:f--p:p--f"; /* mutually exclusive */ 62 "nNf:p:V"
63 opts = getopt32(argv, "nNf:p:V", &fcontext, &prefix); 63 "\0"
64 "-1" /* at least one param reqd */
65 ":?:f--p:p--f" /* mutually exclusive */
66 , &fcontext, &prefix
67 );
64 argv += optind; 68 argv += optind;
65 69
66 if (opts & OPT_NOT_TRANS) { 70 if (opts & OPT_NOT_TRANS) {
diff --git a/selinux/runcon.c b/selinux/runcon.c
index 09082d6c2..a5a394427 100644
--- a/selinux/runcon.c
+++ b/selinux/runcon.c
@@ -34,11 +34,6 @@
34//config: depends on SELINUX 34//config: depends on SELINUX
35//config: help 35//config: help
36//config: Enable support to run command in specified security context. 36//config: Enable support to run command in specified security context.
37//config:
38//config:config FEATURE_RUNCON_LONG_OPTIONS
39//config: bool "Enable long options"
40//config: default y
41//config: depends on RUNCON && LONG_OPTS
42 37
43//applet:IF_RUNCON(APPLET(runcon, BB_DIR_USR_BIN, BB_SUID_DROP)) 38//applet:IF_RUNCON(APPLET(runcon, BB_DIR_USR_BIN, BB_SUID_DROP))
44 39
@@ -50,20 +45,11 @@
50//usage:#define runcon_full_usage "\n\n" 45//usage:#define runcon_full_usage "\n\n"
51//usage: "Run PROG in a different security context\n" 46//usage: "Run PROG in a different security context\n"
52//usage: "\n CONTEXT Complete security context\n" 47//usage: "\n CONTEXT Complete security context\n"
53//usage: IF_FEATURE_RUNCON_LONG_OPTIONS(
54//usage: "\n -c,--compute Compute process transition context before modifying"
55//usage: "\n -t,--type TYPE Type (for same role as parent)"
56//usage: "\n -u,--user USER User identity"
57//usage: "\n -r,--role ROLE Role"
58//usage: "\n -l,--range RNG Levelrange"
59//usage: )
60//usage: IF_NOT_FEATURE_RUNCON_LONG_OPTIONS(
61//usage: "\n -c Compute process transition context before modifying" 48//usage: "\n -c Compute process transition context before modifying"
62//usage: "\n -t TYPE Type (for same role as parent)" 49//usage: "\n -t TYPE Type (for same role as parent)"
63//usage: "\n -u USER User identity" 50//usage: "\n -u USER User identity"
64//usage: "\n -r ROLE Role" 51//usage: "\n -r ROLE Role"
65//usage: "\n -l RNG Levelrange" 52//usage: "\n -l RNG Levelrange"
66//usage: )
67 53
68#include <selinux/context.h> 54#include <selinux/context.h>
69/* from deprecated <selinux/flask.h>: */ 55/* from deprecated <selinux/flask.h>: */
@@ -108,7 +94,7 @@ static context_t runcon_compute_new_context(char *user, char *role, char *type,
108 return con; 94 return con;
109} 95}
110 96
111#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS 97#if ENABLE_LONG_OPTS
112static const char runcon_longopts[] ALIGN1 = 98static const char runcon_longopts[] ALIGN1 =
113 "user\0" Required_argument "u" 99 "user\0" Required_argument "u"
114 "role\0" Required_argument "r" 100 "role\0" Required_argument "r"
@@ -140,11 +126,12 @@ int runcon_main(int argc UNUSED_PARAM, char **argv)
140 126
141 selinux_or_die(); 127 selinux_or_die();
142 128
143#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS 129 opts = getopt32long(argv, "^"
144 applet_long_options = runcon_longopts; 130 "r:t:u:l:ch"
145#endif 131 "\0" "-1",
146 opt_complementary = "-1"; 132 runcon_longopts,
147 opts = getopt32(argv, "r:t:u:l:ch", &role, &type, &user, &range); 133 &role, &type, &user, &range
134 );
148 argv += optind; 135 argv += optind;
149 136
150 if (!(opts & OPTS_CONTEXT_COMPONENT)) { 137 if (!(opts & OPTS_CONTEXT_COMPONENT)) {
diff --git a/selinux/sestatus.c b/selinux/sestatus.c
index daf4b223b..6954aca70 100644
--- a/selinux/sestatus.c
+++ b/selinux/sestatus.c
@@ -167,8 +167,7 @@ int sestatus_main(int argc UNUSED_PARAM, char **argv)
167 const char *pol_path; 167 const char *pol_path;
168 int rc; 168 int rc;
169 169
170 opt_complementary = "?0"; /* no arguments are required. */ 170 opts = getopt32(argv, "^" "vb" "\0" "=0"/*no arguments*/);
171 opts = getopt32(argv, "vb");
172 171
173 /* SELinux status: line */ 172 /* SELinux status: line */
174 rc = is_selinux_enabled(); 173 rc = is_selinux_enabled();
diff --git a/selinux/setfiles.c b/selinux/setfiles.c
index 01106bd67..8da47d274 100644
--- a/selinux/setfiles.c
+++ b/selinux/setfiles.c
@@ -610,17 +610,23 @@ int setfiles_main(int argc UNUSED_PARAM, char **argv)
610 610
611 set_matchpathcon_flags(matchpathcon_flags); 611 set_matchpathcon_flags(matchpathcon_flags);
612 612
613 opt_complementary = "vv:v--p:p--v:v--q:q--v";
614 /* Option order must match OPT_x definitions! */ 613 /* Option order must match OPT_x definitions! */
615 if (applet_name[0] == 'r') { /* restorecon */ 614 if (applet_name[0] == 'r') { /* restorecon */
616 flags = getopt32(argv, "de:*f:ilnpqrsvo:FWR", 615 flags = getopt32(argv, "^"
617 &exclude_dir, &input_filename, &out_filename, &verbose); 616 "de:*f:ilnpqrsvo:FWR",
617 "\0" "vv:v--p:p--v:v--q:q--v";
618 &exclude_dir, &input_filename, &out_filename,
619 &verbose
620 );
618 } else { /* setfiles */ 621 } else { /* setfiles */
619 flags = getopt32(argv, "de:*f:ilnpqr:svo:FW" 622 flags = getopt32(argv, "^"
620 IF_FEATURE_SETFILES_CHECK_OPTION("c:"), 623 "de:*f:ilnpqr:svo:FW"
624 IF_FEATURE_SETFILES_CHECK_OPTION("c:"),
625 "\0" "vv:v--p:p--v:v--q:q--v";
621 &exclude_dir, &input_filename, &rootpath, &out_filename, 626 &exclude_dir, &input_filename, &rootpath, &out_filename,
622 IF_FEATURE_SETFILES_CHECK_OPTION(&policyfile,) 627 IF_FEATURE_SETFILES_CHECK_OPTION(&policyfile,)
623 &verbose); 628 &verbose
629 );
624 } 630 }
625 argv += optind; 631 argv += optind;
626 632
@@ -673,7 +679,7 @@ int setfiles_main(int argc UNUSED_PARAM, char **argv)
673 bb_show_usage(); 679 bb_show_usage();
674 xstat(argv[0], &sb); 680 xstat(argv[0], &sb);
675 if (!S_ISREG(sb.st_mode)) { 681 if (!S_ISREG(sb.st_mode)) {
676 bb_error_msg_and_die("spec file %s is not a regular file", argv[0]); 682 bb_error_msg_and_die("'%s' is not a regular file", argv[0]);
677 } 683 }
678 /* Load the file contexts configuration and check it. */ 684 /* Load the file contexts configuration and check it. */
679 rc = matchpathcon_init(argv[0]); 685 rc = matchpathcon_init(argv[0]);
diff --git a/shell/ash.c b/shell/ash.c
index 6a572cbc3..ac1ae05fc 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -215,6 +215,9 @@
215#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT 215#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
216#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT 216#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
217#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT 217#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
218#define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT
219#define BASH_READ_D ENABLE_ASH_BASH_COMPAT
220#define IF_BASH_READ_D IF_ASH_BASH_COMPAT
218 221
219#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 222#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
220/* Bionic at least up to version 24 has no glob() */ 223/* Bionic at least up to version 24 has no glob() */
@@ -452,7 +455,7 @@ struct globals_misc {
452#define S_DFL 1 /* default signal handling (SIG_DFL) */ 455#define S_DFL 1 /* default signal handling (SIG_DFL) */
453#define S_CATCH 2 /* signal is caught */ 456#define S_CATCH 2 /* signal is caught */
454#define S_IGN 3 /* signal is ignored (SIG_IGN) */ 457#define S_IGN 3 /* signal is ignored (SIG_IGN) */
455#define S_HARD_IGN 4 /* signal is ignored permanently */ 458#define S_HARD_IGN 4 /* signal is ignored permanently (it was SIG_IGN on entry to shell) */
456 459
457 /* indicates specified signal received */ 460 /* indicates specified signal received */
458 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 461 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
@@ -1313,6 +1316,10 @@ struct strpush {
1313 int unget; 1316 int unget;
1314}; 1317};
1315 1318
1319/*
1320 * The parsefile structure pointed to by the global variable parsefile
1321 * contains information about the current file being read.
1322 */
1316struct parsefile { 1323struct parsefile {
1317 struct parsefile *prev; /* preceding file on stack */ 1324 struct parsefile *prev; /* preceding file on stack */
1318 int linno; /* current line */ 1325 int linno; /* current line */
@@ -1986,10 +1993,6 @@ nextopt(const char *optstring)
1986 1993
1987/* ============ Shell variables */ 1994/* ============ Shell variables */
1988 1995
1989/*
1990 * The parsefile structure pointed to by the global variable parsefile
1991 * contains information about the current file being read.
1992 */
1993struct shparam { 1996struct shparam {
1994 int nparam; /* # of positional parameters (without $0) */ 1997 int nparam; /* # of positional parameters (without $0) */
1995#if ENABLE_ASH_GETOPTS 1998#if ENABLE_ASH_GETOPTS
@@ -2183,7 +2186,9 @@ extern struct globals_var *const ash_ptr_to_globals_var;
2183static void FAST_FUNC 2186static void FAST_FUNC
2184getoptsreset(const char *value) 2187getoptsreset(const char *value)
2185{ 2188{
2186 shellparam.optind = number(value) ?: 1; 2189 shellparam.optind = 1;
2190 if (is_number(value))
2191 shellparam.optind = number(value) ?: 1;
2187 shellparam.optoff = -1; 2192 shellparam.optoff = -1;
2188} 2193}
2189#endif 2194#endif
@@ -3751,9 +3756,12 @@ setsignal(int signo)
3751#endif 3756#endif
3752 } 3757 }
3753 } 3758 }
3754//TODO: if !rootshell, we reset SIGQUIT to DFL, 3759 /* if !rootshell, we reset SIGQUIT to DFL,
3755//whereas we have to restore it to what shell got on entry 3760 * whereas we have to restore it to what shell got on entry.
3756//from the parent. See comment above 3761 * This is handled by the fact that if signal was IGNored on entry,
3762 * then cur_act is S_HARD_IGN and we never change its sigaction
3763 * (see code below).
3764 */
3757 3765
3758 if (signo == SIGCHLD) 3766 if (signo == SIGCHLD)
3759 new_act = S_CATCH; 3767 new_act = S_CATCH;
@@ -3777,10 +3785,18 @@ setsignal(int signo)
3777 cur_act = S_IGN; /* don't hard ignore these */ 3785 cur_act = S_IGN; /* don't hard ignore these */
3778 } 3786 }
3779 } 3787 }
3788 if (act.sa_handler == SIG_DFL && new_act == S_DFL) {
3789 /* installing SIG_DFL over SIG_DFL is a no-op */
3790 /* saves one sigaction call in each "sh -c SCRIPT" invocation */
3791 *t = S_DFL;
3792 return;
3793 }
3780 } 3794 }
3781 if (cur_act == S_HARD_IGN || cur_act == new_act) 3795 if (cur_act == S_HARD_IGN || cur_act == new_act)
3782 return; 3796 return;
3783 3797
3798 *t = new_act;
3799
3784 act.sa_handler = SIG_DFL; 3800 act.sa_handler = SIG_DFL;
3785 switch (new_act) { 3801 switch (new_act) {
3786 case S_CATCH: 3802 case S_CATCH:
@@ -3790,16 +3806,13 @@ setsignal(int signo)
3790 act.sa_handler = SIG_IGN; 3806 act.sa_handler = SIG_IGN;
3791 break; 3807 break;
3792 } 3808 }
3793
3794 /* flags and mask matter only if !DFL and !IGN, but we do it 3809 /* flags and mask matter only if !DFL and !IGN, but we do it
3795 * for all cases for more deterministic behavior: 3810 * for all cases for more deterministic behavior:
3796 */ 3811 */
3797 act.sa_flags = 0; 3812 act.sa_flags = 0; //TODO: why not SA_RESTART?
3798 sigfillset(&act.sa_mask); 3813 sigfillset(&act.sa_mask);
3799 3814
3800 sigaction_set(signo, &act); 3815 sigaction_set(signo, &act);
3801
3802 *t = new_act;
3803} 3816}
3804#else 3817#else
3805#define setsignal(s) 3818#define setsignal(s)
@@ -5915,12 +5928,10 @@ static void
5915redirect(union node *redir, int flags) 5928redirect(union node *redir, int flags)
5916{ 5929{
5917 struct redirtab *sv; 5930 struct redirtab *sv;
5918 int sv_pos;
5919 5931
5920 if (!redir) 5932 if (!redir)
5921 return; 5933 return;
5922 5934
5923 sv_pos = 0;
5924 sv = NULL; 5935 sv = NULL;
5925 INT_OFF; 5936 INT_OFF;
5926 if (flags & REDIR_PUSH) 5937 if (flags & REDIR_PUSH)
@@ -8076,8 +8087,9 @@ static int
8076patmatch(char *pattern, const char *string) 8087patmatch(char *pattern, const char *string)
8077{ 8088{
8078 char *p = preglob(pattern, 0); 8089 char *p = preglob(pattern, 0);
8079 //bb_error_msg("fnmatch(pattern:'%s',str:'%s')", p, string); 8090 int r = pmatch(p, string);
8080 return pmatch(p, string); 8091 //bb_error_msg("!fnmatch(pattern:'%s',str:'%s',0):%d", p, string, r);
8092 return r;
8081} 8093}
8082 8094
8083/* 8095/*
@@ -8179,7 +8191,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8179 while (*envp) 8191 while (*envp)
8180 putenv(*envp++); 8192 putenv(*envp++);
8181 popredir(/*drop:*/ 1); 8193 popredir(/*drop:*/ 1);
8182 run_applet_no_and_exit(applet_no, cmd, argv); 8194 run_noexec_applet_and_exit(applet_no, cmd, argv);
8183 } 8195 }
8184 /* re-exec ourselves with the new arguments */ 8196 /* re-exec ourselves with the new arguments */
8185 execve(bb_busybox_exec_path, argv, envp); 8197 execve(bb_busybox_exec_path, argv, envp);
@@ -10250,6 +10262,15 @@ evalcommand(union node *cmd, int flags)
10250 expredir(cmd->ncmd.redirect); 10262 expredir(cmd->ncmd.redirect);
10251 redir_stop = pushredir(cmd->ncmd.redirect); 10263 redir_stop = pushredir(cmd->ncmd.redirect);
10252 preverrout_fd = 2; 10264 preverrout_fd = 2;
10265 if (BASH_XTRACEFD && xflag) {
10266 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed.
10267 * we do not emulate this. We only use its value.
10268 */
10269 const char *xtracefd = lookupvar("BASH_XTRACEFD");
10270 if (xtracefd && is_number(xtracefd))
10271 preverrout_fd = atoi(xtracefd);
10272
10273 }
10253 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); 10274 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
10254 10275
10255 path = vpath.var_text; 10276 path = vpath.var_text;
@@ -10383,12 +10404,30 @@ evalcommand(union node *cmd, int flags)
10383#endif 10404#endif
10384 ) { 10405 ) {
10385 listsetvar(varlist.list, VEXPORT|VSTACK); 10406 listsetvar(varlist.list, VEXPORT|VSTACK);
10386 /* run <applet>_main() */ 10407 /*
10408 * Run <applet>_main().
10409 * Signals (^C) can't interrupt here.
10410 * Otherwise we can mangle stdio or malloc internal state.
10411 * This makes applets which can run for a long time
10412 * and/or wait for user input ineligible for NOFORK:
10413 * for example, "yes" or "rm" (rm -i waits for input).
10414 */
10415 INT_OFF;
10387 status = run_nofork_applet(applet_no, argv); 10416 status = run_nofork_applet(applet_no, argv);
10417 /*
10418 * Try enabling NOFORK for "yes" applet.
10419 * ^C _will_ stop it (write returns EINTR),
10420 * but this causes stdout FILE to be stuck
10421 * and needing clearerr(). What if other applets
10422 * also can get EINTRs? Do we need to switch
10423 * our signals to SA_RESTART?
10424 */
10425 /*clearerr(stdout);*/
10426 INT_ON;
10388 break; 10427 break;
10389 } 10428 }
10390#endif 10429#endif
10391 /* Can we avoid forking off? For example, very last command 10430 /* Can we avoid forking? For example, very last command
10392 * in a script or a subshell does not need forking, 10431 * in a script or a subshell does not need forking,
10393 * we can just exec it. 10432 * we can just exec it.
10394 */ 10433 */
@@ -10674,8 +10713,8 @@ preadfd(void)
10674 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 10713 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
10675 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 10714 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
10676 else { 10715 else {
10677 int timeout = -1;
10678# if ENABLE_ASH_IDLE_TIMEOUT 10716# if ENABLE_ASH_IDLE_TIMEOUT
10717 int timeout = -1;
10679 if (iflag) { 10718 if (iflag) {
10680 const char *tmout_var = lookupvar("TMOUT"); 10719 const char *tmout_var = lookupvar("TMOUT");
10681 if (tmout_var) { 10720 if (tmout_var) {
@@ -10684,12 +10723,13 @@ preadfd(void)
10684 timeout = -1; 10723 timeout = -1;
10685 } 10724 }
10686 } 10725 }
10726 line_input_state->timeout = timeout;
10687# endif 10727# endif
10688# if ENABLE_FEATURE_TAB_COMPLETION 10728# if ENABLE_FEATURE_TAB_COMPLETION
10689 line_input_state->path_lookup = pathval(); 10729 line_input_state->path_lookup = pathval();
10690# endif 10730# endif
10691 reinit_unicode_for_ash(); 10731 reinit_unicode_for_ash();
10692 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout); 10732 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
10693 if (nr == 0) { 10733 if (nr == 0) {
10694 /* ^C pressed, "convert" to SIGINT */ 10734 /* ^C pressed, "convert" to SIGINT */
10695 write(STDOUT_FILENO, "^C", 2); 10735 write(STDOUT_FILENO, "^C", 2);
@@ -11399,6 +11439,7 @@ getopts(char *optstr, char *optvar, char **optfirst)
11399 p = *optnext; 11439 p = *optnext;
11400 if (p == NULL || *p != '-' || *++p == '\0') { 11440 if (p == NULL || *p != '-' || *++p == '\0') {
11401 atend: 11441 atend:
11442 unsetvar("OPTARG");
11402 p = NULL; 11443 p = NULL;
11403 done = 1; 11444 done = 1;
11404 goto out; 11445 goto out;
@@ -11411,7 +11452,11 @@ getopts(char *optstr, char *optvar, char **optfirst)
11411 c = *p++; 11452 c = *p++;
11412 for (q = optstr; *q != c;) { 11453 for (q = optstr; *q != c;) {
11413 if (*q == '\0') { 11454 if (*q == '\0') {
11414 if (optstr[0] == ':') { 11455 /* OPTERR is a bashism */
11456 const char *cp = lookupvar("OPTERR");
11457 if ((cp && LONE_CHAR(cp, '0'))
11458 || (optstr[0] == ':')
11459 ) {
11415 sbuf[0] = c; 11460 sbuf[0] = c;
11416 /*sbuf[1] = '\0'; - already is */ 11461 /*sbuf[1] = '\0'; - already is */
11417 setvar0("OPTARG", sbuf); 11462 setvar0("OPTARG", sbuf);
@@ -11428,7 +11473,11 @@ getopts(char *optstr, char *optvar, char **optfirst)
11428 11473
11429 if (*++q == ':') { 11474 if (*++q == ':') {
11430 if (*p == '\0' && (p = *optnext) == NULL) { 11475 if (*p == '\0' && (p = *optnext) == NULL) {
11431 if (optstr[0] == ':') { 11476 /* OPTERR is a bashism */
11477 const char *cp = lookupvar("OPTERR");
11478 if ((cp && LONE_CHAR(cp, '0'))
11479 || (optstr[0] == ':')
11480 ) {
11432 sbuf[0] = c; 11481 sbuf[0] = c;
11433 /*sbuf[1] = '\0'; - already is */ 11482 /*sbuf[1] = '\0'; - already is */
11434 setvar0("OPTARG", sbuf); 11483 setvar0("OPTARG", sbuf);
@@ -12571,7 +12620,7 @@ parsesub: {
12571 STPUTC(c, out); 12620 STPUTC(c, out);
12572 c = pgetc_eatbnl(); 12621 c = pgetc_eatbnl();
12573 } while (isdigit(c)); 12622 } while (isdigit(c));
12574 } else if (is_special(c)) { 12623 } else {
12575 /* $[{[#]]<specialchar>[}] */ 12624 /* $[{[#]]<specialchar>[}] */
12576 int cc = c; 12625 int cc = c;
12577 12626
@@ -12589,10 +12638,16 @@ parsesub: {
12589 cc = '#'; 12638 cc = '#';
12590 } 12639 }
12591 } 12640 }
12641
12642 if (!is_special(cc)) {
12643 if (subtype == VSLENGTH)
12644 subtype = 0;
12645 goto badsub;
12646 }
12647
12592 USTPUTC(cc, out); 12648 USTPUTC(cc, out);
12593 } else {
12594 goto badsub;
12595 } 12649 }
12650
12596 if (c != '}' && subtype == VSLENGTH) { 12651 if (c != '}' && subtype == VSLENGTH) {
12597 /* ${#VAR didn't end with } */ 12652 /* ${#VAR didn't end with } */
12598 goto badsub; 12653 goto badsub;
@@ -13850,21 +13905,23 @@ static const unsigned char timescmd_str[] ALIGN1 = {
13850static int FAST_FUNC 13905static int FAST_FUNC
13851timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 13906timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13852{ 13907{
13853 unsigned long clk_tck, s, t; 13908 unsigned clk_tck;
13854 const unsigned char *p; 13909 const unsigned char *p;
13855 struct tms buf; 13910 struct tms buf;
13856 13911
13857 clk_tck = bb_clk_tck(); 13912 clk_tck = bb_clk_tck();
13858 times(&buf);
13859 13913
13914 times(&buf);
13860 p = timescmd_str; 13915 p = timescmd_str;
13861 do { 13916 do {
13917 unsigned sec, frac;
13918 unsigned long t;
13862 t = *(clock_t *)(((char *) &buf) + p[1]); 13919 t = *(clock_t *)(((char *) &buf) + p[1]);
13863 s = t / clk_tck; 13920 sec = t / clk_tck;
13864 t = t % clk_tck; 13921 frac = t % clk_tck;
13865 out1fmt("%lum%lu.%03lus%c", 13922 out1fmt("%um%u.%03us%c",
13866 s / 60, s % 60, 13923 sec / 60, sec % 60,
13867 (t * 1000) / clk_tck, 13924 (frac * 1000) / clk_tck,
13868 p[0]); 13925 p[0]);
13869 p += 2; 13926 p += 2;
13870 } while (*p); 13927 } while (*p);
@@ -13903,10 +13960,10 @@ letcmd(int argc UNUSED_PARAM, char **argv)
13903 * -p PROMPT Display PROMPT on stderr (if input is from tty) 13960 * -p PROMPT Display PROMPT on stderr (if input is from tty)
13904 * -t SECONDS Timeout after SECONDS (tty or pipe only) 13961 * -t SECONDS Timeout after SECONDS (tty or pipe only)
13905 * -u FD Read from given FD instead of fd 0 13962 * -u FD Read from given FD instead of fd 0
13963 * -d DELIM End on DELIM char, not newline
13906 * This uses unbuffered input, which may be avoidable in some cases. 13964 * This uses unbuffered input, which may be avoidable in some cases.
13907 * TODO: bash also has: 13965 * TODO: bash also has:
13908 * -a ARRAY Read into array[0],[1],etc 13966 * -a ARRAY Read into array[0],[1],etc
13909 * -d DELIM End on DELIM char, not newline
13910 * -e Use line editing (tty only) 13967 * -e Use line editing (tty only)
13911 */ 13968 */
13912static int FAST_FUNC 13969static int FAST_FUNC
@@ -13916,11 +13973,12 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13916 char *opt_p = NULL; 13973 char *opt_p = NULL;
13917 char *opt_t = NULL; 13974 char *opt_t = NULL;
13918 char *opt_u = NULL; 13975 char *opt_u = NULL;
13976 char *opt_d = NULL; /* optimized out if !BASH */
13919 int read_flags = 0; 13977 int read_flags = 0;
13920 const char *r; 13978 const char *r;
13921 int i; 13979 int i;
13922 13980
13923 while ((i = nextopt("p:u:rt:n:s")) != '\0') { 13981 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') {
13924 switch (i) { 13982 switch (i) {
13925 case 'p': 13983 case 'p':
13926 opt_p = optionarg; 13984 opt_p = optionarg;
@@ -13940,6 +13998,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13940 case 'u': 13998 case 'u':
13941 opt_u = optionarg; 13999 opt_u = optionarg;
13942 break; 14000 break;
14001#if BASH_READ_D
14002 case 'd':
14003 opt_d = optionarg;
14004 break;
14005#endif
13943 default: 14006 default:
13944 break; 14007 break;
13945 } 14008 }
@@ -13957,12 +14020,15 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13957 opt_n, 14020 opt_n,
13958 opt_p, 14021 opt_p,
13959 opt_t, 14022 opt_t,
13960 opt_u 14023 opt_u,
14024 opt_d
13961 ); 14025 );
13962 INT_ON; 14026 INT_ON;
13963 14027
13964 if ((uintptr_t)r == 1 && errno == EINTR) { 14028 if ((uintptr_t)r == 1 && errno == EINTR) {
13965 /* to get SIGCHLD: sleep 1 & read x; echo $x */ 14029 /* To get SIGCHLD: sleep 1 & read x; echo $x
14030 * Correct behavior is to not exit "read"
14031 */
13966 if (pending_sig == 0) 14032 if (pending_sig == 0)
13967 goto again; 14033 goto again;
13968 } 14034 }
@@ -14077,7 +14143,8 @@ exitshell(void)
14077 /* NOTREACHED */ 14143 /* NOTREACHED */
14078} 14144}
14079 14145
14080static void 14146/* Don't inline: conserve stack of caller from having our locals too */
14147static NOINLINE void
14081#if ENABLE_PLATFORM_MINGW32 14148#if ENABLE_PLATFORM_MINGW32
14082init(int xp) 14149init(int xp)
14083#else 14150#else
@@ -14086,8 +14153,9 @@ init(void)
14086{ 14153{
14087 /* we will never free this */ 14154 /* we will never free this */
14088 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 14155 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
14156 basepf.linno = 1;
14089 14157
14090 sigmode[SIGCHLD - 1] = S_DFL; 14158 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14091 setsignal(SIGCHLD); 14159 setsignal(SIGCHLD);
14092 14160
14093 /* bash re-enables SIGHUP which is SIG_IGNed on entry. 14161 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
@@ -14098,7 +14166,6 @@ init(void)
14098 { 14166 {
14099 char **envp; 14167 char **envp;
14100 const char *p; 14168 const char *p;
14101 struct stat st1, st2;
14102 14169
14103 initvar(); 14170 initvar();
14104 14171
@@ -14204,6 +14271,7 @@ init(void)
14204#endif 14271#endif
14205 p = lookupvar("PWD"); 14272 p = lookupvar("PWD");
14206 if (p) { 14273 if (p) {
14274 struct stat st1, st2;
14207 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2) 14275 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
14208 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino 14276 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
14209 ) { 14277 ) {
@@ -15161,7 +15229,7 @@ sticky_free(void *base)
15161 * may be used to endorse or promote products derived from this software 15229 * may be used to endorse or promote products derived from this software
15162 * without specific prior written permission. 15230 * without specific prior written permission.
15163 * 15231 *
15164 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15232 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
15165 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15233 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15166 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15234 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15167 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 15235 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/shell/ash_LINENO.patch b/shell/ash_LINENO.patch
new file mode 100644
index 000000000..a71549d6a
--- /dev/null
+++ b/shell/ash_LINENO.patch
@@ -0,0 +1,498 @@
1This patch is a backport from dash of the combination of:
2 [SHELL] Add preliminary LINENO support
3 [VAR] Fix varinit ordering that broke fc
4 [SHELL] Improve LINENO support
5
6Applies cleanly on top of:
7 commit 9832bbaba966f0e52e183f10cd93fad7f8f643fe
8 Date: Tue Aug 15 15:44:41 2017 +0200
9
10Testsuite needs some tweaks (line numbers in some messages change).
11
12Unfortunately, it is somewhat big:
13
14function old new delta
15parse_command 1581 1658 +77
16calcsize 203 272 +69
17copynode 195 257 +62
18lookupvar 59 108 +49
19evaltree 494 534 +40
20evalfor 152 187 +35
21evalcase 278 313 +35
22evalcommand 1547 1581 +34
23evalsubshell 169 199 +30
24linenovar - 22 +22
25raise_error_syntax 11 29 +18
26evalfun 266 280 +14
27varinit_data 96 108 +12
28cmdtxt 626 631 +5
29lineno - 4 +4
30funcline - 4 +4
31ash_vmsg 144 141 -3
32startlinno 4 - -4
33funcnest 4 - -4
34xxreadtoken 272 259 -13
35readtoken1 2635 2594 -41
36------------------------------------------------------------------------------
37(add/remove: 3/2 grow/shrink: 13/3 up/down: 510/-65) Total: 445 bytes
38 text data bss dec hex filename
39 912030 563 5844 918437 e03a5 busybox_old
40 912473 587 5844 918904 e0578 busybox_unstripped
41
42diff --git a/shell/ash.c b/shell/ash.c
43index 703802f..93a3814 100644
44--- a/shell/ash.c
45+++ b/shell/ash.c
46@@ -312,6 +312,8 @@ struct globals_misc {
47 /* shell level: 0 for the main shell, 1 for its children, and so on */
48 int shlvl;
49 #define rootshell (!shlvl)
50+ int errlinno;
51+
52 char *minusc; /* argument to -c option */
53
54 char *curdir; // = nullstr; /* current working directory */
55@@ -389,6 +391,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc;
56 #define job_warning (G_misc.job_warning)
57 #define rootpid (G_misc.rootpid )
58 #define shlvl (G_misc.shlvl )
59+#define errlinno (G_misc.errlinno )
60 #define minusc (G_misc.minusc )
61 #define curdir (G_misc.curdir )
62 #define physdir (G_misc.physdir )
63@@ -723,6 +726,7 @@ union node;
64
65 struct ncmd {
66 smallint type; /* Nxxxx */
67+ int linno;
68 union node *assign;
69 union node *args;
70 union node *redirect;
71@@ -736,6 +740,7 @@ struct npipe {
72
73 struct nredir {
74 smallint type;
75+ int linno;
76 union node *n;
77 union node *redirect;
78 };
79@@ -755,6 +760,7 @@ struct nif {
80
81 struct nfor {
82 smallint type;
83+ int linno;
84 union node *args;
85 union node *body;
86 char *var;
87@@ -762,6 +768,7 @@ struct nfor {
88
89 struct ncase {
90 smallint type;
91+ int linno;
92 union node *expr;
93 union node *cases;
94 };
95@@ -773,6 +780,13 @@ struct nclist {
96 union node *body;
97 };
98
99+struct ndefun {
100+ smallint type;
101+ int linno;
102+ char *text;
103+ union node *body;
104+};
105+
106 struct narg {
107 smallint type;
108 union node *next;
109@@ -824,6 +838,7 @@ union node {
110 struct nfor nfor;
111 struct ncase ncase;
112 struct nclist nclist;
113+ struct ndefun ndefun;
114 struct narg narg;
115 struct nfile nfile;
116 struct ndup ndup;
117@@ -1253,7 +1268,6 @@ struct parsefile {
118
119 static struct parsefile basepf; /* top level input file */
120 static struct parsefile *g_parsefile = &basepf; /* current input file */
121-static int startlinno; /* line # where last token started */
122 static char *commandname; /* currently executing command */
123
124
125@@ -1267,7 +1281,7 @@ ash_vmsg(const char *msg, va_list ap)
126 if (strcmp(arg0, commandname))
127 fprintf(stderr, "%s: ", commandname);
128 if (!iflag || g_parsefile->pf_fd > 0)
129- fprintf(stderr, "line %d: ", startlinno);
130+ fprintf(stderr, "line %d: ", errlinno);
131 }
132 vfprintf(stderr, msg, ap);
133 newline_and_flush(stderr);
134@@ -1327,6 +1341,7 @@ static void raise_error_syntax(const char *) NORETURN;
135 static void
136 raise_error_syntax(const char *msg)
137 {
138+ errlinno = g_parsefile->linno;
139 ash_msg_and_raise_error("syntax error: %s", msg);
140 /* NOTREACHED */
141 }
142@@ -1993,6 +2008,9 @@ static void changepath(const char *) FAST_FUNC;
143 static void change_random(const char *) FAST_FUNC;
144 #endif
145
146+static int lineno;
147+static char linenovar[sizeof("LINENO=%d") + sizeof(int)*3] = "LINENO=";
148+
149 static const struct {
150 int flags;
151 const char *var_text;
152@@ -2014,6 +2032,7 @@ static const struct {
153 #if ENABLE_ASH_GETOPTS
154 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
155 #endif
156+ { VSTRFIXED|VTEXTFIXED , linenovar , NULL },
157 #if ENABLE_ASH_RANDOM_SUPPORT
158 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
159 #endif
160@@ -2066,12 +2085,14 @@ extern struct globals_var *const ash_ptr_to_globals_var;
161 #define vps4 (&vps2)[1]
162 #if ENABLE_ASH_GETOPTS
163 # define voptind (&vps4)[1]
164+# define vlineno (&voptind)[1]
165 # if ENABLE_ASH_RANDOM_SUPPORT
166-# define vrandom (&voptind)[1]
167+# define vrandom (&vlineno)[1]
168 # endif
169 #else
170+# define vlineno (&vps4)[1]
171 # if ENABLE_ASH_RANDOM_SUPPORT
172-# define vrandom (&vps4)[1]
173+# define vrandom (&vlineno)[1]
174 # endif
175 #endif
176
177@@ -2209,8 +2230,12 @@ lookupvar(const char *name)
178 if (v->flags & VDYNAMIC)
179 v->var_func(NULL);
180 #endif
181- if (!(v->flags & VUNSET))
182+ if (!(v->flags & VUNSET)) {
183+ if (v == &vlineno && v->var_text == linenovar) {
184+ fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
185+ }
186 return var_end(v->var_text);
187+ }
188 }
189 return NULL;
190 }
191@@ -4783,7 +4808,7 @@ cmdtxt(union node *n)
192 p = "; done";
193 goto dodo;
194 case NDEFUN:
195- cmdputs(n->narg.text);
196+ cmdputs(n->ndefun.text);
197 p = "() { ... }";
198 goto dotail2;
199 case NCMD:
200@@ -8551,6 +8576,9 @@ calcsize(int funcblocksize, union node *n)
201 funcblocksize = calcsize(funcblocksize, n->nclist.next);
202 break;
203 case NDEFUN:
204+ funcblocksize = calcsize(funcblocksize, n->ndefun.body);
205+ funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
206+ break;
207 case NARG:
208 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
209 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
210@@ -8626,6 +8654,7 @@ copynode(union node *n)
211 new->ncmd.redirect = copynode(n->ncmd.redirect);
212 new->ncmd.args = copynode(n->ncmd.args);
213 new->ncmd.assign = copynode(n->ncmd.assign);
214+ new->ncmd.linno = n->ncmd.linno;
215 break;
216 case NPIPE:
217 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
218@@ -8636,6 +8665,7 @@ copynode(union node *n)
219 case NSUBSHELL:
220 new->nredir.redirect = copynode(n->nredir.redirect);
221 new->nredir.n = copynode(n->nredir.n);
222+ new->nredir.linno = n->nredir.linno;
223 break;
224 case NAND:
225 case NOR:
226@@ -8654,10 +8684,12 @@ copynode(union node *n)
227 new->nfor.var = nodeckstrdup(n->nfor.var);
228 new->nfor.body = copynode(n->nfor.body);
229 new->nfor.args = copynode(n->nfor.args);
230+ new->nfor.linno = n->nfor.linno;
231 break;
232 case NCASE:
233 new->ncase.cases = copynode(n->ncase.cases);
234 new->ncase.expr = copynode(n->ncase.expr);
235+ new->ncase.linno = n->ncase.linno;
236 break;
237 case NCLIST:
238 new->nclist.body = copynode(n->nclist.body);
239@@ -8665,6 +8697,10 @@ copynode(union node *n)
240 new->nclist.next = copynode(n->nclist.next);
241 break;
242 case NDEFUN:
243+ new->ndefun.body = copynode(n->ndefun.body);
244+ new->ndefun.text = nodeckstrdup(n->ndefun.text);
245+ new->ndefun.linno = n->ndefun.linno;
246+ break;
247 case NARG:
248 new->narg.backquote = copynodelist(n->narg.backquote);
249 new->narg.text = nodeckstrdup(n->narg.text);
250@@ -8733,7 +8769,7 @@ defun(union node *func)
251 INT_OFF;
252 entry.cmdtype = CMDFUNCTION;
253 entry.u.func = copyfunc(func);
254- addcmdentry(func->narg.text, &entry);
255+ addcmdentry(func->ndefun.text, &entry);
256 INT_ON;
257 }
258
259@@ -8743,8 +8779,8 @@ defun(union node *func)
260 #define SKIPFUNC (1 << 2)
261 static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
262 static int skipcount; /* number of levels to skip */
263-static int funcnest; /* depth of function calls */
264 static int loopnest; /* current loop nesting level */
265+static int funcline; /* starting line number of current function, or 0 if not in a function */
266
267 /* Forward decl way out to parsing code - dotrap needs it */
268 static int evalstring(char *s, int flags);
269@@ -8839,6 +8875,9 @@ evaltree(union node *n, int flags)
270 status = !evaltree(n->nnot.com, EV_TESTED);
271 goto setstatus;
272 case NREDIR:
273+ errlinno = lineno = n->nredir.linno;
274+ if (funcline)
275+ lineno -= funcline - 1;
276 expredir(n->nredir.redirect);
277 pushredir(n->nredir.redirect);
278 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
279@@ -8993,6 +9032,10 @@ evalfor(union node *n, int flags)
280 struct stackmark smark;
281 int status = 0;
282
283+ errlinno = lineno = n->ncase.linno;
284+ if (funcline)
285+ lineno -= funcline - 1;
286+
287 setstackmark(&smark);
288 arglist.list = NULL;
289 arglist.lastp = &arglist.list;
290@@ -9024,6 +9067,10 @@ evalcase(union node *n, int flags)
291 struct stackmark smark;
292 int status = 0;
293
294+ errlinno = lineno = n->ncase.linno;
295+ if (funcline)
296+ lineno -= funcline - 1;
297+
298 setstackmark(&smark);
299 arglist.list = NULL;
300 arglist.lastp = &arglist.list;
301@@ -9058,6 +9105,10 @@ evalsubshell(union node *n, int flags)
302 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
303 int status;
304
305+ errlinno = lineno = n->nredir.linno;
306+ if (funcline)
307+ lineno -= funcline - 1;
308+
309 expredir(n->nredir.redirect);
310 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
311 goto nofork;
312@@ -9365,8 +9416,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
313 struct jmploc *volatile savehandler;
314 struct jmploc jmploc;
315 int e;
316+ int savefuncline;
317
318 saveparam = shellparam;
319+ savefuncline = funcline;
320 savehandler = exception_handler;
321 e = setjmp(jmploc.loc);
322 if (e) {
323@@ -9376,7 +9429,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
324 exception_handler = &jmploc;
325 shellparam.malloced = 0;
326 func->count++;
327- funcnest++;
328+ funcline = func->n.ndefun.linno;
329 INT_ON;
330 shellparam.nparam = argc - 1;
331 shellparam.p = argv + 1;
332@@ -9385,11 +9438,11 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
333 shellparam.optoff = -1;
334 #endif
335 pushlocalvars();
336- evaltree(func->n.narg.next, flags & EV_TESTED);
337+ evaltree(func->n.ndefun.body, flags & EV_TESTED);
338 poplocalvars(0);
339 funcdone:
340 INT_OFF;
341- funcnest--;
342+ funcline = savefuncline;
343 freefunc(func);
344 freeparam(&shellparam);
345 shellparam = saveparam;
346@@ -9753,6 +9806,10 @@ evalcommand(union node *cmd, int flags)
347 char **nargv;
348 smallint cmd_is_exec;
349
350+ errlinno = lineno = cmd->ncmd.linno;
351+ if (funcline)
352+ lineno -= funcline - 1;
353+
354 /* First expand the arguments. */
355 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
356 setstackmark(&smark);
357@@ -9798,7 +9855,7 @@ evalcommand(union node *cmd, int flags)
358 *nargv = NULL;
359
360 lastarg = NULL;
361- if (iflag && funcnest == 0 && argc > 0)
362+ if (iflag && funcline == 0 && argc > 0)
363 lastarg = nargv[-1];
364
365 expredir(cmd->ncmd.redirect);
366@@ -11317,6 +11374,7 @@ simplecmd(void)
367 union node *vars, **vpp;
368 union node **rpp, *redir;
369 int savecheckkwd;
370+ int savelinno;
371 #if BASH_TEST2
372 smallint double_brackets_flag = 0;
373 #endif
374@@ -11330,6 +11388,7 @@ simplecmd(void)
375 rpp = &redir;
376
377 savecheckkwd = CHKALIAS;
378+ savelinno = g_parsefile->linno;
379 for (;;) {
380 int t;
381 checkkwd = savecheckkwd;
382@@ -11419,7 +11478,9 @@ simplecmd(void)
383 }
384 n->type = NDEFUN;
385 checkkwd = CHKNL | CHKKWD | CHKALIAS;
386- n->narg.next = parse_command();
387+ n->ndefun.text = n->narg.text;
388+ n->ndefun.linno = g_parsefile->linno;
389+ n->ndefun.body = parse_command();
390 return n;
391 }
392 IF_BASH_FUNCTION(function_flag = 0;)
393@@ -11435,6 +11496,7 @@ simplecmd(void)
394 *rpp = NULL;
395 n = stzalloc(sizeof(struct ncmd));
396 n->type = NCMD;
397+ n->ncmd.linno = savelinno;
398 n->ncmd.args = args;
399 n->ncmd.assign = vars;
400 n->ncmd.redirect = redir;
401@@ -11450,10 +11512,13 @@ parse_command(void)
402 union node *redir, **rpp;
403 union node **rpp2;
404 int t;
405+ int savelinno;
406
407 redir = NULL;
408 rpp2 = &redir;
409
410+ savelinno = g_parsefile->linno;
411+
412 switch (readtoken()) {
413 default:
414 raise_error_unexpected_syntax(-1);
415@@ -11504,6 +11569,7 @@ parse_command(void)
416 raise_error_syntax("bad for loop variable");
417 n1 = stzalloc(sizeof(struct nfor));
418 n1->type = NFOR;
419+ n1->nfor.linno = savelinno;
420 n1->nfor.var = wordtext;
421 checkkwd = CHKNL | CHKKWD | CHKALIAS;
422 if (readtoken() == TIN) {
423@@ -11544,6 +11610,7 @@ parse_command(void)
424 case TCASE:
425 n1 = stzalloc(sizeof(struct ncase));
426 n1->type = NCASE;
427+ n1->ncase.linno = savelinno;
428 if (readtoken() != TWORD)
429 raise_error_unexpected_syntax(TWORD);
430 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
431@@ -11595,6 +11662,7 @@ parse_command(void)
432 case TLP:
433 n1 = stzalloc(sizeof(struct nredir));
434 n1->type = NSUBSHELL;
435+ n1->nredir.linno = savelinno;
436 n1->nredir.n = list(0);
437 /*n1->nredir.redirect = NULL; - stzalloc did it */
438 t = TRP;
439@@ -11628,6 +11696,7 @@ parse_command(void)
440 if (n1->type != NSUBSHELL) {
441 n2 = stzalloc(sizeof(struct nredir));
442 n2->type = NREDIR;
443+ n2->nredir.linno = savelinno;
444 n2->nredir.n = n1;
445 n1 = n2;
446 }
447@@ -11726,10 +11795,8 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
448 IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */
449 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
450 int dqvarnest; /* levels of variables expansion within double quotes */
451-
452 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
453
454- startlinno = g_parsefile->linno;
455 bqlist = NULL;
456 quotef = 0;
457 IF_FEATURE_SH_MATH(prevsyntax = 0;)
458@@ -11906,7 +11973,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
459 if (syntax != BASESYNTAX && eofmark == NULL)
460 raise_error_syntax("unterminated quoted string");
461 if (varnest != 0) {
462- startlinno = g_parsefile->linno;
463 /* { */
464 raise_error_syntax("missing '}'");
465 }
466@@ -12298,7 +12364,6 @@ parsebackq: {
467
468 case PEOF:
469 IF_ASH_ALIAS(case PEOA:)
470- startlinno = g_parsefile->linno;
471 raise_error_syntax("EOF in backquote substitution");
472
473 case '\n':
474@@ -12380,8 +12445,6 @@ parsearith: {
475 * quoted.
476 * If the token is TREDIR, then we set redirnode to a structure containing
477 * the redirection.
478- * In all cases, the variable startlinno is set to the number of the line
479- * on which the token starts.
480 *
481 * [Change comment: here documents and internal procedures]
482 * [Readtoken shouldn't have any arguments. Perhaps we should make the
483@@ -12419,7 +12482,6 @@ xxreadtoken(void)
484 return lasttoken;
485 }
486 setprompt_if(needprompt, 2);
487- startlinno = g_parsefile->linno;
488 for (;;) { /* until token or start of word found */
489 c = pgetc();
490 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
491@@ -12480,7 +12542,6 @@ xxreadtoken(void)
492 return lasttoken;
493 }
494 setprompt_if(needprompt, 2);
495- startlinno = g_parsefile->linno;
496 for (;;) { /* until token or start of word found */
497 c = pgetc();
498 switch (c) {
diff --git a/shell/ash_test/ash-getopts/getopt_optarg.right b/shell/ash_test/ash-getopts/getopt_optarg.right
new file mode 100644
index 000000000..dff28de57
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_optarg.right
@@ -0,0 +1,18 @@
1*** no OPTIND, optstring:'w:et' args:-q -w e -r -t -y
2Illegal option -q
3var:'?' OPTIND:2 OPTARG:''
4var:'w' OPTIND:4 OPTARG:'e'
5Illegal option -r
6var:'?' OPTIND:5 OPTARG:''
7var:'t' OPTIND:6 OPTARG:''
8Illegal option -y
9var:'?' OPTIND:7 OPTARG:''
10exited: var:'?' OPTIND:7 OPTARG:''
11*** OPTIND=0, optstring:'w:et' args:-w 1 -w2 -w -e -e -t -t
12var:'w' OPTIND:3 OPTARG:'1'
13var:'w' OPTIND:4 OPTARG:'2'
14var:'w' OPTIND:6 OPTARG:'-e'
15var:'e' OPTIND:7 OPTARG:''
16var:'t' OPTIND:8 OPTARG:''
17var:'t' OPTIND:9 OPTARG:''
18exited: var:'?' OPTIND:9 OPTARG:''
diff --git a/shell/ash_test/ash-getopts/getopt_optarg.tests b/shell/ash_test/ash-getopts/getopt_optarg.tests
new file mode 100755
index 000000000..33682e868
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_optarg.tests
@@ -0,0 +1,18 @@
1set -- -q -w e -r -t -y
2echo "*** no OPTIND, optstring:'w:et' args:$*"
3var=QWERTY
4OPTARG=ASDFGH
5while getopts "w:et" var; do
6 echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
7 OPTARG=ASDFGH
8done
9echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
10
11set -- -w 1 -w2 -w -e -e -t -t
12echo "*** OPTIND=0, optstring:'w:et' args:$*"
13OPTIND=0
14while getopts "w:et" var; do
15 echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
16 OPTARG=ASDFGH
17done
18echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
diff --git a/shell/ash_test/ash-getopts/getopt_positional.right b/shell/ash_test/ash-getopts/getopt_positional.right
new file mode 100644
index 000000000..37d0ec845
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_positional.right
@@ -0,0 +1,6 @@
1*** no OPTIND, optstring:'we' args:-q -w -e r -t -y
2Illegal option -q
3var:'?' OPTIND:2
4var:'w' OPTIND:3
5var:'e' OPTIND:4
6exited: var:'?' OPTIND:4
diff --git a/shell/ash_test/ash-getopts/getopt_positional.tests b/shell/ash_test/ash-getopts/getopt_positional.tests
new file mode 100755
index 000000000..a5404a2a0
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_positional.tests
@@ -0,0 +1,8 @@
1set -- -q -w -e r -t -y
2echo "*** no OPTIND, optstring:'we' args:$*"
3var=QWERTY
4while getopts "we" var; do
5 echo "var:'$var' OPTIND:$OPTIND"
6done
7# unfortunately, "rc:0" is shown since while's overall exitcode is "success"
8echo "exited: var:'$var' OPTIND:$OPTIND"
diff --git a/shell/ash_test/ash-getopts/getopt_silent.right b/shell/ash_test/ash-getopts/getopt_silent.right
new file mode 100644
index 000000000..03d4eb149
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_silent.right
@@ -0,0 +1,6 @@
1*** optstring:':ac' args:-a -b -c
21 rc:0 var:'a' OPTIND:2 OPTARG:''
32 rc:0 var:'?' OPTIND:3 OPTARG:'b'
43 rc:0 var:'c' OPTIND:4 OPTARG:''
54 rc:1 var:'?' OPTIND:4 OPTARG:''
65 rc:1 var:'?' OPTIND:4 OPTARG:''
diff --git a/shell/ash_test/ash-getopts/getopt_silent.tests b/shell/ash_test/ash-getopts/getopt_silent.tests
new file mode 100755
index 000000000..097d7ba85
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_silent.tests
@@ -0,0 +1,23 @@
1# Open Group Base Specifications Issue 7:
2# """
3# If an unknown option is met, VAR shall be set to "?". In this case,
4# if the first character in optstring is ":", OPTARG shall be set
5# to the option character found, but no output shall be written
6# to standard error; otherwise, the shell variable OPTARG shall be
7# unset and a diagnostic message shall be written to standard error."
8# ...
9# If an option-argument is missing:
10# If the first character of optstring is ":", VAR shall be set to ":"
11# and OPTARG shall be set to the option character found.
12# """
13
14echo "*** optstring:':ac' args:-a -b -c"
15getopts ":ac" var -a -b -c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
16getopts ":ac" var -a -b -c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
17getopts ":ac" var -a -b -c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
18getopts ":ac" var -a -b -c; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
19# Previous line should result in "rc:1", which is normally treated
20# in getopts loops as exit condition.
21# Nevertheless, let's verify that calling it yet another time doesn't do
22# anything weird:
23getopts ":ac" var -a -b -c; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
diff --git a/shell/ash_test/ash-getopts/getopt_simple.right b/shell/ash_test/ash-getopts/getopt_simple.right
new file mode 100644
index 000000000..07e3c57f5
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_simple.right
@@ -0,0 +1,34 @@
1*** no OPTIND, optstring:'ab' args:-a -b c
2var:'a' OPTIND:2
3var:'b' OPTIND:3
4exited: rc:0 var:'?' OPTIND:3
5*** OPTIND=1, optstring:'ab' args:-a -b c
6var:'a' OPTIND:2
7var:'b' OPTIND:3
8exited: rc:0 var:'?' OPTIND:3
9*** OPTIND=0, optstring:'ab' args:-a -b c
10var:'a' OPTIND:2
11var:'b' OPTIND:3
12exited: rc:0 var:'?' OPTIND:3
13*** unset OPTIND, optstring:'ab' args:-a -b c
14var:'a' OPTIND:2
15var:'b' OPTIND:3
16exited: rc:0 var:'?' OPTIND:3
17*** optstring:'ab' args:-a -b c
181 rc:0 var:'a' OPTIND:2
192 rc:0 var:'b' OPTIND:3
203 rc:1 var:'?' OPTIND:3
21*** unset OPTIND, optstring:'ab' args:-a c -c -b d
22var:'a' OPTIND:2
23exited: rc:0 var:'?' OPTIND:2
24*** unset OPTIND, optstring:'ab' args:-a -c -b d
25var:'a' OPTIND:2
26Illegal option -c
27var:'?' OPTIND:3
28var:'b' OPTIND:4
29exited: rc:0 var:'?' OPTIND:4
30*** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d
31var:'a' OPTIND:2
32var:'?' OPTIND:3
33var:'b' OPTIND:4
34exited: rc:0 var:'?' OPTIND:4
diff --git a/shell/ash_test/ash-getopts/getopt_simple.tests b/shell/ash_test/ash-getopts/getopt_simple.tests
new file mode 100755
index 000000000..8615ae366
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_simple.tests
@@ -0,0 +1,75 @@
1# Simple usage cases for getopts.
2#
3# OPTIND is either not touched at all (first loop with getopts,
4# relying on shell startup init), or getopts state is reset
5# before new loop with "unset OPTIND", "OPTIND=1" or "OPTIND=0".
6#
7# Each option is a separate argument (no "-abc"). This conceptually
8# needs only $OPTIND to hold getopts state.
9#
10# We check that loop does not stop on unknown option (sets "?"),
11# stops on _first_ non-option argument.
12
13echo "*** no OPTIND, optstring:'ab' args:-a -b c"
14var=QWERTY
15while getopts "ab" var -a -b c; do
16 echo "var:'$var' OPTIND:$OPTIND"
17done
18# unfortunately, "rc:0" is shown since while's overall exitcode is "success"
19echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
20
21# Resetting behavior =1
22echo "*** OPTIND=1, optstring:'ab' args:-a -b c"
23OPTIND=1
24while getopts "ab" var -a -b c; do
25 echo "var:'$var' OPTIND:$OPTIND"
26done
27echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
28
29# Resetting behavior =0
30echo "*** OPTIND=0, optstring:'ab' args:-a -b c"
31OPTIND=0
32while getopts "ab" var -a -b c; do
33 echo "var:'$var' OPTIND:$OPTIND"
34done
35echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
36
37# Resetting behavior "unset"
38echo "*** unset OPTIND, optstring:'ab' args:-a -b c"
39unset OPTIND
40while getopts "ab" var -a -b c; do
41 echo "var:'$var' OPTIND:$OPTIND"
42done
43echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
44
45# What is the final exitcode?
46echo "*** optstring:'ab' args:-a -b c"
47unset OPTIND
48getopts "ab" var -a -b c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND"
49getopts "ab" var -a -b c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND"
50getopts "ab" var -a -b c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND"
51
52# Where would it stop? c or -c?
53echo "*** unset OPTIND, optstring:'ab' args:-a c -c -b d"
54unset OPTIND
55while getopts "ab" var -a c -c -b d; do
56 echo "var:'$var' OPTIND:$OPTIND"
57done
58echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
59
60# What happens on unknown option?
61echo "*** unset OPTIND, optstring:'ab' args:-a -c -b d"
62unset OPTIND
63while getopts "ab" var -a -c -b d; do
64 echo "var:'$var' OPTIND:$OPTIND"
65done
66echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
67
68# ORTERR=0 suppresses error message?
69echo "*** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d"
70unset OPTIND
71OPTERR=0
72while getopts "ab" var -a -c -b d; do
73 echo "var:'$var' OPTIND:$OPTIND"
74done
75echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
diff --git a/shell/ash_test/ash-getopts/getopt_test_libc_bug.right b/shell/ash_test/ash-getopts/getopt_test_libc_bug.right
new file mode 100644
index 000000000..f6ad4602d
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_test_libc_bug.right
@@ -0,0 +1,26 @@
1*** optstring:'ac' args:-a -b -c -d e
21 rc:0 var:'a' OPTIND:2 OPTARG:''
3Illegal option -b
42 rc:0 var:'?' OPTIND:3 OPTARG:''
53 rc:0 var:'c' OPTIND:4 OPTARG:''
6Illegal option -d
74 rc:0 var:'?' OPTIND:5 OPTARG:''
85 rc:1 var:'?' OPTIND:5 OPTARG:''
9
10*** optstring:'ac' args:-a -b -c -d e
111 rc:0 var:'a' OPTIND:2 OPTARG:''
12Illegal option -b
132 rc:0 var:'?' OPTIND:3 OPTARG:''
143 rc:0 var:'c' OPTIND:4 OPTARG:''
15Illegal option -d
164 rc:0 var:'?' OPTIND:5 OPTARG:''
175 rc:1 var:'?' OPTIND:5 OPTARG:''
18
19*** optstring:'ac' args:-a -b -c -d e
201 rc:0 var:'a' OPTIND:2 OPTARG:''
21Illegal option -b
222 rc:0 var:'?' OPTIND:3 OPTARG:''
233 rc:0 var:'c' OPTIND:4 OPTARG:''
24Illegal option -d
254 rc:0 var:'?' OPTIND:5 OPTARG:''
265 rc:1 var:'?' OPTIND:5 OPTARG:''
diff --git a/shell/ash_test/ash-getopts/getopt_test_libc_bug.tests b/shell/ash_test/ash-getopts/getopt_test_libc_bug.tests
new file mode 100755
index 000000000..fcaac81a2
--- /dev/null
+++ b/shell/ash_test/ash-getopts/getopt_test_libc_bug.tests
@@ -0,0 +1,38 @@
1# This test can fail with libc with buggy getopt() implementation.
2# If getopt() wants to parse multi-option args (-abc),
3# it needs to remember a position within current arg.
4#
5# If this position is kept as a POINTER, not an offset,
6# and if argv[] ADDRESSES (not contents!) change, it blows up.
7
8echo "*** optstring:'ac' args:-a -b -c -d e"
9getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
10getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
11getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
12getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
13getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
14
15# Above: args are (usually) in the same locations in memory.
16# Below: variable allocations change the location.
17
18echo
19echo "*** optstring:'ac' args:-a -b -c -d e"
20unset OPTIND
21OPTARG=QWERTY; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
22NEWVAR=NEWVAL; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
23VAR111=NEWVAL; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
24VAR222=NEWVAL; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
25VAR333=NEWVAL; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
26
27# Slightly different attempts to force reallocations
28
29echo
30echo "*** optstring:'ac' args:-a -b -c -d e"
31unset OPTIND
32export OPTARG; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
33export NEWVAR; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
34export VAR111; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
35export VAR222; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
36export VAR333; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
37
38# All copies of code above should generate identical output
diff --git a/shell/ash_test/ash-parsing/groups_and_keywords2.right b/shell/ash_test/ash-parsing/groups_and_keywords2.right
new file mode 100644
index 000000000..3fcbeb662
--- /dev/null
+++ b/shell/ash_test/ash-parsing/groups_and_keywords2.right
@@ -0,0 +1,3 @@
1./groups_and_keywords2.tests: eval: line 1: syntax error: unexpected ")"
2Fail:2
3./groups_and_keywords2.tests: line 8: syntax error: unexpected ")"
diff --git a/shell/ash_test/ash-parsing/groups_and_keywords2.tests b/shell/ash_test/ash-parsing/groups_and_keywords2.tests
new file mode 100755
index 000000000..ab33b909f
--- /dev/null
+++ b/shell/ash_test/ash-parsing/groups_and_keywords2.tests
@@ -0,0 +1,9 @@
1# This is an error
2(eval 'if() { echo; }')
3echo Fail:$?
4# ^^^^^^ bash prints 1, but interactively it sets $? = 2
5# we print 2
6
7# This is an error, and it aborts in script
8if() { echo; }
9echo Not reached
diff --git a/shell/ash_test/ash-vars/param_expand_assign.right b/shell/ash_test/ash-vars/param_expand_assign.right
index 9b07d8cd4..6e9ea1379 100644
--- a/shell/ash_test/ash-vars/param_expand_assign.right
+++ b/shell/ash_test/ash-vars/param_expand_assign.right
@@ -1,6 +1,6 @@
1SHELL: line 1: syntax error: bad substitution 1SHELL: line 1: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution 2SHELL: line 1: syntax error: bad substitution
30 3SHELL: line 1: syntax error: bad substitution
40 40
5SHELL: line 1: 1: bad variable name 5SHELL: line 1: 1: bad variable name
6SHELL: line 1: 1: bad variable name 6SHELL: line 1: 1: bad variable name
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c
index c0c5e197c..c86308d3b 100644
--- a/shell/ash_test/printenv.c
+++ b/shell/ash_test/printenv.c
@@ -56,7 +56,7 @@ main (argc, argv)
56 if (**argv == **envp && strncmp (*envp, *argv, len) == 0) 56 if (**argv == **envp && strncmp (*envp, *argv, len) == 0)
57 { 57 {
58 eval = *envp + len; 58 eval = *envp + len;
59 /* If the environment variable doesn't have an `=', ignore it. */ 59 /* If the environment variable doesn't have an '=', ignore it. */
60 if (*eval == '=') 60 if (*eval == '=')
61 { 61 {
62 puts (eval + 1); 62 puts (eval + 1);
diff --git a/shell/cttyhack.c b/shell/cttyhack.c
index 9004b4763..849fe9e48 100644
--- a/shell/cttyhack.c
+++ b/shell/cttyhack.c
@@ -6,7 +6,7 @@
6 */ 6 */
7#include "libbb.h" 7#include "libbb.h"
8 8
9//applet:IF_CTTYHACK(APPLET(cttyhack, BB_DIR_BIN, BB_SUID_DROP)) 9//applet:IF_CTTYHACK(APPLET_NOEXEC(cttyhack, cttyhack, BB_DIR_BIN, BB_SUID_DROP, cttyhack))
10 10
11//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o 11//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o
12 12
diff --git a/shell/hush.c b/shell/hush.c
index 9f946d82f..cdc3a8618 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -48,7 +48,7 @@
48 * tilde expansion 48 * tilde expansion
49 * aliases 49 * aliases
50 * builtins mandated by standards we don't support: 50 * builtins mandated by standards we don't support:
51 * [un]alias, command, fc, getopts, times: 51 * [un]alias, command, fc:
52 * command -v CMD: print "/path/to/CMD" 52 * command -v CMD: print "/path/to/CMD"
53 * prints "CMD" for builtins 53 * prints "CMD" for builtins
54 * prints "alias ALIAS='EXPANSION'" for aliases 54 * prints "alias ALIAS='EXPANSION'" for aliases
@@ -58,8 +58,6 @@
58 * (can use this to override standalone shell as well) 58 * (can use this to override standalone shell as well)
59 * -p: use default $PATH 59 * -p: use default $PATH
60 * command BLTIN: disables special-ness (e.g. errors do not abort) 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 61 * fc -l[nr] [BEG] [END]: list range of commands in history
64 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands 62 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands
65 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP 63 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP
@@ -265,6 +263,11 @@
265//config: default y 263//config: default y
266//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 264//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
267//config: 265//config:
266//config:config HUSH_TIMES
267//config: bool "times builtin"
268//config: default y
269//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
270//config:
268//config:config HUSH_READ 271//config:config HUSH_READ
269//config: bool "read builtin" 272//config: bool "read builtin"
270//config: default y 273//config: default y
@@ -290,6 +293,11 @@
290//config: default y 293//config: default y
291//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 294//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
292//config: 295//config:
296//config:config HUSH_GETOPTS
297//config: bool "getopts builtin"
298//config: default y
299//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
300//config:
293//config:config HUSH_MEMLEAK 301//config:config HUSH_MEMLEAK
294//config: bool "memleak builtin (debugging)" 302//config: bool "memleak builtin (debugging)"
295//config: default n 303//config: default n
@@ -325,6 +333,7 @@
325#if ENABLE_HUSH_CASE 333#if ENABLE_HUSH_CASE
326# include <fnmatch.h> 334# include <fnmatch.h>
327#endif 335#endif
336#include <sys/times.h>
328#include <sys/utsname.h> /* for setting $HOSTNAME */ 337#include <sys/utsname.h> /* for setting $HOSTNAME */
329 338
330#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ 339#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
@@ -352,6 +361,7 @@
352#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT 361#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
353#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT 362#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
354#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) 363#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
364#define BASH_READ_D ENABLE_HUSH_BASH_COMPAT
355 365
356 366
357/* Build knobs */ 367/* Build knobs */
@@ -977,6 +987,9 @@ static int builtin_readonly(char **argv) FAST_FUNC;
977static int builtin_fg_bg(char **argv) FAST_FUNC; 987static int builtin_fg_bg(char **argv) FAST_FUNC;
978static int builtin_jobs(char **argv) FAST_FUNC; 988static int builtin_jobs(char **argv) FAST_FUNC;
979#endif 989#endif
990#if ENABLE_HUSH_GETOPTS
991static int builtin_getopts(char **argv) FAST_FUNC;
992#endif
980#if ENABLE_HUSH_HELP 993#if ENABLE_HUSH_HELP
981static int builtin_help(char **argv) FAST_FUNC; 994static int builtin_help(char **argv) FAST_FUNC;
982#endif 995#endif
@@ -1010,6 +1023,9 @@ static int builtin_trap(char **argv) FAST_FUNC;
1010#if ENABLE_HUSH_TYPE 1023#if ENABLE_HUSH_TYPE
1011static int builtin_type(char **argv) FAST_FUNC; 1024static int builtin_type(char **argv) FAST_FUNC;
1012#endif 1025#endif
1026#if ENABLE_HUSH_TIMES
1027static int builtin_times(char **argv) FAST_FUNC;
1028#endif
1013static int builtin_true(char **argv) FAST_FUNC; 1029static int builtin_true(char **argv) FAST_FUNC;
1014#if ENABLE_HUSH_UMASK 1030#if ENABLE_HUSH_UMASK
1015static int builtin_umask(char **argv) FAST_FUNC; 1031static int builtin_umask(char **argv) FAST_FUNC;
@@ -1070,6 +1086,9 @@ static const struct built_in_command bltins1[] = {
1070#if ENABLE_HUSH_JOB 1086#if ENABLE_HUSH_JOB
1071 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"), 1087 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"),
1072#endif 1088#endif
1089#if ENABLE_HUSH_GETOPTS
1090 BLTIN("getopts" , builtin_getopts , NULL),
1091#endif
1073#if ENABLE_HUSH_HELP 1092#if ENABLE_HUSH_HELP
1074 BLTIN("help" , builtin_help , NULL), 1093 BLTIN("help" , builtin_help , NULL),
1075#endif 1094#endif
@@ -1104,6 +1123,9 @@ static const struct built_in_command bltins1[] = {
1104#if BASH_SOURCE 1123#if BASH_SOURCE
1105 BLTIN("source" , builtin_source , NULL), 1124 BLTIN("source" , builtin_source , NULL),
1106#endif 1125#endif
1126#if ENABLE_HUSH_TIMES
1127 BLTIN("times" , builtin_times , NULL),
1128#endif
1107#if ENABLE_HUSH_TRAP 1129#if ENABLE_HUSH_TRAP
1108 BLTIN("trap" , builtin_trap , "Trap signals"), 1130 BLTIN("trap" , builtin_trap , "Trap signals"),
1109#endif 1131#endif
@@ -1272,7 +1294,7 @@ static void xxfree(void *ptr)
1272 * HUSH_DEBUG >= 2 prints line number in this file where it was detected. 1294 * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
1273 */ 1295 */
1274#if HUSH_DEBUG < 2 1296#if HUSH_DEBUG < 2
1275# define die_if_script(lineno, ...) die_if_script(__VA_ARGS__) 1297# define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__)
1276# define syntax_error(lineno, msg) syntax_error(msg) 1298# define syntax_error(lineno, msg) syntax_error(msg)
1277# define syntax_error_at(lineno, msg) syntax_error_at(msg) 1299# define syntax_error_at(lineno, msg) syntax_error_at(msg)
1278# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) 1300# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
@@ -1280,7 +1302,16 @@ static void xxfree(void *ptr)
1280# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) 1302# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
1281#endif 1303#endif
1282 1304
1283static void die_if_script(unsigned lineno, const char *fmt, ...) 1305static void die_if_script(void)
1306{
1307 if (!G_interactive_fd) {
1308 if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */
1309 xfunc_error_retval = G.last_exitcode;
1310 xfunc_die();
1311 }
1312}
1313
1314static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
1284{ 1315{
1285 va_list p; 1316 va_list p;
1286 1317
@@ -1290,8 +1321,7 @@ static void die_if_script(unsigned lineno, const char *fmt, ...)
1290 va_start(p, fmt); 1321 va_start(p, fmt);
1291 bb_verror_msg(fmt, p, NULL); 1322 bb_verror_msg(fmt, p, NULL);
1292 va_end(p); 1323 va_end(p);
1293 if (!G_interactive_fd) 1324 die_if_script();
1294 xfunc_die();
1295} 1325}
1296 1326
1297static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) 1327static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
@@ -1300,16 +1330,20 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
1300 bb_error_msg("syntax error: %s", msg); 1330 bb_error_msg("syntax error: %s", msg);
1301 else 1331 else
1302 bb_error_msg("syntax error"); 1332 bb_error_msg("syntax error");
1333 die_if_script();
1303} 1334}
1304 1335
1305static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) 1336static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
1306{ 1337{
1307 bb_error_msg("syntax error at '%s'", msg); 1338 bb_error_msg("syntax error at '%s'", msg);
1339 die_if_script();
1308} 1340}
1309 1341
1310static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) 1342static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
1311{ 1343{
1312 bb_error_msg("syntax error: unterminated %s", s); 1344 bb_error_msg("syntax error: unterminated %s", s);
1345//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
1346// die_if_script();
1313} 1347}
1314 1348
1315static void syntax_error_unterm_ch(unsigned lineno, char ch) 1349static void syntax_error_unterm_ch(unsigned lineno, char ch)
@@ -1327,17 +1361,18 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1327 bb_error_msg("hush.c:%u", lineno); 1361 bb_error_msg("hush.c:%u", lineno);
1328#endif 1362#endif
1329 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); 1363 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
1364 die_if_script();
1330} 1365}
1331 1366
1332#if HUSH_DEBUG < 2 1367#if HUSH_DEBUG < 2
1333# undef die_if_script 1368# undef msg_and_die_if_script
1334# undef syntax_error 1369# undef syntax_error
1335# undef syntax_error_at 1370# undef syntax_error_at
1336# undef syntax_error_unterm_ch 1371# undef syntax_error_unterm_ch
1337# undef syntax_error_unterm_str 1372# undef syntax_error_unterm_str
1338# undef syntax_error_unexpected_ch 1373# undef syntax_error_unexpected_ch
1339#else 1374#else
1340# define die_if_script(...) die_if_script(__LINE__, __VA_ARGS__) 1375# define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__)
1341# define syntax_error(msg) syntax_error(__LINE__, msg) 1376# define syntax_error(msg) syntax_error(__LINE__, msg)
1342# define syntax_error_at(msg) syntax_error_at(__LINE__, msg) 1377# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
1343# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) 1378# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
@@ -1800,7 +1835,7 @@ static void restore_ttypgrp_and__exit(void)
1800 * echo END_OF_SCRIPT 1835 * echo END_OF_SCRIPT
1801 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". 1836 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
1802 * This makes "echo END_OF_SCRIPT" executed twice. 1837 * This makes "echo END_OF_SCRIPT" executed twice.
1803 * Similar problems can be seen with die_if_script() -> xfunc_die() 1838 * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die()
1804 * and in `cmd` handling. 1839 * and in `cmd` handling.
1805 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 1840 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
1806 */ 1841 */
@@ -1966,6 +2001,9 @@ static int check_and_run_traps(void)
1966 break; 2001 break;
1967#if ENABLE_HUSH_JOB 2002#if ENABLE_HUSH_JOB
1968 case SIGHUP: { 2003 case SIGHUP: {
2004//TODO: why are we doing this? ash and dash don't do this,
2005//they have no handler for SIGHUP at all,
2006//they rely on kernel to send SIGHUP+SIGCONT to orphaned process groups
1969 struct pipe *job; 2007 struct pipe *job;
1970 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); 2008 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
1971 /* bash is observed to signal whole process groups, 2009 /* bash is observed to signal whole process groups,
@@ -2411,18 +2449,17 @@ static int get_user_input(struct in_str *i)
2411 /* buglet: SIGINT will not make new prompt to appear _at once_, 2449 /* buglet: SIGINT will not make new prompt to appear _at once_,
2412 * only after <Enter>. (^C works immediately) */ 2450 * only after <Enter>. (^C works immediately) */
2413 r = read_line_input(G.line_input_state, prompt_str, 2451 r = read_line_input(G.line_input_state, prompt_str,
2414 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, 2452 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
2415 /*timeout*/ -1
2416 ); 2453 );
2417 /* read_line_input intercepts ^C, "convert" it to SIGINT */ 2454 /* read_line_input intercepts ^C, "convert" it to SIGINT */
2418 if (r == 0) { 2455 if (r == 0)
2419 write(STDOUT_FILENO, "^C", 2);
2420 raise(SIGINT); 2456 raise(SIGINT);
2421 }
2422 check_and_run_traps(); 2457 check_and_run_traps();
2423 if (r != 0 && !G.flag_SIGINT) 2458 if (r != 0 && !G.flag_SIGINT)
2424 break; 2459 break;
2425 /* ^C or SIGINT: repeat */ 2460 /* ^C or SIGINT: repeat */
2461 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2462 write(STDOUT_FILENO, "^C", 2);
2426 G.last_exitcode = 128 + SIGINT; 2463 G.last_exitcode = 128 + SIGINT;
2427 } 2464 }
2428 if (r < 0) { 2465 if (r < 0) {
@@ -3384,7 +3421,7 @@ static int done_command(struct parse_context *ctx)
3384#if 0 /* Instead we emit error message at run time */ 3421#if 0 /* Instead we emit error message at run time */
3385 if (ctx->pending_redirect) { 3422 if (ctx->pending_redirect) {
3386 /* For example, "cmd >" (no filename to redirect to) */ 3423 /* For example, "cmd >" (no filename to redirect to) */
3387 die_if_script("syntax error: %s", "invalid redirect"); 3424 syntax_error("invalid redirect");
3388 ctx->pending_redirect = NULL; 3425 ctx->pending_redirect = NULL;
3389 } 3426 }
3390#endif 3427#endif
@@ -3950,7 +3987,7 @@ static int parse_redirect(struct parse_context *ctx,
3950#if 0 /* Instead we emit error message at run time */ 3987#if 0 /* Instead we emit error message at run time */
3951 if (ctx->pending_redirect) { 3988 if (ctx->pending_redirect) {
3952 /* For example, "cmd > <file" */ 3989 /* For example, "cmd > <file" */
3953 die_if_script("syntax error: %s", "invalid redirect"); 3990 syntax_error("invalid redirect");
3954 } 3991 }
3955#endif 3992#endif
3956 /* Set ctx->pending_redirect, so we know what to do at the 3993 /* Set ctx->pending_redirect, so we know what to do at the
@@ -5022,10 +5059,16 @@ static struct pipe *parse_stream(char **pstring,
5022 else 5059 else
5023 o_free_unsafe(&ctx.as_string); 5060 o_free_unsafe(&ctx.as_string);
5024#endif 5061#endif
5025 debug_leave(); 5062 if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) {
5063 /* Example: bare "{ }", "()" */
5064 G.last_exitcode = 2; /* bash compat */
5065 syntax_error_unexpected_ch(ch);
5066 goto parse_error2;
5067 }
5026 debug_printf_parse("parse_stream return %p: " 5068 debug_printf_parse("parse_stream return %p: "
5027 "end_trigger char found\n", 5069 "end_trigger char found\n",
5028 ctx.list_head); 5070 ctx.list_head);
5071 debug_leave();
5029 return ctx.list_head; 5072 return ctx.list_head;
5030 } 5073 }
5031 } 5074 }
@@ -5283,8 +5326,8 @@ static struct pipe *parse_stream(char **pstring,
5283 /* proper use of this character is caught by end_trigger: 5326 /* proper use of this character is caught by end_trigger:
5284 * if we see {, we call parse_group(..., end_trigger='}') 5327 * if we see {, we call parse_group(..., end_trigger='}')
5285 * and it will match } earlier (not here). */ 5328 * and it will match } earlier (not here). */
5286 syntax_error_unexpected_ch(ch);
5287 G.last_exitcode = 2; 5329 G.last_exitcode = 2;
5330 syntax_error_unexpected_ch(ch);
5288 goto parse_error2; 5331 goto parse_error2;
5289 default: 5332 default:
5290 if (HUSH_DEBUG) 5333 if (HUSH_DEBUG)
@@ -5514,7 +5557,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
5514 if (errmsg_p) 5557 if (errmsg_p)
5515 *errmsg_p = math_state.errmsg; 5558 *errmsg_p = math_state.errmsg;
5516 if (math_state.errmsg) 5559 if (math_state.errmsg)
5517 die_if_script(math_state.errmsg); 5560 msg_and_die_if_script(math_state.errmsg);
5518 return res; 5561 return res;
5519} 5562}
5520#endif 5563#endif
@@ -5781,7 +5824,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5781 /* in bash, len=-n means strlen()-n */ 5824 /* in bash, len=-n means strlen()-n */
5782 len = (arith_t)strlen(val) - beg + len; 5825 len = (arith_t)strlen(val) - beg + len;
5783 if (len < 0) /* bash compat */ 5826 if (len < 0) /* bash compat */
5784 die_if_script("%s: substring expression < 0", var); 5827 msg_and_die_if_script("%s: substring expression < 0", var);
5785 } 5828 }
5786 if (len <= 0 || !val || beg >= strlen(val)) { 5829 if (len <= 0 || !val || beg >= strlen(val)) {
5787 arith_err: 5830 arith_err:
@@ -5795,7 +5838,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5795 } 5838 }
5796 debug_printf_varexp("val:'%s'\n", val); 5839 debug_printf_varexp("val:'%s'\n", val);
5797#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ 5840#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
5798 die_if_script("malformed ${%s:...}", var); 5841 msg_and_die_if_script("malformed ${%s:...}", var);
5799 val = NULL; 5842 val = NULL;
5800#endif 5843#endif
5801 } else { /* one of "-=+?" */ 5844 } else { /* one of "-=+?" */
@@ -5832,7 +5875,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5832 exp_word = to_be_freed; 5875 exp_word = to_be_freed;
5833 if (exp_op == '?') { 5876 if (exp_op == '?') {
5834 /* mimic bash message */ 5877 /* mimic bash message */
5835 die_if_script("%s: %s", 5878 msg_and_die_if_script("%s: %s",
5836 var, 5879 var,
5837 exp_word[0] 5880 exp_word[0]
5838 ? exp_word 5881 ? exp_word
@@ -5849,7 +5892,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5849 /* ${var=[word]} or ${var:=[word]} */ 5892 /* ${var=[word]} or ${var:=[word]} */
5850 if (isdigit(var[0]) || var[0] == '#') { 5893 if (isdigit(var[0]) || var[0] == '#') {
5851 /* mimic bash message */ 5894 /* mimic bash message */
5852 die_if_script("$%s: cannot assign in this way", var); 5895 msg_and_die_if_script("$%s: cannot assign in this way", var);
5853 val = NULL; 5896 val = NULL;
5854 } else { 5897 } else {
5855 char *new_var = xasprintf("%s=%s", var, val); 5898 char *new_var = xasprintf("%s=%s", var, val);
@@ -6698,7 +6741,8 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
6698 int moved_to; 6741 int moved_to;
6699 int i; 6742 int i;
6700 6743
6701 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { 6744 i = 0;
6745 if (sq) for (; sq[i].orig_fd >= 0; i++) {
6702 /* If we collide with an already moved fd... */ 6746 /* If we collide with an already moved fd... */
6703 if (fd == sq[i].moved_to) { 6747 if (fd == sq[i].moved_to) {
6704 sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); 6748 sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd);
@@ -6726,7 +6770,8 @@ static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
6726{ 6770{
6727 int i; 6771 int i;
6728 6772
6729 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { 6773 i = 0;
6774 if (sq) for (; sq[i].orig_fd >= 0; i++) {
6730 /* If we collide with an already moved fd... */ 6775 /* If we collide with an already moved fd... */
6731 if (fd == sq[i].orig_fd) { 6776 if (fd == sq[i].orig_fd) {
6732 /* Examples: 6777 /* Examples:
@@ -6863,7 +6908,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
6863 * "cmd >" (no filename) 6908 * "cmd >" (no filename)
6864 * "cmd > <file" (2nd redirect starts too early) 6909 * "cmd > <file" (2nd redirect starts too early)
6865 */ 6910 */
6866 die_if_script("syntax error: %s", "invalid redirect"); 6911 syntax_error("invalid redirect");
6867 continue; 6912 continue;
6868 } 6913 }
6869 mode = redir_table[redir->rd_type].mode; 6914 mode = redir_table[redir->rd_type].mode;
@@ -7363,8 +7408,10 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7363 */ 7408 */
7364 close_saved_fds_and_FILE_fds(); 7409 close_saved_fds_and_FILE_fds();
7365//FIXME: should also close saved redir fds 7410//FIXME: should also close saved redir fds
7411 /* Without this, "rm -i FILE" can't be ^C'ed: */
7412 switch_off_special_sigs(G.special_sig_mask);
7366 debug_printf_exec("running applet '%s'\n", argv[0]); 7413 debug_printf_exec("running applet '%s'\n", argv[0]);
7367 run_applet_no_and_exit(a, argv[0], argv); 7414 run_noexec_applet_and_exit(a, argv[0], argv);
7368 } 7415 }
7369# endif 7416# endif
7370 /* Re-exec ourselves */ 7417 /* Re-exec ourselves */
@@ -8045,6 +8092,24 @@ static NOINLINE int run_pipe(struct pipe *pi)
8045 add_vars(old_vars); 8092 add_vars(old_vars);
8046/* clean_up_and_ret0: */ 8093/* clean_up_and_ret0: */
8047 restore_redirects(squirrel); 8094 restore_redirects(squirrel);
8095 /*
8096 * Try "usleep 99999999" + ^C + "echo $?"
8097 * with FEATURE_SH_NOFORK=y.
8098 */
8099 if (!funcp) {
8100 /* It was builtin or nofork.
8101 * if this would be a real fork/execed program,
8102 * it should have died if a fatal sig was received.
8103 * But OTOH, there was no separate process,
8104 * the sig was sent to _shell_, not to non-existing
8105 * child.
8106 * Let's just handle ^C only, this one is obvious:
8107 * we aren't ok with exitcode 0 when ^C was pressed
8108 * during builtin/nofork.
8109 */
8110 if (sigismember(&G.pending_set, SIGINT))
8111 rcode = 128 + SIGINT;
8112 }
8048 clean_up_and_ret1: 8113 clean_up_and_ret1:
8049 free(argv_expanded); 8114 free(argv_expanded);
8050 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 8115 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
@@ -8060,6 +8125,14 @@ static NOINLINE int run_pipe(struct pipe *pi)
8060 if (rcode == 0) { 8125 if (rcode == 0) {
8061 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 8126 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
8062 argv_expanded[0], argv_expanded[1]); 8127 argv_expanded[0], argv_expanded[1]);
8128 /*
8129 * Note: signals (^C) can't interrupt here.
8130 * We remember them and they will be acted upon
8131 * after applet returns.
8132 * This makes applets which can run for a long time
8133 * and/or wait for user input ineligible for NOFORK:
8134 * for example, "yes" or "rm" (rm -i waits for input).
8135 */
8063 rcode = run_nofork_applet(n, argv_expanded); 8136 rcode = run_nofork_applet(n, argv_expanded);
8064 } 8137 }
8065 goto clean_up_and_ret; 8138 goto clean_up_and_ret;
@@ -8491,7 +8564,7 @@ static int run_list(struct pipe *pi)
8491 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; 8564 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
8492 G.last_bg_pid_exitcode = 0; 8565 G.last_bg_pid_exitcode = 0;
8493 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); 8566 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
8494/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ 8567/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */
8495 rcode = EXIT_SUCCESS; 8568 rcode = EXIT_SUCCESS;
8496 goto check_traps; 8569 goto check_traps;
8497 } else { 8570 } else {
@@ -8600,6 +8673,10 @@ static void install_sighandlers(unsigned mask)
8600 */ 8673 */
8601 if (sig == SIGCHLD) 8674 if (sig == SIGCHLD)
8602 continue; 8675 continue;
8676 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
8677 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
8678 */
8679 //if (sig == SIGHUP) continue; - TODO?
8603 if (old_handler == SIG_IGN) { 8680 if (old_handler == SIG_IGN) {
8604 /* oops... restore back to IGN, and record this fact */ 8681 /* oops... restore back to IGN, and record this fact */
8605 install_sighandler(sig, old_handler); 8682 install_sighandler(sig, old_handler);
@@ -9381,13 +9458,20 @@ static int FAST_FUNC builtin_read(char **argv)
9381 char *opt_p = NULL; 9458 char *opt_p = NULL;
9382 char *opt_t = NULL; 9459 char *opt_t = NULL;
9383 char *opt_u = NULL; 9460 char *opt_u = NULL;
9461 char *opt_d = NULL; /* optimized out if !BASH */
9384 const char *ifs; 9462 const char *ifs;
9385 int read_flags; 9463 int read_flags;
9386 9464
9387 /* "!": do not abort on errors. 9465 /* "!": do not abort on errors.
9388 * Option string must start with "sr" to match BUILTIN_READ_xxx 9466 * Option string must start with "sr" to match BUILTIN_READ_xxx
9389 */ 9467 */
9390 read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); 9468 read_flags = getopt32(argv,
9469#if BASH_READ_D
9470 "!srn:p:t:u:d:", &opt_n, &opt_p, &opt_t, &opt_u, &opt_d
9471#else
9472 "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u
9473#endif
9474 );
9391 if (read_flags == (uint32_t)-1) 9475 if (read_flags == (uint32_t)-1)
9392 return EXIT_FAILURE; 9476 return EXIT_FAILURE;
9393 argv += optind; 9477 argv += optind;
@@ -9401,7 +9485,8 @@ static int FAST_FUNC builtin_read(char **argv)
9401 opt_n, 9485 opt_n,
9402 opt_p, 9486 opt_p,
9403 opt_t, 9487 opt_t,
9404 opt_u 9488 opt_u,
9489 opt_d
9405 ); 9490 );
9406 9491
9407 if ((uintptr_t)r == 1 && errno == EINTR) { 9492 if ((uintptr_t)r == 1 && errno == EINTR) {
@@ -9786,6 +9871,93 @@ static int FAST_FUNC builtin_shift(char **argv)
9786 return EXIT_FAILURE; 9871 return EXIT_FAILURE;
9787} 9872}
9788 9873
9874#if ENABLE_HUSH_GETOPTS
9875static int FAST_FUNC builtin_getopts(char **argv)
9876{
9877/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
9878
9879TODO:
9880If a required argument is not found, and getopts is not silent,
9881a question mark (?) is placed in VAR, OPTARG is unset, and a
9882diagnostic message is printed. If getopts is silent, then a
9883colon (:) is placed in VAR and OPTARG is set to the option
9884character found.
9885
9886Test that VAR is a valid variable name?
9887
9888"Whenever the shell is invoked, OPTIND shall be initialized to 1"
9889*/
9890 char cbuf[2];
9891 const char *cp, *optstring, *var;
9892 int c, exitcode;
9893
9894 optstring = *++argv;
9895 if (!optstring || !(var = *++argv)) {
9896 bb_error_msg("usage: getopts OPTSTRING VAR [ARGS]");
9897 return EXIT_FAILURE;
9898 }
9899
9900 c = 0;
9901 if (optstring[0] != ':') {
9902 cp = get_local_var_value("OPTERR");
9903 /* 0 if "OPTERR=0", 1 otherwise */
9904 c = (!cp || NOT_LONE_CHAR(cp, '0'));
9905 }
9906 opterr = c;
9907 cp = get_local_var_value("OPTIND");
9908 optind = cp ? atoi(cp) : 0;
9909 optarg = NULL;
9910 cbuf[1] = '\0';
9911
9912 /* getopts stops on first non-option. Add "+" to force that */
9913 /*if (optstring[0] != '+')*/ {
9914 char *s = alloca(strlen(optstring) + 2);
9915 sprintf(s, "+%s", optstring);
9916 optstring = s;
9917 }
9918
9919 if (argv[1])
9920 argv[0] = G.global_argv[0]; /* for error messages */
9921 else
9922 argv = G.global_argv;
9923 c = getopt(string_array_len(argv), argv, optstring);
9924
9925 /* Set OPTARG */
9926 /* Always set or unset, never left as-is, even on exit/error:
9927 * "If no option was found, or if the option that was found
9928 * does not have an option-argument, OPTARG shall be unset."
9929 */
9930 cp = optarg;
9931 if (c == '?') {
9932 /* If ":optstring" and unknown option is seen,
9933 * it is stored to OPTARG.
9934 */
9935 if (optstring[1] == ':') {
9936 cbuf[0] = optopt;
9937 cp = cbuf;
9938 }
9939 }
9940 if (cp)
9941 set_local_var_from_halves("OPTARG", cp);
9942 else
9943 unset_local_var("OPTARG");
9944
9945 /* Convert -1 to "?" */
9946 exitcode = EXIT_SUCCESS;
9947 if (c < 0) { /* -1: end of options */
9948 exitcode = EXIT_FAILURE;
9949 c = '?';
9950 }
9951
9952 /* Set VAR and OPTIND */
9953 cbuf[0] = c;
9954 set_local_var_from_halves(var, cbuf);
9955 set_local_var_from_halves("OPTIND", utoa(optind));
9956
9957 return exitcode;
9958}
9959#endif
9960
9789static int FAST_FUNC builtin_source(char **argv) 9961static int FAST_FUNC builtin_source(char **argv)
9790{ 9962{
9791 char *arg_path, *filename; 9963 char *arg_path, *filename;
@@ -10178,6 +10350,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
10178 /* So, did we get a signal? */ 10350 /* So, did we get a signal? */
10179 sig = check_and_run_traps(); 10351 sig = check_and_run_traps();
10180 if (sig /*&& sig != SIGCHLD - always true */) { 10352 if (sig /*&& sig != SIGCHLD - always true */) {
10353 /* Do this for any (non-ignored) signal, not only for ^C */
10181 ret = 128 + sig; 10354 ret = 128 + sig;
10182 break; 10355 break;
10183 } 10356 }
@@ -10344,6 +10517,41 @@ static int FAST_FUNC builtin_return(char **argv)
10344} 10517}
10345#endif 10518#endif
10346 10519
10520#if ENABLE_HUSH_TIMES
10521static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM)
10522{
10523 static const uint8_t times_tbl[] ALIGN1 = {
10524 ' ', offsetof(struct tms, tms_utime),
10525 '\n', offsetof(struct tms, tms_stime),
10526 ' ', offsetof(struct tms, tms_cutime),
10527 '\n', offsetof(struct tms, tms_cstime),
10528 0
10529 };
10530 const uint8_t *p;
10531 unsigned clk_tck;
10532 struct tms buf;
10533
10534 clk_tck = bb_clk_tck();
10535
10536 times(&buf);
10537 p = times_tbl;
10538 do {
10539 unsigned sec, frac;
10540 unsigned long t;
10541 t = *(clock_t *)(((char *) &buf) + p[1]);
10542 sec = t / clk_tck;
10543 frac = t % clk_tck;
10544 printf("%um%u.%03us%c",
10545 sec / 60, sec % 60,
10546 (frac * 1000) / clk_tck,
10547 p[0]);
10548 p += 2;
10549 } while (*p);
10550
10551 return EXIT_SUCCESS;
10552}
10553#endif
10554
10347#if ENABLE_HUSH_MEMLEAK 10555#if ENABLE_HUSH_MEMLEAK
10348static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) 10556static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
10349{ 10557{
diff --git a/shell/hush_test/hush-getopts/getopt_optarg.right b/shell/hush_test/hush-getopts/getopt_optarg.right
new file mode 100644
index 000000000..9dbd8460e
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_optarg.right
@@ -0,0 +1,18 @@
1*** no OPTIND, optstring:'w:et' args:-q -w e -r -t -y
2./getopt_optarg.tests: invalid option -- q
3var:'?' OPTIND:2 OPTARG:''
4var:'w' OPTIND:4 OPTARG:'e'
5./getopt_optarg.tests: invalid option -- r
6var:'?' OPTIND:5 OPTARG:''
7var:'t' OPTIND:6 OPTARG:''
8./getopt_optarg.tests: invalid option -- y
9var:'?' OPTIND:7 OPTARG:''
10exited: var:'?' OPTIND:7 OPTARG:''
11*** OPTIND=0, optstring:'w:et' args:-w 1 -w2 -w -e -e -t -t
12var:'w' OPTIND:3 OPTARG:'1'
13var:'w' OPTIND:4 OPTARG:'2'
14var:'w' OPTIND:6 OPTARG:'-e'
15var:'e' OPTIND:7 OPTARG:''
16var:'t' OPTIND:8 OPTARG:''
17var:'t' OPTIND:9 OPTARG:''
18exited: var:'?' OPTIND:9 OPTARG:''
diff --git a/shell/hush_test/hush-getopts/getopt_optarg.tests b/shell/hush_test/hush-getopts/getopt_optarg.tests
new file mode 100755
index 000000000..881cc7884
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_optarg.tests
@@ -0,0 +1,24 @@
1(
2
3set -- -q -w e -r -t -y
4echo "*** no OPTIND, optstring:'w:et' args:$*"
5var=QWERTY
6OPTARG=ASDFGH
7while getopts "w:et" var; do
8 echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
9 OPTARG=ASDFGH
10done
11echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
12
13set -- -w 1 -w2 -w -e -e -t -t
14echo "*** OPTIND=0, optstring:'w:et' args:$*"
15OPTIND=0
16while getopts "w:et" var; do
17 echo "var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
18 OPTARG=ASDFGH
19done
20echo "exited: var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
21
22) 2>&1 \
23| sed -e 's/ unrecognized option: / invalid option -- /' \
24 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/hush_test/hush-getopts/getopt_positional.right b/shell/hush_test/hush-getopts/getopt_positional.right
new file mode 100644
index 000000000..f1c942476
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_positional.right
@@ -0,0 +1,6 @@
1*** no OPTIND, optstring:'we' args:-q -w -e r -t -y
2./getopt_positional.tests: invalid option -- q
3var:'?' OPTIND:2
4var:'w' OPTIND:3
5var:'e' OPTIND:4
6exited: var:'?' OPTIND:4
diff --git a/shell/hush_test/hush-getopts/getopt_positional.tests b/shell/hush_test/hush-getopts/getopt_positional.tests
new file mode 100755
index 000000000..20716bb0c
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_positional.tests
@@ -0,0 +1,13 @@
1(
2
3set -- -q -w -e r -t -y
4echo "*** no OPTIND, optstring:'we' args:$*"
5var=QWERTY
6while getopts "we" var; do
7 echo "var:'$var' OPTIND:$OPTIND"
8done
9echo "exited: var:'$var' OPTIND:$OPTIND"
10
11) 2>&1 \
12| sed -e 's/ unrecognized option: / invalid option -- /' \
13 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/hush_test/hush-getopts/getopt_silent.right b/shell/hush_test/hush-getopts/getopt_silent.right
new file mode 100644
index 000000000..03d4eb149
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_silent.right
@@ -0,0 +1,6 @@
1*** optstring:':ac' args:-a -b -c
21 rc:0 var:'a' OPTIND:2 OPTARG:''
32 rc:0 var:'?' OPTIND:3 OPTARG:'b'
43 rc:0 var:'c' OPTIND:4 OPTARG:''
54 rc:1 var:'?' OPTIND:4 OPTARG:''
65 rc:1 var:'?' OPTIND:4 OPTARG:''
diff --git a/shell/hush_test/hush-getopts/getopt_silent.tests b/shell/hush_test/hush-getopts/getopt_silent.tests
new file mode 100755
index 000000000..5f255db7f
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_silent.tests
@@ -0,0 +1,29 @@
1# Open Group Base Specifications Issue 7:
2# """
3# If an unknown option is met, VAR shall be set to "?". In this case,
4# if the first character in optstring is ":", OPTARG shall be set
5# to the option character found, but no output shall be written
6# to standard error; otherwise, the shell variable OPTARG shall be
7# unset and a diagnostic message shall be written to standard error."
8# ...
9# If an option-argument is missing:
10# If the first character of optstring is ":", VAR shall be set to ":"
11# and OPTARG shall be set to the option character found.
12# """
13
14(
15
16echo "*** optstring:':ac' args:-a -b -c"
17getopts ":ac" var -a -b -c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
18getopts ":ac" var -a -b -c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
19getopts ":ac" var -a -b -c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
20getopts ":ac" var -a -b -c; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
21# Previous line should result in "rc:1", which is normally treated
22# in getopts loops as exit condition.
23# Nevertheless, let's verify that calling it yet another time doesn't do
24# anything weird:
25getopts ":ac" var -a -b -c; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
26
27) 2>&1 \
28| sed -e 's/ unrecognized option: / invalid option -- /' \
29 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/hush_test/hush-getopts/getopt_simple.right b/shell/hush_test/hush-getopts/getopt_simple.right
new file mode 100644
index 000000000..b4855fa1a
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_simple.right
@@ -0,0 +1,34 @@
1*** no OPTIND, optstring:'ab' args:-a -b c
2var:'a' OPTIND:2
3var:'b' OPTIND:3
4exited: rc:0 var:'?' OPTIND:3
5*** OPTIND=1, optstring:'ab' args:-a -b c
6var:'a' OPTIND:2
7var:'b' OPTIND:3
8exited: rc:0 var:'?' OPTIND:3
9*** OPTIND=0, optstring:'ab' args:-a -b c
10var:'a' OPTIND:2
11var:'b' OPTIND:3
12exited: rc:0 var:'?' OPTIND:3
13*** unset OPTIND, optstring:'ab' args:-a -b c
14var:'a' OPTIND:2
15var:'b' OPTIND:3
16exited: rc:0 var:'?' OPTIND:3
17*** optstring:'ab' args:-a -b c
181 rc:0 var:'a' OPTIND:2
192 rc:0 var:'b' OPTIND:3
203 rc:1 var:'?' OPTIND:3
21*** unset OPTIND, optstring:'ab' args:-a c -c -b d
22var:'a' OPTIND:2
23exited: rc:0 var:'?' OPTIND:2
24*** unset OPTIND, optstring:'ab' args:-a -c -b d
25var:'a' OPTIND:2
26./getopt_simple.tests: invalid option -- c
27var:'?' OPTIND:3
28var:'b' OPTIND:4
29exited: rc:0 var:'?' OPTIND:4
30*** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d
31var:'a' OPTIND:2
32var:'?' OPTIND:3
33var:'b' OPTIND:4
34exited: rc:0 var:'?' OPTIND:4
diff --git a/shell/hush_test/hush-getopts/getopt_simple.tests b/shell/hush_test/hush-getopts/getopt_simple.tests
new file mode 100755
index 000000000..50718cc98
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_simple.tests
@@ -0,0 +1,81 @@
1# Simple usage cases for getopts.
2#
3# OPTIND is either not touched at all (first loop with getopts,
4# relying on shell startup init), or getopts state is reset
5# before new loop with "unset OPTIND", "OPTIND=1" or "OPTIND=0".
6#
7# Each option is a separate argument (no "-abc"). This conceptually
8# needs only $OPTIND to hold getopts state.
9#
10# We check that loop does not stop on unknown option (sets "?"),
11# stops on _first_ non-option argument.
12
13(
14
15echo "*** no OPTIND, optstring:'ab' args:-a -b c"
16var=QWERTY
17while getopts "ab" var -a -b c; do
18 echo "var:'$var' OPTIND:$OPTIND"
19done
20# unfortunately, "rc:0" is shown since while's overall exitcode is "success"
21echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
22
23# Resetting behavior =1
24echo "*** OPTIND=1, optstring:'ab' args:-a -b c"
25OPTIND=1
26while getopts "ab" var -a -b c; do
27 echo "var:'$var' OPTIND:$OPTIND"
28done
29echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
30
31# Resetting behavior =0
32echo "*** OPTIND=0, optstring:'ab' args:-a -b c"
33OPTIND=0
34while getopts "ab" var -a -b c; do
35 echo "var:'$var' OPTIND:$OPTIND"
36done
37echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
38
39# Resetting behavior "unset"
40echo "*** unset OPTIND, optstring:'ab' args:-a -b c"
41unset OPTIND
42while getopts "ab" var -a -b c; do
43 echo "var:'$var' OPTIND:$OPTIND"
44done
45echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
46
47# What is the final exitcode?
48echo "*** optstring:'ab' args:-a -b c"
49unset OPTIND
50getopts "ab" var -a -b c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND"
51getopts "ab" var -a -b c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND"
52getopts "ab" var -a -b c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND"
53
54# Where would it stop? c or -c?
55echo "*** unset OPTIND, optstring:'ab' args:-a c -c -b d"
56unset OPTIND
57while getopts "ab" var -a c -c -b d; do
58 echo "var:'$var' OPTIND:$OPTIND"
59done
60echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
61
62# What happens on unknown option?
63echo "*** unset OPTIND, optstring:'ab' args:-a -c -b d"
64unset OPTIND
65while getopts "ab" var -a -c -b d; do
66 echo "var:'$var' OPTIND:$OPTIND"
67done
68echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
69
70# ORTERR=0 suppresses error message?
71echo "*** unset OPTIND, OPTERR=0, optstring:'ab' args:-a -c -b d"
72unset OPTIND
73OPTERR=0
74while getopts "ab" var -a -c -b d; do
75 echo "var:'$var' OPTIND:$OPTIND"
76done
77echo "exited: rc:$? var:'$var' OPTIND:$OPTIND"
78
79) 2>&1 \
80| sed -e 's/ unrecognized option: / invalid option -- /' \
81 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/hush_test/hush-getopts/getopt_test_libc_bug.right b/shell/hush_test/hush-getopts/getopt_test_libc_bug.right
new file mode 100644
index 000000000..6694e8f0c
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_test_libc_bug.right
@@ -0,0 +1,26 @@
1*** optstring:'ac' args:-a -b -c -d e
21 rc:0 var:'a' OPTIND:2 OPTARG:''
3./getopt_test_libc_bug.tests: invalid option -- b
42 rc:0 var:'?' OPTIND:3 OPTARG:''
53 rc:0 var:'c' OPTIND:4 OPTARG:''
6./getopt_test_libc_bug.tests: invalid option -- d
74 rc:0 var:'?' OPTIND:5 OPTARG:''
85 rc:1 var:'?' OPTIND:5 OPTARG:''
9
10*** optstring:'ac' args:-a -b -c -d e
111 rc:0 var:'a' OPTIND:2 OPTARG:''
12./getopt_test_libc_bug.tests: invalid option -- b
132 rc:0 var:'?' OPTIND:3 OPTARG:''
143 rc:0 var:'c' OPTIND:4 OPTARG:''
15./getopt_test_libc_bug.tests: invalid option -- d
164 rc:0 var:'?' OPTIND:5 OPTARG:''
175 rc:1 var:'?' OPTIND:5 OPTARG:''
18
19*** optstring:'ac' args:-a -b -c -d e
201 rc:0 var:'a' OPTIND:2 OPTARG:''
21./getopt_test_libc_bug.tests: invalid option -- b
222 rc:0 var:'?' OPTIND:3 OPTARG:''
233 rc:0 var:'c' OPTIND:4 OPTARG:''
24./getopt_test_libc_bug.tests: invalid option -- d
254 rc:0 var:'?' OPTIND:5 OPTARG:''
265 rc:1 var:'?' OPTIND:5 OPTARG:''
diff --git a/shell/hush_test/hush-getopts/getopt_test_libc_bug.tests b/shell/hush_test/hush-getopts/getopt_test_libc_bug.tests
new file mode 100755
index 000000000..ab7bc3b09
--- /dev/null
+++ b/shell/hush_test/hush-getopts/getopt_test_libc_bug.tests
@@ -0,0 +1,44 @@
1# This test can fail with libc with buggy getopt() implementation.
2# If getopt() wants to parse multi-option args (-abc),
3# it needs to remember a position within current arg.
4#
5# If this position is kept as a POINTER, not an offset,
6# and if argv[] ADDRESSES (not contents!) change, it blows up.
7
8(
9
10echo "*** optstring:'ac' args:-a -b -c -d e"
11getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
12getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
13getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
14getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
15getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
16
17# Above: args are (usually) in the same locations in memory.
18# Below: variable allocations change the location.
19
20echo
21echo "*** optstring:'ac' args:-a -b -c -d e"
22unset OPTIND
23OPTARG=QWERTY; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
24NEWVAR=NEWVAL; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
25VAR111=NEWVAL; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
26VAR222=NEWVAL; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
27VAR333=NEWVAL; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
28
29# Slightly different attempts to force reallocations
30
31echo
32echo "*** optstring:'ac' args:-a -b -c -d e"
33unset OPTIND
34export OPTARG; getopts "ac" var -a -b -c -d e; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
35export NEWVAR; getopts "ac" var -a -b -c -d e; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
36export VAR111; getopts "ac" var -a -b -c -d e; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
37export VAR222; getopts "ac" var -a -b -c -d e; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
38export VAR333; getopts "ac" var -a -b -c -d e; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
39
40# All copies of code above should generate identical output
41
42) 2>&1 \
43| sed -e 's/ unrecognized option: / invalid option -- /' \
44 -e 's/ illegal option -- / invalid option -- /' \
diff --git a/shell/hush_test/hush-parsing/groups_and_keywords2.right b/shell/hush_test/hush-parsing/groups_and_keywords2.right
new file mode 100644
index 000000000..ae74a5db9
--- /dev/null
+++ b/shell/hush_test/hush-parsing/groups_and_keywords2.right
@@ -0,0 +1,3 @@
1hush: syntax error: unexpected )
2Fail:2
3hush: syntax error: unexpected )
diff --git a/shell/hush_test/hush-parsing/groups_and_keywords2.tests b/shell/hush_test/hush-parsing/groups_and_keywords2.tests
new file mode 100755
index 000000000..ab33b909f
--- /dev/null
+++ b/shell/hush_test/hush-parsing/groups_and_keywords2.tests
@@ -0,0 +1,9 @@
1# This is an error
2(eval 'if() { echo; }')
3echo Fail:$?
4# ^^^^^^ bash prints 1, but interactively it sets $? = 2
5# we print 2
6
7# This is an error, and it aborts in script
8if() { echo; }
9echo Not reached
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 1dd0edc39..3fbc7c531 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -80,7 +80,7 @@ do_test()
80 case $? in 80 case $? in
81 0) echo " ok";; 81 0) echo " ok";;
82 77) echo " skip (feature disabled)";; 82 77) echo " skip (feature disabled)";;
83 *) echo " fail"; tret=1;; 83 *) echo " fail ($?)"; tret=1;;
84 esac 84 esac
85 done 85 done
86 exit ${tret} 86 exit ${tret}
diff --git a/shell/math.c b/shell/math.c
index 006221b6a..f01f24362 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -743,7 +743,7 @@ arith(arith_state_t *math_state, const char *expr)
743 * may be used to endorse or promote products derived from this software 743 * may be used to endorse or promote products derived from this software
744 * without specific prior written permission. 744 * without specific prior written permission.
745 * 745 *
746 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 746 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
747 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 747 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
748 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 748 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
749 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 749 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 750adc5d8..bc34de404 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -58,7 +58,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
58 const char *opt_n, 58 const char *opt_n,
59 const char *opt_p, 59 const char *opt_p,
60 const char *opt_t, 60 const char *opt_t,
61 const char *opt_u 61 const char *opt_u,
62 const char *opt_d
62) 63)
63{ 64{
64 struct pollfd pfd[1]; 65 struct pollfd pfd[1];
@@ -68,6 +69,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
68 int nchars; /* -n NUM */ 69 int nchars; /* -n NUM */
69 char **pp; 70 char **pp;
70 char *buffer; 71 char *buffer;
72 char delim;
71 struct termios tty, old_tty; 73 struct termios tty, old_tty;
72 const char *retval; 74 const char *retval;
73 int bufpos; /* need to be able to hold -1 */ 75 int bufpos; /* need to be able to hold -1 */
@@ -188,6 +190,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
188 end_ms += (unsigned)monotonic_ms(); 190 end_ms += (unsigned)monotonic_ms();
189 buffer = NULL; 191 buffer = NULL;
190 bufpos = 0; 192 bufpos = 0;
193 delim = opt_d ? *opt_d : '\n';
191 do { 194 do {
192 char c; 195 char c;
193 int timeout; 196 int timeout;
@@ -243,14 +246,14 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
243 continue; 246 continue;
244 } 247 }
245 } 248 }
246 if (c == '\n') 249 if (c == delim) /* '\n' or -d CHAR */
247 break; 250 break;
248 251
249 /* $IFS splitting. NOT done if we run "read" 252 /* $IFS splitting. NOT done if we run "read"
250 * without variable names (bash compat). 253 * without variable names (bash compat).
251 * Thus, "read" and "read REPLY" are not the same. 254 * Thus, "read" and "read REPLY" are not the same.
252 */ 255 */
253 if (argv[0]) { 256 if (!opt_d && argv[0]) {
254/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ 257/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
255 const char *is_ifs = strchr(ifs, c); 258 const char *is_ifs = strchr(ifs, c);
256 if (startword && is_ifs) { 259 if (startword && is_ifs) {
diff --git a/shell/shell_common.h b/shell/shell_common.h
index a82535c86..875fd9ea7 100644
--- a/shell/shell_common.h
+++ b/shell/shell_common.h
@@ -34,6 +34,11 @@ enum {
34 BUILTIN_READ_SILENT = 1 << 0, 34 BUILTIN_READ_SILENT = 1 << 0,
35 BUILTIN_READ_RAW = 1 << 1, 35 BUILTIN_READ_RAW = 1 << 1,
36}; 36};
37//TODO? do not provide bashisms if not asked for:
38//#if !ENABLE_HUSH_BASH_COMPAT && !ENABLE_ASH_BASH_COMPAT
39//#define shell_builtin_read(setvar,argv,ifs,read_flags,n,p,t,u,d)
40// shell_builtin_read(setvar,argv,ifs,read_flags)
41//#endif
37const char* FAST_FUNC 42const char* FAST_FUNC
38shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), 43shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
39 char **argv, 44 char **argv,
@@ -42,7 +47,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
42 const char *opt_n, 47 const char *opt_n,
43 const char *opt_p, 48 const char *opt_p,
44 const char *opt_t, 49 const char *opt_t,
45 const char *opt_u 50 const char *opt_u,
51 const char *opt_d
46); 52);
47 53
48int FAST_FUNC 54int FAST_FUNC
diff --git a/sysklogd/logger.c b/sysklogd/logger.c
index 6769a8175..1e0384c09 100644
--- a/sysklogd/logger.c
+++ b/sysklogd/logger.c
@@ -77,14 +77,14 @@ static int pencode(char *s)
77 ; 77 ;
78 if (*s) { 78 if (*s) {
79 *s = '\0'; 79 *s = '\0';
80 fac = decode(save, facilitynames); 80 fac = decode(save, bb_facilitynames);
81 if (fac < 0) 81 if (fac < 0)
82 bb_error_msg_and_die("unknown %s name: %s", "facility", save); 82 bb_error_msg_and_die("unknown %s name: %s", "facility", save);
83 *s++ = '.'; 83 *s++ = '.';
84 } else { 84 } else {
85 s = save; 85 s = save;
86 } 86 }
87 lev = decode(s, prioritynames); 87 lev = decode(s, bb_prioritynames);
88 if (lev < 0) 88 if (lev < 0)
89 bb_error_msg_and_die("unknown %s name: %s", "priority", save); 89 bb_error_msg_and_die("unknown %s name: %s", "priority", save);
90 return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK)); 90 return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
@@ -167,7 +167,7 @@ int logger_main(int argc UNUSED_PARAM, char **argv)
167 * may be used to endorse or promote products derived from this software 167 * may be used to endorse or promote products derived from this software
168 * without specific prior written permission. 168 * without specific prior written permission.
169 * 169 *
170 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 170 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
171 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 171 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 172 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
173 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 173 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 31730a7f9..4265f4f90 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -447,7 +447,7 @@ static void parse_syslogdcfg(const char *file)
447 primap = 0xff; /* all 8 log levels enabled */ 447 primap = 0xff; /* all 8 log levels enabled */
448 else { 448 else {
449 uint8_t priority; 449 uint8_t priority;
450 code = find_by_name(t, prioritynames); 450 code = find_by_name(t, bb_prioritynames);
451 if (!code) 451 if (!code)
452 goto cfgerr; 452 goto cfgerr;
453 primap = 0; 453 primap = 0;
@@ -480,7 +480,7 @@ static void parse_syslogdcfg(const char *file)
480 next_facility = strchr(t, ','); 480 next_facility = strchr(t, ',');
481 if (next_facility) 481 if (next_facility)
482 *next_facility++ = '\0'; 482 *next_facility++ = '\0';
483 code = find_by_name(t, facilitynames); 483 code = find_by_name(t, bb_facilitynames);
484 if (!code) 484 if (!code)
485 goto cfgerr; 485 goto cfgerr;
486 /* "mark" is not a real facility, skip it */ 486 /* "mark" is not a real facility, skip it */
@@ -797,9 +797,9 @@ static void parse_fac_prio_20(int pri, char *res20)
797{ 797{
798 const CODE *c_pri, *c_fac; 798 const CODE *c_pri, *c_fac;
799 799
800 c_fac = find_by_val(LOG_FAC(pri) << 3, facilitynames); 800 c_fac = find_by_val(LOG_FAC(pri) << 3, bb_facilitynames);
801 if (c_fac) { 801 if (c_fac) {
802 c_pri = find_by_val(LOG_PRI(pri), prioritynames); 802 c_pri = find_by_val(LOG_PRI(pri), bb_prioritynames);
803 if (c_pri) { 803 if (c_pri) {
804 snprintf(res20, 20, "%s.%s", c_fac->c_name, c_pri->c_name); 804 snprintf(res20, 20, "%s.%s", c_fac->c_name, c_pri->c_name);
805 return; 805 return;
@@ -1109,8 +1109,7 @@ int syslogd_main(int argc UNUSED_PARAM, char **argv)
1109 INIT_G(); 1109 INIT_G();
1110 1110
1111 /* No non-option params */ 1111 /* No non-option params */
1112 opt_complementary = "=0"; 1112 opts = getopt32(argv, "^"OPTION_STR"\0""=0", OPTION_PARAM);
1113 opts = getopt32(argv, OPTION_STR, OPTION_PARAM);
1114#if ENABLE_FEATURE_REMOTE_LOG 1113#if ENABLE_FEATURE_REMOTE_LOG
1115 while (remoteAddrList) { 1114 while (remoteAddrList) {
1116 remoteHost_t *rh = xzalloc(sizeof(*rh)); 1115 remoteHost_t *rh = xzalloc(sizeof(*rh));
diff --git a/sysklogd/syslogd_and_logger.c b/sysklogd/syslogd_and_logger.c
index 6458a9332..6d06a718b 100644
--- a/sysklogd/syslogd_and_logger.c
+++ b/sysklogd/syslogd_and_logger.c
@@ -43,6 +43,17 @@ typedef struct _code {
43 */ 43 */
44#endif 44#endif
45 45
46/* musl decided to be funny and it implements these as giant defines
47 * of the form: ((CODE *)(const CODE []){ ... })
48 * Which works, but causes _every_ function using them
49 * to have a copy on stack (at least with gcc-6.3.0).
50 * If we reference them just once, this saves 150 bytes.
51 * The pointers themselves are optimized out
52 * (no size change on uclibc).
53 */
54static const CODE *const bb_prioritynames = prioritynames;
55static const CODE *const bb_facilitynames = facilitynames;
56
46#if ENABLE_SYSLOGD 57#if ENABLE_SYSLOGD
47#include "syslogd.c" 58#include "syslogd.c"
48#endif 59#endif
diff --git a/testsuite/mdev.tests b/testsuite/mdev.tests
index 8515aff31..8e53ec564 100755
--- a/testsuite/mdev.tests
+++ b/testsuite/mdev.tests
@@ -168,7 +168,7 @@ SKIP=
168# continuing to use directory structure from prev test 168# continuing to use directory structure from prev test
169rm -rf mdev.testdir/dev/* 169rm -rf mdev.testdir/dev/*
170echo "sda 0:0 644 @echo @echo TEST" >mdev.testdir/etc/mdev.conf 170echo "sda 0:0 644 @echo @echo TEST" >mdev.testdir/etc/mdev.conf
171optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH ASH_ECHO 171optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME SH_IS_ASH ASH_ECHO
172testing "mdev command" \ 172testing "mdev command" \
173 "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; 173 "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
174 ls -lnR mdev.testdir/dev | $FILTER_LS" \ 174 ls -lnR mdev.testdir/dev | $FILTER_LS" \
@@ -183,7 +183,7 @@ SKIP=
183# continuing to use directory structure from prev test 183# continuing to use directory structure from prev test
184rm -rf mdev.testdir/dev/* 184rm -rf mdev.testdir/dev/*
185echo "sda 0:0 644 =block/ @echo @echo TEST:\$MDEV" >mdev.testdir/etc/mdev.conf 185echo "sda 0:0 644 =block/ @echo @echo TEST:\$MDEV" >mdev.testdir/etc/mdev.conf
186optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH 186optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME SH_IS_ASH
187testing "mdev move and command" \ 187testing "mdev move and command" \
188 "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; 188 "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
189 ls -lnR mdev.testdir/dev | $FILTER_LS2" \ 189 ls -lnR mdev.testdir/dev | $FILTER_LS2" \
diff --git a/testsuite/parse.tests b/testsuite/parse.tests
index 904e1a17a..2cbed6f31 100755
--- a/testsuite/parse.tests
+++ b/testsuite/parse.tests
@@ -5,13 +5,13 @@
5 5
6. ./testing.sh 6. ./testing.sh
7 7
8COLLAPSE=$(( 0x00010000)) 8COLLAPSE=$(( 0x00010000))
9TRIM=$(( 0x00020000)) 9TRIM=$(( 0x00020000))
10GREEDY=$(( 0x00040000)) 10GREEDY=$(( 0x00040000))
11MIN_DIE=$(( 0x00100000)) 11MIN_DIE=$(( 0x00100000))
12KEEP_COPY=$((0x00200000)) 12KEEP_COPY=$(( 0x00200000))
13ESCAPE=$(( 0x00400000)) 13EOL_COMMENTS=$((0x00400000))
14NORMAL=$(( COLLAPSE | TRIM | GREEDY)) 14NORMAL=$(( COLLAPSE | TRIM | GREEDY | EOL_COMMENTS))
15 15
16# testing "description" "command" "result" "infile" "stdin" 16# testing "description" "command" "result" "infile" "stdin"
17 17
@@ -27,6 +27,34 @@ testing "parse notrim" \
27 "-" \ 27 "-" \
28 " sda 0:0 644 @echo @echo TEST \n" 28 " sda 0:0 644 @echo @echo TEST \n"
29 29
30testing "parse comments" \
31 "parse -n 4 -m 3 -f $((NORMAL - EOL_COMMENTS)) -" \
32 "[sda][0:0][644][@echo @echo TEST #this is not eaten]\n" \
33 "-" \
34 "\
35# sda 0:0 644 @echo @echo TEST - this gets eaten
36 sda 0:0 644 @echo @echo TEST #this is not eaten
37"
38
39testing "parse bad comment" \
40 "parse -n 2 -m 2 -d '#=' -f $((GREEDY)) - 2>&1" \
41 "\
42[var][val]
43parse: bad line 3: 1 tokens found, 2 needed
44[ #this][ok]
45[ #this][=ok]
46[ #this][=ok=ok=ok=]
47" \
48 "-" \
49 "\
50# this gets eaten
51var=val
52 #this causes error msg
53 #this=ok
54 #this==ok
55 #this==ok=ok=ok=
56"
57
30FILE=__parse 58FILE=__parse
31cat >$FILE <<EOF 59cat >$FILE <<EOF
32# 60#
@@ -96,6 +124,8 @@ cat >$FILE.res <<EOF
96[option][dns][129.219.13.81] 124[option][dns][129.219.13.81]
97[option][domain][local] 125[option][domain][local]
98[option][lease][864000] 126[option][lease][864000]
127[option][msstaticroutes][10.0.0.0/8][10.127.0.1]
128[option][staticroutes][10.0.0.0/8][10.127.0.1,][10.11.12.0/24][10.11.12.1]
99[option][0x08][01020304] 129[option][0x08][01020304]
100EOF 130EOF
101 131
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index 1675b07b1..b7cd74ca5 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -279,7 +279,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
279testing "tar does not extract into symlinks" "\ 279testing "tar does not extract into symlinks" "\
280>>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$? 280>>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
281" "\ 281" "\
282tar: can't create symlink 'passwd' to '/tmp/passwd' 282tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract
2830 2830
284" \ 284" \
285"" "\ 285"" "\
@@ -299,7 +299,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
299testing "tar -k does not extract into symlinks" "\ 299testing "tar -k does not extract into symlinks" "\
300>>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$? 300>>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
301" "\ 301" "\
302tar: can't create symlink 'passwd' to '/tmp/passwd' 302tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract
3030 3030
304" \ 304" \
305"" "\ 305"" "\
@@ -324,11 +324,11 @@ rm -rf etc usr
324' "\ 324' "\
325etc/ssl/certs/3b2716e5.0 325etc/ssl/certs/3b2716e5.0
326etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem 326etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
327tar: skipping unsafe symlink to '/usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract
327etc/ssl/certs/f80cc7f6.0 328etc/ssl/certs/f80cc7f6.0
328usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt 329usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt
3290 3300
330etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem 331etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
331etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem -> /usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt
332etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem 332etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
333" \ 333" \
334"" "" 334"" ""
@@ -346,9 +346,9 @@ ls symlink/bb_test_evilfile
346' "\ 346' "\
347anything.txt 347anything.txt
348symlink 348symlink
349tar: skipping unsafe symlink to '/tmp' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract
349symlink/bb_test_evilfile 350symlink/bb_test_evilfile
350tar: can't create symlink 'symlink' to '/tmp' 3510
3511
352ls: /tmp/bb_test_evilfile: No such file or directory 352ls: /tmp/bb_test_evilfile: No such file or directory
353ls: bb_test_evilfile: No such file or directory 353ls: bb_test_evilfile: No such file or directory
354symlink/bb_test_evilfile 354symlink/bb_test_evilfile
diff --git a/util-linux/acpid.c b/util-linux/acpid.c
index 3c3811752..4f491fa14 100644
--- a/util-linux/acpid.c
+++ b/util-linux/acpid.c
@@ -264,8 +264,12 @@ int acpid_main(int argc UNUSED_PARAM, char **argv)
264 264
265 INIT_G(); 265 INIT_G();
266 266
267 opt_complementary = "df:e--e"; 267 opts = getopt32(argv, "^"
268 opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"), 268 "c:de:fl:a:M:"
269 IF_FEATURE_PIDFILE("p:")
270 IF_FEATURE_ACPID_COMPAT("g:m:s:S:v")
271 "\0"
272 "df:e--e",
269 &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map 273 &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
270 IF_FEATURE_PIDFILE(, &opt_pidfile) 274 IF_FEATURE_PIDFILE(, &opt_pidfile)
271 IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL) 275 IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
diff --git a/util-linux/blkdiscard.c b/util-linux/blkdiscard.c
index 8f6a4ab6c..5863f0aab 100644
--- a/util-linux/blkdiscard.c
+++ b/util-linux/blkdiscard.c
@@ -11,8 +11,9 @@
11//config: help 11//config: help
12//config: blkdiscard discards sectors on a given device. 12//config: blkdiscard discards sectors on a given device.
13 13
14//applet:IF_BLKDISCARD(APPLET_NOEXEC(blkdiscard, blkdiscard, BB_DIR_USR_BIN, BB_SUID_DROP, blkdiscard))
15
14//kbuild:lib-$(CONFIG_BLKDISCARD) += blkdiscard.o 16//kbuild:lib-$(CONFIG_BLKDISCARD) += blkdiscard.o
15//applet:IF_BLKDISCARD(APPLET(blkdiscard, BB_DIR_USR_BIN, BB_SUID_DROP))
16 17
17//usage:#define blkdiscard_trivial_usage 18//usage:#define blkdiscard_trivial_usage
18//usage: "[-o OFS] [-l LEN] [-s] DEVICE" 19//usage: "[-o OFS] [-l LEN] [-s] DEVICE"
@@ -44,7 +45,6 @@ int blkdiscard_main(int argc UNUSED_PARAM, char **argv)
44 uint64_t offset; /* Leaving these two variables out does not */ 45 uint64_t offset; /* Leaving these two variables out does not */
45 uint64_t length; /* shrink code size and hampers readability. */ 46 uint64_t length; /* shrink code size and hampers readability. */
46 uint64_t range[2]; 47 uint64_t range[2];
47// struct stat st;
48 int fd; 48 int fd;
49 49
50 enum { 50 enum {
@@ -53,8 +53,7 @@ int blkdiscard_main(int argc UNUSED_PARAM, char **argv)
53 OPT_SECURE = (1 << 2), 53 OPT_SECURE = (1 << 2),
54 }; 54 };
55 55
56 opt_complementary = "=1"; 56 opts = getopt32(argv, "^" "o:l:s" "\0" "=1", &offset_str, &length_str);
57 opts = getopt32(argv, "o:l:s", &offset_str, &length_str);
58 argv += optind; 57 argv += optind;
59 58
60 fd = xopen(argv[0], O_RDWR|O_EXCL); 59 fd = xopen(argv[0], O_RDWR|O_EXCL);
diff --git a/util-linux/blkid.c b/util-linux/blkid.c
index 0bd701aae..a56b69661 100644
--- a/util-linux/blkid.c
+++ b/util-linux/blkid.c
@@ -16,12 +16,12 @@
16//config: 16//config:
17//config:config FEATURE_BLKID_TYPE 17//config:config FEATURE_BLKID_TYPE
18//config: bool "Print filesystem type" 18//config: bool "Print filesystem type"
19//config: default n 19//config: default y
20//config: depends on BLKID 20//config: depends on BLKID
21//config: help 21//config: help
22//config: Show TYPE="filesystem type" 22//config: Show TYPE="filesystem type"
23 23
24//applet:IF_BLKID(APPLET(blkid, BB_DIR_SBIN, BB_SUID_DROP)) 24//applet:IF_BLKID(APPLET_NOEXEC(blkid, blkid, BB_DIR_SBIN, BB_SUID_DROP, blkid))
25 25
26//kbuild:lib-$(CONFIG_BLKID) += blkid.o 26//kbuild:lib-$(CONFIG_BLKID) += blkid.o
27 27
diff --git a/util-linux/blockdev.c b/util-linux/blockdev.c
index 9e1fef206..e53ade995 100644
--- a/util-linux/blockdev.c
+++ b/util-linux/blockdev.c
@@ -11,7 +11,7 @@
11//config: help 11//config: help
12//config: Performs some ioctls with block devices. 12//config: Performs some ioctls with block devices.
13 13
14//applet:IF_BLOCKDEV(APPLET(blockdev, BB_DIR_SBIN, BB_SUID_DROP)) 14//applet:IF_BLOCKDEV(APPLET_NOEXEC(blockdev, blockdev, BB_DIR_SBIN, BB_SUID_DROP, blockdev))
15 15
16//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o 16//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
17 17
diff --git a/util-linux/cal.c b/util-linux/cal.c
index 091fdbd2b..10df0ae8b 100644
--- a/util-linux/cal.c
+++ b/util-linux/cal.c
@@ -376,7 +376,7 @@ static char *build_row(char *p, unsigned *dp)
376 * may be used to endorse or promote products derived from this software 376 * may be used to endorse or promote products derived from this software
377 * without specific prior written permission. 377 * without specific prior written permission.
378 * 378 *
379 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 379 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
380 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 380 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
381 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 381 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
382 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 382 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/util-linux/chrt.c b/util-linux/chrt.c
index 4bc8b6cfa..2712ea3e3 100644
--- a/util-linux/chrt.c
+++ b/util-linux/chrt.c
@@ -12,7 +12,7 @@
12//config: manipulate real-time attributes of a process. 12//config: manipulate real-time attributes of a process.
13//config: This requires sched_{g,s}etparam support in your libc. 13//config: This requires sched_{g,s}etparam support in your libc.
14 14
15//applet:IF_CHRT(APPLET(chrt, BB_DIR_USR_BIN, BB_SUID_DROP)) 15//applet:IF_CHRT(APPLET_NOEXEC(chrt, chrt, BB_DIR_USR_BIN, BB_SUID_DROP, chrt))
16 16
17//kbuild:lib-$(CONFIG_CHRT) += chrt.o 17//kbuild:lib-$(CONFIG_CHRT) += chrt.o
18 18
@@ -77,8 +77,7 @@ int chrt_main(int argc UNUSED_PARAM, char **argv)
77 int policy = SCHED_RR; 77 int policy = SCHED_RR;
78 78
79 /* only one policy accepted */ 79 /* only one policy accepted */
80 opt_complementary = "r--fo:f--ro:o--rf"; 80 opt = getopt32(argv, "^+" "mprfo" "\0" "r--fo:f--ro:o--rf");
81 opt = getopt32(argv, "+mprfo");
82 if (opt & OPT_m) { /* print min/max and exit */ 81 if (opt & OPT_m) { /* print min/max and exit */
83 show_min_max(SCHED_FIFO); 82 show_min_max(SCHED_FIFO);
84 show_min_max(SCHED_RR); 83 show_min_max(SCHED_RR);
diff --git a/util-linux/eject.c b/util-linux/eject.c
index 8095cbef0..6c30facd2 100644
--- a/util-linux/eject.c
+++ b/util-linux/eject.c
@@ -124,8 +124,9 @@ int eject_main(int argc UNUSED_PARAM, char **argv)
124 unsigned flags; 124 unsigned flags;
125 const char *device; 125 const char *device;
126 126
127 opt_complementary = "?1:t--T:T--t"; 127 flags = getopt32(argv, "^" "tT"IF_FEATURE_EJECT_SCSI("s")
128 flags = getopt32(argv, "tT" IF_FEATURE_EJECT_SCSI("s")); 128 "\0" "?1:t--T:T--t"
129 );
129 device = argv[optind] ? argv[optind] : "/dev/cdrom"; 130 device = argv[optind] ? argv[optind] : "/dev/cdrom";
130 131
131 /* We used to do "umount <device>" here, but it was buggy 132 /* We used to do "umount <device>" here, but it was buggy
diff --git a/util-linux/fallocate.c b/util-linux/fallocate.c
index 70e7e178f..1a02a322f 100644
--- a/util-linux/fallocate.c
+++ b/util-linux/fallocate.c
@@ -82,8 +82,7 @@ int fallocate_main(int argc UNUSED_PARAM, char **argv)
82 int fd; 82 int fd;
83 83
84 /* exactly one non-option arg */ 84 /* exactly one non-option arg */
85 opt_complementary = "=1"; 85 opts = getopt32(argv, "^" "l:o:" "\0" "=1", &str_l, &str_o);
86 opts = getopt32(argv, "l:o:", &str_l, &str_o);
87 if (!(opts & 1)) 86 if (!(opts & 1))
88 bb_show_usage(); 87 bb_show_usage();
89 88
diff --git a/util-linux/fatattr.c b/util-linux/fatattr.c
index 9fb566d5a..770b1d2f9 100644
--- a/util-linux/fatattr.c
+++ b/util-linux/fatattr.c
@@ -15,7 +15,7 @@
15//config: help 15//config: help
16//config: fatattr lists or changes the file attributes on a fat file system. 16//config: fatattr lists or changes the file attributes on a fat file system.
17 17
18//applet:IF_FATATTR(APPLET(fatattr, BB_DIR_BIN, BB_SUID_DROP)) 18//applet:IF_FATATTR(APPLET_NOEXEC(fatattr, fatattr, BB_DIR_BIN, BB_SUID_DROP, fatattr))
19 19
20//kbuild:lib-$(CONFIG_FATATTR) += fatattr.o 20//kbuild:lib-$(CONFIG_FATATTR) += fatattr.o
21 21
diff --git a/util-linux/fdformat.c b/util-linux/fdformat.c
index 6faaf1b10..855269c30 100644
--- a/util-linux/fdformat.c
+++ b/util-linux/fdformat.c
@@ -66,8 +66,7 @@ int fdformat_main(int argc UNUSED_PARAM, char **argv)
66 struct floppy_struct param; 66 struct floppy_struct param;
67 struct format_descr descr; 67 struct format_descr descr;
68 68
69 opt_complementary = "=1"; /* must have 1 param */ 69 verify = !getopt32(argv, "^" "n" "\0" "=1");
70 verify = !getopt32(argv, "n");
71 argv += optind; 70 argv += optind;
72 71
73 xstat(*argv, &st); 72 xstat(*argv, &st);
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index e00f85864..c4318b6c4 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -644,7 +644,7 @@ read_line(const char *prompt)
644{ 644{
645 int sz; 645 int sz;
646 646
647 sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer), /*timeout*/ -1); 647 sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer));
648 if (sz <= 0) 648 if (sz <= 0)
649 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */ 649 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
650 650
@@ -2848,7 +2848,7 @@ xselect(void)
2848 if (dos_compatible_flag) { 2848 if (dos_compatible_flag) {
2849 sector_offset = g_sectors; 2849 sector_offset = g_sectors;
2850 puts("Warning: setting sector offset for DOS " 2850 puts("Warning: setting sector offset for DOS "
2851 "compatiblity"); 2851 "compatibility");
2852 } 2852 }
2853 update_units(); 2853 update_units();
2854 break; 2854 break;
diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c
index 89f1f323c..1141b7801 100644
--- a/util-linux/fdisk_osf.c
+++ b/util-linux/fdisk_osf.c
@@ -18,7 +18,7 @@
18 * may be used to endorse or promote products derived from this software 18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission. 19 * without specific prior written permission.
20 * 20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
diff --git a/util-linux/flock.c b/util-linux/flock.c
index ec35af18f..dd0bfd430 100644
--- a/util-linux/flock.c
+++ b/util-linux/flock.c
@@ -38,17 +38,15 @@ int flock_main(int argc UNUSED_PARAM, char **argv)
38 }; 38 };
39 39
40#if ENABLE_LONG_OPTS 40#if ENABLE_LONG_OPTS
41 static const char getopt_longopts[] ALIGN1 = 41 static const char flock_longopts[] ALIGN1 =
42 "shared\0" No_argument "s" 42 "shared\0" No_argument "s"
43 "exclusive\0" No_argument "x" 43 "exclusive\0" No_argument "x"
44 "unlock\0" No_argument "u" 44 "unlock\0" No_argument "u"
45 "nonblock\0" No_argument "n" 45 "nonblock\0" No_argument "n"
46 ; 46 ;
47 applet_long_options = getopt_longopts;
48#endif 47#endif
49 opt_complementary = "-1";
50 48
51 opt = getopt32(argv, "+sxnu"); 49 opt = getopt32long(argv, "^+" "sxnu" "\0" "-1", flock_longopts);
52 argv += optind; 50 argv += optind;
53 51
54 if (argv[1]) { 52 if (argv[1]) {
diff --git a/util-linux/freeramdisk.c b/util-linux/freeramdisk.c
index 55187cb40..6752e49d8 100644
--- a/util-linux/freeramdisk.c
+++ b/util-linux/freeramdisk.c
@@ -33,9 +33,9 @@
33//config: ramdisk. If you have no use for freeing memory from a ramdisk, leave 33//config: ramdisk. If you have no use for freeing memory from a ramdisk, leave
34//config: this disabled. 34//config: this disabled.
35 35
36// APPLET_ODDNAME:name main location suid_type help 36// APPLET_ODDNAME:name main location suid_type help
37//applet:IF_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, BB_DIR_BIN, BB_SUID_DROP, fdflush)) 37//applet:IF_FDFLUSH( APPLET_ODDNAME(fdflush, freeramdisk, BB_DIR_BIN, BB_SUID_DROP, fdflush ))
38//applet:IF_FREERAMDISK(APPLET(freeramdisk, BB_DIR_SBIN, BB_SUID_DROP)) 38//applet:IF_FREERAMDISK(APPLET_NOEXEC(freeramdisk, freeramdisk, BB_DIR_SBIN, BB_SUID_DROP, freeramdisk))
39 39
40//kbuild:lib-$(CONFIG_FDFLUSH) += freeramdisk.o 40//kbuild:lib-$(CONFIG_FDFLUSH) += freeramdisk.o
41//kbuild:lib-$(CONFIG_FREERAMDISK) += freeramdisk.o 41//kbuild:lib-$(CONFIG_FREERAMDISK) += freeramdisk.o
@@ -67,8 +67,12 @@ int freeramdisk_main(int argc UNUSED_PARAM, char **argv)
67 fd = xopen(single_argv(argv), O_RDWR); 67 fd = xopen(single_argv(argv), O_RDWR);
68 68
69 // Act like freeramdisk, fdflush, or both depending on configuration. 69 // Act like freeramdisk, fdflush, or both depending on configuration.
70 ioctl_or_perror_and_die(fd, (ENABLE_FREERAMDISK && applet_name[1] == 'r') 70 ioctl_or_perror_and_die(fd,
71 || !ENABLE_FDFLUSH ? BLKFLSBUF : FDFLUSH, NULL, "%s", argv[1]); 71 ((ENABLE_FREERAMDISK && applet_name[1] == 'r') || !ENABLE_FDFLUSH)
72 ? BLKFLSBUF
73 : FDFLUSH,
74 NULL, "%s", argv[1]
75 );
72 76
73 if (ENABLE_FEATURE_CLEAN_UP) close(fd); 77 if (ENABLE_FEATURE_CLEAN_UP) close(fd);
74 78
diff --git a/util-linux/fsck_minix.c b/util-linux/fsck_minix.c
index 8c2b7d8de..c4612f251 100644
--- a/util-linux/fsck_minix.c
+++ b/util-linux/fsck_minix.c
@@ -173,7 +173,10 @@ struct globals {
173 173
174 /* Bigger stuff */ 174 /* Bigger stuff */
175 struct termios sv_termios; 175 struct termios sv_termios;
176 char superblock_buffer[BLOCK_SIZE]; 176 union {
177 char superblock_buffer[BLOCK_SIZE];
178 struct minix_superblock Super;
179 } u;
177 char add_zone_ind_blk[BLOCK_SIZE]; 180 char add_zone_ind_blk[BLOCK_SIZE];
178 char add_zone_dind_blk[BLOCK_SIZE]; 181 char add_zone_dind_blk[BLOCK_SIZE];
179 IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];) 182 IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];)
@@ -207,7 +210,7 @@ struct globals {
207#define name_depth (G.name_depth ) 210#define name_depth (G.name_depth )
208#define name_component (G.name_component ) 211#define name_component (G.name_component )
209#define sv_termios (G.sv_termios ) 212#define sv_termios (G.sv_termios )
210#define superblock_buffer (G.superblock_buffer ) 213#define superblock_buffer (G.u.superblock_buffer)
211#define add_zone_ind_blk (G.add_zone_ind_blk ) 214#define add_zone_ind_blk (G.add_zone_ind_blk )
212#define add_zone_dind_blk (G.add_zone_dind_blk ) 215#define add_zone_dind_blk (G.add_zone_dind_blk )
213#define add_zone_tind_blk (G.add_zone_tind_blk ) 216#define add_zone_tind_blk (G.add_zone_tind_blk )
@@ -247,7 +250,7 @@ enum {
247#define Inode1 (((struct minix1_inode *) inode_buffer)-1) 250#define Inode1 (((struct minix1_inode *) inode_buffer)-1)
248#define Inode2 (((struct minix2_inode *) inode_buffer)-1) 251#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
249 252
250#define Super (*(struct minix_superblock *)(superblock_buffer)) 253#define Super (G.u.Super)
251 254
252#if ENABLE_FEATURE_MINIX2 255#if ENABLE_FEATURE_MINIX2
253# define ZONES ((unsigned)(version2 ? Super.s_zones : Super.s_nzones)) 256# define ZONES ((unsigned)(version2 ? Super.s_zones : Super.s_nzones))
@@ -1232,8 +1235,7 @@ int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
1232 1235
1233 INIT_G(); 1236 INIT_G();
1234 1237
1235 opt_complementary = "=1:ar"; /* one argument; -a assumes -r */ 1238 getopt32(argv, "^" OPTION_STR "\0" "=1:ar" /* one arg; -a assumes -r */);
1236 getopt32(argv, OPTION_STR);
1237 argv += optind; 1239 argv += optind;
1238 device_name = argv[0]; 1240 device_name = argv[0];
1239 1241
diff --git a/util-linux/fsfreeze.c b/util-linux/fsfreeze.c
index 5c10c8044..2e2257337 100644
--- a/util-linux/fsfreeze.c
+++ b/util-linux/fsfreeze.c
@@ -13,7 +13,7 @@
13//config: help 13//config: help
14//config: Halt new accesses and flush writes on a mounted filesystem. 14//config: Halt new accesses and flush writes on a mounted filesystem.
15 15
16//applet:IF_FSFREEZE(APPLET(fsfreeze, BB_DIR_USR_SBIN, BB_SUID_DROP)) 16//applet:IF_FSFREEZE(APPLET_NOEXEC(fsfreeze, fsfreeze, BB_DIR_USR_SBIN, BB_SUID_DROP, fsfreeze))
17 17
18//kbuild:lib-$(CONFIG_FSFREEZE) += fsfreeze.o 18//kbuild:lib-$(CONFIG_FSFREEZE) += fsfreeze.o
19 19
@@ -36,15 +36,15 @@ int fsfreeze_main(int argc UNUSED_PARAM, char **argv)
36 unsigned opts; 36 unsigned opts;
37 int fd; 37 int fd;
38 38
39 applet_long_options =
40 "freeze\0" No_argument "\xff"
41 "unfreeze\0" No_argument "\xfe"
42 ;
43 /* exactly one non-option arg: the mountpoint */ 39 /* exactly one non-option arg: the mountpoint */
44 /* one of opts is required */ 40 /* one of opts is required */
45 /* opts are mutually exclusive */ 41 /* opts are mutually exclusive */
46 opt_complementary = "=1:""\xff:\xfe:""\xff--\xfe:\xfe--\xff"; 42 opts = getopt32long(argv, "^"
47 opts = getopt32(argv, ""); 43 "" /* no opts */
44 "\0" "=1:""\xff:\xfe:""\xff--\xfe:\xfe--\xff",
45 "freeze\0" No_argument "\xff"
46 "unfreeze\0" No_argument "\xfe"
47 );
48 48
49 fd = xopen(argv[optind], O_RDONLY); 49 fd = xopen(argv[optind], O_RDONLY);
50 /* Works with NULL arg on linux-4.8.0 */ 50 /* Works with NULL arg on linux-4.8.0 */
diff --git a/util-linux/fstrim.c b/util-linux/fstrim.c
index 6d0d61d92..4acfa567a 100644
--- a/util-linux/fstrim.c
+++ b/util-linux/fstrim.c
@@ -15,7 +15,7 @@
15//config: help 15//config: help
16//config: Discard unused blocks on a mounted filesystem. 16//config: Discard unused blocks on a mounted filesystem.
17 17
18//applet:IF_FSTRIM(APPLET(fstrim, BB_DIR_SBIN, BB_SUID_DROP)) 18//applet:IF_FSTRIM(APPLET_NOEXEC(fstrim, fstrim, BB_DIR_SBIN, BB_SUID_DROP, fstrim))
19 19
20//kbuild:lib-$(CONFIG_FSTRIM) += fstrim.o 20//kbuild:lib-$(CONFIG_FSTRIM) += fstrim.o
21 21
@@ -63,17 +63,17 @@ int fstrim_main(int argc UNUSED_PARAM, char **argv)
63 }; 63 };
64 64
65#if ENABLE_LONG_OPTS 65#if ENABLE_LONG_OPTS
66 static const char getopt_longopts[] ALIGN1 = 66 static const char fstrim_longopts[] ALIGN1 =
67 "offset\0" Required_argument "o" 67 "offset\0" Required_argument "o"
68 "length\0" Required_argument "l" 68 "length\0" Required_argument "l"
69 "minimum\0" Required_argument "m" 69 "minimum\0" Required_argument "m"
70 "verbose\0" No_argument "v" 70 "verbose\0" No_argument "v"
71 ; 71 ;
72 applet_long_options = getopt_longopts;
73#endif 72#endif
74 73
75 opt_complementary = "=1"; /* exactly one non-option arg: the mountpoint */ 74 opts = getopt32long(argv, "^" "o:l:m:v" "\0" "=1", fstrim_longopts,
76 opts = getopt32(argv, "o:l:m:v", &arg_o, &arg_l, &arg_m); 75 &arg_o, &arg_l, &arg_m
76 );
77 77
78 memset(&range, 0, sizeof(range)); 78 memset(&range, 0, sizeof(range));
79 range.len = ULLONG_MAX; 79 range.len = ULLONG_MAX;
diff --git a/util-linux/getopt.c b/util-linux/getopt.c
index cf1bc592f..a151b7e56 100644
--- a/util-linux/getopt.c
+++ b/util-linux/getopt.c
@@ -42,32 +42,18 @@
42//config: 42//config:
43//config:config FEATURE_GETOPT_LONG 43//config:config FEATURE_GETOPT_LONG
44//config: bool "Support -l LONGOPTs" 44//config: bool "Support -l LONGOPTs"
45//config: default y if LONG_OPTS 45//config: default y
46//config: depends on GETOPT 46//config: depends on GETOPT && LONG_OPTS
47//config: help 47//config: help
48//config: Enable support for long options (option -l). 48//config: Enable support for long options (option -l).
49 49
50//applet:IF_GETOPT(APPLET(getopt, BB_DIR_BIN, BB_SUID_DROP)) 50//applet:IF_GETOPT(APPLET_NOEXEC(getopt, getopt, BB_DIR_BIN, BB_SUID_DROP, getopt))
51 51
52//kbuild:lib-$(CONFIG_GETOPT) += getopt.o 52//kbuild:lib-$(CONFIG_GETOPT) += getopt.o
53 53
54//usage:#define getopt_trivial_usage 54//usage:#define getopt_trivial_usage
55//usage: "[OPTIONS] [--] OPTSTRING PARAMS" 55//usage: "[OPTIONS] [--] OPTSTRING PARAMS"
56//usage:#define getopt_full_usage "\n\n" 56//usage:#define getopt_full_usage "\n\n"
57//usage: IF_LONG_OPTS(
58//usage: IF_FEATURE_GETOPT_LONG(
59//usage: " -a,--alternative Allow long options starting with single -\n"
60//usage: " -l,--longoptions LOPT[,...] Long options to recognize\n"
61//usage: )
62//usage: " -n,--name PROGNAME The name under which errors are reported"
63//usage: "\n -o,--options OPTSTRING Short options to recognize"
64//usage: "\n -q,--quiet No error messages on unrecognized options"
65//usage: "\n -Q,--quiet-output No normal output"
66//usage: "\n -s,--shell SHELL Set shell quoting conventions"
67//usage: "\n -T,--test Version test (exits with 4)"
68//usage: "\n -u,--unquoted Don't quote output"
69//usage: )
70//usage: IF_NOT_LONG_OPTS(
71//usage: IF_FEATURE_GETOPT_LONG( 57//usage: IF_FEATURE_GETOPT_LONG(
72//usage: " -a Allow long options starting with single -\n" 58//usage: " -a Allow long options starting with single -\n"
73//usage: " -l LOPT[,...] Long options to recognize\n" 59//usage: " -l LOPT[,...] Long options to recognize\n"
@@ -79,7 +65,6 @@
79//usage: "\n -s SHELL Set shell quoting conventions" 65//usage: "\n -s SHELL Set shell quoting conventions"
80//usage: "\n -T Version test (exits with 4)" 66//usage: "\n -T Version test (exits with 4)"
81//usage: "\n -u Don't quote output" 67//usage: "\n -u Don't quote output"
82//usage: )
83//usage: IF_FEATURE_GETOPT_LONG( /* example uses -l, needs FEATURE_GETOPT_LONG */ 68//usage: IF_FEATURE_GETOPT_LONG( /* example uses -l, needs FEATURE_GETOPT_LONG */
84//usage: "\n" 69//usage: "\n"
85//usage: "\nExample:" 70//usage: "\nExample:"
@@ -411,8 +396,7 @@ int getopt_main(int argc, char **argv)
411#if !ENABLE_FEATURE_GETOPT_LONG 396#if !ENABLE_FEATURE_GETOPT_LONG
412 opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg); 397 opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
413#else 398#else
414 applet_long_options = getopt_longopts; 399 opt = getopt32long(argv, "+o:n:qQs:Tual:*", getopt_longopts,
415 opt = getopt32(argv, "+o:n:qQs:Tual:*",
416 &optstr, &name, &s_arg, &l_arg); 400 &optstr, &name, &s_arg, &l_arg);
417 /* Effectuate the read options for the applet itself */ 401 /* Effectuate the read options for the applet itself */
418 while (l_arg) { 402 while (l_arg) {
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index 37e58f2d0..6cf6d0297 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -73,8 +73,9 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
73#define OPT_s (1 << 1) 73#define OPT_s (1 << 1)
74#define OPT_a (1 << 2) 74#define OPT_a (1 << 2)
75#define OPT_p (1 << 3) 75#define OPT_p (1 << 3)
76 opt_complementary = "?1"; /* 1 argument max */ 76 opt = getopt32(argv, "^" "l:s:apg:+c:+" "\0" "?1" /* 1 argument max */,
77 opt = getopt32(argv, "l:s:apg:+c:+", &opt_l, &opt_s, &bytes, &cols); 77 &opt_l, &opt_s, &bytes, &cols
78 );
78 argv += optind; 79 argv += optind;
79 80
80 dumper->dump_vflag = ALL; 81 dumper->dump_vflag = ALL;
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c
index 5df56de15..29f51021e 100644
--- a/util-linux/hwclock.c
+++ b/util-linux/hwclock.c
@@ -16,11 +16,6 @@
16//config: shutdown in the hardware clock, so the hardware will keep the 16//config: shutdown in the hardware clock, so the hardware will keep the
17//config: correct time when Linux is _not_ running. 17//config: correct time when Linux is _not_ running.
18//config: 18//config:
19//config:config FEATURE_HWCLOCK_LONG_OPTIONS
20//config: bool "Support long options (--hctosys,...)"
21//config: default y
22//config: depends on HWCLOCK && LONG_OPTS
23//config:
24//config:config FEATURE_HWCLOCK_ADJTIME_FHS 19//config:config FEATURE_HWCLOCK_ADJTIME_FHS
25//config: bool "Use FHS /var/lib/hwclock/adjtime" 20//config: bool "Use FHS /var/lib/hwclock/adjtime"
26//config: default n # util-linux-ng in Fedora 13 still uses /etc/adjtime 21//config: default n # util-linux-ng in Fedora 13 still uses /etc/adjtime
@@ -293,12 +288,12 @@ static void set_system_clock_timezone(int utc)
293} 288}
294 289
295//usage:#define hwclock_trivial_usage 290//usage:#define hwclock_trivial_usage
296//usage: IF_FEATURE_HWCLOCK_LONG_OPTIONS( 291//usage: IF_LONG_OPTS(
297//usage: "[-r|--show] [-s|--hctosys] [-w|--systohc] [-t|--systz]" 292//usage: "[-r|--show] [-s|--hctosys] [-w|--systohc] [--systz]"
298//usage: " [-l|--localtime] [-u|--utc]" 293//usage: " [--localtime] [-u|--utc]"
299//usage: " [-f|--rtc FILE]" 294//usage: " [-f|--rtc FILE]"
300//usage: ) 295//usage: )
301//usage: IF_NOT_FEATURE_HWCLOCK_LONG_OPTIONS( 296//usage: IF_NOT_LONG_OPTS(
302//usage: "[-r] [-s] [-w] [-t] [-l] [-u] [-f FILE]" 297//usage: "[-r] [-s] [-w] [-t] [-l] [-u] [-f FILE]"
303//usage: ) 298//usage: )
304//usage:#define hwclock_full_usage "\n\n" 299//usage:#define hwclock_full_usage "\n\n"
@@ -306,12 +301,18 @@ static void set_system_clock_timezone(int utc)
306//usage: "\n -r Show hardware clock time" 301//usage: "\n -r Show hardware clock time"
307//usage: "\n -s Set system time from hardware clock" 302//usage: "\n -s Set system time from hardware clock"
308//usage: "\n -w Set hardware clock from system time" 303//usage: "\n -w Set hardware clock from system time"
309//usage: "\n -t Set in-kernel timezone, correct system time" 304//usage: IF_LONG_OPTS(
305//usage: "\n --systz Set in-kernel timezone, correct system time"
306//usage: )
310//usage: "\n if hardware clock is in local time" 307//usage: "\n if hardware clock is in local time"
311//usage: "\n -u Assume hardware clock is kept in UTC" 308//usage: "\n -u Assume hardware clock is kept in UTC"
312//usage: "\n -l Assume hardware clock is kept in local time" 309//usage: IF_LONG_OPTS(
310//usage: "\n --localtime Assume hardware clock is kept in local time"
311//usage: )
313//usage: "\n -f FILE Use specified device (e.g. /dev/rtc2)" 312//usage: "\n -f FILE Use specified device (e.g. /dev/rtc2)"
314 313
314//TODO: get rid of incompatible -t and -l aliases to --systz and --localtime
315
315#define HWCLOCK_OPT_LOCALTIME 0x01 316#define HWCLOCK_OPT_LOCALTIME 0x01
316#define HWCLOCK_OPT_UTC 0x02 317#define HWCLOCK_OPT_UTC 0x02
317#define HWCLOCK_OPT_SHOW 0x04 318#define HWCLOCK_OPT_SHOW 0x04
@@ -327,7 +328,7 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
327 unsigned opt; 328 unsigned opt;
328 int utc; 329 int utc;
329 330
330#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS 331#if ENABLE_LONG_OPTS
331 static const char hwclock_longopts[] ALIGN1 = 332 static const char hwclock_longopts[] ALIGN1 =
332 "localtime\0" No_argument "l" /* short opt is non-standard */ 333 "localtime\0" No_argument "l" /* short opt is non-standard */
333 "utc\0" No_argument "u" 334 "utc\0" No_argument "u"
@@ -337,14 +338,16 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
337 "systz\0" No_argument "t" /* short opt is non-standard */ 338 "systz\0" No_argument "t" /* short opt is non-standard */
338 "rtc\0" Required_argument "f" 339 "rtc\0" Required_argument "f"
339 ; 340 ;
340 applet_long_options = hwclock_longopts;
341#endif 341#endif
342 342
343 /* Initialize "timezone" (libc global variable) */ 343 /* Initialize "timezone" (libc global variable) */
344 tzset(); 344 tzset();
345 345
346 opt_complementary = "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l"; 346 opt = getopt32long(argv,
347 opt = getopt32(argv, "lurswtf:", &rtcname); 347 "^lurswtf:" "\0" "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l",
348 hwclock_longopts,
349 &rtcname
350 );
348 351
349 /* If -u or -l wasn't given check if we are using utc */ 352 /* If -u or -l wasn't given check if we are using utc */
350 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) 353 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
diff --git a/util-linux/ionice.c b/util-linux/ionice.c
index c7b7f0373..5b9664d25 100644
--- a/util-linux/ionice.c
+++ b/util-linux/ionice.c
@@ -14,7 +14,7 @@
14//config: Set/set program io scheduling class and priority 14//config: Set/set program io scheduling class and priority
15//config: Requires kernel >= 2.6.13 15//config: Requires kernel >= 2.6.13
16 16
17//applet:IF_IONICE(APPLET(ionice, BB_DIR_BIN, BB_SUID_DROP)) 17//applet:IF_IONICE(APPLET_NOEXEC(ionice, ionice, BB_DIR_BIN, BB_SUID_DROP, ionice))
18 18
19//kbuild:lib-$(CONFIG_IONICE) += ionice.o 19//kbuild:lib-$(CONFIG_IONICE) += ionice.o
20 20
diff --git a/util-linux/ipcrm.c b/util-linux/ipcrm.c
index c51d33143..a93ceee11 100644
--- a/util-linux/ipcrm.c
+++ b/util-linux/ipcrm.c
@@ -15,19 +15,10 @@
15//config: communication (IPC) objects and the associated data structures 15//config: communication (IPC) objects and the associated data structures
16//config: from the system. 16//config: from the system.
17 17
18//applet:IF_IPCRM(APPLET(ipcrm, BB_DIR_USR_BIN, BB_SUID_DROP)) 18//applet:IF_IPCRM(APPLET_NOEXEC(ipcrm, ipcrm, BB_DIR_USR_BIN, BB_SUID_DROP, ipcrm))
19 19
20//kbuild:lib-$(CONFIG_IPCRM) += ipcrm.o 20//kbuild:lib-$(CONFIG_IPCRM) += ipcrm.o
21 21
22//usage:#define ipcrm_trivial_usage
23//usage: "[-MQS key] [-mqs id]"
24//usage:#define ipcrm_full_usage "\n\n"
25//usage: "Upper-case options MQS remove an object by shmkey value.\n"
26//usage: "Lower-case options remove an object by shmid value.\n"
27//usage: "\n -mM Remove memory segment after last detach"
28//usage: "\n -qQ Remove message queue"
29//usage: "\n -sS Remove semaphore"
30
31#include "libbb.h" 22#include "libbb.h"
32 23
33/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ 24/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
@@ -94,6 +85,14 @@ static int remove_ids(type_id type, char **argv)
94} 85}
95#endif /* IPCRM_LEGACY */ 86#endif /* IPCRM_LEGACY */
96 87
88//usage:#define ipcrm_trivial_usage
89//usage: "[-MQS key] [-mqs id]"
90//usage:#define ipcrm_full_usage "\n\n"
91//usage: "Upper-case options MQS remove an object by shmkey value.\n"
92//usage: "Lower-case options remove an object by shmid value.\n"
93//usage: "\n -mM Remove memory segment after last detach"
94//usage: "\n -qQ Remove message queue"
95//usage: "\n -sS Remove semaphore"
97 96
98int ipcrm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 97int ipcrm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
99int ipcrm_main(int argc, char **argv) 98int ipcrm_main(int argc, char **argv)
@@ -137,28 +136,20 @@ int ipcrm_main(int argc, char **argv)
137#endif /* IPCRM_LEGACY */ 136#endif /* IPCRM_LEGACY */
138 137
139 /* process new syntax to conform with SYSV ipcrm */ 138 /* process new syntax to conform with SYSV ipcrm */
140 while ((c = getopt(argc, argv, "q:m:s:Q:M:S:h?")) != -1) { 139 while ((c = getopt(argc, argv, "q:m:s:Q:M:S:")) != -1) {
141 int result; 140 int result;
142 int id = 0; 141 int id;
143 int iskey = isupper(c); 142 int iskey;
144
145 /* needed to delete semaphores */ 143 /* needed to delete semaphores */
146 union semun arg; 144 union semun arg;
147 145
148 arg.val = 0; 146 if (c == '?') /* option not in the string */
149
150 if ((c == '?') || (c == 'h')) {
151 bb_show_usage(); 147 bb_show_usage();
152 }
153
154 /* we don't need case information any more */
155 c = tolower(c);
156 148
157 /* make sure the option is in range: allowed are q, m, s */ 149 id = 0;
158 if (c != 'q' && c != 'm' && c != 's') { 150 arg.val = 0;
159 bb_show_usage();
160 }
161 151
152 iskey = !(c & 0x20); /* uppercase? */
162 if (iskey) { 153 if (iskey) {
163 /* keys are in hex or decimal */ 154 /* keys are in hex or decimal */
164 key_t key = xstrtoul(optarg, 0); 155 key_t key = xstrtoul(optarg, 0);
@@ -169,6 +160,7 @@ int ipcrm_main(int argc, char **argv)
169 continue; 160 continue;
170 } 161 }
171 162
163 c |= 0x20; /* lowercase. c is 'q', 'm' or 's' now */
172 /* convert key to id */ 164 /* convert key to id */
173 id = ((c == 'q') ? msgget(key, 0) : 165 id = ((c == 'q') ? msgget(key, 0) :
174 (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0)); 166 (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0));
diff --git a/util-linux/ipcs.c b/util-linux/ipcs.c
index 1404930d4..7092ecd92 100644
--- a/util-linux/ipcs.c
+++ b/util-linux/ipcs.c
@@ -15,26 +15,10 @@
15//config: The ipcs utility is used to provide information on the currently 15//config: The ipcs utility is used to provide information on the currently
16//config: allocated System V interprocess (IPC) objects in the system. 16//config: allocated System V interprocess (IPC) objects in the system.
17 17
18//applet:IF_IPCS(APPLET(ipcs, BB_DIR_USR_BIN, BB_SUID_DROP)) 18//applet:IF_IPCS(APPLET_NOEXEC(ipcs, ipcs, BB_DIR_USR_BIN, BB_SUID_DROP, ipcs))
19 19
20//kbuild:lib-$(CONFIG_IPCS) += ipcs.o 20//kbuild:lib-$(CONFIG_IPCS) += ipcs.o
21 21
22//usage:#define ipcs_trivial_usage
23//usage: "[[-smq] -i shmid] | [[-asmq] [-tcplu]]"
24//usage:#define ipcs_full_usage "\n\n"
25//usage: " -i Show specific resource"
26//usage: "\nResource specification:"
27//usage: "\n -m Shared memory segments"
28//usage: "\n -q Message queues"
29//usage: "\n -s Semaphore arrays"
30//usage: "\n -a All (default)"
31//usage: "\nOutput format:"
32//usage: "\n -t Time"
33//usage: "\n -c Creator"
34//usage: "\n -p Pid"
35//usage: "\n -l Limits"
36//usage: "\n -u Summary"
37
38/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */ 22/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
39/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */ 23/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
40/* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */ 24/* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */
@@ -585,6 +569,22 @@ static void print_sem(int semid)
585 bb_putchar('\n'); 569 bb_putchar('\n');
586} 570}
587 571
572//usage:#define ipcs_trivial_usage
573//usage: "[[-smq] -i shmid] | [[-asmq] [-tcplu]]"
574//usage:#define ipcs_full_usage "\n\n"
575//usage: " -i Show specific resource"
576//usage: "\nResource specification:"
577//usage: "\n -m Shared memory segments"
578//usage: "\n -q Message queues"
579//usage: "\n -s Semaphore arrays"
580//usage: "\n -a All (default)"
581//usage: "\nOutput format:"
582//usage: "\n -t Time"
583//usage: "\n -c Creator"
584//usage: "\n -p Pid"
585//usage: "\n -l Limits"
586//usage: "\n -u Summary"
587
588int ipcs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 588int ipcs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
589int ipcs_main(int argc UNUSED_PARAM, char **argv) 589int ipcs_main(int argc UNUSED_PARAM, char **argv)
590{ 590{
@@ -632,6 +632,10 @@ int ipcs_main(int argc UNUSED_PARAM, char **argv)
632 flags |= flag_msg | flag_shm | flag_sem; 632 flags |= flag_msg | flag_shm | flag_sem;
633 bb_putchar('\n'); 633 bb_putchar('\n');
634 634
635 if (flags & flag_msg) {
636 do_msg();
637 bb_putchar('\n');
638 }
635 if (flags & flag_shm) { 639 if (flags & flag_shm) {
636 do_shm(); 640 do_shm();
637 bb_putchar('\n'); 641 bb_putchar('\n');
@@ -640,9 +644,5 @@ int ipcs_main(int argc UNUSED_PARAM, char **argv)
640 do_sem(); 644 do_sem();
641 bb_putchar('\n'); 645 bb_putchar('\n');
642 } 646 }
643 if (flags & flag_msg) {
644 do_msg();
645 bb_putchar('\n');
646 }
647 fflush_stdout_and_exit(EXIT_SUCCESS); 647 fflush_stdout_and_exit(EXIT_SUCCESS);
648} 648}
diff --git a/util-linux/losetup.c b/util-linux/losetup.c
index c608de6cc..6b171d710 100644
--- a/util-linux/losetup.c
+++ b/util-linux/losetup.c
@@ -15,9 +15,9 @@
15//config: file or block device, and to query the status of a loop device. This 15//config: file or block device, and to query the status of a loop device. This
16//config: version does not currently support enabling data encryption. 16//config: version does not currently support enabling data encryption.
17 17
18//kbuild:lib-$(CONFIG_LOSETUP) += losetup.o 18//applet:IF_LOSETUP(APPLET_NOEXEC(losetup, losetup, BB_DIR_SBIN, BB_SUID_DROP, losetup))
19 19
20//applet:IF_LOSETUP(APPLET(losetup, BB_DIR_SBIN, BB_SUID_DROP)) 20//kbuild:lib-$(CONFIG_LOSETUP) += losetup.o
21 21
22//usage:#define losetup_trivial_usage 22//usage:#define losetup_trivial_usage
23//usage: "[-r] [-o OFS] {-f|LOOPDEV} FILE - associate loop devices\n" 23//usage: "[-r] [-o OFS] {-f|LOOPDEV} FILE - associate loop devices\n"
@@ -57,8 +57,7 @@ int losetup_main(int argc UNUSED_PARAM, char **argv)
57 OPT_r = (1 << 4), /* must be last */ 57 OPT_r = (1 << 4), /* must be last */
58 }; 58 };
59 59
60 opt_complementary = "?2:d--ofar:a--ofr"; 60 opt = getopt32(argv, "^" "do:far" "\0" "?2:d--ofar:a--ofr", &opt_o);
61 opt = getopt32(argv, "do:far", &opt_o);
62 argv += optind; 61 argv += optind;
63 62
64 /* LOOPDEV */ 63 /* LOOPDEV */
diff --git a/util-linux/lspci.c b/util-linux/lspci.c
index 3877deb67..0000fbfda 100644
--- a/util-linux/lspci.c
+++ b/util-linux/lspci.c
@@ -16,7 +16,7 @@
16//config: 16//config:
17//config: This version uses sysfs (/sys/bus/pci/devices) only. 17//config: This version uses sysfs (/sys/bus/pci/devices) only.
18 18
19//applet:IF_LSPCI(APPLET(lspci, BB_DIR_USR_BIN, BB_SUID_DROP)) 19//applet:IF_LSPCI(APPLET_NOEXEC(lspci, lspci, BB_DIR_USR_BIN, BB_SUID_DROP, lspci))
20 20
21//kbuild:lib-$(CONFIG_LSPCI) += lspci.o 21//kbuild:lib-$(CONFIG_LSPCI) += lspci.o
22 22
diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c
index cabf047cf..33639413a 100644
--- a/util-linux/lsusb.c
+++ b/util-linux/lsusb.c
@@ -16,7 +16,7 @@
16//config: 16//config:
17//config: This version uses sysfs (/sys/bus/usb/devices) only. 17//config: This version uses sysfs (/sys/bus/usb/devices) only.
18 18
19//applet:IF_LSUSB(APPLET(lsusb, BB_DIR_USR_BIN, BB_SUID_DROP)) 19//applet:IF_LSUSB(APPLET_NOEXEC(lsusb, lsusb, BB_DIR_USR_BIN, BB_SUID_DROP, lsusb))
20 20
21//kbuild:lib-$(CONFIG_LSUSB) += lsusb.o 21//kbuild:lib-$(CONFIG_LSUSB) += lsusb.o
22 22
diff --git a/util-linux/mesg.c b/util-linux/mesg.c
index c4371eb24..91c05317e 100644
--- a/util-linux/mesg.c
+++ b/util-linux/mesg.c
@@ -26,7 +26,7 @@
26//config: If you set this option to N, "mesg y" will enable writing 26//config: If you set this option to N, "mesg y" will enable writing
27//config: by anybody at all. This is not recommended. 27//config: by anybody at all. This is not recommended.
28 28
29//applet:IF_MESG(APPLET(mesg, BB_DIR_USR_BIN, BB_SUID_DROP)) 29//applet:IF_MESG(APPLET_NOFORK(mesg, mesg, BB_DIR_USR_BIN, BB_SUID_DROP, mesg))
30 30
31//kbuild:lib-$(CONFIG_MESG) += mesg.o 31//kbuild:lib-$(CONFIG_MESG) += mesg.o
32 32
@@ -60,10 +60,15 @@ int mesg_main(int argc UNUSED_PARAM, char **argv)
60 bb_show_usage(); 60 bb_show_usage();
61 } 61 }
62 62
63 /* We are a NOFORK applet.
64 * (Not that it's very useful, but code is trivially NOFORK-safe).
65 * Play nice. Do not leak anything.
66 */
67
63 if (!isatty(STDIN_FILENO)) 68 if (!isatty(STDIN_FILENO))
64 bb_error_msg_and_die("not a tty"); 69 bb_error_msg_and_die("not a tty");
65 70
66 xfstat(STDIN_FILENO, &sb, "stderr"); 71 xfstat(STDIN_FILENO, &sb, "stdin");
67 if (c == 0) { 72 if (c == 0) {
68 puts((sb.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n"); 73 puts((sb.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n");
69 return EXIT_SUCCESS; 74 return EXIT_SUCCESS;
diff --git a/util-linux/mkfs_minix.c b/util-linux/mkfs_minix.c
index 88647e9d8..69dfcd123 100644
--- a/util-linux/mkfs_minix.c
+++ b/util-linux/mkfs_minix.c
@@ -142,7 +142,10 @@ struct globals {
142 unsigned currently_testing; 142 unsigned currently_testing;
143 143
144 char root_block[BLOCK_SIZE]; 144 char root_block[BLOCK_SIZE];
145 char superblock_buffer[BLOCK_SIZE]; 145 union {
146 char superblock_buffer[BLOCK_SIZE];
147 struct minix_superblock SB;
148 } u;
146 char boot_block_buffer[512]; 149 char boot_block_buffer[512];
147 unsigned short good_blocks_table[MAX_GOOD_BLOCKS]; 150 unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
148 /* check_blocks(): buffer[] was the biggest static in entire bbox */ 151 /* check_blocks(): buffer[] was the biggest static in entire bbox */
@@ -166,7 +169,7 @@ static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
166#define INODE_BUF1 (((struct minix1_inode*)G.inode_buffer) - 1) 169#define INODE_BUF1 (((struct minix1_inode*)G.inode_buffer) - 1)
167#define INODE_BUF2 (((struct minix2_inode*)G.inode_buffer) - 1) 170#define INODE_BUF2 (((struct minix2_inode*)G.inode_buffer) - 1)
168 171
169#define SB (*(struct minix_superblock*)G.superblock_buffer) 172#define SB (G.u.SB)
170 173
171#define SB_INODES (SB.s_ninodes) 174#define SB_INODES (SB.s_ninodes)
172#define SB_IMAPS (SB.s_imap_blocks) 175#define SB_IMAPS (SB.s_imap_blocks)
@@ -234,7 +237,7 @@ static void write_tables(void)
234 xlseek(dev_fd, BLOCK_SIZE, SEEK_SET); 237 xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
235 238
236 msg_eol = "can't write superblock"; 239 msg_eol = "can't write superblock";
237 xwrite(dev_fd, G.superblock_buffer, BLOCK_SIZE); 240 xwrite(dev_fd, G.u.superblock_buffer, BLOCK_SIZE);
238 241
239 msg_eol = "can't write inode map"; 242 msg_eol = "can't write inode map";
240 xwrite(dev_fd, G.inode_map, SB_IMAPS * BLOCK_SIZE); 243 xwrite(dev_fd, G.inode_map, SB_IMAPS * BLOCK_SIZE);
@@ -541,7 +544,7 @@ static void setup_tables(void)
541 unsigned sb_zmaps; 544 unsigned sb_zmaps;
542 unsigned i; 545 unsigned i;
543 546
544 /* memset(G.superblock_buffer, 0, BLOCK_SIZE); */ 547 /* memset(G.u.superblock_buffer, 0, BLOCK_SIZE); */
545 /* memset(G.boot_block_buffer, 0, 512); */ 548 /* memset(G.boot_block_buffer, 0, 512); */
546 SB_MAGIC = G.magic; 549 SB_MAGIC = G.magic;
547 SB_ZONE_SIZE = 0; 550 SB_ZONE_SIZE = 0;
diff --git a/util-linux/mkfs_reiser.c b/util-linux/mkfs_reiser.c
index c7d99b018..390aef86c 100644
--- a/util-linux/mkfs_reiser.c
+++ b/util-linux/mkfs_reiser.c
@@ -180,8 +180,7 @@ int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv)
180 180
181 // using global "option_mask32" instead of local "opts": 181 // using global "option_mask32" instead of local "opts":
182 // we are register starved here 182 // we are register starved here
183 opt_complementary = "-1"; 183 /*opts =*/ getopt32(argv, "^" "b:+j:s:o:t:B:h:u:l:fqd" "\0" "-1",
184 /*opts =*/ getopt32(argv, "b:+j:s:o:t:B:h:u:l:fqd",
185 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &label); 184 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &label);
186 argv += optind; // argv[0] -- device 185 argv += optind; // argv[0] -- device
187 186
diff --git a/util-linux/mkfs_vfat.c b/util-linux/mkfs_vfat.c
index f9768ed56..426854b1e 100644
--- a/util-linux/mkfs_vfat.c
+++ b/util-linux/mkfs_vfat.c
@@ -269,8 +269,9 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
269 OPT_v = 1 << 16, // verbose 269 OPT_v = 1 << 16, // verbose
270 }; 270 };
271 271
272 opt_complementary = "-1";//:b+:f+:F+:h+:r+:R+:s+:S+:vv:c--l:l--c"; 272 opts = getopt32(argv, "^"
273 opts = getopt32(argv, "Ab:cCf:F:h:Ii:l:m:n:r:R:s:S:v", 273 "Ab:cCf:F:h:Ii:l:m:n:r:R:s:S:v"
274 "\0" "-1", //:b+:f+:F+:h+:r+:R+:s+:S+:vv:c--l:l--c
274 NULL, NULL, NULL, NULL, NULL, 275 NULL, NULL, NULL, NULL, NULL,
275 NULL, NULL, &volume_label, NULL, NULL, NULL, NULL); 276 NULL, NULL, &volume_label, NULL, NULL, NULL, NULL);
276 argv += optind; 277 argv += optind;
diff --git a/util-linux/mkswap.c b/util-linux/mkswap.c
index e44e13c0d..71449882d 100644
--- a/util-linux/mkswap.c
+++ b/util-linux/mkswap.c
@@ -119,9 +119,8 @@ int mkswap_main(int argc UNUSED_PARAM, char **argv)
119 119
120 INIT_G(); 120 INIT_G();
121 121
122 opt_complementary = "-1"; /* at least one param */
123 /* TODO: -p PAGESZ, -U UUID */ 122 /* TODO: -p PAGESZ, -U UUID */
124 getopt32(argv, "L:", &label); 123 getopt32(argv, "^" "L:" "\0" "-1"/*at least one arg*/, &label);
125 argv += optind; 124 argv += optind;
126 125
127 fd = xopen(argv[0], O_WRONLY); 126 fd = xopen(argv[0], O_WRONLY);
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 4d5c2243a..4eade0869 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -69,7 +69,6 @@
69//config: bool "Support mounting NFS file systems on Linux < 2.6.23" 69//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
70//config: default n 70//config: default n
71//config: depends on MOUNT 71//config: depends on MOUNT
72//config: select FEATURE_HAVE_RPC
73//config: select FEATURE_SYSLOG 72//config: select FEATURE_SYSLOG
74//config: help 73//config: help
75//config: Enable mounting of NFS file systems on Linux kernels prior 74//config: Enable mounting of NFS file systems on Linux kernels prior
@@ -2205,10 +2204,14 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
2205 2204
2206 // Parse remaining options 2205 // Parse remaining options
2207 // Max 2 params; -o is a list, -v is a counter 2206 // Max 2 params; -o is a list, -v is a counter
2208 opt_complementary = "?2" IF_FEATURE_MOUNT_VERBOSE("vv"); 2207 opt = getopt32(argv, "^"
2209 opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch 2208 OPTION_STR
2210 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname) 2209 "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2211 IF_FEATURE_MOUNT_VERBOSE(, &verbose)); 2210 &lst_o, &fstype, &O_optmatch
2211 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2212 IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2213 );
2214
2212 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o 2215 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2213 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r 2216 if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2214 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w 2217 if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
diff --git a/util-linux/mountpoint.c b/util-linux/mountpoint.c
index b7f048196..6b21a5fb3 100644
--- a/util-linux/mountpoint.c
+++ b/util-linux/mountpoint.c
@@ -14,7 +14,7 @@
14//config: help 14//config: help
15//config: mountpoint checks if the directory is a mountpoint. 15//config: mountpoint checks if the directory is a mountpoint.
16 16
17//applet:IF_MOUNTPOINT(APPLET(mountpoint, BB_DIR_BIN, BB_SUID_DROP)) 17//applet:IF_MOUNTPOINT(APPLET_NOEXEC(mountpoint, mountpoint, BB_DIR_BIN, BB_SUID_DROP, mountpoint))
18 18
19//kbuild:lib-$(CONFIG_MOUNTPOINT) += mountpoint.o 19//kbuild:lib-$(CONFIG_MOUNTPOINT) += mountpoint.o
20 20
@@ -43,8 +43,7 @@ int mountpoint_main(int argc UNUSED_PARAM, char **argv)
43 char *arg; 43 char *arg;
44 int rc, opt; 44 int rc, opt;
45 45
46 opt_complementary = "=1"; /* must have one argument */ 46 opt = getopt32(argv, "^" "qdxn" "\0" "=1");
47 opt = getopt32(argv, "qdxn");
48#define OPT_q (1) 47#define OPT_q (1)
49#define OPT_d (2) 48#define OPT_d (2)
50#define OPT_x (4) 49#define OPT_x (4)
diff --git a/util-linux/nsenter.c b/util-linux/nsenter.c
index 12c86b0ed..d91b0b509 100644
--- a/util-linux/nsenter.c
+++ b/util-linux/nsenter.c
@@ -13,14 +13,6 @@
13//config: select PLATFORM_LINUX 13//config: select PLATFORM_LINUX
14//config: help 14//config: help
15//config: Run program with namespaces of other processes. 15//config: Run program with namespaces of other processes.
16//config:
17//config:config FEATURE_NSENTER_LONG_OPTS
18//config: bool "Enable long options"
19//config: default y
20//config: depends on NSENTER && LONG_OPTS
21//config: help
22//config: Support long options for the nsenter applet. This makes
23//config: the busybox implementation more compatible with upstream.
24 16
25//applet:IF_NSENTER(APPLET(nsenter, BB_DIR_USR_BIN, BB_SUID_DROP)) 17//applet:IF_NSENTER(APPLET(nsenter, BB_DIR_USR_BIN, BB_SUID_DROP))
26 18
@@ -28,22 +20,6 @@
28 20
29//usage:#define nsenter_trivial_usage 21//usage:#define nsenter_trivial_usage
30//usage: "[OPTIONS] [PROG [ARGS]]" 22//usage: "[OPTIONS] [PROG [ARGS]]"
31//usage:#if ENABLE_FEATURE_NSENTER_LONG_OPTS
32//usage:#define nsenter_full_usage "\n"
33//usage: "\n -t,--target PID Target process to get namespaces from"
34//usage: "\n -m,--mount[=FILE] Enter mount namespace"
35//usage: "\n -u,--uts[=FILE] Enter UTS namespace (hostname etc)"
36//usage: "\n -i,--ipc[=FILE] Enter System V IPC namespace"
37//usage: "\n -n,--net[=FILE] Enter network namespace"
38//usage: "\n -p,--pid[=FILE] Enter pid namespace"
39//usage: "\n -U,--user[=FILE] Enter user namespace"
40//usage: "\n -S,--setuid UID Set uid in entered namespace"
41//usage: "\n -G,--setgid GID Set gid in entered namespace"
42//usage: "\n --preserve-credentials Don't touch uids or gids"
43//usage: "\n -r,--root[=DIR] Set root directory"
44//usage: "\n -w,--wd[=DIR] Set working directory"
45//usage: "\n -F,--no-fork Don't fork before exec'ing PROG"
46//usage:#else
47//usage:#define nsenter_full_usage "\n" 23//usage:#define nsenter_full_usage "\n"
48//usage: "\n -t PID Target process to get namespaces from" 24//usage: "\n -t PID Target process to get namespaces from"
49//usage: "\n -m[FILE] Enter mount namespace" 25//usage: "\n -m[FILE] Enter mount namespace"
@@ -54,10 +30,12 @@
54//usage: "\n -U[FILE] Enter user namespace" 30//usage: "\n -U[FILE] Enter user namespace"
55//usage: "\n -S UID Set uid in entered namespace" 31//usage: "\n -S UID Set uid in entered namespace"
56//usage: "\n -G GID Set gid in entered namespace" 32//usage: "\n -G GID Set gid in entered namespace"
33//usage: IF_LONG_OPTS(
34//usage: "\n --preserve-credentials Don't touch uids or gids"
35//usage: )
57//usage: "\n -r[DIR] Set root directory" 36//usage: "\n -r[DIR] Set root directory"
58//usage: "\n -w[DIR] Set working directory" 37//usage: "\n -w[DIR] Set working directory"
59//usage: "\n -F Don't fork before exec'ing PROG" 38//usage: "\n -F Don't fork before exec'ing PROG"
60//usage:#endif
61 39
62#include <sched.h> 40#include <sched.h>
63#ifndef CLONE_NEWUTS 41#ifndef CLONE_NEWUTS
@@ -101,7 +79,7 @@ enum {
101 OPT_root = 1 << 9, 79 OPT_root = 1 << 9,
102 OPT_wd = 1 << 10, 80 OPT_wd = 1 << 10,
103 OPT_nofork = 1 << 11, 81 OPT_nofork = 1 << 11,
104 OPT_prescred = (1 << 12) * ENABLE_FEATURE_NSENTER_LONG_OPTS, 82 OPT_prescred = (1 << 12) * ENABLE_LONG_OPTS,
105}; 83};
106enum { 84enum {
107 NS_USR_POS = 0, 85 NS_USR_POS = 0,
@@ -130,7 +108,7 @@ static const struct namespace_descr ns_list[] = {
130 */ 108 */
131static const char opt_str[] ALIGN1 = "U::i::u::n::p::m::""t+S+G+r::w::F"; 109static const char opt_str[] ALIGN1 = "U::i::u::n::p::m::""t+S+G+r::w::F";
132 110
133#if ENABLE_FEATURE_NSENTER_LONG_OPTS 111#if ENABLE_LONG_OPTS
134static const char nsenter_longopts[] ALIGN1 = 112static const char nsenter_longopts[] ALIGN1 =
135 "user\0" Optional_argument "U" 113 "user\0" Optional_argument "U"
136 "ipc\0" Optional_argument "i" 114 "ipc\0" Optional_argument "i"
@@ -190,8 +168,7 @@ int nsenter_main(int argc UNUSED_PARAM, char **argv)
190 168
191 memset(ns_ctx_list, 0, sizeof(ns_ctx_list)); 169 memset(ns_ctx_list, 0, sizeof(ns_ctx_list));
192 170
193 IF_FEATURE_NSENTER_LONG_OPTS(applet_long_options = nsenter_longopts); 171 opts = getopt32long(argv, opt_str, nsenter_longopts,
194 opts = getopt32(argv, opt_str,
195 &ns_ctx_list[NS_USR_POS].path, 172 &ns_ctx_list[NS_USR_POS].path,
196 &ns_ctx_list[NS_IPC_POS].path, 173 &ns_ctx_list[NS_IPC_POS].path,
197 &ns_ctx_list[NS_UTS_POS].path, 174 &ns_ctx_list[NS_UTS_POS].path,
diff --git a/util-linux/pivot_root.c b/util-linux/pivot_root.c
index 331038057..d6a26b912 100644
--- a/util-linux/pivot_root.c
+++ b/util-linux/pivot_root.c
@@ -21,7 +21,7 @@
21//config: Note: This is for initrd in linux 2.4. Under initramfs (introduced 21//config: Note: This is for initrd in linux 2.4. Under initramfs (introduced
22//config: in linux 2.6) use switch_root instead. 22//config: in linux 2.6) use switch_root instead.
23 23
24//applet:IF_PIVOT_ROOT(APPLET(pivot_root, BB_DIR_SBIN, BB_SUID_DROP)) 24//applet:IF_PIVOT_ROOT(APPLET_NOFORK(pivot_root, pivot_root, BB_DIR_SBIN, BB_SUID_DROP, pivot_root))
25 25
26//kbuild:lib-$(CONFIG_PIVOT_ROOT) += pivot_root.o 26//kbuild:lib-$(CONFIG_PIVOT_ROOT) += pivot_root.o
27 27
@@ -33,7 +33,7 @@
33 33
34#include "libbb.h" 34#include "libbb.h"
35 35
36extern int pivot_root(const char * new_root,const char * put_old); 36extern int pivot_root(const char *new_root, const char *put_old);
37 37
38int pivot_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 38int pivot_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
39int pivot_root_main(int argc, char **argv) 39int pivot_root_main(int argc, char **argv)
@@ -41,6 +41,8 @@ int pivot_root_main(int argc, char **argv)
41 if (argc != 3) 41 if (argc != 3)
42 bb_show_usage(); 42 bb_show_usage();
43 43
44 /* NOFORK applet. Hardly matters wrt performance, but code is trivial */
45
44 if (pivot_root(argv[1], argv[2]) < 0) { 46 if (pivot_root(argv[1], argv[2]) < 0) {
45 /* prints "pivot_root: <strerror text>" */ 47 /* prints "pivot_root: <strerror text>" */
46 bb_perror_nomsg_and_die(); 48 bb_perror_nomsg_and_die();
diff --git a/util-linux/rdate.c b/util-linux/rdate.c
index 14ce591e9..f27294e25 100644
--- a/util-linux/rdate.c
+++ b/util-linux/rdate.c
@@ -81,8 +81,7 @@ int rdate_main(int argc UNUSED_PARAM, char **argv)
81 time_t remote_time; 81 time_t remote_time;
82 unsigned flags; 82 unsigned flags;
83 83
84 opt_complementary = "-1"; 84 flags = getopt32(argv, "^" "sp" "\0" "-1");
85 flags = getopt32(argv, "sp");
86 85
87 remote_time = askremotedate(argv[optind]); 86 remote_time = askremotedate(argv[optind]);
88 87
diff --git a/util-linux/rdev.c b/util-linux/rdev.c
index 2ffe07688..7eb7413a8 100644
--- a/util-linux/rdev.c
+++ b/util-linux/rdev.c
@@ -14,7 +14,7 @@
14//config: help 14//config: help
15//config: Print the device node associated with the filesystem mounted at '/'. 15//config: Print the device node associated with the filesystem mounted at '/'.
16 16
17//applet:IF_RDEV(APPLET(rdev, BB_DIR_USR_SBIN, BB_SUID_DROP)) 17//applet:IF_RDEV(APPLET_NOEXEC(rdev, rdev, BB_DIR_USR_SBIN, BB_SUID_DROP, rdev))
18 18
19//kbuild:lib-$(CONFIG_RDEV) += rdev.o 19//kbuild:lib-$(CONFIG_RDEV) += rdev.o
20 20
diff --git a/util-linux/readprofile.c b/util-linux/readprofile.c
index b045657d8..394ece1dd 100644
--- a/util-linux/readprofile.c
+++ b/util-linux/readprofile.c
@@ -266,8 +266,10 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
266 printf("%6u %-40s %8.4f\n", 266 printf("%6u %-40s %8.4f\n",
267 total, "total", total/(double)(fn_add-add0)); 267 total, "total", total/(double)(fn_add-add0));
268 268
269 fclose(map); 269 if (ENABLE_FEATURE_CLEAN_UP) {
270 free(buf); 270 fclose(map);
271 free(buf);
272 }
271 273
272 return EXIT_SUCCESS; 274 return EXIT_SUCCESS;
273} 275}
diff --git a/util-linux/renice.c b/util-linux/renice.c
index 23cbca88d..70c494b3d 100644
--- a/util-linux/renice.c
+++ b/util-linux/renice.c
@@ -25,7 +25,7 @@
25//config: Renice alters the scheduling priority of one or more running 25//config: Renice alters the scheduling priority of one or more running
26//config: processes. 26//config: processes.
27 27
28//applet:IF_RENICE(APPLET(renice, BB_DIR_USR_BIN, BB_SUID_DROP)) 28//applet:IF_RENICE(APPLET_NOEXEC(renice, renice, BB_DIR_USR_BIN, BB_SUID_DROP, renice))
29 29
30//kbuild:lib-$(CONFIG_RENICE) += renice.o 30//kbuild:lib-$(CONFIG_RENICE) += renice.o
31 31
diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c
index 4c47c5369..8ffa4f3a6 100644
--- a/util-linux/rtcwake.c
+++ b/util-linux/rtcwake.c
@@ -154,11 +154,11 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv)
154 "seconds\0" Required_argument "s" 154 "seconds\0" Required_argument "s"
155 "time\0" Required_argument "t" 155 "time\0" Required_argument "t"
156 ; 156 ;
157 applet_long_options = rtcwake_longopts;
158#endif 157#endif
159 /* Must have -s or -t, exclusive */ 158 opt = getopt32long(argv,
160 opt_complementary = "s:t:s--t:t--s"; 159 /* Must have -s or -t, exclusive */
161 opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time); 160 "^alud:m:s:t:" "\0" "s:t:s--t:t--s", rtcwake_longopts,
161 &rtcname, &suspend, &opt_seconds, &opt_time);
162 162
163 /* this is the default 163 /* this is the default
164 if (opt & RTCWAKE_OPT_AUTO) 164 if (opt & RTCWAKE_OPT_AUTO)
diff --git a/util-linux/script.c b/util-linux/script.c
index 9eebb51a4..aac77c3ba 100644
--- a/util-linux/script.c
+++ b/util-linux/script.c
@@ -21,15 +21,25 @@
21//kbuild:lib-$(CONFIG_SCRIPT) += script.o 21//kbuild:lib-$(CONFIG_SCRIPT) += script.o
22 22
23//usage:#define script_trivial_usage 23//usage:#define script_trivial_usage
24//usage: "[-afq" IF_SCRIPTREPLAY("t") "] [-c PROG] [OUTFILE]" 24//usage: "[-afq] [-t[FILE]] [-c PROG] [OUTFILE]"
25//usage:#define script_full_usage "\n\n" 25//usage:#define script_full_usage "\n\n"
26//usage: " -a Append output" 26//usage: "Default OUTFILE is 'typescript'"
27//usage: "\n"
28//usage: "\n -a Append output"
27//usage: "\n -c PROG Run PROG, not shell" 29//usage: "\n -c PROG Run PROG, not shell"
28//usage: "\n -f Flush output after each write" 30/* Accepted but has no effect (we never buffer output) */
31/*//usage: "\n -f Flush output after each write"*/
29//usage: "\n -q Quiet" 32//usage: "\n -q Quiet"
30//usage: IF_SCRIPTREPLAY( 33//usage: "\n -t[FILE] Send timing to stderr or FILE"
31//usage: "\n -t Send timing to stderr" 34
32//usage: ) 35//util-linux-2.28:
36//-e: return exit code of the child
37
38//FYI (reported as bbox bug #2749):
39// > script -q -c 'echo -e -n "1\n2\n3\n"' /dev/null </dev/null >123.txt
40// > The output file on full-blown ubuntu system contains 6 bytes.
41// > Output on Busybox system (arm-linux) contains extra '\r' byte in each line.
42//however, in my test, "script" from util-linux-2.28 seems to also add '\r' bytes.
33 43
34#include "libbb.h" 44#include "libbb.h"
35#include "common_bufsiz.h" 45#include "common_bufsiz.h"
@@ -46,6 +56,8 @@ int script_main(int argc UNUSED_PARAM, char **argv)
46 char pty_line[GETPTY_BUFSIZE]; 56 char pty_line[GETPTY_BUFSIZE];
47 struct termios tt, rtt; 57 struct termios tt, rtt;
48 struct winsize win; 58 struct winsize win;
59 FILE *timing_fp;
60 const char *str_t = NULL;
49 const char *fname = "typescript"; 61 const char *fname = "typescript";
50 const char *shell; 62 const char *shell;
51 char shell_opt[] = "-i"; 63 char shell_opt[] = "-i";
@@ -59,19 +71,19 @@ int script_main(int argc UNUSED_PARAM, char **argv)
59 }; 71 };
60 72
61#if ENABLE_LONG_OPTS 73#if ENABLE_LONG_OPTS
62 static const char getopt_longopts[] ALIGN1 = 74 static const char script_longopts[] ALIGN1 =
63 "append\0" No_argument "a" 75 "append\0" No_argument "a"
64 "command\0" Required_argument "c" 76 "command\0" Required_argument "c"
65 "flush\0" No_argument "f" 77 "flush\0" No_argument "f"
66 "quiet\0" No_argument "q" 78 "quiet\0" No_argument "q"
67 IF_SCRIPTREPLAY("timing\0" No_argument "t") 79 "timing\0" Optional_argument "t"
68 ; 80 ;
69
70 applet_long_options = getopt_longopts;
71#endif 81#endif
72 82
73 opt_complementary = "?1"; /* max one arg */ 83 opt = getopt32long(argv, "^" "ac:fqt::" "\0" "?1"/* max one arg */,
74 opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg); 84 script_longopts,
85 &shell_arg, &str_t
86 );
75 //argc -= optind; 87 //argc -= optind;
76 argv += optind; 88 argv += optind;
77 if (argv[0]) { 89 if (argv[0]) {
@@ -87,6 +99,10 @@ int script_main(int argc UNUSED_PARAM, char **argv)
87 if (!(opt & OPT_q)) { 99 if (!(opt & OPT_q)) {
88 printf("Script started, file is %s\n", fname); 100 printf("Script started, file is %s\n", fname);
89 } 101 }
102 timing_fp = stderr;
103 if (str_t) {
104 timing_fp = xfopen_for_write(str_t);
105 }
90 106
91 shell = get_shell_name(); 107 shell = get_shell_name();
92 108
@@ -120,8 +136,9 @@ int script_main(int argc UNUSED_PARAM, char **argv)
120 /* parent */ 136 /* parent */
121 struct pollfd pfd[2]; 137 struct pollfd pfd[2];
122 int outfd, count, loop; 138 int outfd, count, loop;
123 double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0; 139 double oldtime = time(NULL);
124 smallint fd_count = 2; 140 smallint fd_count = 2;
141
125#define buf bb_common_bufsiz1 142#define buf bb_common_bufsiz1
126 setup_common_bufsiz(); 143 setup_common_bufsiz();
127 144
@@ -151,20 +168,21 @@ int script_main(int argc UNUSED_PARAM, char **argv)
151 goto restore; 168 goto restore;
152 } 169 }
153 if (count > 0) { 170 if (count > 0) {
154 if (ENABLE_SCRIPTREPLAY && (opt & OPT_t)) { 171 if (opt & OPT_t) {
155 struct timeval tv; 172 struct timeval tv;
156 double newtime; 173 double newtime;
157 174
158 gettimeofday(&tv, NULL); 175 gettimeofday(&tv, NULL);
159 newtime = tv.tv_sec + (double) tv.tv_usec / 1000000; 176 newtime = tv.tv_sec + (double) tv.tv_usec / 1000000;
160 fprintf(stderr, "%f %u\n", newtime - oldtime, count); 177 fprintf(timing_fp, "%f %u\n", newtime - oldtime, count);
161 oldtime = newtime; 178 oldtime = newtime;
162 } 179 }
163 full_write(STDOUT_FILENO, buf, count); 180 full_write(STDOUT_FILENO, buf, count);
164 full_write(outfd, buf, count); 181 full_write(outfd, buf, count);
165 if (opt & OPT_f) { 182 // If we'd be using (buffered) FILE i/o, we'd need this:
166 fsync(outfd); 183 //if (opt & OPT_f) {
167 } 184 // fflush(outfd);
185 //}
168 } 186 }
169 } 187 }
170 if (pfd[1].revents) { 188 if (pfd[1].revents) {
diff --git a/util-linux/scriptreplay.c b/util-linux/scriptreplay.c
index 7e9850103..e3083ab93 100644
--- a/util-linux/scriptreplay.c
+++ b/util-linux/scriptreplay.c
@@ -5,7 +5,6 @@
5 * pascal.bellard@ads-lu.com 5 * pascal.bellard@ads-lu.com
6 * 6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 *
9 */ 8 */
10//config:config SCRIPTREPLAY 9//config:config SCRIPTREPLAY
11//config: bool "scriptreplay (2.6 kb)" 10//config: bool "scriptreplay (2.6 kb)"
@@ -19,7 +18,7 @@
19//kbuild:lib-$(CONFIG_SCRIPTREPLAY) += scriptreplay.o 18//kbuild:lib-$(CONFIG_SCRIPTREPLAY) += scriptreplay.o
20 19
21//usage:#define scriptreplay_trivial_usage 20//usage:#define scriptreplay_trivial_usage
22//usage: "timingfile [typescript [divisor]]" 21//usage: "TIMINGFILE [TYPESCRIPT [DIVISOR]]"
23//usage:#define scriptreplay_full_usage "\n\n" 22//usage:#define scriptreplay_full_usage "\n\n"
24//usage: "Play back typescripts, using timing information" 23//usage: "Play back typescripts, using timing information"
25 24
diff --git a/util-linux/setarch.c b/util-linux/setarch.c
index d4b568832..520865318 100644
--- a/util-linux/setarch.c
+++ b/util-linux/setarch.c
@@ -30,10 +30,10 @@
30//config: help 30//config: help
31//config: Alias to "setarch linux64". 31//config: Alias to "setarch linux64".
32 32
33//applet:IF_SETARCH(APPLET(setarch, BB_DIR_BIN, BB_SUID_DROP)) 33//applet:IF_SETARCH(APPLET_NOEXEC(setarch, setarch, BB_DIR_BIN, BB_SUID_DROP, setarch))
34// APPLET_ODDNAME:name main location suid_type help 34// APPLET_NOEXEC:name main location suid_type help
35//applet:IF_LINUX32(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32)) 35//applet:IF_LINUX32(APPLET_NOEXEC(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32))
36//applet:IF_LINUX64(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64)) 36//applet:IF_LINUX64(APPLET_NOEXEC(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64))
37 37
38//kbuild:lib-$(CONFIG_SETARCH) += setarch.o 38//kbuild:lib-$(CONFIG_SETARCH) += setarch.o
39//kbuild:lib-$(CONFIG_LINUX32) += setarch.o 39//kbuild:lib-$(CONFIG_LINUX32) += setarch.o
diff --git a/util-linux/setpriv.c b/util-linux/setpriv.c
index 0f85428ab..12ab1bd66 100644
--- a/util-linux/setpriv.c
+++ b/util-linux/setpriv.c
@@ -5,7 +5,6 @@
5 * Copyright (C) 2017 by <assafgordon@gmail.com> 5 * Copyright (C) 2017 by <assafgordon@gmail.com>
6 * 6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 *
9 */ 8 */
10//config:config SETPRIV 9//config:config SETPRIV
11//config: bool "setpriv (3.4 kb)" 10//config: bool "setpriv (3.4 kb)"
@@ -125,96 +124,10 @@ enum {
125}; 124};
126 125
127#if ENABLE_FEATURE_SETPRIV_CAPABILITIES 126#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
128struct caps { 127DEFINE_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
135static const char *const capabilities[] = {
136 "chown",
137 "dac_override",
138 "dac_read_search",
139 "fowner",
140 "fsetid",
141 "kill",
142 "setgid",
143 "setuid",
144 "setpcap",
145 "linux_immutable",
146 "net_bind_service",
147 "net_broadcast",
148 "net_admin",
149 "net_raw",
150 "ipc_lock",
151 "ipc_owner",
152 "sys_module",
153 "sys_rawio",
154 "sys_chroot",
155 "sys_ptrace",
156 "sys_pacct",
157 "sys_admin",
158 "sys_boot",
159 "sys_nice",
160 "sys_resource",
161 "sys_time",
162 "sys_tty_config",
163 "mknod",
164 "lease",
165 "audit_write",
166 "audit_control",
167 "setfcap",
168 "mac_override",
169 "mac_admin",
170 "syslog",
171 "wake_alarm",
172 "block_suspend",
173 "audit_read",
174};
175# endif /* FEATURE_SETPRIV_CAPABILITY_NAMES */
176 128
177static void getcaps(struct caps *caps) 129static unsigned parse_cap(const char *cap)
178{ 130{
179 static const uint8_t versions[] = {
180 _LINUX_CAPABILITY_U32S_3, /* = 2 (fits into byte) */
181 _LINUX_CAPABILITY_U32S_2, /* = 2 */
182 _LINUX_CAPABILITY_U32S_1, /* = 1 */
183 };
184 int i;
185
186 caps->header.pid = 0;
187 for (i = 0; i < ARRAY_SIZE(versions); i++) {
188 caps->header.version = versions[i];
189 if (capget(&caps->header, NULL) == 0)
190 goto got_it;
191 }
192 bb_simple_perror_msg_and_die("capget");
193 got_it:
194
195 switch (caps->header.version) {
196 case _LINUX_CAPABILITY_VERSION_1:
197 caps->u32s = _LINUX_CAPABILITY_U32S_1;
198 break;
199 case _LINUX_CAPABILITY_VERSION_2:
200 caps->u32s = _LINUX_CAPABILITY_U32S_2;
201 break;
202 case _LINUX_CAPABILITY_VERSION_3:
203 caps->u32s = _LINUX_CAPABILITY_U32S_3;
204 break;
205 default:
206 bb_error_msg_and_die("unsupported capability version");
207 }
208
209 caps->data = xmalloc(sizeof(caps->data[0]) * caps->u32s);
210 if (capget(&caps->header, caps->data) < 0)
211 bb_simple_perror_msg_and_die("capget");
212}
213
214static void parse_cap(unsigned long *index, const char *cap)
215{
216 unsigned long i;
217
218 switch (cap[0]) { 131 switch (cap[0]) {
219 case '-': 132 case '-':
220 break; 133 break;
@@ -226,26 +139,7 @@ static void parse_cap(unsigned long *index, const char *cap)
226 } 139 }
227 140
228 cap++; 141 cap++;
229 if ((sscanf(cap, "cap_%lu", &i)) == 1) { 142 return cap_name_to_number(cap);
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} 143}
250 144
251static void set_inh_caps(char *capstring) 145static void set_inh_caps(char *capstring)
@@ -256,11 +150,11 @@ static void set_inh_caps(char *capstring)
256 150
257 capstring = strtok(capstring, ","); 151 capstring = strtok(capstring, ",");
258 while (capstring) { 152 while (capstring) {
259 unsigned long cap; 153 unsigned cap;
260 154
261 parse_cap(&cap, capstring); 155 cap = parse_cap(capstring);
262 if (CAP_TO_INDEX(cap) >= caps.u32s) 156 if (CAP_TO_INDEX(cap) >= caps.u32s)
263 bb_error_msg_and_die("invalid capability cap"); 157 bb_error_msg_and_die("invalid capability '%s'", capstring);
264 158
265 if (capstring[0] == '+') 159 if (capstring[0] == '+')
266 caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap); 160 caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap);
@@ -269,11 +163,8 @@ static void set_inh_caps(char *capstring)
269 capstring = strtok(NULL, ","); 163 capstring = strtok(NULL, ",");
270 } 164 }
271 165
272 if ((capset(&caps.header, caps.data)) < 0) 166 if (capset(&caps.header, caps.data) != 0)
273 bb_perror_msg_and_die("capset"); 167 bb_perror_msg_and_die("capset");
274
275 if (ENABLE_FEATURE_CLEAN_UP)
276 free(caps.data);
277} 168}
278 169
279static void set_ambient_caps(char *string) 170static void set_ambient_caps(char *string)
@@ -282,9 +173,9 @@ static void set_ambient_caps(char *string)
282 173
283 cap = strtok(string, ","); 174 cap = strtok(string, ",");
284 while (cap) { 175 while (cap) {
285 unsigned long index; 176 unsigned index;
286 177
287 parse_cap(&index, cap); 178 index = parse_cap(cap);
288 if (cap[0] == '+') { 179 if (cap[0] == '+') {
289 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, index, 0, 0) < 0) 180 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, index, 0, 0) < 0)
290 bb_perror_msg("cap_ambient_raise"); 181 bb_perror_msg("cap_ambient_raise");
@@ -298,16 +189,7 @@ static void set_ambient_caps(char *string)
298#endif /* FEATURE_SETPRIV_CAPABILITIES */ 189#endif /* FEATURE_SETPRIV_CAPABILITIES */
299 190
300#if ENABLE_FEATURE_SETPRIV_DUMP 191#if ENABLE_FEATURE_SETPRIV_DUMP
301# if ENABLE_FEATURE_SETPRIV_CAPABILITY_NAMES 192# if !ENABLE_FEATURE_SETPRIV_CAPABILITY_NAMES
302static void printf_cap(const char *pfx, unsigned cap_no)
303{
304 if (cap_no < ARRAY_SIZE(capabilities)) {
305 printf("%s%s", pfx, capabilities[cap_no]);
306 return;
307 }
308 printf("%scap_%u", pfx, cap_no);
309}
310# else
311# define printf_cap(pfx, cap_no) printf("%scap_%u", (pfx), (cap_no)) 193# define printf_cap(pfx, cap_no) printf("%scap_%u", (pfx), (cap_no))
312# endif 194# endif
313 195
@@ -396,10 +278,9 @@ static int dump(void)
396 bb_putchar('\n'); 278 bb_putchar('\n');
397# endif 279# endif
398 280
399 if (ENABLE_FEATURE_CLEAN_UP) { 281 if (ENABLE_FEATURE_CLEAN_UP)
400 IF_FEATURE_SETPRIV_CAPABILITIES(free(caps.data);)
401 free(gids); 282 free(gids);
402 } 283
403 return EXIT_SUCCESS; 284 return EXIT_SUCCESS;
404} 285}
405#endif /* FEATURE_SETPRIV_DUMP */ 286#endif /* FEATURE_SETPRIV_DUMP */
@@ -421,9 +302,12 @@ int setpriv_main(int argc UNUSED_PARAM, char **argv)
421 int opts; 302 int opts;
422 IF_FEATURE_SETPRIV_CAPABILITIES(char *inh_caps, *ambient_caps;) 303 IF_FEATURE_SETPRIV_CAPABILITIES(char *inh_caps, *ambient_caps;)
423 304
424 applet_long_options = setpriv_longopts; 305 opts = getopt32long(argv, "+"
425 opts = getopt32(argv, "+"IF_FEATURE_SETPRIV_DUMP("d") 306 IF_FEATURE_SETPRIV_DUMP("d")
426 IF_FEATURE_SETPRIV_CAPABILITIES("\xfe:\xfd:", &inh_caps, &ambient_caps)); 307 IF_FEATURE_SETPRIV_CAPABILITIES("\xfe:\xfd:"),
308 setpriv_longopts
309 IF_FEATURE_SETPRIV_CAPABILITIES(, &inh_caps, &ambient_caps)
310 );
427 argv += optind; 311 argv += optind;
428 312
429#if ENABLE_FEATURE_SETPRIV_DUMP 313#if ENABLE_FEATURE_SETPRIV_DUMP
diff --git a/util-linux/setsid.c b/util-linux/setsid.c
index 60cab2fcf..8385a9115 100644
--- a/util-linux/setsid.c
+++ b/util-linux/setsid.c
@@ -37,8 +37,8 @@ int setsid_main(int argc UNUSED_PARAM, char **argv)
37{ 37{
38 unsigned opt; 38 unsigned opt;
39 39
40 opt_complementary = "-1"; /* at least one arg */ 40 /* +: stop on first non-opt */
41 opt = getopt32(argv, "+c"); /* +: stop on first non-opt */ 41 opt = getopt32(argv, "^+" "c" "\0" "-1"/* at least one arg */);
42 argv += optind; 42 argv += optind;
43 43
44 /* setsid() is allowed only when we are not a process group leader. 44 /* setsid() is allowed only when we are not a process group leader.
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c
index 32708934e..080b05e45 100644
--- a/util-linux/switch_root.c
+++ b/util-linux/switch_root.c
@@ -24,22 +24,33 @@
24//config: * Because the Linux kernel uses rootfs internally as the starting 24//config: * Because the Linux kernel uses rootfs internally as the starting
25//config: and ending point for searching through the kernel's doubly linked 25//config: and ending point for searching through the kernel's doubly linked
26//config: list of active mount points. That's why. 26//config: list of active mount points. That's why.
27//config:
28// RUN_INIT config item is in klibc-utils
27 29
28//applet:IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP)) 30//applet:IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP))
31// APPLET_ODDNAME:name main location suid_type help
32//applet:IF_RUN_INIT( APPLET_ODDNAME(run-init, switch_root, BB_DIR_SBIN, BB_SUID_DROP, run_init))
29 33
30//kbuild:lib-$(CONFIG_SWITCH_ROOT) += switch_root.o 34//kbuild:lib-$(CONFIG_SWITCH_ROOT) += switch_root.o
31 35//kbuild:lib-$(CONFIG_RUN_INIT) += switch_root.o
32//usage:#define switch_root_trivial_usage
33//usage: "[-c /dev/console] NEW_ROOT NEW_INIT [ARGS]"
34//usage:#define switch_root_full_usage "\n\n"
35//usage: "Free initramfs and switch to another root fs:\n"
36//usage: "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n"
37//usage: "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n"
38//usage: "\n -c DEV Reopen stdio to DEV after switch"
39 36
40#include <sys/vfs.h> 37#include <sys/vfs.h>
41#include <sys/mount.h> 38#include <sys/mount.h>
39#if ENABLE_RUN_INIT
40# include <sys/prctl.h>
41# include <linux/capability.h>
42// #include <sys/capability.h>
43// This header is in libcap, but the functions are in libc.
44// Comment in the header says this above capset/capget:
45/* system calls - look to libc for function to system call mapping */
46extern int capset(cap_user_header_t header, cap_user_data_t data);
47extern int capget(cap_user_header_t header, const cap_user_data_t data);
48// so for bbox, let's just repeat the declarations.
49// This way, libcap needs not be installed in build environment.
50#endif
51
42#include "libbb.h" 52#include "libbb.h"
53
43// Make up for header deficiencies 54// Make up for header deficiencies
44#ifndef RAMFS_MAGIC 55#ifndef RAMFS_MAGIC
45# define RAMFS_MAGIC ((unsigned)0x858458f6) 56# define RAMFS_MAGIC ((unsigned)0x858458f6)
@@ -89,17 +100,125 @@ static void delete_contents(const char *directory, dev_t rootdev)
89 } 100 }
90} 101}
91 102
103#if ENABLE_RUN_INIT
104DEFINE_STRUCT_CAPS;
105
106static void drop_capset(int cap_idx)
107{
108 struct caps caps;
109
110 getcaps(&caps);
111 caps.data[CAP_TO_INDEX(cap_idx)].inheritable &= ~CAP_TO_MASK(cap_idx);
112 if (capset(&caps.header, caps.data) != 0)
113 bb_perror_msg_and_die("capset");
114}
115
116static void drop_bounding_set(int cap_idx)
117{
118 int ret;
119
120 ret = prctl(PR_CAPBSET_READ, cap_idx, 0, 0, 0);
121 if (ret < 0)
122 bb_perror_msg_and_die("prctl: %s", "PR_CAPBSET_READ");
123
124 if (ret == 1) {
125 ret = prctl(PR_CAPBSET_DROP, cap_idx, 0, 0, 0);
126 if (ret != 0)
127 bb_perror_msg_and_die("prctl: %s", "PR_CAPBSET_DROP");
128 }
129}
130
131static void drop_usermodehelper(const char *filename, int cap_idx)
132{
133 unsigned lo, hi;
134 char buf[sizeof(int)*3 * 2 + 8];
135 int fd;
136 int ret;
137
138 ret = open_read_close(filename, buf, sizeof(buf) - 1);
139 if (ret < 0)
140 return; /* assuming files do not exist */
141
142 buf[ret] = '\0';
143 ret = sscanf(buf, "%u %u", &lo, &hi);
144 if (ret != 2)
145 bb_perror_msg_and_die("can't parse file '%s'", filename);
146
147 if (cap_idx < 32)
148 lo &= ~(1 << cap_idx);
149 else
150 hi &= ~(1 << (cap_idx - 32));
151
152 fd = xopen(filename, O_WRONLY);
153 fdprintf(fd, "%u %u", lo, hi);
154 close(fd);
155}
156
157static void drop_capabilities(char *string)
158{
159 char *cap;
160
161 cap = strtok(string, ",");
162 while (cap) {
163 unsigned cap_idx;
164
165 cap_idx = cap_name_to_number(cap);
166 drop_usermodehelper("/proc/sys/kernel/usermodehelper/bset", cap_idx);
167 drop_usermodehelper("/proc/sys/kernel/usermodehelper/inheritable", cap_idx);
168 drop_bounding_set(cap_idx);
169 drop_capset(cap_idx);
170 bb_error_msg("dropped capability: %s", cap);
171 cap = strtok(NULL, ",");
172 }
173}
174#endif
175
92int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 176int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
93int switch_root_main(int argc UNUSED_PARAM, char **argv) 177int switch_root_main(int argc UNUSED_PARAM, char **argv)
94{ 178{
95 char *newroot, *console = NULL; 179 char *newroot, *console = NULL;
96 struct stat st; 180 struct stat st;
97 struct statfs stfs; 181 struct statfs stfs;
182 unsigned dry_run = 0;
98 dev_t rootdev; 183 dev_t rootdev;
99 184
100 // Parse args (-c console) 185 // Parse args. '+': stop at first non-option
101 opt_complementary = "-2"; // minimum 2 params 186 if (ENABLE_SWITCH_ROOT && (!ENABLE_RUN_INIT || applet_name[0] == 's')) {
102 getopt32(argv, "+c:", &console); // '+': stop at first non-option 187//usage:#define switch_root_trivial_usage
188//usage: "[-c CONSOLE_DEV] NEW_ROOT NEW_INIT [ARGS]"
189//usage:#define switch_root_full_usage "\n\n"
190//usage: "Free initramfs and switch to another root fs:\n"
191//usage: "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n"
192//usage: "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n"
193//usage: "\n -c DEV Reopen stdio to DEV after switch"
194 getopt32(argv, "^+"
195 "c:"
196 "\0" "-2" /* minimum 2 args */,
197 &console
198 );
199 } else {
200#if ENABLE_RUN_INIT
201//usage:#define run_init_trivial_usage
202//usage: "[-d CAP,CAP...] [-n] [-c CONSOLE_DEV] NEW_ROOT NEW_INIT [ARGS]"
203//usage:#define run_init_full_usage "\n\n"
204//usage: "Free initramfs and switch to another root fs:\n"
205//usage: "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n"
206//usage: "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n"
207//usage: "\n -c DEV Reopen stdio to DEV after switch"
208//usage: "\n -d CAPS Drop capabilities"
209//usage: "\n -n Dry run"
210 char *cap_list = NULL;
211 dry_run = getopt32(argv, "^+"
212 "c:d:n"
213 "\0" "-2" /* minimum 2 args */,
214 &console,
215 &cap_list
216 );
217 dry_run >>= 2; // -n
218 if (cap_list)
219 drop_capabilities(cap_list);
220#endif
221 }
103 argv += optind; 222 argv += optind;
104 newroot = *argv++; 223 newroot = *argv++;
105 224
@@ -108,9 +227,12 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv)
108 xstat("/", &st); 227 xstat("/", &st);
109 rootdev = st.st_dev; 228 rootdev = st.st_dev;
110 xstat(".", &st); 229 xstat(".", &st);
111 if (st.st_dev == rootdev || getpid() != 1) { 230 if (st.st_dev == rootdev) {
112 // Show usage, it says new root must be a mountpoint 231 // Show usage, it says new root must be a mountpoint
113 // and we must be PID 1 232 bb_show_usage();
233 }
234 if (!dry_run && getpid() != 1) {
235 // Show usage, it says we must be PID 1
114 bb_show_usage(); 236 bb_show_usage();
115 } 237 }
116 238
@@ -118,7 +240,7 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv)
118 // we mean it. I could make this a CONFIG option, but I would get email 240 // we mean it. I could make this a CONFIG option, but I would get email
119 // from all the people who WILL destroy their filesystems. 241 // from all the people who WILL destroy their filesystems.
120 if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) { 242 if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) {
121 bb_error_msg_and_die("/init is not a regular file"); 243 bb_error_msg_and_die("'%s' is not a regular file", "/init");
122 } 244 }
123 statfs("/", &stfs); // this never fails 245 statfs("/", &stfs); // this never fails
124 if ((unsigned)stfs.f_type != RAMFS_MAGIC 246 if ((unsigned)stfs.f_type != RAMFS_MAGIC
@@ -127,13 +249,15 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv)
127 bb_error_msg_and_die("root filesystem is not ramfs/tmpfs"); 249 bb_error_msg_and_die("root filesystem is not ramfs/tmpfs");
128 } 250 }
129 251
130 // Zap everything out of rootdev 252 if (!dry_run) {
131 delete_contents("/", rootdev); 253 // Zap everything out of rootdev
254 delete_contents("/", rootdev);
132 255
133 // Overmount / with newdir and chroot into it 256 // Overmount / with newdir and chroot into it
134 if (mount(".", "/", NULL, MS_MOVE, NULL)) { 257 if (mount(".", "/", NULL, MS_MOVE, NULL)) {
135 // For example, fails when newroot is not a mountpoint 258 // For example, fails when newroot is not a mountpoint
136 bb_perror_msg_and_die("error moving root"); 259 bb_perror_msg_and_die("error moving root");
260 }
137 } 261 }
138 xchroot("."); 262 xchroot(".");
139 // The chdir is needed to recalculate "." and ".." links 263 // The chdir is needed to recalculate "." and ".." links
@@ -149,8 +273,17 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv)
149 } 273 }
150 } 274 }
151 275
152 // Exec real init 276 if (dry_run) {
153 execv(argv[0], argv); 277 // Does NEW_INIT look like it can be executed?
278 //xstat(argv[0], &st);
279 //if (!S_ISREG(st.st_mode))
280 // bb_perror_msg_and_die("'%s' is not a regular file", argv[0]);
281 if (access(argv[0], X_OK) == 0)
282 return 0;
283 } else {
284 // Exec NEW_INIT
285 execv(argv[0], argv);
286 }
154 bb_perror_msg_and_die("can't execute '%s'", argv[0]); 287 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
155} 288}
156 289
diff --git a/util-linux/taskset.c b/util-linux/taskset.c
index 9957b1a71..401a1bcb7 100644
--- a/util-linux/taskset.c
+++ b/util-linux/taskset.c
@@ -22,7 +22,7 @@
22//config: affinity parameter 0xHHHHHHHHHHHHHHHHHHHH can be arbitrarily long 22//config: affinity parameter 0xHHHHHHHHHHHHHHHHHHHH can be arbitrarily long
23//config: in this case. Otherwise, it is limited to sizeof(long). 23//config: in this case. Otherwise, it is limited to sizeof(long).
24 24
25//applet:IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP)) 25//applet:IF_TASKSET(APPLET_NOEXEC(taskset, taskset, BB_DIR_USR_BIN, BB_SUID_DROP, taskset))
26//kbuild:lib-$(CONFIG_TASKSET) += taskset.o 26//kbuild:lib-$(CONFIG_TASKSET) += taskset.o
27 27
28//usage:#define taskset_trivial_usage 28//usage:#define taskset_trivial_usage
@@ -123,8 +123,7 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
123 * Indeed, util-linux-2.13-pre7 uses: 123 * Indeed, util-linux-2.13-pre7 uses:
124 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */ 124 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
125 125
126 opt_complementary = "-1"; /* at least 1 arg */ 126 opt_p = getopt32(argv, "^+" "p" "\0" "-1" /* at least 1 arg */);
127 opt_p = getopt32(argv, "+p");
128 argv += optind; 127 argv += optind;
129 128
130 aff = *argv++; 129 aff = *argv++;
diff --git a/util-linux/uevent.c b/util-linux/uevent.c
index 252e8fb64..7ff866cec 100644
--- a/util-linux/uevent.c
+++ b/util-linux/uevent.c
@@ -32,7 +32,10 @@
32#define env ((char **)bb_common_bufsiz1) 32#define env ((char **)bb_common_bufsiz1)
33#define INIT_G() do { setup_common_bufsiz(); } while (0) 33#define INIT_G() do { setup_common_bufsiz(); } while (0)
34enum { 34enum {
35 MAX_ENV = COMMON_BUFSIZE / sizeof(env[0]) - 1, 35 MAX_ENV = COMMON_BUFSIZE / sizeof(char*) - 1,
36 /* sizeof(env[0]) instead of sizeof(char*)
37 * makes gcc-6.3.0 emit "strict-aliasing" warning.
38 */
36}; 39};
37 40
38#ifndef SO_RCVBUFFORCE 41#ifndef SO_RCVBUFFORCE
diff --git a/util-linux/umount.c b/util-linux/umount.c
index 122c0f579..a6405dfcc 100644
--- a/util-linux/umount.c
+++ b/util-linux/umount.c
@@ -24,7 +24,20 @@
24//config: help 24//config: help
25//config: Support -a option to unmount all currently mounted filesystems. 25//config: Support -a option to unmount all currently mounted filesystems.
26 26
27//applet:IF_UMOUNT(APPLET(umount, BB_DIR_BIN, BB_SUID_DROP)) 27//applet:IF_UMOUNT(APPLET_NOEXEC(umount, umount, BB_DIR_BIN, BB_SUID_DROP, umount))
28/*
29 * On one hand, in some weird situations you'd want umount
30 * to not do anything surprising, to behave as a usual fork+execed executable.
31 *
32 * OTOH, there can be situations where execing would not succeed, or even hang
33 * (say, if executable is on a filesystem which is in trouble and accesses to it
34 * block in kernel).
35 * In this case, you might be actually happy if your standalone bbox shell
36 * does not fork+exec, but only forks and calls umount_main() which it already has!
37 * Let's go with NOEXEC.
38 *
39 * bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed.
40 */
28 41
29//kbuild:lib-$(CONFIG_UMOUNT) += umount.o 42//kbuild:lib-$(CONFIG_UMOUNT) += umount.o
30 43
diff --git a/util-linux/unshare.c b/util-linux/unshare.c
index e7b95c72b..6a3da9f91 100644
--- a/util-linux/unshare.c
+++ b/util-linux/unshare.c
@@ -137,7 +137,7 @@ static const struct namespace_descr ns_list[] = {
137 * we are forced to use "fake" letters for them. 137 * we are forced to use "fake" letters for them.
138 * '+': stop at first non-option. 138 * '+': stop at first non-option.
139 */ 139 */
140static const char opt_str[] ALIGN1 = "+muinpU""fr""\xfd::""\xfe:""\xff:"; 140#define OPT_STR "+muinpU""fr""\xfd::""\xfe:""\xff:"
141static const char unshare_longopts[] ALIGN1 = 141static const char unshare_longopts[] ALIGN1 =
142 "mount\0" Optional_argument "\xf0" 142 "mount\0" Optional_argument "\xf0"
143 "uts\0" Optional_argument "\xf1" 143 "uts\0" Optional_argument "\xf1"
@@ -210,7 +210,7 @@ int unshare_main(int argc UNUSED_PARAM, char **argv)
210 prop_str = PRIVATE_STR; 210 prop_str = PRIVATE_STR;
211 setgrp_str = NULL; 211 setgrp_str = NULL;
212 212
213 opt_complementary = 213 opts = getopt32long(argv, "^" OPT_STR "\0"
214 "\xf0""m" /* long opts (via their "fake chars") imply short opts */ 214 "\xf0""m" /* long opts (via their "fake chars") imply short opts */
215 ":\xf1""u" 215 ":\xf1""u"
216 ":\xf2""i" 216 ":\xf2""i"
@@ -219,16 +219,14 @@ int unshare_main(int argc UNUSED_PARAM, char **argv)
219 ":\xf5""U" 219 ":\xf5""U"
220 ":ru" /* --map-root-user or -r implies -u */ 220 ":ru" /* --map-root-user or -r implies -u */
221 ":\xfd""m" /* --mount-proc implies -m */ 221 ":\xfd""m" /* --mount-proc implies -m */
222 ; 222 , unshare_longopts,
223 applet_long_options = unshare_longopts; 223 &proc_mnt_target, &prop_str, &setgrp_str,
224 opts = getopt32(argv, opt_str, 224 &ns_ctx_list[NS_MNT_POS].path,
225 &proc_mnt_target, &prop_str, &setgrp_str, 225 &ns_ctx_list[NS_UTS_POS].path,
226 &ns_ctx_list[NS_MNT_POS].path, 226 &ns_ctx_list[NS_IPC_POS].path,
227 &ns_ctx_list[NS_UTS_POS].path, 227 &ns_ctx_list[NS_NET_POS].path,
228 &ns_ctx_list[NS_IPC_POS].path, 228 &ns_ctx_list[NS_PID_POS].path,
229 &ns_ctx_list[NS_NET_POS].path, 229 &ns_ctx_list[NS_USR_POS].path
230 &ns_ctx_list[NS_PID_POS].path,
231 &ns_ctx_list[NS_USR_POS].path
232 ); 230 );
233 argv += optind; 231 argv += optind;
234 //bb_error_msg("opts:0x%x", opts); 232 //bb_error_msg("opts:0x%x", opts);