summaryrefslogtreecommitdiff
path: root/release/src/router/busybox/miscutils
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/router/busybox/miscutils')
-rw-r--r--[-rwxr-xr-x]release/src/router/busybox/miscutils/Config.in543
-rw-r--r--release/src/router/busybox/miscutils/Kbuild41
-rw-r--r--release/src/router/busybox/miscutils/Makefile30
-rwxr-xr-xrelease/src/router/busybox/miscutils/Makefile.in43
-rw-r--r--release/src/router/busybox/miscutils/adjtimex.c210
-rw-r--r--release/src/router/busybox/miscutils/bbconfig.c12
-rw-r--r--release/src/router/busybox/miscutils/chat.c435
-rw-r--r--release/src/router/busybox/miscutils/chrt.c123
-rw-r--r--release/src/router/busybox/miscutils/crond.c1633
-rw-r--r--release/src/router/busybox/miscutils/crontab.c528
-rw-r--r--release/src/router/busybox/miscutils/dc.c187
-rw-r--r--release/src/router/busybox/miscutils/devfsd.c1801
-rw-r--r--release/src/router/busybox/miscutils/devmem.c128
-rw-r--r--release/src/router/busybox/miscutils/dutmp.c66
-rw-r--r--release/src/router/busybox/miscutils/eject.c116
-rw-r--r--release/src/router/busybox/miscutils/fbsplash.c407
-rw-r--r--release/src/router/busybox/miscutils/fbsplash.cfg9
-rw-r--r--release/src/router/busybox/miscutils/flash_eraseall.c194
-rw-r--r--release/src/router/busybox/miscutils/hdparm.c3432
-rw-r--r--release/src/router/busybox/miscutils/inotifyd.c176
-rw-r--r--release/src/router/busybox/miscutils/ionice.c99
-rw-r--r--release/src/router/busybox/miscutils/last.c133
-rw-r--r--release/src/router/busybox/miscutils/last_fancy.c297
-rw-r--r--release/src/router/busybox/miscutils/less.c1799
-rw-r--r--release/src/router/busybox/miscutils/makedevs.c213
-rw-r--r--release/src/router/busybox/miscutils/man.c278
-rw-r--r--release/src/router/busybox/miscutils/microcom.c171
-rw-r--r--release/src/router/busybox/miscutils/mountpoint.c72
-rw-r--r--release/src/router/busybox/miscutils/mt.c169
-rw-r--r--release/src/router/busybox/miscutils/raidautorun.c25
-rw-r--r--release/src/router/busybox/miscutils/readahead.c40
-rw-r--r--release/src/router/busybox/miscutils/runlevel.c43
-rw-r--r--release/src/router/busybox/miscutils/rx.c254
-rw-r--r--release/src/router/busybox/miscutils/setsid.c36
-rw-r--r--release/src/router/busybox/miscutils/strings.c197
-rw-r--r--release/src/router/busybox/miscutils/taskset.c137
-rw-r--r--release/src/router/busybox/miscutils/time.c737
-rw-r--r--release/src/router/busybox/miscutils/timeout.c115
-rw-r--r--release/src/router/busybox/miscutils/ttysize.c44
-rw-r--r--release/src/router/busybox/miscutils/watchdog.c118
40 files changed, 10723 insertions, 4368 deletions
diff --git a/release/src/router/busybox/miscutils/Config.in b/release/src/router/busybox/miscutils/Config.in
index 0c56f0ee..7feaf4a8 100755..100644
--- a/release/src/router/busybox/miscutils/Config.in
+++ b/release/src/router/busybox/miscutils/Config.in
@@ -5,126 +5,548 @@
menu "Miscellaneous Utilities"
-config CONFIG_ADJTIMEX
+config ADJTIMEX
bool "adjtimex"
default n
help
Adjtimex reads and optionally sets adjustment parameters for
the Linux clock adjustment algorithm.
-config CONFIG_CROND
+config BBCONFIG
+ bool "bbconfig"
+ default n
+ help
+ The bbconfig applet will print the config file with which
+ busybox was built.
+
+config CHAT
+ bool "chat"
+ default n
+ help
+ Simple chat utility.
+
+config FEATURE_CHAT_NOFAIL
+ bool "Enable NOFAIL expect strings"
+ depends on CHAT
+ default y
+ help
+ When enabled expect strings which are started with a dash trigger
+ no-fail mode. That is when expectation is not met within timeout
+ the script is not terminated but sends next SEND string and waits
+ for next EXPECT string. This allows to compose far more flexible
+ scripts.
+
+config FEATURE_CHAT_TTY_HIFI
+ bool "Force STDIN to be a TTY"
+ depends on CHAT
+ default n
+ help
+ Original chat always treats STDIN as a TTY device and sets for it
+ so-called raw mode. This option turns on such behaviour.
+
+config FEATURE_CHAT_IMPLICIT_CR
+ bool "Enable implicit Carriage Return"
+ depends on CHAT
+ default y
+ help
+ When enabled make chat to terminate all SEND strings with a "\r"
+ unless "\c" is met anywhere in the string.
+
+config FEATURE_CHAT_SWALLOW_OPTS
+ bool "Swallow options"
+ depends on CHAT
+ default n
+ help
+ Busybox chat require no options. To make it not fail when used
+ in place of original chat (which has a bunch of options) turn
+ this on.
+
+config FEATURE_CHAT_SEND_ESCAPES
+ bool "Support weird SEND escapes"
+ depends on CHAT
+ default n
+ help
+ Original chat uses some escape sequences in SEND arguments which
+ are not sent to device but rather performs special actions.
+ E.g. "\K" means to send a break sequence to device.
+ "\d" delays execution for a second, "\p" -- for a 1/100 of second.
+ Before turning this option on think twice: do you really need them?
+
+config FEATURE_CHAT_VAR_ABORT_LEN
+ bool "Support variable-length ABORT conditions"
+ depends on CHAT
+ default n
+ help
+ Original chat uses fixed 50-bytes length ABORT conditions. Say N here.
+
+config FEATURE_CHAT_CLR_ABORT
+ bool "Support revoking of ABORT conditions"
+ depends on CHAT
+ default n
+ help
+ Support CLR_ABORT directive.
+
+config CHRT
+ bool "chrt"
+ default n
+ help
+ manipulate real-time attributes of a process.
+ This requires sched_{g,s}etparam support in your libc.
+
+config CROND
bool "crond"
default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
help
Crond is a background daemon that parses individual crontab
files and executes commands on behalf of the users in question.
+ This is a port of dcron from slackware. It uses files of the
+ format /var/spool/cron/crontabs/<username> files, for example:
+ $ cat /var/spool/cron/crontabs/root
+ # Run daily cron jobs at 4:40 every day:
+ 40 4 * * * /etc/cron/daily > /dev/null 2>&1
-config CONFIG_FEATURE_CROND_CALL_SENDMAIL
- bool " Using /usr/sbin/sendmail?"
+config FEATURE_CROND_D
+ bool "Support option -d to redirect output to stderr"
+ depends on CROND
default n
- depends on CONFIG_CROND
help
- Support call /usr/sbin/sendmail for send cmd outputs.
+ -d sets loglevel to 0 (most verbose) and directs all output to stderr.
-config CONFIG_CRONTAB
+config FEATURE_CROND_CALL_SENDMAIL
+ bool "Using /usr/sbin/sendmail?"
+ default n
+ depends on CROND
+ help
+ Support calling /usr/sbin/sendmail for send cmd outputs.
+
+config FEATURE_CROND_DIR
+ string "crond spool directory"
+ default "/var/spool/cron"
+ depends on CROND || CRONTAB
+ help
+ Location of crond spool.
+
+config CRONTAB
bool "crontab"
default n
+ select FEATURE_SUID
help
- Crontab manipulates the crontab for a particular user. Only
+ Crontab manipulates the crontab for a particular user. Only
the superuser may specify a different user and/or crontab directory.
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
-config CONFIG_DC
+config DC
bool "dc"
default n
help
Dc is a reverse-polish desk calculator which supports unlimited
precision arithmetic.
-config CONFIG_DUTMP
- bool "dutmp"
+config FEATURE_DC_LIBM
+ bool "Enable power and exp functions (requires libm)"
+ default n
+ depends on DC
+ help
+ Enable power and exp functions.
+ NOTE: This will require libm to be present for linking.
+
+config DEVFSD
+ bool "devfsd (obsolete)"
+ default n
+ select FEATURE_SYSLOG
+ help
+ This is deprecated and should NOT be used anymore.
+ Use linux >= 2.6 (optionally with hotplug) and mdev instead!
+ See docs/mdev.txt for detailed instructions on how to use mdev
+ instead.
+
+ Provides compatibility with old device names on a devfs systems.
+ You should set it to true if you have devfs enabled.
+ The following keywords in devsfd.conf are supported:
+ "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
+ "PERMISSIONS", "EXECUTE", "COPY", "IGNORE",
+ "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT".
+
+ But only if they are written UPPERCASE!!!!!!!!
+
+config DEVFSD_MODLOAD
+ bool "Adds support for MODLOAD keyword in devsfd.conf"
+ default n
+ depends on DEVFSD
+ help
+ This actually doesn't work with busybox modutils but needs
+ the external modutils.
+
+config DEVFSD_FG_NP
+ bool "Enables the -fg and -np options"
+ default n
+ depends on DEVFSD
+ help
+ -fg Run the daemon in the foreground.
+ -np Exit after parsing the configuration file.
+ Do not poll for events.
+
+config DEVFSD_VERBOSE
+ bool "Increases logging (and size)"
+ default n
+ depends on DEVFSD
+ help
+ Increases logging to stderr or syslog.
+
+config FEATURE_DEVFS
+ bool "Use devfs names for all devices (obsolete)"
+ default n
+ help
+ This is obsolete and should NOT be used anymore.
+ Use linux >= 2.6 (optionally with hotplug) and mdev instead!
+
+ For legacy systems -- if there is no way around devfsd -- this
+ tells busybox to look for names like /dev/loop/0 instead of
+ /dev/loop0. If your /dev directory has normal names instead of
+ devfs names, you don't want this.
+
+config DEVMEM
+ bool "devmem"
+ default n
+ help
+ devmem is a small program that reads and writes from physical
+ memory using /dev/mem.
+
+config EJECT
+ bool "eject"
default n
help
- 'dutmp' is a utility used by the Linux Router Project (as far as I
- know nobody else uses it). It dumps the contents of the utmp file to
- STDOUT with each field seperated by a colon. IP addresses are are
- given in hex in native byte order. It is intended that this format
- can then be parsed by shell scripts.
+ Used to eject cdroms. (defaults to /dev/cdrom)
+
+config FEATURE_EJECT_SCSI
+ bool "SCSI support"
+ default n
+ depends on EJECT
+ help
+ Add the -s option to eject, this allows to eject SCSI-Devices and
+ usb-storage devices.
+
+config FBSPLASH
+ bool "fbsplash"
+ default n
+ help
+ Shows splash image and progress bar on framebuffer device.
+ Can be used during boot phase of an embedded device. ~2kb.
+ Usage:
+ - use kernel option 'vga=xxx' or otherwise enable fb device.
+ - put somewhere fbsplash.cfg file and an image in .ppm format.
+ - $ setsid fbsplash [params] &
+ -c: hide cursor
+ -d /dev/fbN: framebuffer device (if not /dev/fb0)
+ -s path_to_image_file (can be "-" for stdin)
+ -i path_to_cfg_file (can be "-" for stdin)
+ -f path_to_fifo (can be "-" for stdin)
+ - if you want to run it only in presence of kernel parameter:
+ grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params] &
+ - commands for fifo:
+ "NN" (ASCII decimal number) - percentage to show on progress bar
+ "exit" - well you guessed it
+
+config FLASH_ERASEALL
+ bool "flash_eraseall"
+ default n
+ help
+ The flash_eraseall binary from mtd-utils as of git head c4c6a59eb.
+ This utility is used to erase the whole MTD device.
+
+config IONICE
+ bool "ionice"
+ default n
+ help
+ Set/set program io scheduling class and priority
+ Requires kernel >= 2.6.13
+
+config INOTIFYD
+ bool "inotifyd"
+ default n
+ help
+ Simple inotify daemon. Reports filesystem changes. Requires
+ kernel >= 2.6.13
+
+config LAST
+ bool "last"
+ default n
+ select FEATURE_WTMP
+ help
+ 'last' displays a list of the last users that logged into the system.
+
+choice
+ prompt "Choose last implementation"
+ depends on LAST
+ default FEATURE_LAST_SMALL
-config CONFIG_HDPARM
+config FEATURE_LAST_SMALL
+ bool "small"
+ help
+ This is a small version of last with just the basic set of
+ features.
+
+config FEATURE_LAST_FANCY
+ bool "huge"
+ help
+ 'last' displays detailed information about the last users that
+ logged into the system (mimics sysvinit last). +900 bytes.
+endchoice
+
+config LESS
+ bool "less"
+ default n
+ help
+ 'less' is a pager, meaning that it displays text files. It possesses
+ a wide array of features, and is an improvement over 'more'.
+
+config FEATURE_LESS_MAXLINES
+ int "Max number of input lines less will try to eat"
+ default 9999999
+ depends on LESS
+
+config FEATURE_LESS_BRACKETS
+ bool "Enable bracket searching"
+ default y
+ depends on LESS
+ help
+ This option adds the capability to search for matching left and right
+ brackets, facilitating programming.
+
+config FEATURE_LESS_FLAGS
+ bool "Enable extra flags"
+ default y
+ depends on LESS
+ help
+ The extra flags provided do the following:
+
+ The -M flag enables a more sophisticated status line.
+ The -m flag enables a simpler status line with a percentage.
+
+config FEATURE_LESS_MARKS
+ bool "Enable marks"
+ default n
+ depends on LESS
+ help
+ Marks enable positions in a file to be stored for easy reference.
+
+config FEATURE_LESS_REGEXP
+ bool "Enable regular expressions"
+ default n
+ depends on LESS
+ help
+ Enable regular expressions, allowing complex file searches.
+
+config FEATURE_LESS_WINCH
+ bool "Enable automatic resizing on window size changes"
+ default n
+ depends on LESS
+ help
+ Makes less track window size changes.
+
+config FEATURE_LESS_DASHCMD
+ bool "Enable flag changes ('-' command)"
+ default n
+ depends on LESS
+ help
+ This enables the ability to change command-line flags within
+ less itself ('-' keyboard command).
+
+config FEATURE_LESS_LINENUMS
+ bool "Enable dynamic switching of line numbers"
+ default n
+ depends on FEATURE_LESS_DASHCMD
+ help
+ Enable "-N" command.
+
+config HDPARM
bool "hdparm"
default n
help
- Get/Set hard drive parameters. Primarily intended for ATA
- drives. Adds about 13k (or around 30k if you enable the
- CONFIG_FEATURE_HDPARM_GET_IDENTITY option)....
+ Get/Set hard drive parameters. Primarily intended for ATA
+ drives. Adds about 13k (or around 30k if you enable the
+ FEATURE_HDPARM_GET_IDENTITY option)....
-config CONFIG_FEATURE_HDPARM_GET_IDENTITY
- bool " Support obtaining detailed information directly from drives"
+config FEATURE_HDPARM_GET_IDENTITY
+ bool "Support obtaining detailed information directly from drives"
default y
- depends on CONFIG_HDPARM
+ depends on HDPARM
help
- Enables the -I and -Istdin options to obtain detailed information
+ Enables the -I and -i options to obtain detailed information
directly from drives about their capabilities and supported ATA
- feature set. Enabling this option will add about 16k...
+ feature set. If no device name is specified, hdparm will read
+ identify data from stdin. Enabling this option will add about 16k...
-config CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
- bool " Register an IDE interface (DANGEROUS)"
+config FEATURE_HDPARM_HDIO_SCAN_HWIF
+ bool "Register an IDE interface (DANGEROUS)"
default n
- depends on CONFIG_HDPARM
+ depends on HDPARM
help
Enables the 'hdparm -R' option to register an IDE interface.
This is dangerous stuff, so you should probably say N.
-config CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
- bool " Un-register an IDE interface (DANGEROUS)"
+config FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+ bool "Un-register an IDE interface (DANGEROUS)"
default n
- depends on CONFIG_HDPARM
+ depends on HDPARM
help
Enables the 'hdparm -U' option to un-register an IDE interface.
This is dangerous stuff, so you should probably say N.
-config CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
- bool " perform device reset (DANGEROUS)"
+config FEATURE_HDPARM_HDIO_DRIVE_RESET
+ bool "Perform device reset (DANGEROUS)"
default n
- depends on CONFIG_HDPARM
+ depends on HDPARM
help
Enables the 'hdparm -w' option to perform a device reset.
This is dangerous stuff, so you should probably say N.
-config CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
- bool " tristate device for hotswap (DANGEROUS)"
+config FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+ bool "Tristate device for hotswap (DANGEROUS)"
default n
- depends on CONFIG_HDPARM
+ depends on HDPARM
help
Enables the 'hdparm -x' option to tristate device for hotswap,
- and the '-b' option to get/set bus state. This is dangerous
+ and the '-b' option to get/set bus state. This is dangerous
stuff, so you should probably say N.
-config CONFIG_MAKEDEVS
+config FEATURE_HDPARM_HDIO_GETSET_DMA
+ bool "Get/set using_dma flag"
+ default n
+ depends on HDPARM
+ help
+ Enables the 'hdparm -d' option to get/set using_dma flag.
+
+config MAKEDEVS
bool "makedevs"
default n
help
- 'makedevs' is a utility used and created by the Linux Router Project.
- It creates a large number of device special files (/dev devices)
- rather quickly, and can be considerably faster then running mknod a
- zillion times.
+ 'makedevs' is a utility used to create a batch of devices with
+ one command.
+ .
+ There are two choices for command line behaviour, the interface
+ as used by LEAF/Linux Router Project, or a device table file.
+ .
+ 'leaf' is traditionally what busybox follows, it allows multiple
+ devices of a particluar type to be created per command.
+ e.g. /dev/hda[0-9]
+ Device properties are passed as command line arguments.
+ .
+ 'table' reads device properties from a file or stdin, allowing
+ a batch of unrelated devices to be made with one command.
+ User/group names are allowed as an alternative to uid/gid.
+
+choice
+ prompt "Choose makedevs behaviour"
+ depends on MAKEDEVS
+ default FEATURE_MAKEDEVS_TABLE
+
+config FEATURE_MAKEDEVS_LEAF
+ bool "leaf"
-config CONFIG_MT
+config FEATURE_MAKEDEVS_TABLE
+ bool "table"
+
+endchoice
+
+config MAN
+ bool "man"
+ default n
+ help
+ Format and display manual pages.
+
+config MICROCOM
+ bool "microcom"
+ default n
+ help
+ The poor man's minicom utility for chatting with serial port devices.
+
+config MOUNTPOINT
+ bool "mountpoint"
+ default n
+ help
+ mountpoint checks if the directory is a mountpoint.
+
+config MT
bool "mt"
default n
help
- Mt is used to control tape devices. You can use the mt utility
+ mt is used to control tape devices. You can use the mt utility
to advance or rewind a tape past a specified number of archive
files on the tape.
-config CONFIG_STRINGS
+config RAIDAUTORUN
+ bool "raidautorun"
+ default n
+ help
+ raidautorun tells the kernel md driver to
+ search and start RAID arrays.
+
+config READAHEAD
+ bool "readahead"
+ default n
+ depends on LFS
+ help
+ Preload the files listed on the command line into RAM cache so that
+ subsequent reads on these files will not block on disk I/O.
+
+ This applet just calls the readahead(2) system call on each file.
+ It is mainly useful in system startup scripts to preload files
+ or executables before they are used. When used at the right time
+ (in particular when a CPU bound process is running) it can
+ significantly speed up system startup.
+
+ As readahead(2) blocks until each file has been read, it is best to
+ run this applet as a background job.
+
+config RUNLEVEL
+ bool "runlevel"
+ default n
+ help
+ find the current and previous system runlevel.
+
+ This applet uses utmp but does not rely on busybox supporing
+ utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc.
+
+config RX
+ bool "rx"
+ default n
+ help
+ Receive files using the Xmodem protocol.
+
+config SETSID
+ bool "setsid"
+ default n
+ help
+ setsid runs a program in a new session
+
+config STRINGS
bool "strings"
default n
help
- Strings prints the printable character sequences for each file
+ strings prints the printable character sequences for each file
specified.
-config CONFIG_TIME
+config TASKSET
+ bool "taskset"
+ default n
+ help
+ Retrieve or set a processes's CPU affinity.
+ This requires sched_{g,s}etaffinity support in your libc.
+
+config FEATURE_TASKSET_FANCY
+ bool "Fancy output"
+ default y
+ depends on TASKSET
+ help
+ Add code for fancy output. This merely silences a compiler-warning
+ and adds about 135 Bytes. May be needed for machines with alot
+ of CPUs.
+
+config TIME
bool "time"
default n
help
@@ -132,16 +554,31 @@ config CONFIG_TIME
When the command finishes, time writes a message to standard output
giving timing statistics about this program run.
-config CONFIG_WATCHDOG
+config TIMEOUT
+ bool "timeout"
+ default n
+ help
+ Runs a program and watches it. If it does not terminate in
+ specified number of seconds, it is sent a signal.
+
+config TTYSIZE
+ bool "ttysize"
+ default n
+ help
+ A replacement for "stty size". Unlike stty, can report only width,
+ only height, or both, in any order. It also does not complain on
+ error, but returns default 80x24.
+ Usage in shell scripts: width=`ttysize w`.
+
+config WATCHDOG
bool "watchdog"
default n
help
- The watchdog utility is used with hardware or softwate watchdog
- device drivers. It opens the specified watchdog device special file
- and periodically writes a magic character to the device. If the
+ The watchdog utility is used with hardware or software watchdog
+ device drivers. It opens the specified watchdog device special file
+ and periodically writes a magic character to the device. If the
watchdog applet ever fails to write the magic character within a
certain amount of time, the watchdog device assumes the system has
hung, and will cause the hardware to reboot.
endmenu
-
diff --git a/release/src/router/busybox/miscutils/Kbuild b/release/src/router/busybox/miscutils/Kbuild
new file mode 100644
index 00000000..23d7d8d4
--- /dev/null
+++ b/release/src/router/busybox/miscutils/Kbuild
@@ -0,0 +1,41 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ADJTIMEX) += adjtimex.o
+lib-$(CONFIG_BBCONFIG) += bbconfig.o
+lib-$(CONFIG_CHAT) += chat.o
+lib-$(CONFIG_CHRT) += chrt.o
+lib-$(CONFIG_CROND) += crond.o
+lib-$(CONFIG_CRONTAB) += crontab.o
+lib-$(CONFIG_DC) += dc.o
+lib-$(CONFIG_DEVFSD) += devfsd.o
+lib-$(CONFIG_DEVMEM) += devmem.o
+lib-$(CONFIG_EJECT) += eject.o
+lib-$(CONFIG_FBSPLASH) += fbsplash.o
+lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o
+lib-$(CONFIG_IONICE) += ionice.o
+lib-$(CONFIG_HDPARM) += hdparm.o
+lib-$(CONFIG_INOTIFYD) += inotifyd.o
+lib-$(CONFIG_FEATURE_LAST_SMALL)+= last.o
+lib-$(CONFIG_FEATURE_LAST_FANCY)+= last_fancy.o
+lib-$(CONFIG_LESS) += less.o
+lib-$(CONFIG_MAKEDEVS) += makedevs.o
+lib-$(CONFIG_MAN) += man.o
+lib-$(CONFIG_MICROCOM) += microcom.o
+lib-$(CONFIG_MOUNTPOINT) += mountpoint.o
+lib-$(CONFIG_MT) += mt.o
+lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o
+lib-$(CONFIG_READAHEAD) += readahead.o
+lib-$(CONFIG_RUNLEVEL) += runlevel.o
+lib-$(CONFIG_RX) += rx.o
+lib-$(CONFIG_SETSID) += setsid.o
+lib-$(CONFIG_STRINGS) += strings.o
+lib-$(CONFIG_TASKSET) += taskset.o
+lib-$(CONFIG_TIME) += time.o
+lib-$(CONFIG_TIMEOUT) += timeout.o
+lib-$(CONFIG_TTYSIZE) += ttysize.o
+lib-$(CONFIG_WATCHDOG) += watchdog.o
diff --git a/release/src/router/busybox/miscutils/Makefile b/release/src/router/busybox/miscutils/Makefile
deleted file mode 100644
index 374ce68f..00000000
--- a/release/src/router/busybox/miscutils/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-TOPDIR:= ../
-MISCUTILS_DIR:=./
-include $(TOPDIR).config
-include $(TOPDIR)Rules.mak
-include Makefile.in
-all: $(libraries-y)
--include $(TOPDIR).depend
-
-clean:
- rm -f *.o *.a $(AR_TARGET)
-
diff --git a/release/src/router/busybox/miscutils/Makefile.in b/release/src/router/busybox/miscutils/Makefile.in
deleted file mode 100755
index 0f200f4f..00000000
--- a/release/src/router/busybox/miscutils/Makefile.in
+++ /dev/null
@@ -1,43 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-MISCUTILS_AR:=miscutils.a
-ifndef $(MISCUTILS_DIR)
-MISCUTILS_DIR:=$(TOPDIR)miscutils/
-endif
-
-
-MISCUTILS-y:=
-MISCUTILS-$(CONFIG_ADJTIMEX) += adjtimex.o
-MISCUTILS-$(CONFIG_CROND) += crond.o
-MISCUTILS-$(CONFIG_CRONTAB) += crontab.o
-MISCUTILS-$(CONFIG_DC) += dc.o
-MISCUTILS-$(CONFIG_DUTMP) += dutmp.o
-MISCUTILS-$(CONFIG_HDPARM) += hdparm.o
-MISCUTILS-$(CONFIG_MAKEDEVS) += makedevs.o
-MISCUTILS-$(CONFIG_MT) += mt.o
-MISCUTILS-$(CONFIG_STRINGS) += strings.o
-MISCUTILS-$(CONFIG_TIME) += time.o
-MISCUTILS-$(CONFIG_WATCHDOG) += watchdog.o
-
-libraries-y+=$(MISCUTILS_DIR)$(MISCUTILS_AR)
-
-$(MISCUTILS_DIR)$(MISCUTILS_AR): $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y))
- $(AR) -ro $@ $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y))
-
diff --git a/release/src/router/busybox/miscutils/adjtimex.c b/release/src/router/busybox/miscutils/adjtimex.c
index e941f6b4..07f08342 100644
--- a/release/src/router/busybox/miscutils/adjtimex.c
+++ b/release/src/router/busybox/miscutils/adjtimex.c
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
/*
* adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables.
*
@@ -5,153 +6,129 @@
* Last hack: March 2001
* Copyright 1997, 2000, 2001 Larry Doolittle <LRDoolittle@lbl.gov>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (Version 2,
- * June 1991) as published by the Free Software Foundation. At the
- * time of writing, that license was published by the FSF with the URL
- * http://www.gnu.org/copyleft/gpl.html, and is incorporated herein by
- * reference.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This adjtimex(1) is very similar in intent to adjtimex(8) by Steven
- * Dick <ssd@nevets.oau.org> and Jim Van Zandt <jrv@vanzandt.mv.com>
- * (see http://metalab.unc.edu/pub/Linux/system/admin/time/adjtimex*).
- * That version predates this one, and is _much_ bigger and more
- * featureful. My independently written version was very similar to
- * Steven's from the start, because they both follow the kernel timex
- * structure. I further tweaked this version to be equivalent to Steven's
- * where possible, but I don't like getopt_long, so the actual usage
- * syntax is incompatible.
- *
- * Amazingly enough, my Red Hat 5.2 sys/timex (and sub-includes)
- * don't actually give a prototype for adjtimex(2), so building
- * this code (with -Wall) gives a warning. Later versions of
- * glibc fix this issue.
- *
- * This program is too simple for a Makefile, just build with:
- * gcc -Wall -O adjtimex.c -o adjtimex
- *
* busyboxed 20 March 2001, Larry Doolittle <ldoolitt@recycle.lbl.gov>
- * It will autosense if it is built in a busybox environment, based
- * on the BB_VER preprocessor macro.
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
*/
-#include <stdio.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <unistd.h>
+#include "libbb.h"
#include <sys/timex.h>
-#include "busybox.h"
-
-static struct {int bit; char *name;} statlist[] = {
- { STA_PLL, "PLL" },
- { STA_PPSFREQ, "PPSFREQ" },
- { STA_PPSTIME, "PPSTIME" },
- { STA_FLL, "FFL" },
- { STA_INS, "INS" },
- { STA_DEL, "DEL" },
- { STA_UNSYNC, "UNSYNC" },
- { STA_FREQHOLD, "FREQHOLD" },
- { STA_PPSSIGNAL, "PPSSIGNAL" },
- { STA_PPSJITTER, "PPSJITTER" },
- { STA_PPSWANDER, "PPSWANDER" },
- { STA_PPSERROR, "PPSERROR" },
- { STA_CLOCKERR, "CLOCKERR" },
- { 0, NULL } };
-static char *ret_code_descript[] = {
- "clock synchronized",
- "insert leap second",
- "delete leap second",
- "leap second in progress",
- "leap second has occurred",
- "clock not synchronized" };
+static const uint16_t statlist_bit[] = {
+ STA_PLL,
+ STA_PPSFREQ,
+ STA_PPSTIME,
+ STA_FLL,
+ STA_INS,
+ STA_DEL,
+ STA_UNSYNC,
+ STA_FREQHOLD,
+ STA_PPSSIGNAL,
+ STA_PPSJITTER,
+ STA_PPSWANDER,
+ STA_PPSERROR,
+ STA_CLOCKERR,
+ 0
+};
+static const char statlist_name[] =
+ "PLL" "\0"
+ "PPSFREQ" "\0"
+ "PPSTIME" "\0"
+ "FFL" "\0"
+ "INS" "\0"
+ "DEL" "\0"
+ "UNSYNC" "\0"
+ "FREQHOLD" "\0"
+ "PPSSIGNAL" "\0"
+ "PPSJITTER" "\0"
+ "PPSWANDER" "\0"
+ "PPSERROR" "\0"
+ "CLOCKERR"
+;
-#ifdef BB_VER
-#define main adjtimex_main
-#else
-void usage(char *prog)
-{
- fprintf(stderr,
- "Usage: %s [ -q ] [ -o offset ] [ -f frequency ] [ -p timeconstant ] [ -t tick ]\n",
- prog);
-}
-#define bb_show_usage() usage(argv[0])
-#endif
+static const char ret_code_descript[] =
+ "clock synchronized" "\0"
+ "insert leap second" "\0"
+ "delete leap second" "\0"
+ "leap second in progress" "\0"
+ "leap second has occurred" "\0"
+ "clock not synchronized"
+;
-int main(int argc, char ** argv)
+int adjtimex_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int adjtimex_main(int argc, char **argv)
{
+ enum {
+ OPT_quiet = 0x1
+ };
+ unsigned opt;
+ char *opt_o, *opt_f, *opt_p, *opt_t;
struct timex txc;
- int quiet=0;
- int c, i, ret, sep;
- char *descript;
+ int i, ret;
+ const char *descript;
txc.modes=0;
- for (;;) {
- c = getopt( argc, argv, "qo:f:p:t:");
- if (c == EOF) break;
- switch (c) {
- case 'q':
- quiet=1;
- break;
- case 'o':
- txc.offset = atoi(optarg);
- txc.modes |= ADJ_OFFSET_SINGLESHOT;
- break;
- case 'f':
- txc.freq = atoi(optarg);
- txc.modes |= ADJ_FREQUENCY;
- break;
- case 'p':
- txc.constant = atoi(optarg);
- txc.modes |= ADJ_TIMECONST;
- break;
- case 't':
- txc.tick = atoi(optarg);
- txc.modes |= ADJ_TICK;
- break;
- default:
- bb_show_usage();
- exit(1);
- }
+
+ opt = getopt32(argv, "qo:f:p:t:",
+ &opt_o, &opt_f, &opt_p, &opt_t);
+ //if (opt & 0x1) // -q
+ if (opt & 0x2) { // -o
+ txc.offset = xatol(opt_o);
+ txc.modes |= ADJ_OFFSET_SINGLESHOT;
+ }
+ if (opt & 0x4) { // -f
+ txc.freq = xatol(opt_f);
+ txc.modes |= ADJ_FREQUENCY;
+ }
+ if (opt & 0x8) { // -p
+ txc.constant = xatol(opt_p);
+ txc.modes |= ADJ_TIMECONST;
+ }
+ if (opt & 0x10) { // -t
+ txc.tick = xatol(opt_t);
+ txc.modes |= ADJ_TICK;
}
if (argc != optind) { /* no valid non-option parameters */
bb_show_usage();
- exit(1);
}
ret = adjtimex(&txc);
- if (ret < 0) perror("adjtimex");
-
- if (!quiet && ret>=0) {
+ if (ret < 0) {
+ bb_perror_nomsg_and_die();
+ }
+
+ if (!(opt & OPT_quiet)) {
+ int sep;
+ const char *name;
+
printf(
" mode: %d\n"
"-o offset: %ld\n"
"-f frequency: %ld\n"
" maxerror: %ld\n"
" esterror: %ld\n"
- " status: %d ( ",
+ " status: %d (",
txc.modes, txc.offset, txc.freq, txc.maxerror,
txc.esterror, txc.status);
/* representative output of next code fragment:
"PLL | PPSTIME" */
- sep=0;
- for (i=0; statlist[i].name; i++) {
- if (txc.status & statlist[i].bit) {
- if (sep) fputs(" | ",stdout);
- fputs(statlist[i].name,stdout);
- sep=1;
+ name = statlist_name;
+ sep = 0;
+ for (i = 0; statlist_bit[i]; i++) {
+ if (txc.status & statlist_bit[i]) {
+ if (sep)
+ fputs(" | ", stdout);
+ fputs(name, stdout);
+ sep = 1;
}
+ name += strlen(name) + 1;
}
descript = "error";
- if (ret >= 0 && ret <= 5) descript = ret_code_descript[ret];
- printf(" )\n"
+ if (ret <= 5)
+ descript = nth_string(ret_code_descript, ret);
+ printf(")\n"
"-p timeconstant: %ld\n"
" precision: %ld\n"
" tolerance: %ld\n"
@@ -163,5 +140,6 @@ int main(int argc, char ** argv)
txc.precision, txc.tolerance, txc.tick,
(long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript);
}
- return (ret<0);
+
+ return 0;
}
diff --git a/release/src/router/busybox/miscutils/bbconfig.c b/release/src/router/busybox/miscutils/bbconfig.c
new file mode 100644
index 00000000..689052e8
--- /dev/null
+++ b/release/src/router/busybox/miscutils/bbconfig.c
@@ -0,0 +1,12 @@
+/* vi: set sw=4 ts=4: */
+/* This file was released into the public domain by Paul Fox.
+ */
+#include "libbb.h"
+#include "bbconfigopts.h"
+
+int bbconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int bbconfig_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+ printf(bbconfig_config);
+ return 0;
+}
diff --git a/release/src/router/busybox/miscutils/chat.c b/release/src/router/busybox/miscutils/chat.c
new file mode 100644
index 00000000..3ffd7b22
--- /dev/null
+++ b/release/src/router/busybox/miscutils/chat.c
@@ -0,0 +1,435 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bare bones chat utility
+ * inspired by ppp's chat
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
+
+// default timeout: 45 sec
+#define DEFAULT_CHAT_TIMEOUT 45*1000
+// max length of "abort string",
+// i.e. device reply which causes termination
+#define MAX_ABORT_LEN 50
+
+// possible exit codes
+enum {
+ ERR_OK = 0, // all's well
+ ERR_MEM, // read too much while expecting
+ ERR_IO, // signalled or I/O error
+ ERR_TIMEOUT, // timed out while expecting
+ ERR_ABORT, // first abort condition was met
+// ERR_ABORT2, // second abort condition was met
+// ...
+};
+
+// exit code
+#define exitcode bb_got_signal
+
+// trap for critical signals
+static void signal_handler(UNUSED_PARAM int signo)
+{
+ // report I/O error condition
+ exitcode = ERR_IO;
+}
+
+#if !ENABLE_FEATURE_CHAT_IMPLICIT_CR
+#define unescape(s, nocr) unescape(s)
+#endif
+static size_t unescape(char *s, int *nocr)
+{
+ char *start = s;
+ char *p = s;
+
+ while (*s) {
+ char c = *s;
+ // do we need special processing?
+ // standard escapes + \s for space and \N for \0
+ // \c inhibits terminating \r for commands and is noop for expects
+ if ('\\' == c) {
+ c = *++s;
+ if (c) {
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+ if ('c' == c) {
+ *nocr = 1;
+ goto next;
+ }
+#endif
+ if ('N' == c) {
+ c = '\0';
+ } else if ('s' == c) {
+ c = ' ';
+#if ENABLE_FEATURE_CHAT_NOFAIL
+ // unescape leading dash only
+ // TODO: and only for expect, not command string
+ } else if ('-' == c && (start + 1 == s)) {
+ //c = '-';
+#endif
+ } else {
+ c = bb_process_escape_sequence((const char **)&s);
+ s--;
+ }
+ }
+ // ^A becomes \001, ^B -- \002 and so on...
+ } else if ('^' == c) {
+ c = *++s-'@';
+ }
+ // put unescaped char
+ *p++ = c;
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+ next:
+#endif
+ // next char
+ s++;
+ }
+ *p = '\0';
+
+ return p - start;
+}
+
+int chat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chat_main(int argc UNUSED_PARAM, char **argv)
+{
+ int record_fd = -1;
+ bool echo = 0;
+ // collection of device replies which cause unconditional termination
+ llist_t *aborts = NULL;
+ // inactivity period
+ int timeout = DEFAULT_CHAT_TIMEOUT;
+ // maximum length of abort string
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+ size_t max_abort_len = 0;
+#else
+#define max_abort_len MAX_ABORT_LEN
+#endif
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+ struct termios tio0, tio;
+#endif
+ // directive names
+ enum {
+ DIR_HANGUP = 0,
+ DIR_ABORT,
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+ DIR_CLR_ABORT,
+#endif
+ DIR_TIMEOUT,
+ DIR_ECHO,
+ DIR_SAY,
+ DIR_RECORD,
+ };
+
+ // make x* functions fail with correct exitcode
+ xfunc_error_retval = ERR_IO;
+
+ // trap vanilla signals to prevent process from being killed suddenly
+ bb_signals(0
+ + (1 << SIGHUP)
+ + (1 << SIGINT)
+ + (1 << SIGTERM)
+ + (1 << SIGPIPE)
+ , signal_handler);
+
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+ tcgetattr(STDIN_FILENO, &tio);
+ tio0 = tio;
+ cfmakeraw(&tio);
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
+#endif
+
+#if ENABLE_FEATURE_CHAT_SWALLOW_OPTS
+ getopt32(argv, "vVsSE");
+ argv += optind;
+#else
+ argv++; // goto first arg
+#endif
+ // handle chat expect-send pairs
+ while (*argv) {
+ // directive given? process it
+ int key = index_in_strings(
+ "HANGUP\0" "ABORT\0"
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+ "CLR_ABORT\0"
+#endif
+ "TIMEOUT\0" "ECHO\0" "SAY\0" "RECORD\0"
+ , *argv
+ );
+ if (key >= 0) {
+ // cache directive value
+ char *arg = *++argv;
+ // OFF -> 0, anything else -> 1
+ bool onoff = (0 != strcmp("OFF", arg));
+ // process directive
+ if (DIR_HANGUP == key) {
+ // turn SIGHUP on/off
+ signal(SIGHUP, onoff ? signal_handler : SIG_IGN);
+ } else if (DIR_ABORT == key) {
+ // append the string to abort conditions
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+ size_t len = strlen(arg);
+ if (len > max_abort_len)
+ max_abort_len = len;
+#endif
+ llist_add_to_end(&aborts, arg);
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+ } else if (DIR_CLR_ABORT == key) {
+ // remove the string from abort conditions
+ // N.B. gotta refresh maximum length too...
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+ max_abort_len = 0;
+#endif
+ for (llist_t *l = aborts; l; l = l->link) {
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+ size_t len = strlen(l->data);
+#endif
+ if (!strcmp(arg, l->data)) {
+ llist_unlink(&aborts, l);
+ continue;
+ }
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+ if (len > max_abort_len)
+ max_abort_len = len;
+#endif
+ }
+#endif
+ } else if (DIR_TIMEOUT == key) {
+ // set new timeout
+ // -1 means OFF
+ timeout = atoi(arg) * 1000;
+ // 0 means default
+ // >0 means value in msecs
+ if (!timeout)
+ timeout = DEFAULT_CHAT_TIMEOUT;
+ } else if (DIR_ECHO == key) {
+ // turn echo on/off
+ // N.B. echo means dumping device input/output to stderr
+ echo = onoff;
+ } else if (DIR_RECORD == key) {
+ // turn record on/off
+ // N.B. record means dumping device input to a file
+ // close previous record_fd
+ if (record_fd > 0)
+ close(record_fd);
+ // N.B. do we have to die here on open error?
+ record_fd = (onoff) ? xopen(arg, O_WRONLY|O_CREAT|O_TRUNC) : -1;
+ } else if (DIR_SAY == key) {
+ // just print argument verbatim
+ // TODO: should we use full_write() to avoid unistd/stdio conflict?
+ bb_error_msg("%s", arg);
+ }
+ // next, please!
+ argv++;
+ // ordinary expect-send pair!
+ } else {
+ //-----------------------
+ // do expect
+ //-----------------------
+ int expect_len;
+ size_t buf_len = 0;
+ size_t max_len = max_abort_len;
+
+ struct pollfd pfd;
+#if ENABLE_FEATURE_CHAT_NOFAIL
+ int nofail = 0;
+#endif
+ char *expect = *argv++;
+
+ // sanity check: shall we really expect something?
+ if (!expect)
+ goto expect_done;
+
+#if ENABLE_FEATURE_CHAT_NOFAIL
+ // if expect starts with -
+ if ('-' == *expect) {
+ // swallow -
+ expect++;
+ // and enter nofail mode
+ nofail++;
+ }
+#endif
+
+#ifdef ___TEST___BUF___ // test behaviour with a small buffer
+# undef COMMON_BUFSIZE
+# define COMMON_BUFSIZE 6
+#endif
+ // expand escape sequences in expect
+ expect_len = unescape(expect, &expect_len /*dummy*/);
+ if (expect_len > max_len)
+ max_len = expect_len;
+ // sanity check:
+ // we should expect more than nothing but not more than input buffer
+ // TODO: later we'll get rid of fixed-size buffer
+ if (!expect_len)
+ goto expect_done;
+ if (max_len >= COMMON_BUFSIZE) {
+ exitcode = ERR_MEM;
+ goto expect_done;
+ }
+
+ // get reply
+ pfd.fd = STDIN_FILENO;
+ pfd.events = POLLIN;
+ while (!exitcode
+ && poll(&pfd, 1, timeout) > 0
+ && (pfd.revents & POLLIN)
+ ) {
+#define buf bb_common_bufsiz1
+ llist_t *l;
+ ssize_t delta;
+
+ // read next char from device
+ if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) {
+ // dump device input if RECORD fname
+ if (record_fd > 0) {
+ full_write(record_fd, buf+buf_len, 1);
+ }
+ // dump device input if ECHO ON
+ if (echo > 0) {
+// if (buf[buf_len] < ' ') {
+// full_write(STDERR_FILENO, "^", 1);
+// buf[buf_len] += '@';
+// }
+ full_write(STDERR_FILENO, buf+buf_len, 1);
+ }
+ buf_len++;
+ // move input frame if we've reached higher bound
+ if (buf_len > COMMON_BUFSIZE) {
+ memmove(buf, buf+buf_len-max_len, max_len);
+ buf_len = max_len;
+ }
+ }
+ // N.B. rule of thumb: values being looked for can
+ // be found only at the end of input buffer
+ // this allows to get rid of strstr() and memmem()
+
+ // TODO: make expect and abort strings processed uniformly
+ // abort condition is met? -> bail out
+ for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) {
+ size_t len = strlen(l->data);
+ delta = buf_len-len;
+ if (delta >= 0 && !memcmp(buf+delta, l->data, len))
+ goto expect_done;
+ }
+ exitcode = ERR_OK;
+
+ // expected reply received? -> goto next command
+ delta = buf_len - expect_len;
+ if (delta >= 0 && !memcmp(buf+delta, expect, expect_len))
+ goto expect_done;
+#undef buf
+ } /* while (have data) */
+
+ // device timed out or unexpected reply received
+ exitcode = ERR_TIMEOUT;
+ expect_done:
+#if ENABLE_FEATURE_CHAT_NOFAIL
+ // on success and when in nofail mode
+ // we should skip following subsend-subexpect pairs
+ if (nofail) {
+ if (!exitcode) {
+ // find last send before non-dashed expect
+ while (*argv && argv[1] && '-' == argv[1][0])
+ argv += 2;
+ // skip the pair
+ // N.B. do we really need this?!
+ if (!*argv++ || !*argv++)
+ break;
+ }
+ // nofail mode also clears all but IO errors (or signals)
+ if (ERR_IO != exitcode)
+ exitcode = ERR_OK;
+ }
+#endif
+ // bail out unless we expected successfully
+ if (exitcode)
+ break;
+
+ //-----------------------
+ // do send
+ //-----------------------
+ if (*argv) {
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+ int nocr = 0; // inhibit terminating command with \r
+#endif
+ char *loaded = NULL; // loaded command
+ size_t len;
+ char *buf = *argv++;
+
+ // if command starts with @
+ // load "real" command from file named after @
+ if ('@' == *buf) {
+ // skip the @ and any following white-space
+ trim(++buf);
+ buf = loaded = xmalloc_xopen_read_close(buf, NULL);
+ }
+ // expand escape sequences in command
+ len = unescape(buf, &nocr);
+
+ // send command
+ alarm(timeout);
+ pfd.fd = STDOUT_FILENO;
+ pfd.events = POLLOUT;
+ while (len && !exitcode
+ && poll(&pfd, 1, -1) > 0
+ && (pfd.revents & POLLOUT)
+ ) {
+#if ENABLE_FEATURE_CHAT_SEND_ESCAPES
+ // "\\d" means 1 sec delay, "\\p" means 0.01 sec delay
+ // "\\K" means send BREAK
+ char c = *buf;
+ if ('\\' == c) {
+ c = *++buf;
+ if ('d' == c) {
+ sleep(1);
+ len--;
+ continue;
+ }
+ if ('p' == c) {
+ usleep(10000);
+ len--;
+ continue;
+ }
+ if ('K' == c) {
+ tcsendbreak(STDOUT_FILENO, 0);
+ len--;
+ continue;
+ }
+ buf--;
+ }
+ if (safe_write(STDOUT_FILENO, buf, 1) != 1)
+ break;
+ len--;
+ buf++;
+#else
+ len -= full_write(STDOUT_FILENO, buf, len);
+#endif
+ } /* while (can write) */
+ alarm(0);
+
+ // report I/O error if there still exists at least one non-sent char
+ if (len)
+ exitcode = ERR_IO;
+
+ // free loaded command (if any)
+ if (loaded)
+ free(loaded);
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+ // or terminate command with \r (if not inhibited)
+ else if (!nocr)
+ xwrite(STDOUT_FILENO, "\r", 1);
+#endif
+ // bail out unless we sent command successfully
+ if (exitcode)
+ break;
+ } /* if (*argv) */
+ }
+ } /* while (*argv) */
+
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
+#endif
+
+ return exitcode;
+}
diff --git a/release/src/router/busybox/miscutils/chrt.c b/release/src/router/busybox/miscutils/chrt.c
new file mode 100644
index 00000000..cc5660be
--- /dev/null
+++ b/release/src/router/busybox/miscutils/chrt.c
@@ -0,0 +1,123 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chrt - manipulate real-time attributes of a process
+ * Copyright (c) 2006-2007 Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sched.h>
+#include "libbb.h"
+#ifndef _POSIX_PRIORITY_SCHEDULING
+#warning your system may be foobared
+#endif
+static const struct {
+ int policy;
+ char name[12];
+} policies[] = {
+ {SCHED_OTHER, "SCHED_OTHER"},
+ {SCHED_FIFO, "SCHED_FIFO"},
+ {SCHED_RR, "SCHED_RR"}
+};
+
+static void show_min_max(int pol)
+{
+ const char *fmt = "%s min/max priority\t: %d/%d\n\0%s not supported?\n";
+ int max, min;
+ max = sched_get_priority_max(pol);
+ min = sched_get_priority_min(pol);
+ if (max >= 0 && min >= 0)
+ printf(fmt, policies[pol].name, min, max);
+ else {
+ fmt += 29;
+ printf(fmt, policies[pol].name);
+ }
+}
+
+#define OPT_m (1<<0)
+#define OPT_p (1<<1)
+#define OPT_r (1<<2)
+#define OPT_f (1<<3)
+#define OPT_o (1<<4)
+
+int chrt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chrt_main(int argc UNUSED_PARAM, char **argv)
+{
+ pid_t pid = 0;
+ unsigned opt;
+ struct sched_param sp;
+ char *pid_str;
+ char *priority = priority; /* for compiler */
+ const char *current_new;
+ int policy = SCHED_RR;
+
+ /* at least 1 arg; only one policy accepted */
+ opt_complementary = "-1:r--fo:f--ro:r--fo";
+ opt = getopt32(argv, "+mprfo");
+ if (opt & OPT_r)
+ policy = SCHED_RR;
+ if (opt & OPT_f)
+ policy = SCHED_FIFO;
+ if (opt & OPT_o)
+ policy = SCHED_OTHER;
+ if (opt & OPT_m) { /* print min/max */
+ show_min_max(SCHED_FIFO);
+ show_min_max(SCHED_RR);
+ show_min_max(SCHED_OTHER);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ argv += optind;
+ if (opt & OPT_p) {
+ pid_str = *argv++;
+ if (*argv) { /* "-p <priority> <pid> [...]" */
+ priority = pid_str;
+ pid_str = *argv;
+ }
+ /* else "-p <pid>", and *argv == NULL */
+ pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
+ } else {
+ priority = *argv++;
+ if (!*argv)
+ bb_show_usage();
+ }
+
+ current_new = "current\0new";
+ if (opt & OPT_p) {
+ int pol;
+ print_rt_info:
+ pol = sched_getscheduler(pid);
+ if (pol < 0)
+ bb_perror_msg_and_die("can't %cet pid %d's policy", 'g', pid);
+ printf("pid %d's %s scheduling policy: %s\n",
+ pid, current_new, policies[pol].name);
+ if (sched_getparam(pid, &sp))
+ bb_perror_msg_and_die("can't get pid %d's attributes", pid);
+ printf("pid %d's %s scheduling priority: %d\n",
+ pid, current_new, sp.sched_priority);
+ if (!*argv) {
+ /* Either it was just "-p <pid>",
+ * or it was "-p <priority> <pid>" and we came here
+ * for the second time (see goto below) */
+ return EXIT_SUCCESS;
+ }
+ *argv = NULL;
+ current_new += 8;
+ }
+
+ /* from the manpage of sched_getscheduler:
+ [...] sched_priority can have a value in the range 0 to 99.
+ [...] SCHED_OTHER or SCHED_BATCH must be assigned static priority 0.
+ [...] SCHED_FIFO or SCHED_RR can have static priority in 1..99 range.
+ */
+ sp.sched_priority = xstrtou_range(priority, 0, policy != SCHED_OTHER ? 1 : 0, 99);
+
+ if (sched_setscheduler(pid, policy, &sp) < 0)
+ bb_perror_msg_and_die("can't %cet pid %d's policy", 's', pid);
+
+ if (!*argv) /* "-p <priority> <pid> [...]" */
+ goto print_rt_info;
+
+ BB_EXECVP(*argv, argv);
+ bb_simple_perror_msg_and_die(*argv);
+}
diff --git a/release/src/router/busybox/miscutils/crond.c b/release/src/router/busybox/miscutils/crond.c
index f0920136..804fe0b2 100644
--- a/release/src/router/busybox/miscutils/crond.c
+++ b/release/src/router/busybox/miscutils/crond.c
@@ -1,48 +1,32 @@
+/* vi: set sw=4 ts=4: */
/*
* crond -d[#] -c <crondir> -f -b
*
* run as root, but NOT setuid root
*
* Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
- * May be distributed under the GNU General Public License
+ * (version 2.3.2)
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
*
- * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-#define VERSION "2.3.2"
-
-#undef FEATURE_DEBUG_OPT
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <unistd.h>
+#include "libbb.h"
#include <syslog.h>
-#include <signal.h>
-#include <getopt.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-#include "busybox.h"
+/* glibc frees previous setenv'ed value when we do next setenv()
+ * of the same variable. uclibc does not do this! */
+#if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */
+#define SETENV_LEAKS 0
+#else
+#define SETENV_LEAKS 1
+#endif
-#define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
-#ifndef CRONTABS
-#define CRONTABS "/var/spool/cron/crontabs"
-#endif
-#ifndef TMPDIR
-#define TMPDIR "/var/spool/cron"
-#endif
+#define TMPDIR CONFIG_FEATURE_CROND_DIR
+#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
#ifndef SENDMAIL
-#define SENDMAIL "/usr/sbin/sendmail"
+#define SENDMAIL "sendmail"
#endif
#ifndef SENDMAIL_ARGS
#define SENDMAIL_ARGS "-ti", "oem"
@@ -51,723 +35,593 @@
#define CRONUPDATE "cron.update"
#endif
#ifndef MAXLINES
-#define MAXLINES 256 /* max lines in non-root crontabs */
+#define MAXLINES 256 /* max lines in non-root crontabs */
#endif
-static const char def_sh[] = "/bin/sh";
-
typedef struct CronFile {
- struct CronFile *cf_Next;
- struct CronLine *cf_LineBase;
- char *cf_User; /* username */
- int cf_Ready; /* bool: one or more jobs ready */
- int cf_Running; /* bool: one or more jobs running */
- int cf_Deleted; /* marked for deletion, ignore */
+ struct CronFile *cf_Next;
+ struct CronLine *cf_LineBase;
+ char *cf_User; /* username */
+ smallint cf_Ready; /* bool: one or more jobs ready */
+ smallint cf_Running; /* bool: one or more jobs running */
+ smallint cf_Deleted; /* marked for deletion, ignore */
} CronFile;
typedef struct CronLine {
- struct CronLine *cl_Next;
- char *cl_Shell; /* shell command */
- pid_t cl_Pid; /* running pid, 0, or armed (-1) */
- int cl_MailFlag; /* running pid is for mail */
- int cl_MailPos; /* 'empty file' size */
- char cl_Mins[60]; /* 0-59 */
- char cl_Hrs[24]; /* 0-23 */
- char cl_Days[32]; /* 1-31 */
- char cl_Mons[12]; /* 0-11 */
- char cl_Dow[7]; /* 0-6, beginning sunday */
+ struct CronLine *cl_Next;
+ char *cl_Shell; /* shell command */
+ pid_t cl_Pid; /* running pid, 0, or armed (-1) */
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ int cl_MailPos; /* 'empty file' size */
+ smallint cl_MailFlag; /* running pid is for mail */
+ char *cl_MailTo; /* whom to mail results */
+#endif
+ /* ordered by size, not in natural order. makes code smaller: */
+ char cl_Dow[7]; /* 0-6, beginning sunday */
+ char cl_Mons[12]; /* 0-11 */
+ char cl_Hrs[24]; /* 0-23 */
+ char cl_Days[32]; /* 1-31 */
+ char cl_Mins[60]; /* 0-59 */
} CronLine;
-#define RUN_RANOUT 1
-#define RUN_RUNNING 2
-#define RUN_FAILED 3
#define DaemonUid 0
-#ifdef FEATURE_DEBUG_OPT
-static short DebugOpt;
+
+enum {
+ OPT_l = (1 << 0),
+ OPT_L = (1 << 1),
+ OPT_f = (1 << 2),
+ OPT_b = (1 << 3),
+ OPT_S = (1 << 4),
+ OPT_c = (1 << 5),
+ OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D,
+};
+#if ENABLE_FEATURE_CROND_D
+#define DebugOpt (option_mask32 & OPT_d)
+#else
+#define DebugOpt 0
#endif
-static short LogLevel = 8;
-static const char *LogFile;
-static const char *CDir = CRONTABS;
-static void startlogger(void);
+struct globals {
+ unsigned LogLevel; /* = 8; */
+ const char *LogFile;
+ const char *CDir; /* = CRONTABS; */
+ CronFile *FileBase;
+#if SETENV_LEAKS
+ char *env_var_user;
+ char *env_var_home;
+#endif
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define LogLevel (G.LogLevel )
+#define LogFile (G.LogFile )
+#define CDir (G.CDir )
+#define FileBase (G.FileBase )
+#define env_var_user (G.env_var_user )
+#define env_var_home (G.env_var_home )
+#define INIT_G() do { \
+ LogLevel = 8; \
+ CDir = CRONTABS; \
+} while (0)
+
static void CheckUpdates(void);
static void SynchronizeDir(void);
static int TestJobs(time_t t1, time_t t2);
static void RunJobs(void);
static int CheckJobs(void);
-
static void RunJob(const char *user, CronLine *line);
-#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
static void EndJob(const char *user, CronLine *line);
#else
-#define EndJob(user, line) line->cl_Pid = 0
+#define EndJob(user, line) ((line)->cl_Pid = 0)
#endif
-
static void DeleteFile(const char *userName);
-static CronFile *FileBase;
+#define LVL5 "\x05"
+#define LVL7 "\x07"
+#define LVL8 "\x08"
+#define LVL9 "\x09"
+#define WARN9 "\x49"
+#define DIE9 "\xc9"
+/* level >= 20 is "error" */
+#define ERR20 "\x14"
-static void
-crondlog(const char *ctl, ...)
+static void crondlog(const char *ctl, ...)
{
- va_list va;
- int level = (int)(ctl[0] & 0xf);
- int type = level == 20 ?
- LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
-
-
- va_start(va, ctl);
- if (level >= LogLevel) {
-
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt) vfprintf(stderr, ctl, va);
- else
-#endif
- if (LogFile == 0) vsyslog(type, ctl, va);
- else {
- int logfd;
-
- if ((logfd = open(LogFile, O_WRONLY|O_CREAT|O_APPEND, 600)) >= 0) {
- vdprintf(logfd, ctl, va);
- close(logfd);
-#ifdef FEATURE_DEBUG_OPT
- } else {
- bb_perror_msg("Can't open log file");
-#endif
+ va_list va;
+ int level = (ctl[0] & 0x1f);
+
+ va_start(va, ctl);
+ if (level >= (int)LogLevel) {
+ /* Debug mode: all to (non-redirected) stderr, */
+ /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
+ if (!DebugOpt && LogFile) {
+ /* Otherwise (log to file): we reopen log file at every write: */
+ int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0666);
+ if (logfd >= 0)
+ xmove_fd(logfd, STDERR_FILENO);
}
- }
- }
- va_end(va);
- if(ctl[0] & 0200)
- exit(20);
+// TODO: ERR -> error, WARN -> warning, LVL -> info
+ bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
+ }
+ va_end(va);
+ if (ctl[0] & 0x80)
+ exit(20);
}
-int
-crond_main(int ac, char **av)
+int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int crond_main(int argc UNUSED_PARAM, char **argv)
{
- unsigned long opt;
- char *lopt, *Lopt, *copt;
-#ifdef FEATURE_DEBUG_OPT
- char *dopt;
- bb_opt_complementaly = "f-b:b-f:S-L:L-S:d-l";
-#else
- bb_opt_complementaly = "f-b:b-f:S-L:L-S";
-#endif
-
- opterr = 0; /* disable getopt 'errors' message.*/
- opt = bb_getopt_ulflags(ac, av, "l:L:fbSc:"
-#ifdef FEATURE_DEBUG_OPT
- "d:"
-#endif
- , &lopt, &Lopt, &copt
-#ifdef FEATURE_DEBUG_OPT
- , &dopt
-#endif
- );
- if(opt & 1)
- LogLevel = atoi(lopt);
- if(opt & 2)
- if (*Lopt != 0) LogFile = Lopt;
- if(opt & 32) {
- if (*copt != 0) CDir = copt;
+ unsigned opt;
+
+ INIT_G();
+
+ /* "-b after -f is ignored", and so on for every pair a-b */
+ opt_complementary = "f-b:b-f:S-L:L-S" USE_FEATURE_CROND_D(":d-l")
+ ":l+:d+"; /* -l and -d have numeric param */
+ opt = getopt32(argv, "l:L:fbSc:" USE_FEATURE_CROND_D("d:"),
+ &LogLevel, &LogFile, &CDir
+ USE_FEATURE_CROND_D(,&LogLevel));
+ /* both -d N and -l N set the same variable: LogLevel */
+
+ if (!(opt & OPT_f)) {
+ /* close stdin, stdout, stderr.
+ * close unused descriptors - don't need them. */
+ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
}
-#ifdef FEATURE_DEBUG_OPT
- if(opt & 64) {
- DebugOpt = atoi(dopt);
- LogLevel = 0;
- }
-#endif
- /*
- * change directory
- */
-
- if (chdir(CDir) != 0)
- bb_perror_msg_and_die("%s", CDir);
-
- signal(SIGHUP,SIG_IGN); /* hmm.. but, if kill -HUP original
- * version - his died. ;(
- */
- /*
- * close stdin and stdout, stderr.
- * close unused descriptors - don't need.
- * optional detach from controlling terminal
- */
-
- if (!(opt & 4)) {
- if(daemon(1, 0) < 0) {
- bb_perror_msg_and_die("daemon");
- }
-#if defined(__uClinux__)
- else {
- /* reexec for vfork() do continue parent */
- vfork_daemon_rexec(ac, av, "-f");
+ if (!DebugOpt && LogFile == NULL) {
+ /* logging to syslog */
+ openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
+ logmode = LOGMODE_SYSLOG;
}
-#endif /* uClinux */
- }
-
- (void)startlogger(); /* need if syslog mode selected */
-
- /*
- * main loop - synchronize to 1 second after the minute, minimum sleep
- * of 1 second.
- */
-
- crondlog("\011%s " VERSION " dillon, started, log level %d\n", bb_applet_name,
- LogLevel);
-
- SynchronizeDir();
-
- {
- time_t t1 = time(NULL);
- time_t t2;
- long dt;
- short rescan = 60;
- short sleep_time = 60;
-
- for (;;) {
- sleep((sleep_time + 1) - (short)(time(NULL) % sleep_time));
-
- t2 = time(NULL);
- dt = t2 - t1;
-
- /*
- * The file 'cron.update' is checked to determine new cron
- * jobs. The directory is rescanned once an hour to deal
- * with any screwups.
- *
- * check for disparity. Disparities over an hour either way
- * result in resynchronization. A reverse-indexed disparity
- * less then an hour causes us to effectively sleep until we
- * match the original time (i.e. no re-execution of jobs that
- * have just been run). A forward-indexed disparity less then
- * an hour causes intermediate jobs to be run, but only once
- * in the worst case.
- *
- * when running jobs, the inequality used is greater but not
- * equal to t1, and less then or equal to t2.
- */
-
- if (--rescan == 0) {
- rescan = 60;
- SynchronizeDir();
- }
- CheckUpdates();
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt)
- crondlog("\005Wakeup dt=%d\n", dt);
-#endif
- if (dt < -60*60 || dt > 60*60) {
- t1 = t2;
- crondlog("\111time disparity of %d minutes detected\n", dt / 60);
- } else if (dt > 0) {
- TestJobs(t1, t2);
- RunJobs();
- sleep(5);
- if (CheckJobs() > 0)
- sleep_time = 10;
- else
- sleep_time = 60;
- t1 = t2;
- }
+
+ xchdir(CDir);
+ //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
+ xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
+ crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel);
+ SynchronizeDir();
+
+ /* main loop - synchronize to 1 second after the minute, minimum sleep
+ * of 1 second. */
+ {
+ time_t t1 = time(NULL);
+ time_t t2;
+ long dt;
+ int rescan = 60;
+ int sleep_time = 60;
+
+ write_pidfile("/var/run/crond.pid");
+ for (;;) {
+ sleep((sleep_time + 1) - (time(NULL) % sleep_time));
+
+ t2 = time(NULL);
+ dt = (long)t2 - (long)t1;
+
+ /*
+ * The file 'cron.update' is checked to determine new cron
+ * jobs. The directory is rescanned once an hour to deal
+ * with any screwups.
+ *
+ * check for disparity. Disparities over an hour either way
+ * result in resynchronization. A reverse-indexed disparity
+ * less then an hour causes us to effectively sleep until we
+ * match the original time (i.e. no re-execution of jobs that
+ * have just been run). A forward-indexed disparity less then
+ * an hour causes intermediate jobs to be run, but only once
+ * in the worst case.
+ *
+ * when running jobs, the inequality used is greater but not
+ * equal to t1, and less then or equal to t2.
+ */
+ if (--rescan == 0) {
+ rescan = 60;
+ SynchronizeDir();
+ }
+ CheckUpdates();
+ if (DebugOpt)
+ crondlog(LVL5 "wakeup dt=%ld", dt);
+ if (dt < -60 * 60 || dt > 60 * 60) {
+ crondlog(WARN9 "time disparity of %d minutes detected", dt / 60);
+ } else if (dt > 0) {
+ TestJobs(t1, t2);
+ RunJobs();
+ sleep(5);
+ if (CheckJobs() > 0) {
+ sleep_time = 10;
+ } else {
+ sleep_time = 60;
+ }
+ }
+ t1 = t2;
+ }
}
- }
- /* not reached */
+ return 0; /* not reached */
}
-
-#if defined(FEATURE_DEBUG_OPT) || defined(CONFIG_FEATURE_CROND_CALL_SENDMAIL)
-/*
- write to temp file..
-*/
-static void
-fdprintf(int fd, const char *ctl, ...)
+#if SETENV_LEAKS
+/* We set environment *before* vfork (because we want to use vfork),
+ * so we cannot use setenv() - repeated calls to setenv() may leak memory!
+ * Using putenv(), and freeing memory after unsetenv() won't leak */
+static void safe_setenv(char **pvar_val, const char *var, const char *val)
{
- va_list va;
+ char *var_val = *pvar_val;
- va_start(va, ctl);
- vdprintf(fd, ctl, va);
- va_end(va);
+ if (var_val) {
+ bb_unsetenv(var_val);
+ free(var_val);
+ }
+ *pvar_val = xasprintf("%s=%s", var, val);
+ putenv(*pvar_val);
}
#endif
-
-static int
-ChangeUser(const char *user)
+static void SetEnv(struct passwd *pas)
{
- struct passwd *pas;
-
- /*
- * Obtain password entry and change privilages
- */
-
- if ((pas = getpwnam(user)) == 0) {
- crondlog("\011failed to get uid for %s", user);
- return(-1);
- }
- setenv("USER", pas->pw_name, 1);
- setenv("HOME", pas->pw_dir, 1);
- setenv("SHELL", def_sh, 1);
-
- /*
- * Change running state to the user in question
- */
-
- if (initgroups(user, pas->pw_gid) < 0) {
- crondlog("\011initgroups failed: %s %m", user);
- return(-1);
- }
- /* drop all priviledges */
- if (setgid(pas->pw_gid) < 0) {
- crondlog("\011setgid failed: %s %d", user, pas->pw_gid);
- return(-1);
- }
- if (setuid(pas->pw_uid) < 0) {
- crondlog("\011setuid failed: %s %d", user, pas->pw_uid);
- return(-1);
- }
- if (chdir(pas->pw_dir) < 0) {
- crondlog("\011chdir failed: %s: %m", pas->pw_dir);
- if (chdir(TMPDIR) < 0) {
- crondlog("\011chdir failed: %s: %m", TMPDIR);
- return(-1);
- }
- }
- return(pas->pw_uid);
+#if SETENV_LEAKS
+ safe_setenv(&env_var_user, "USER", pas->pw_name);
+ safe_setenv(&env_var_home, "HOME", pas->pw_dir);
+ /* if we want to set user's shell instead: */
+ /*safe_setenv(env_var_user, "SHELL", pas->pw_shell);*/
+#else
+ xsetenv("USER", pas->pw_name);
+ xsetenv("HOME", pas->pw_dir);
+#endif
+ /* currently, we use constant one: */
+ /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
}
-static void
-startlogger(void)
+static void ChangeUser(struct passwd *pas)
{
- if (LogFile == 0)
- openlog(bb_applet_name, LOG_CONS|LOG_PID, LOG_CRON);
-#ifdef FEATURE_DEBUG_OPT
- else { /* test logfile */
- int logfd;
-
- if ((logfd = open(LogFile, O_WRONLY|O_CREAT|O_APPEND, 600)) >= 0)
- close(logfd);
- else
- bb_perror_msg("Failed to open log file '%s' reason", LogFile);
- }
-#endif
+ /* careful: we're after vfork! */
+ change_identity(pas); /* - initgroups, setgid, setuid */
+ if (chdir(pas->pw_dir) < 0) {
+ crondlog(LVL9 "can't chdir(%s)", pas->pw_dir);
+ if (chdir(TMPDIR) < 0) {
+ crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */
+ }
+ }
}
+static const char DowAry[] ALIGN1 =
+ "sun""mon""tue""wed""thu""fri""sat"
+ /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
+;
-static const char * const DowAry[] = {
- "sun",
- "mon",
- "tue",
- "wed",
- "thu",
- "fri",
- "sat",
-
- "Sun",
- "Mon",
- "Tue",
- "Wed",
- "Thu",
- "Fri",
- "Sat",
- NULL
-};
-
-static const char * const MonAry[] = {
- "jan",
- "feb",
- "mar",
- "apr",
- "may",
- "jun",
- "jul",
- "aug",
- "sep",
- "oct",
- "nov",
- "dec",
-
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- NULL
-};
+static const char MonAry[] ALIGN1 =
+ "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec"
+ /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */
+;
-static char *
-ParseField(char *user, char *ary, int modvalue, int off,
- const char * const *names, char *ptr)
+static void ParseField(char *user, char *ary, int modvalue, int off,
+ const char *names, char *ptr)
+/* 'names' is a pointer to a set of 3-char abbreviations */
{
- char *base = ptr;
- int n1 = -1;
- int n2 = -1;
+ char *base = ptr;
+ int n1 = -1;
+ int n2 = -1;
+
+ // this can't happen due to config_read()
+ /*if (base == NULL)
+ return;*/
+
+ while (1) {
+ int skip = 0;
+
+ /* Handle numeric digit or symbol or '*' */
+ if (*ptr == '*') {
+ n1 = 0; /* everything will be filled */
+ n2 = modvalue - 1;
+ skip = 1;
+ ++ptr;
+ } else if (isdigit(*ptr)) {
+ if (n1 < 0) {
+ n1 = strtol(ptr, &ptr, 10) + off;
+ } else {
+ n2 = strtol(ptr, &ptr, 10) + off;
+ }
+ skip = 1;
+ } else if (names) {
+ int i;
+
+ for (i = 0; names[i]; i += 3) {
+ /* was using strncmp before... */
+ if (strncasecmp(ptr, &names[i], 3) == 0) {
+ ptr += 3;
+ if (n1 < 0) {
+ n1 = i / 3;
+ } else {
+ n2 = i / 3;
+ }
+ skip = 1;
+ break;
+ }
+ }
+ }
- if (base == NULL)
- return(NULL);
+ /* handle optional range '-' */
+ if (skip == 0) {
+ goto err;
+ }
+ if (*ptr == '-' && n2 < 0) {
+ ++ptr;
+ continue;
+ }
- while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
- int skip = 0;
+ /*
+ * collapse single-value ranges, handle skipmark, and fill
+ * in the character array appropriately.
+ */
+ if (n2 < 0) {
+ n2 = n1;
+ }
+ if (*ptr == '/') {
+ skip = strtol(ptr + 1, &ptr, 10);
+ }
- /*
- * Handle numeric digit or symbol or '*'
- */
+ /*
+ * fill array, using a failsafe is the easiest way to prevent
+ * an endless loop
+ */
+ {
+ int s0 = 1;
+ int failsafe = 1024;
+
+ --n1;
+ do {
+ n1 = (n1 + 1) % modvalue;
+
+ if (--s0 == 0) {
+ ary[n1 % modvalue] = 1;
+ s0 = skip;
+ }
+ if (--failsafe == 0) {
+ goto err;
+ }
+ } while (n1 != n2);
- if (*ptr == '*') {
- n1 = 0; /* everything will be filled */
- n2 = modvalue - 1;
- skip = 1;
- ++ptr;
- } else if (*ptr >= '0' && *ptr <= '9') {
- if (n1 < 0)
- n1 = strtol(ptr, &ptr, 10) + off;
- else
- n2 = strtol(ptr, &ptr, 10) + off;
- skip = 1;
- } else if (names) {
- int i;
-
- for (i = 0; names[i]; ++i) {
- if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
- break;
}
- }
- if (names[i]) {
- ptr += strlen(names[i]);
- if (n1 < 0)
- n1 = i;
- else
- n2 = i;
- skip = 1;
- }
+ if (*ptr != ',') {
+ break;
+ }
+ ++ptr;
+ n1 = -1;
+ n2 = -1;
}
- /*
- * handle optional range '-'
- */
-
- if (skip == 0) {
- crondlog("\111failed user %s parsing %s\n", user, base);
- return(NULL);
- }
- if (*ptr == '-' && n2 < 0) {
- ++ptr;
- continue;
+ if (*ptr) {
+ err:
+ crondlog(WARN9 "user %s: parse error at %s", user, base);
+ return;
}
- /*
- * collapse single-value ranges, handle skipmark, and fill
- * in the character array appropriately.
- */
-
- if (n2 < 0)
- n2 = n1;
-
- if (*ptr == '/')
- skip = strtol(ptr + 1, &ptr, 10);
-
- /*
- * fill array, using a failsafe is the easiest way to prevent
- * an endless loop
- */
-
- {
- int s0 = 1;
- int failsafe = 1024;
-
- --n1;
- do {
- n1 = (n1 + 1) % modvalue;
-
- if (--s0 == 0) {
- ary[n1 % modvalue] = 1;
- s0 = skip;
- }
- } while (n1 != n2 && --failsafe);
-
- if (failsafe == 0) {
- crondlog("\111failed user %s parsing %s\n", user, base);
- return(NULL);
- }
+ if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */
+ /* can't use crondlog, it inserts '\n' */
+ int i;
+ for (i = 0; i < modvalue; ++i)
+ fprintf(stderr, "%d", (unsigned char)ary[i]);
+ fputc('\n', stderr);
}
- if (*ptr != ',')
- break;
- ++ptr;
- n1 = -1;
- n2 = -1;
- }
-
- if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
- crondlog("\111failed user %s parsing %s\n", user, base);
- return(NULL);
- }
-
- while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
- ++ptr;
-
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt) {
- int i;
-
- for (i = 0; i < modvalue; ++i)
- crondlog("\005%d", ary[i]);
- crondlog("\005\n");
- }
-#endif
-
- return(ptr);
}
-static void
-FixDayDow(CronLine *line)
+static void FixDayDow(CronLine *line)
{
- short i;
- short weekUsed = 0;
- short daysUsed = 0;
-
- for (i = 0; i < arysize(line->cl_Dow); ++i) {
- if (line->cl_Dow[i] == 0) {
- weekUsed = 1;
- break;
+ unsigned i;
+ int weekUsed = 0;
+ int daysUsed = 0;
+
+ for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) {
+ if (line->cl_Dow[i] == 0) {
+ weekUsed = 1;
+ break;
+ }
}
- }
- for (i = 0; i < arysize(line->cl_Days); ++i) {
- if (line->cl_Days[i] == 0) {
- daysUsed = 1;
- break;
+ for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) {
+ if (line->cl_Days[i] == 0) {
+ daysUsed = 1;
+ break;
+ }
+ }
+ if (weekUsed != daysUsed) {
+ if (weekUsed)
+ memset(line->cl_Days, 0, sizeof(line->cl_Days));
+ else /* daysUsed */
+ memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
}
- }
- if (weekUsed && !daysUsed) {
- memset(line->cl_Days, 0, sizeof(line->cl_Days));
- }
- if (daysUsed && !weekUsed) {
- memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
- }
}
-
-
-static void
-SynchronizeFile(const char *fileName)
+static void SynchronizeFile(const char *fileName)
{
- int maxEntries = MAXLINES;
- int maxLines;
- char buf[1024];
-
- if (strcmp(fileName, "root") == 0)
- maxEntries = 65535;
- maxLines = maxEntries * 10;
+ struct parser_t *parser;
+ struct stat sbuf;
+ int maxLines;
+ char *tokens[6];
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ char *mailTo = NULL;
+#endif
- if (fileName) {
- FILE *fi;
+ if (!fileName)
+ return;
DeleteFile(fileName);
+ parser = config_open(fileName);
+ if (!parser)
+ return;
- if ((fi = fopen(fileName, "r")) != NULL) {
- struct stat sbuf;
+ maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;
- if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
- CronFile *file = calloc(1, sizeof(CronFile));
+ if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
+ CronFile *file = xzalloc(sizeof(CronFile));
CronLine **pline;
+ int n;
- file->cf_User = strdup(fileName);
+ file->cf_User = xstrdup(fileName);
pline = &file->cf_LineBase;
- while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
- CronLine line;
- char *ptr;
-
- if (buf[0])
- buf[strlen(buf)-1] = 0;
-
- if (buf[0] == 0 || buf[0] == '#' || buf[0] == ' ' || buf[0] == '\t')
- continue;
-
- if (--maxEntries == 0)
- break;
+ while (1) {
+ CronLine *line;
- memset(&line, 0, sizeof(line));
+ if (!--maxLines)
+ break;
+ n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
+ if (!n)
+ break;
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt)
- crondlog("\111User %s Entry %s\n", fileName, buf);
-#endif
-
- /*
- * parse date ranges
- */
-
- ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
- ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
- ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
- ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
- ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
-
- /*
- * check failure
- */
-
- if (ptr == NULL)
- continue;
-
- /*
- * fix days and dow - if one is not * and the other
- * is *, the other is set to 0, and vise-versa
- */
-
- FixDayDow(&line);
-
- *pline = calloc(1, sizeof(CronLine));
- **pline = line;
-
- /*
- * copy command
- */
-
- (*pline)->cl_Shell = strdup(ptr);
-
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt) {
- crondlog("\111 Command %s\n", ptr);
- }
+ if (DebugOpt)
+ crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
+
+ /* check if line is setting MAILTO= */
+ if (0 == strncmp(tokens[0], "MAILTO=", 7)) {
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ free(mailTo);
+ mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
+#endif /* otherwise just ignore such lines */
+ continue;
+ }
+ /* check if a minimum of tokens is specified */
+ if (n < 6)
+ continue;
+ *pline = line = xzalloc(sizeof(*line));
+ /* parse date ranges */
+ ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, tokens[0]);
+ ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, tokens[1]);
+ ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, tokens[2]);
+ ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, tokens[3]);
+ ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, tokens[4]);
+ /*
+ * fix days and dow - if one is not "*" and the other
+ * is "*", the other is set to 0, and vise-versa
+ */
+ FixDayDow(line);
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ /* copy mailto (can be NULL) */
+ line->cl_MailTo = xstrdup(mailTo);
#endif
-
- pline = &((*pline)->cl_Next);
+ /* copy command */
+ line->cl_Shell = xstrdup(tokens[5]);
+ if (DebugOpt) {
+ crondlog(LVL5 " command:%s", tokens[5]);
+ }
+ pline = &line->cl_Next;
+//bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
}
*pline = NULL;
file->cf_Next = FileBase;
FileBase = file;
- if (maxLines == 0 || maxEntries == 0)
- crondlog("\111Maximum number of lines reached for user %s\n", fileName);
- }
- fclose(fi);
+ if (maxLines == 0) {
+ crondlog(WARN9 "user %s: too many lines", fileName);
+ }
}
- }
+ config_close(parser);
}
-static void
-CheckUpdates(void)
+static void CheckUpdates(void)
{
- FILE *fi;
- char buf[256];
-
- if ((fi = fopen(CRONUPDATE, "r")) != NULL) {
- remove(CRONUPDATE);
- while (fgets(buf, sizeof(buf), fi) != NULL) {
- SynchronizeFile(strtok(buf, " \t\r\n"));
+ FILE *fi;
+ char buf[256];
+
+ fi = fopen_for_read(CRONUPDATE);
+ if (fi != NULL) {
+ unlink(CRONUPDATE);
+ while (fgets(buf, sizeof(buf), fi) != NULL) {
+ /* use first word only */
+ SynchronizeFile(strtok(buf, " \t\r\n"));
+ }
+ fclose(fi);
}
- fclose(fi);
- }
}
-static void
-SynchronizeDir(void)
+static void SynchronizeDir(void)
{
- /*
- * Attempt to delete the database.
- */
-
- for (;;) {
CronFile *file;
+ /* Attempt to delete the database. */
+ again:
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (!file->cf_Deleted) {
+ DeleteFile(file->cf_User);
+ goto again;
+ }
+ }
- for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next)
- ;
- if (file == NULL)
- break;
- DeleteFile(file->cf_User);
- }
-
- /*
- * Remove cron update file
- *
- * Re-chdir, in case directory was renamed & deleted, or otherwise
- * screwed up.
- *
- * scan directory and add associated users
- */
-
- remove(CRONUPDATE);
- if (chdir(CDir) < 0) {
- crondlog("\311unable to find %s\n", CDir);
- }
- {
- DIR *dir;
- struct dirent *den;
-
- if ((dir = opendir("."))) {
- while ((den = readdir(dir))) {
- if (strchr(den->d_name, '.') != NULL)
- continue;
- if (getpwnam(den->d_name))
- SynchronizeFile(den->d_name);
- else
- crondlog("\007ignoring %s\n", den->d_name);
- }
- closedir(dir);
- } else {
- crondlog("\311Unable to open current dir!\n");
+ /*
+ * Remove cron update file
+ *
+ * Re-chdir, in case directory was renamed & deleted, or otherwise
+ * screwed up.
+ *
+ * scan directory and add associated users
+ */
+ unlink(CRONUPDATE);
+ if (chdir(CDir) < 0) {
+ crondlog(DIE9 "can't chdir(%s)", CDir);
+ }
+ {
+ DIR *dir = opendir(".");
+ struct dirent *den;
+
+ if (!dir)
+ crondlog(DIE9 "can't chdir(%s)", "."); /* exits */
+ while ((den = readdir(dir)) != NULL) {
+ if (strchr(den->d_name, '.') != NULL) {
+ continue;
+ }
+ if (getpwnam(den->d_name)) {
+ SynchronizeFile(den->d_name);
+ } else {
+ crondlog(LVL7 "ignoring %s", den->d_name);
+ }
+ }
+ closedir(dir);
}
- }
}
-
/*
* DeleteFile() - delete user database
*
* Note: multiple entries for same user may exist if we were unable to
* completely delete a database due to running processes.
*/
-
-static void
-DeleteFile(const char *userName)
+static void DeleteFile(const char *userName)
{
- CronFile **pfile = &FileBase;
- CronFile *file;
-
- while ((file = *pfile) != NULL) {
- if (strcmp(userName, file->cf_User) == 0) {
- CronLine **pline = &file->cf_LineBase;
- CronLine *line;
-
- file->cf_Running = 0;
- file->cf_Deleted = 1;
+ CronFile **pfile = &FileBase;
+ CronFile *file;
- while ((line = *pline) != NULL) {
- if (line->cl_Pid > 0) {
- file->cf_Running = 1;
- pline = &line->cl_Next;
+ while ((file = *pfile) != NULL) {
+ if (strcmp(userName, file->cf_User) == 0) {
+ CronLine **pline = &file->cf_LineBase;
+ CronLine *line;
+
+ file->cf_Running = 0;
+ file->cf_Deleted = 1;
+
+ while ((line = *pline) != NULL) {
+ if (line->cl_Pid > 0) {
+ file->cf_Running = 1;
+ pline = &line->cl_Next;
+ } else {
+ *pline = line->cl_Next;
+ free(line->cl_Shell);
+ free(line);
+ }
+ }
+ if (file->cf_Running == 0) {
+ *pfile = file->cf_Next;
+ free(file->cf_User);
+ free(file);
+ } else {
+ pfile = &file->cf_Next;
+ }
} else {
- *pline = line->cl_Next;
- free(line->cl_Shell);
- free(line);
+ pfile = &file->cf_Next;
}
- }
- if (file->cf_Running == 0) {
- *pfile = file->cf_Next;
- free(file->cf_User);
- free(file);
- } else {
- pfile = &file->cf_Next;
- }
- } else {
- pfile = &file->cf_Next;
}
- }
}
/*
@@ -777,90 +631,77 @@ DeleteFile(const char *userName)
* period is about a minute (one scan). Worst case it will be one
* hour (60 scans).
*/
-
-static int
-TestJobs(time_t t1, time_t t2)
+static int TestJobs(time_t t1, time_t t2)
{
- short nJobs = 0;
- time_t t;
-
- /*
- * Find jobs > t1 and <= t2
- */
-
- for (t = t1 - t1 % 60; t <= t2; t += 60) {
- if (t > t1) {
- struct tm *tp = localtime(&t);
- CronFile *file;
- CronLine *line;
-
- for (file = FileBase; file; file = file->cf_Next) {
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt)
- crondlog("\005FILE %s:\n", file->cf_User);
-#endif
- if (file->cf_Deleted)
- continue;
- for (line = file->cf_LineBase; line; line = line->cl_Next) {
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt)
- crondlog("\005 LINE %s\n", line->cl_Shell);
-#endif
- if (line->cl_Mins[tp->tm_min] &&
- line->cl_Hrs[tp->tm_hour] &&
- (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday]) &&
- line->cl_Mons[tp->tm_mon]
- ) {
-#ifdef FEATURE_DEBUG_OPT
+ int nJobs = 0;
+ time_t t;
+
+ /* Find jobs > t1 and <= t2 */
+
+ for (t = t1 - t1 % 60; t <= t2; t += 60) {
+ struct tm *tp;
+ CronFile *file;
+ CronLine *line;
+
+ if (t <= t1)
+ continue;
+
+ tp = localtime(&t);
+ for (file = FileBase; file; file = file->cf_Next) {
if (DebugOpt)
- crondlog("\005 JobToDo: %d %s\n", line->cl_Pid, line->cl_Shell);
-#endif
- if (line->cl_Pid > 0) {
- crondlog("\010 process already running: %s %s\n",
- file->cf_User,
- line->cl_Shell
- );
- } else if (line->cl_Pid == 0) {
- line->cl_Pid = -1;
- file->cf_Ready = 1;
- ++nJobs;
+ crondlog(LVL5 "file %s:", file->cf_User);
+ if (file->cf_Deleted)
+ continue;
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (DebugOpt)
+ crondlog(LVL5 " line %s", line->cl_Shell);
+ if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour]
+ && (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
+ && line->cl_Mons[tp->tm_mon]
+ ) {
+ if (DebugOpt) {
+ crondlog(LVL5 " job: %d %s",
+ (int)line->cl_Pid, line->cl_Shell);
+ }
+ if (line->cl_Pid > 0) {
+ crondlog(LVL8 "user %s: process already running: %s",
+ file->cf_User, line->cl_Shell);
+ } else if (line->cl_Pid == 0) {
+ line->cl_Pid = -1;
+ file->cf_Ready = 1;
+ ++nJobs;
+ }
+ }
}
- }
}
- }
}
- }
- return(nJobs);
+ return nJobs;
}
-static void
-RunJobs(void)
+static void RunJobs(void)
{
- CronFile *file;
- CronLine *line;
-
- for (file = FileBase; file; file = file->cf_Next) {
- if (file->cf_Ready) {
- file->cf_Ready = 0;
-
- for (line = file->cf_LineBase; line; line = line->cl_Next) {
- if (line->cl_Pid < 0) {
-
- RunJob(file->cf_User, line);
-
- crondlog("\010USER %s pid %3d cmd %s\n",
- file->cf_User,
- line->cl_Pid,
- line->cl_Shell
- );
- if (line->cl_Pid < 0)
- file->cf_Ready = 1;
- else if (line->cl_Pid > 0)
- file->cf_Running = 1;
+ CronFile *file;
+ CronLine *line;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (!file->cf_Ready)
+ continue;
+
+ file->cf_Ready = 0;
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (line->cl_Pid >= 0)
+ continue;
+
+ RunJob(file->cf_User, line);
+ crondlog(LVL8 "USER %s pid %3d cmd %s",
+ file->cf_User, (int)line->cl_Pid, line->cl_Shell);
+ if (line->cl_Pid < 0) {
+ file->cf_Ready = 1;
+ } else if (line->cl_Pid > 0) {
+ file->cf_Running = 1;
+ }
}
- }
}
- }
}
/*
@@ -869,232 +710,220 @@ RunJobs(void)
* Check for job completion, return number of jobs still running after
* all done.
*/
-
-static int
-CheckJobs(void)
+static int CheckJobs(void)
{
- CronFile *file;
- CronLine *line;
- int nStillRunning = 0;
-
- for (file = FileBase; file; file = file->cf_Next) {
- if (file->cf_Running) {
- file->cf_Running = 0;
-
- for (line = file->cf_LineBase; line; line = line->cl_Next) {
- if (line->cl_Pid > 0) {
- int status;
- int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
-
- if (r < 0 || r == line->cl_Pid) {
- EndJob(file->cf_User, line);
- if (line->cl_Pid)
- file->cf_Running = 1;
- } else if (r == 0) {
- file->cf_Running = 1;
- }
+ CronFile *file;
+ CronLine *line;
+ int nStillRunning = 0;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Running) {
+ file->cf_Running = 0;
+
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ int status, r;
+ if (line->cl_Pid <= 0)
+ continue;
+
+ r = waitpid(line->cl_Pid, &status, WNOHANG);
+ if (r < 0 || r == line->cl_Pid) {
+ EndJob(file->cf_User, line);
+ if (line->cl_Pid) {
+ file->cf_Running = 1;
+ }
+ } else if (r == 0) {
+ file->cf_Running = 1;
+ }
+ }
}
- }
+ nStillRunning += file->cf_Running;
}
- nStillRunning += file->cf_Running;
- }
- return(nStillRunning);
+ return nStillRunning;
}
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+
+// TODO: sendmail should be _run-time_ option, not compile-time!
-#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
static void
ForkJob(const char *user, CronLine *line, int mailFd,
- const char *prog, const char *cmd, const char *arg, const char *mailf)
+ const char *prog, const char *cmd, const char *arg,
+ const char *mail_filename)
{
- /*
- * Fork as the user in question and run program
- */
- pid_t pid = fork();
+ struct passwd *pas;
+ pid_t pid;
+
+ /* prepare things before vfork */
+ pas = getpwnam(user);
+ if (!pas) {
+ crondlog(LVL9 "can't get uid for %s", user);
+ goto err;
+ }
+ SetEnv(pas);
+
+ pid = vfork();
+ if (pid == 0) {
+ /* CHILD */
+ /* change running state to the user in question */
+ ChangeUser(pas);
+ if (DebugOpt) {
+ crondlog(LVL5 "child running %s", prog);
+ }
+ if (mailFd >= 0) {
+ xmove_fd(mailFd, mail_filename ? 1 : 0);
+ dup2(1, 2);
+ }
+ /* crond 3.0pl1-100 puts tasks in separate process groups */
+ bb_setpgrp();
+ execlp(prog, prog, cmd, arg, (char *) NULL);
+ crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg);
+ if (mail_filename) {
+ fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
+ }
+ _exit(EXIT_SUCCESS);
+ }
- line->cl_Pid = pid;
- if (pid == 0) {
- /*
- * CHILD
- */
+ line->cl_Pid = pid;
+ if (pid < 0) {
+ /* FORK FAILED */
+ crondlog(ERR20 "can't vfork");
+ err:
+ line->cl_Pid = 0;
+ if (mail_filename) {
+ unlink(mail_filename);
+ }
+ } else if (mail_filename) {
+ /* PARENT, FORK SUCCESS
+ * rename mail-file based on pid of process
+ */
+ char mailFile2[128];
+
+ snprintf(mailFile2, sizeof(mailFile2), "%s/cron.%s.%d", TMPDIR, user, pid);
+ rename(mail_filename, mailFile2); // TODO: xrename?
+ }
/*
- * Change running state to the user in question
+ * Close the mail file descriptor.. we can't just leave it open in
+ * a structure, closing it later, because we might run out of descriptors
*/
-
- if (ChangeUser(user) < 0)
- exit(0);
-
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt)
- crondlog("\005Child Running %s\n", prog);
-#endif
-
if (mailFd >= 0) {
- dup2(mailFd, mailf != NULL);
- dup2((mailf ? mailFd : 1), 2);
- close(mailFd);
+ close(mailFd);
}
- execl(prog, prog, cmd, arg, NULL);
- crondlog("\024unable to exec, user %s cmd %s %s %s\n", user,
- prog, cmd, arg);
- if(mailf)
- fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
- exit(0);
- } else if (pid < 0) {
- /*
- * FORK FAILED
- */
- crondlog("\024couldn't fork, user %s\n", user);
- line->cl_Pid = 0;
- if(mailf)
- remove(mailf);
- } else if(mailf) {
- /*
- * PARENT, FORK SUCCESS
- *
- * rename mail-file based on pid of process
- */
- char mailFile2[128];
-
- snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d",
- user, pid);
- rename(mailf, mailFile2);
- }
- /*
- * Close the mail file descriptor.. we can't just leave it open in
- * a structure, closing it later, because we might run out of descriptors
- */
-
- if (mailFd >= 0)
- close(mailFd);
}
-static void
-RunJob(const char *user, CronLine *line)
+static void RunJob(const char *user, CronLine *line)
{
- char mailFile[128];
- int mailFd;
-
- line->cl_Pid = 0;
- line->cl_MailFlag = 0;
-
- /*
- * open mail file - owner root so nobody can screw with it.
- */
-
- snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d",
- user, getpid());
- mailFd = open(mailFile, O_CREAT|O_TRUNC|O_WRONLY|O_EXCL|O_APPEND, 0600);
-
- if (mailFd >= 0) {
- line->cl_MailFlag = 1;
- fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
- line->cl_Shell);
- line->cl_MailPos = lseek(mailFd, 0, 1);
- } else {
- crondlog("\024unable to create mail file user %s file %s, output to /dev/null\n",
- user, mailFile);
- }
-
- ForkJob(user, line, mailFd, def_sh, "-c", line->cl_Shell, mailFile);
+ char mailFile[128];
+ int mailFd = -1;
+
+ line->cl_Pid = 0;
+ line->cl_MailFlag = 0;
+
+ if (line->cl_MailTo) {
+ /* open mail file - owner root so nobody can screw with it. */
+ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid());
+ mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
+
+ if (mailFd >= 0) {
+ line->cl_MailFlag = 1;
+ fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_MailTo,
+ line->cl_Shell);
+ line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
+ } else {
+ crondlog(ERR20 "cannot create mail file %s for user %s, "
+ "discarding output", mailFile, user);
+ }
+ }
+
+ ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
}
/*
* EndJob - called when job terminates and when mail terminates
*/
-
-static void
-EndJob(const char *user, CronLine *line)
+static void EndJob(const char *user, CronLine *line)
{
- int mailFd;
- char mailFile[128];
- struct stat sbuf;
-
- /*
- * No job
- */
-
- if (line->cl_Pid <= 0) {
- line->cl_Pid = 0;
- return;
- }
-
- /*
- * End of job and no mail file
- * End of sendmail job
- */
-
- snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d",
- user, line->cl_Pid);
- line->cl_Pid = 0;
-
- if (line->cl_MailFlag != 1)
- return;
-
- line->cl_MailFlag = 0;
-
- /*
- * End of primary job - check for mail file. If size has increased and
- * the file is still valid, we sendmail it.
- */
-
- mailFd = open(mailFile, O_RDONLY);
- remove(mailFile);
- if (mailFd < 0) {
- return;
- }
-
- if (fstat(mailFd, &sbuf) < 0 ||
- sbuf.st_uid != DaemonUid ||
- sbuf.st_nlink != 0 ||
- sbuf.st_size == line->cl_MailPos ||
- !S_ISREG(sbuf.st_mode)
- ) {
- close(mailFd);
- return;
- }
- ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
-}
-#else
-/* crond whithout sendmail */
+ int mailFd;
+ char mailFile[128];
+ struct stat sbuf;
+
+ /* No job */
+ if (line->cl_Pid <= 0) {
+ line->cl_Pid = 0;
+ return;
+ }
-static void
-RunJob(const char *user, CronLine *line)
-{
/*
- * Fork as the user in question and run program
+ * End of job and no mail file
+ * End of sendmail job
*/
- pid_t pid = fork();
+ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
+ line->cl_Pid = 0;
- if (pid == 0) {
- /*
- * CHILD
- */
+ if (line->cl_MailFlag == 0) {
+ return;
+ }
+ line->cl_MailFlag = 0;
/*
- * Change running state to the user in question
+ * End of primary job - check for mail file. If size has increased and
+ * the file is still valid, we sendmail it.
*/
+ mailFd = open(mailFile, O_RDONLY);
+ unlink(mailFile);
+ if (mailFd < 0) {
+ return;
+ }
- if (ChangeUser(user) < 0)
- exit(0);
+ if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
+ || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
+ || !S_ISREG(sbuf.st_mode)
+ ) {
+ close(mailFd);
+ return;
+ }
+ if (line->cl_MailTo)
+ ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
+}
-#ifdef FEATURE_DEBUG_OPT
- if (DebugOpt)
- crondlog("\005Child Running %s\n", def_sh);
-#endif
+#else /* crond without sendmail */
- execl(def_sh, def_sh, "-c", line->cl_Shell, NULL);
- crondlog("\024unable to exec, user %s cmd %s -c %s\n", user,
- def_sh, line->cl_Shell);
- exit(0);
- } else if (pid < 0) {
- /*
- * FORK FAILED
- */
- crondlog("\024couldn't fork, user %s\n", user);
- pid = 0;
- }
- line->cl_Pid = pid;
+static void RunJob(const char *user, CronLine *line)
+{
+ struct passwd *pas;
+ pid_t pid;
+
+ /* prepare things before vfork */
+ pas = getpwnam(user);
+ if (!pas) {
+ crondlog(LVL9 "can't get uid for %s", user);
+ goto err;
+ }
+ SetEnv(pas);
+
+ /* fork as the user in question and run program */
+ pid = vfork();
+ if (pid == 0) {
+ /* CHILD */
+ /* change running state to the user in question */
+ ChangeUser(pas);
+ if (DebugOpt) {
+ crondlog(LVL5 "child running %s", DEFAULT_SHELL);
+ }
+ /* crond 3.0pl1-100 puts tasks in separate process groups */
+ bb_setpgrp();
+ execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, (char *) NULL);
+ crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user,
+ DEFAULT_SHELL, "-c", line->cl_Shell);
+ _exit(EXIT_SUCCESS);
+ }
+ if (pid < 0) {
+ /* FORK FAILED */
+ crondlog(ERR20 "can't vfork");
+ err:
+ pid = 0;
+ }
+ line->cl_Pid = pid;
}
-#endif /* CONFIG_FEATURE_CROND_CALL_SENDMAIL */
+
+#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */
diff --git a/release/src/router/busybox/miscutils/crontab.c b/release/src/router/busybox/miscutils/crontab.c
index 6b944646..34b80ea3 100644
--- a/release/src/router/busybox/miscutils/crontab.c
+++ b/release/src/router/busybox/miscutils/crontab.c
@@ -1,391 +1,227 @@
+/* vi: set sw=4 ts=4: */
/*
* CRONTAB
*
* usually setuid root, -c option only works if getuid() == geteuid()
*
* Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
- * May be distributed under the GNU General Public License
- *
- * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
*
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <syslog.h>
-#include <signal.h>
-#include <getopt.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
+#include "libbb.h"
-#ifndef CRONTABS
-#define CRONTABS "/var/spool/cron/crontabs"
-#endif
-#ifndef TMPDIR
-#define TMPDIR "/var/spool/cron"
-#endif
+#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
#ifndef CRONUPDATE
#define CRONUPDATE "cron.update"
#endif
-#ifndef PATH_VI
-#define PATH_VI "/bin/vi" /* location of vi */
-#endif
-#include "busybox.h"
-
-static const char *CDir = CRONTABS;
-
-static void EditFile(const char *user, const char *file);
-static int GetReplaceStream(const char *user, const char *file);
-static int ChangeUser(const char *user, short dochdir);
-
-int
-crontab_main(int ac, char **av)
+static void change_user(const struct passwd *pas)
{
- enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
- const struct passwd *pas;
- const char *repFile = NULL;
- int repFd = 0;
- int i;
- char caller[256]; /* user that ran program */
- int UserId;
-
- UserId = getuid();
- if ((pas = getpwuid(UserId)) == NULL)
- bb_perror_msg_and_die("getpwuid");
+ xsetenv("USER", pas->pw_name);
+ xsetenv("HOME", pas->pw_dir);
+ xsetenv("SHELL", DEFAULT_SHELL);
- strncpy(caller, pas->pw_name, sizeof(caller));
+ /* initgroups, setgid, setuid */
+ change_identity(pas);
- i = 1;
- if (ac > 1) {
- if (av[1][0] == '-' && av[1][1] == 0) {
- option = REPLACE;
- ++i;
- } else if (av[1][0] != '-') {
- option = REPLACE;
- ++i;
- repFile = av[1];
+ if (chdir(pas->pw_dir) < 0) {
+ bb_perror_msg("chdir(%s) by %s failed",
+ pas->pw_dir, pas->pw_name);
+ xchdir("/tmp");
}
- }
-
- for (; i < ac; ++i) {
- char *ptr = av[i];
-
- if (*ptr != '-')
- break;
- ptr += 2;
+}
- switch(ptr[-1]) {
- case 'l':
- if (ptr[-1] == 'l')
- option = LIST;
- /* fall through */
- case 'e':
- if (ptr[-1] == 'e')
- option = EDIT;
- /* fall through */
- case 'd':
- if (ptr[-1] == 'd')
- option = DELETE;
- /* fall through */
- case 'u':
- if (i + 1 < ac && av[i+1][0] != '-') {
- ++i;
- if (getuid() == geteuid()) {
- pas = getpwnam(av[i]);
- if (pas) {
- UserId = pas->pw_uid;
- } else {
- bb_error_msg_and_die("user %s unknown", av[i]);
- }
- } else {
- bb_error_msg_and_die("only the superuser may specify a user");
- }
- }
- break;
- case 'c':
- if (getuid() == geteuid()) {
- CDir = (*ptr) ? ptr : av[++i];
- } else {
- bb_error_msg_and_die("-c option: superuser only");
- }
- break;
- default:
- i = ac;
- break;
+static void edit_file(const struct passwd *pas, const char *file)
+{
+ const char *ptr;
+ int pid = vfork();
+
+ if (pid < 0) /* failure */
+ bb_perror_msg_and_die("vfork");
+ if (pid) { /* parent */
+ wait4pid(pid);
+ return;
}
- }
- if (i != ac || option == NONE)
- bb_show_usage();
-
- /*
- * Get password entry
- */
- if ((pas = getpwuid(UserId)) == NULL)
- bb_perror_msg_and_die("getpwuid");
-
- /*
- * If there is a replacement file, obtain a secure descriptor to it.
- */
-
- if (repFile) {
- repFd = GetReplaceStream(caller, repFile);
- if (repFd < 0)
- bb_error_msg_and_die("unable to read replacement file");
- }
-
- /*
- * Change directory to our crontab directory
- */
-
- if (chdir(CDir) < 0)
- bb_perror_msg_and_die("cannot change dir to %s", CDir);
-
- /*
- * Handle options as appropriate
- */
-
- switch(option) {
- case LIST:
- {
- FILE *fi;
- char buf[1024];
-
- if ((fi = fopen(pas->pw_name, "r"))) {
- while (fgets(buf, sizeof(buf), fi) != NULL)
- fputs(buf, stdout);
- fclose(fi);
- } else {
- bb_error_msg("no crontab for %s", pas->pw_name);
- }
+ /* CHILD - change user and run editor */
+ change_user(pas);
+ ptr = getenv("VISUAL");
+ if (!ptr) {
+ ptr = getenv("EDITOR");
+ if (!ptr)
+ ptr = "vi";
}
- break;
- case EDIT:
- {
- FILE *fi;
- int fd;
- int n;
- char tmp[128];
- char buf[1024];
- snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid());
- if ((fd = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) >= 0) {
- chown(tmp, getuid(), getgid());
- if ((fi = fopen(pas->pw_name, "r"))) {
- while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
- write(fd, buf, n);
- }
- EditFile(caller, tmp);
- remove(tmp);
- lseek(fd, 0L, 0);
- repFd = fd;
- } else {
- bb_error_msg_and_die("unable to create %s", tmp);
- }
-
- }
- option = REPLACE;
- /* fall through */
- case REPLACE:
- {
- char buf[1024];
- char path[1024];
- int fd;
- int n;
+ BB_EXECLP(ptr, ptr, file, NULL);
+ bb_perror_msg_and_die("exec %s", ptr);
+}
- snprintf(path, sizeof(path), "%s.new", pas->pw_name);
- if ((fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600)) >= 0) {
- while ((n = read(repFd, buf, sizeof(buf))) > 0) {
- write(fd, buf, n);
+static int open_as_user(const struct passwd *pas, const char *file)
+{
+ pid_t pid;
+ char c;
+
+ pid = vfork();
+ if (pid < 0) /* ERROR */
+ bb_perror_msg_and_die("vfork");
+ if (pid) { /* PARENT */
+ if (wait4pid(pid) == 0) {
+ /* exitcode 0: child says it can read */
+ return open(file, O_RDONLY);
}
- close(fd);
- rename(path, pas->pw_name);
- } else {
- bb_error_msg("unable to create %s/%s", CDir, path);
- }
- close(repFd);
+ return -1;
}
- break;
- case DELETE:
- remove(pas->pw_name);
- break;
- case NONE:
- default:
- break;
- }
-
- /*
- * Bump notification file. Handle window where crond picks file up
- * before we can write our entry out.
- */
-
- if (option == REPLACE || option == DELETE) {
- FILE *fo;
- struct stat st;
- while ((fo = fopen(CRONUPDATE, "a"))) {
- fprintf(fo, "%s\n", pas->pw_name);
- fflush(fo);
- if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
- fclose(fo);
- break;
- }
- fclose(fo);
- /* loop */
- }
- if (fo == NULL) {
- bb_error_msg("unable to append to %s/%s", CDir, CRONUPDATE);
- }
- }
- return 0;
+ /* CHILD */
+ /* initgroups, setgid, setuid */
+ change_identity(pas);
+ /* We just try to read one byte. If it works, file is readable
+ * under this user. We signal that by exiting with 0. */
+ _exit(safe_read(xopen(file, O_RDONLY), &c, 1) < 0);
}
-static int
-GetReplaceStream(const char *user, const char *file)
+int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int crontab_main(int argc UNUSED_PARAM, char **argv)
{
- int filedes[2];
- int pid;
- int fd;
- int n;
- char buf[1024];
-
- if (pipe(filedes) < 0) {
- perror("pipe");
- return(-1);
- }
- if ((pid = fork()) < 0) {
- perror("fork");
- return(-1);
- }
- if (pid > 0) {
- /*
- * PARENT
+ const struct passwd *pas;
+ const char *crontab_dir = CRONTABS;
+ char *tmp_fname;
+ char *new_fname;
+ char *user_name; /* -u USER */
+ int fd;
+ int src_fd;
+ int opt_ler;
+
+ /* file [opts] Replace crontab from file
+ * - [opts] Replace crontab from stdin
+ * -u user User
+ * -c dir Crontab directory
+ * -l List crontab for user
+ * -e Edit crontab for user
+ * -r Delete crontab for user
+ * bbox also supports -d == -r, but most other crontab
+ * implementations do not. Deprecated.
*/
-
- close(filedes[1]);
- if (read(filedes[0], buf, 1) != 1) {
- close(filedes[0]);
- filedes[0] = -1;
+ enum {
+ OPT_u = (1 << 0),
+ OPT_c = (1 << 1),
+ OPT_l = (1 << 2),
+ OPT_e = (1 << 3),
+ OPT_r = (1 << 4),
+ OPT_ler = OPT_l + OPT_e + OPT_r,
+ };
+
+ opt_complementary = "?1:dr"; /* max one argument; -d implies -r */
+ opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir);
+ argv += optind;
+
+ if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */
+ /* run by non-root? */
+ if (opt_ler & (OPT_u|OPT_c))
+ bb_error_msg_and_die("only root can use -c or -u");
}
- return(filedes[0]);
- }
-
- /*
- * CHILD
- */
-
- close(filedes[0]);
-
- if (ChangeUser(user, 0) < 0)
- exit(0);
- fd = open(file, O_RDONLY);
- if (fd < 0) {
- bb_error_msg("unable to open %s", file);
- exit(0);
- }
- buf[0] = 0;
- write(filedes[1], buf, 1);
- while ((n = read(fd, buf, sizeof(buf))) > 0) {
- write(filedes[1], buf, n);
- }
- exit(0);
-}
-
-static void
-EditFile(const char *user, const char *file)
-{
- int pid;
+ if (opt_ler & OPT_u) {
+ pas = xgetpwnam(user_name);
+ } else {
+ pas = xgetpwuid(getuid());
+ }
- if ((pid = fork()) == 0) {
- /*
- * CHILD - change user and run editor
- */
- char *ptr;
- char visual[1024];
+#define user_name DONT_USE_ME_BEYOND_THIS_POINT
+
+ /* From now on, keep only -l, -e, -r bits */
+ opt_ler &= OPT_ler;
+ if ((opt_ler - 1) & opt_ler) /* more than one bit set? */
+ bb_show_usage();
+
+ /* Read replacement file under user's UID/GID/group vector */
+ src_fd = STDIN_FILENO;
+ if (!opt_ler) { /* Replace? */
+ if (!argv[0])
+ bb_show_usage();
+ if (NOT_LONE_DASH(argv[0])) {
+ src_fd = open_as_user(pas, argv[0]);
+ if (src_fd < 0)
+ bb_error_msg_and_die("user %s cannot read %s",
+ pas->pw_name, argv[0]);
+ }
+ }
- if (ChangeUser(user, 1) < 0)
- exit(0);
- if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) > 256)
- ptr = PATH_VI;
+ /* cd to our crontab directory */
+ xchdir(crontab_dir);
- snprintf(visual, sizeof(visual), "%s %s", ptr, file);
- execl("/bin/sh", "/bin/sh", "-c", visual, NULL);
- perror("exec");
- exit(0);
- }
- if (pid < 0) {
- /*
- * PARENT - failure
- */
- bb_perror_msg_and_die("fork");
- }
- wait4(pid, NULL, 0, NULL);
-}
+ tmp_fname = NULL;
-static void
-log(const char *ctl, ...)
-{
- va_list va;
- char buf[1024];
+ /* Handle requested operation */
+ switch (opt_ler) {
- va_start(va, ctl);
- vsnprintf(buf, sizeof(buf), ctl, va);
- syslog(LOG_NOTICE, "%s",buf );
- va_end(va);
-}
+ default: /* case OPT_r: Delete */
+ unlink(pas->pw_name);
+ break;
-static int
-ChangeUser(const char *user, short dochdir)
-{
- struct passwd *pas;
+ case OPT_l: /* List */
+ {
+ char *args[2] = { pas->pw_name, NULL };
+ return bb_cat(args);
+ /* list exits,
+ * the rest go play with cron update file */
+ }
- /*
- * Obtain password entry and change privilages
- */
+ case OPT_e: /* Edit */
+ tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid());
+ /* No O_EXCL: we don't want to be stuck if earlier crontabs
+ * were killed, leaving stale temp file behind */
+ src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ fchown(src_fd, pas->pw_uid, pas->pw_gid);
+ fd = open(pas->pw_name, O_RDONLY);
+ if (fd >= 0) {
+ bb_copyfd_eof(fd, src_fd);
+ close(fd);
+ xlseek(src_fd, 0, SEEK_SET);
+ }
+ close_on_exec_on(src_fd); /* don't want editor to see this fd */
+ edit_file(pas, tmp_fname);
+ /* fall through */
+
+ case 0: /* Replace (no -l, -e, or -r were given) */
+ new_fname = xasprintf("%s.new", pas->pw_name);
+ fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600);
+ if (fd >= 0) {
+ bb_copyfd_eof(src_fd, fd);
+ close(fd);
+ xrename(new_fname, pas->pw_name);
+ } else {
+ bb_error_msg("cannot create %s/%s",
+ crontab_dir, new_fname);
+ }
+ if (tmp_fname)
+ unlink(tmp_fname);
+ /*free(tmp_fname);*/
+ /*free(new_fname);*/
- if ((pas = getpwnam(user)) == 0) {
- log("failed to get uid for %s", user);
- return(-1);
- }
- setenv("USER", pas->pw_name, 1);
- setenv("HOME", pas->pw_dir, 1);
- setenv("SHELL", "/bin/sh", 1);
+ } /* switch */
- /*
- * Change running state to the user in question
- */
+ /* Bump notification file. Handle window where crond picks file up
+ * before we can write our entry out.
+ */
+ while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) {
+ struct stat st;
- if (initgroups(user, pas->pw_gid) < 0) {
- log("initgroups failed: %s %m", user);
- return(-1);
- }
- if (setregid(pas->pw_gid, pas->pw_gid) < 0) {
- log("setregid failed: %s %d", user, pas->pw_gid);
- return(-1);
- }
- if (setreuid(pas->pw_uid, pas->pw_uid) < 0) {
- log("setreuid failed: %s %d", user, pas->pw_uid);
- return(-1);
- }
- if (dochdir) {
- if (chdir(pas->pw_dir) < 0) {
- if (chdir(TMPDIR) < 0) {
- log("chdir failed: %s %s", user, pas->pw_dir);
- log("chdir failed: %s " TMPDIR, user);
- return(-1);
- }
+ fdprintf(fd, "%s\n", pas->pw_name);
+ if (fstat(fd, &st) != 0 || st.st_nlink != 0) {
+ /*close(fd);*/
+ break;
+ }
+ /* st.st_nlink == 0:
+ * file was deleted, maybe crond missed our notification */
+ close(fd);
+ /* loop */
+ }
+ if (fd < 0) {
+ bb_error_msg("cannot append to %s/%s",
+ crontab_dir, CRONUPDATE);
}
- }
- return(pas->pw_uid);
+ return 0;
}
diff --git a/release/src/router/busybox/miscutils/dc.c b/release/src/router/busybox/miscutils/dc.c
index 5e367fe6..ff2bc3bc 100644
--- a/release/src/router/busybox/miscutils/dc.c
+++ b/release/src/router/busybox/miscutils/dc.c
@@ -1,21 +1,32 @@
/* vi: set sw=4 ts=4: */
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
#include <math.h>
-#include "busybox.h"
/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */
-static double stack[100];
-static unsigned int pointer;
-static unsigned char base;
+
+struct globals {
+ unsigned pointer;
+ unsigned base;
+ double stack[1];
+};
+enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define pointer (G.pointer )
+#define base (G.base )
+#define stack (G.stack )
+#define INIT_G() do { \
+ base = 10; \
+} while (0)
+
static void push(double a)
{
- if (pointer >= (sizeof(stack) / sizeof(*stack)))
+ if (pointer >= STACK_SIZE)
bb_error_msg_and_die("stack overflow");
stack[pointer++] = a;
}
@@ -44,6 +55,15 @@ static void mul(void)
push(pop() * pop());
}
+#if ENABLE_FEATURE_DC_LIBM
+static void power(void)
+{
+ double topower = pop();
+
+ push(pow(pop(), topower));
+}
+#endif
+
static void divide(void)
{
double divisor = pop();
@@ -51,46 +71,79 @@ static void divide(void)
push(pop() / divisor);
}
+static void mod(void)
+{
+ unsigned d = pop();
+
+ push((unsigned) pop() % d);
+}
+
static void and(void)
{
- push((unsigned int) pop() & (unsigned int) pop());
+ push((unsigned) pop() & (unsigned) pop());
}
static void or(void)
{
- push((unsigned int) pop() | (unsigned int) pop());
+ push((unsigned) pop() | (unsigned) pop());
}
static void eor(void)
{
- push((unsigned int) pop() ^ (unsigned int) pop());
+ push((unsigned) pop() ^ (unsigned) pop());
}
static void not(void)
{
- push(~(unsigned int) pop());
+ push(~(unsigned) pop());
}
static void set_output_base(void)
{
- base=(unsigned char)pop();
- if ((base != 10) && (base != 16)) {
- fprintf(stderr, "Error: base = %d is not supported.\n", base);
- base=10;
+ static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
+ unsigned b = (unsigned)pop();
+
+ base = *strchrnul(bases, b);
+ if (base == 0) {
+ bb_error_msg("error, base %u is not supported", b);
+ base = 10;
}
}
static void print_base(double print)
{
- if (base == 16)
- printf("%x\n", (unsigned int)print);
- else
- printf("%g\n", print);
+ unsigned x, i;
+
+ if (base == 10) {
+ printf("%g\n", print);
+ return;
+ }
+
+ x = (unsigned)print;
+ switch (base) {
+ case 16:
+ printf("%x\n", x);
+ break;
+ case 8:
+ printf("%o\n", x);
+ break;
+ default: /* base 2 */
+ i = (unsigned)INT_MAX + 1;
+ do {
+ if (x & i) break;
+ i >>= 1;
+ } while (i > 1);
+ do {
+ bb_putchar('1' - !(x & i));
+ i >>= 1;
+ } while (i);
+ bb_putchar('\n');
+ }
}
static void print_stack_no_pop(void)
{
- unsigned int i=pointer;
+ unsigned i = pointer;
while (i)
print_base(stack[--i]);
}
@@ -100,13 +153,8 @@ static void print_no_pop(void)
print_base(stack[pointer-1]);
}
-static void print(void)
-{
- print_base(pop());
-}
-
struct op {
- const char *name;
+ const char name[4];
void (*function) (void);
};
@@ -119,26 +167,32 @@ static const struct op operators[] = {
{"mul", mul},
{"/", divide},
{"div", divide},
+#if ENABLE_FEATURE_DC_LIBM
+ {"**", power},
+ {"exp", power},
+ {"pow", power},
+#endif
+ {"%", mod},
+ {"mod", mod},
{"and", and},
{"or", or},
{"not", not},
{"eor", eor},
+ {"xor", eor},
{"p", print_no_pop},
{"f", print_stack_no_pop},
{"o", set_output_base},
- {0, 0}
+ { /* zero filled */ }
};
static void stack_machine(const char *argument)
{
- char *endPointer = 0;
+ char *endPointer;
double d;
const struct op *o = operators;
- if (argument == 0) {
- print();
+ if (argument == 0)
return;
- }
d = strtod(argument, &endPointer);
@@ -147,69 +201,56 @@ static void stack_machine(const char *argument)
return;
}
- while (o->name != 0) {
+ while (o->name[0]) {
if (strcmp(o->name, argument) == 0) {
- (*(o->function)) ();
+ o->function();
return;
}
o++;
}
- bb_error_msg_and_die("%s: syntax error.", argument);
+ bb_error_msg_and_die("%s: syntax error", argument);
}
/* return pointer to next token in buffer and set *buffer to one char
- * past the end of the above mentioned token
+ * past the end of the above mentioned token
*/
static char *get_token(char **buffer)
{
- char *start = NULL;
- char *current = *buffer;
-
- while (isspace(*current)) { current++; }
- if (*current != 0) {
- start = current;
- while (!isspace(*current) && current != 0) { current++; }
- *buffer = current;
+ char *current = skip_whitespace(*buffer);
+ if (*current != '\0') {
+ *buffer = skip_non_whitespace(current);
+ return current;
}
- return start;
+ return NULL;
}
-/* In Perl one might say, scalar m|\s*(\S+)\s*|g */
-static int number_of_tokens(char *buffer)
+int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dc_main(int argc UNUSED_PARAM, char **argv)
{
- int i = 0;
- char *b = buffer;
- while (get_token(&b)) { i++; }
- return i;
-}
+ INIT_G();
-int dc_main(int argc, char **argv)
-{
- /* take stuff from stdin if no args are given */
- if (argc <= 1) {
- int i, len;
- char *line = NULL;
- char *cursor = NULL;
- char *token = NULL;
- while ((line = bb_get_chomped_line_from_file(stdin))) {
+ argv++;
+ if (!argv[0]) {
+ /* take stuff from stdin if no args are given */
+ char *line;
+ char *cursor;
+ char *token;
+ while ((line = xmalloc_fgetline(stdin)) != NULL) {
cursor = line;
- len = number_of_tokens(line);
- for (i = 0; i < len; i++) {
+ while (1) {
token = get_token(&cursor);
- *cursor++ = 0;
+ if (!token) break;
+ *cursor++ = '\0';
stack_machine(token);
}
free(line);
}
} else {
- if (*argv[1]=='-')
+ if (argv[0][0] == '-')
bb_show_usage();
- while (argc >= 2) {
- stack_machine(argv[1]);
- argv++;
- argc--;
- }
+ do {
+ stack_machine(*argv);
+ } while (*++argv);
}
- stack_machine(0);
return EXIT_SUCCESS;
}
diff --git a/release/src/router/busybox/miscutils/devfsd.c b/release/src/router/busybox/miscutils/devfsd.c
new file mode 100644
index 00000000..61b97dce
--- /dev/null
+++ b/release/src/router/busybox/miscutils/devfsd.c
@@ -0,0 +1,1801 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ devfsd implementation for busybox
+
+ Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
+
+ Busybox version is based on some previous work and ideas
+ Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+
+ devfsd.c
+
+ Main file for devfsd (devfs daemon for Linux).
+
+ Copyright (C) 1998-2002 Richard Gooch
+
+ devfsd.h
+
+ Header file for devfsd (devfs daemon for Linux).
+
+ Copyright (C) 1998-2000 Richard Gooch
+
+ compat_name.c
+
+ Compatibility name file for devfsd (build compatibility names).
+
+ Copyright (C) 1998-2002 Richard Gooch
+
+ expression.c
+
+ This code provides Borne Shell-like expression expansion.
+
+ Copyright (C) 1997-1999 Richard Gooch
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Richard Gooch may be reached by email at rgooch@atnf.csiro.au
+ The postal address is:
+ Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+*/
+#include "libbb.h"
+#include "xregex.h"
+#include <syslog.h>
+
+#include <sys/un.h>
+#include <sys/sysmacros.h>
+
+/* Various defines taken from linux/major.h */
+#define IDE0_MAJOR 3
+#define IDE1_MAJOR 22
+#define IDE2_MAJOR 33
+#define IDE3_MAJOR 34
+#define IDE4_MAJOR 56
+#define IDE5_MAJOR 57
+#define IDE6_MAJOR 88
+#define IDE7_MAJOR 89
+#define IDE8_MAJOR 90
+#define IDE9_MAJOR 91
+
+
+/* Various defines taken from linux/devfs_fs.h */
+#define DEVFSD_PROTOCOL_REVISION_KERNEL 5
+#define DEVFSD_IOCTL_BASE 'd'
+/* These are the various ioctls */
+#define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int)
+#define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int)
+#define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int)
+#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
+#define DEVFSD_NOTIFY_REGISTERED 0
+#define DEVFSD_NOTIFY_UNREGISTERED 1
+#define DEVFSD_NOTIFY_ASYNC_OPEN 2
+#define DEVFSD_NOTIFY_CLOSE 3
+#define DEVFSD_NOTIFY_LOOKUP 4
+#define DEVFSD_NOTIFY_CHANGE 5
+#define DEVFSD_NOTIFY_CREATE 6
+#define DEVFSD_NOTIFY_DELETE 7
+#define DEVFS_PATHLEN 1024
+/* Never change this otherwise the binary interface will change */
+
+struct devfsd_notify_struct
+{ /* Use native C types to ensure same types in kernel and user space */
+ unsigned int type; /* DEVFSD_NOTIFY_* value */
+ unsigned int mode; /* Mode of the inode or device entry */
+ unsigned int major; /* Major number of device entry */
+ unsigned int minor; /* Minor number of device entry */
+ unsigned int uid; /* Uid of process, inode or device entry */
+ unsigned int gid; /* Gid of process, inode or device entry */
+ unsigned int overrun_count; /* Number of lost events */
+ unsigned int namelen; /* Number of characters not including '\0' */
+ /* The device name MUST come last */
+ char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */
+};
+
+#define BUFFER_SIZE 16384
+#define DEVFSD_VERSION "1.3.25"
+#define CONFIG_FILE "/etc/devfsd.conf"
+#define MODPROBE "/sbin/modprobe"
+#define MODPROBE_SWITCH_1 "-k"
+#define MODPROBE_SWITCH_2 "-C"
+#define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
+#define MAX_ARGS (6 + 1)
+#define MAX_SUBEXPR 10
+#define STRING_LENGTH 255
+
+/* for get_uid_gid() */
+#define UID 0
+#define GID 1
+
+/* fork_and_execute() */
+# define DIE 1
+# define NO_DIE 0
+
+/* for dir_operation() */
+#define RESTORE 0
+#define SERVICE 1
+#define READ_CONFIG 2
+
+/* Update only after changing code to reflect new protocol */
+#define DEVFSD_PROTOCOL_REVISION_DAEMON 5
+
+/* Compile-time check */
+#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
+#error protocol version mismatch. Update your kernel headers
+#endif
+
+#define AC_PERMISSIONS 0
+#define AC_MODLOAD 1
+#define AC_EXECUTE 2
+#define AC_MFUNCTION 3 /* not supported by busybox */
+#define AC_CFUNCTION 4 /* not supported by busybox */
+#define AC_COPY 5
+#define AC_IGNORE 6
+#define AC_MKOLDCOMPAT 7
+#define AC_MKNEWCOMPAT 8
+#define AC_RMOLDCOMPAT 9
+#define AC_RMNEWCOMPAT 10
+#define AC_RESTORE 11
+
+struct permissions_type
+{
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+};
+
+struct execute_type
+{
+ char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */
+};
+
+struct copy_type
+{
+ const char *source;
+ const char *destination;
+};
+
+struct action_type
+{
+ unsigned int what;
+ unsigned int when;
+};
+
+struct config_entry_struct
+{
+ struct action_type action;
+ regex_t preg;
+ union
+ {
+ struct permissions_type permissions;
+ struct execute_type execute;
+ struct copy_type copy;
+ }
+ u;
+ struct config_entry_struct *next;
+};
+
+struct get_variable_info
+{
+ const struct devfsd_notify_struct *info;
+ const char *devname;
+ char devpath[STRING_LENGTH];
+};
+
+static void dir_operation(int , const char * , int, unsigned long*);
+static void service(struct stat statbuf, char *path);
+static int st_expr_expand(char *, unsigned, const char *, const char *(*)(const char *, void *), void *);
+static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
+static int mksymlink(const char *oldpath, const char *newpath);
+static void read_config_file(char *path, int optional, unsigned long *event_mask);
+static void process_config_line(const char *, unsigned long *);
+static int do_servicing(int, unsigned long);
+static void service_name(const struct devfsd_notify_struct *);
+static void action_permissions(const struct devfsd_notify_struct *, const struct config_entry_struct *);
+static void action_execute(const struct devfsd_notify_struct *, const struct config_entry_struct *,
+ const regmatch_t *, unsigned);
+static void action_modload(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
+static void action_copy(const struct devfsd_notify_struct *, const struct config_entry_struct *,
+ const regmatch_t *, unsigned);
+static void action_compat(const struct devfsd_notify_struct *, unsigned);
+static void free_config(void);
+static void restore(char *spath, struct stat source_stat, int rootlen);
+static int copy_inode(const char *, const struct stat *, mode_t, const char *, const struct stat *);
+static mode_t get_mode(const char *);
+static void signal_handler(int);
+static const char *get_variable(const char *, void *);
+static int make_dir_tree(const char *);
+static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
+ const char *, const regmatch_t *, unsigned);
+static void expand_regexp(char *, size_t, const char *, const char *, const regmatch_t *, unsigned);
+static const char *expand_variable( char *, unsigned, unsigned *, const char *,
+ const char *(*)(const char *, void *), void *);
+static const char *get_variable_v2(const char *, const char *(*)(const char *, void *), void *);
+static char get_old_ide_name(unsigned , unsigned);
+static char *write_old_sd_name(char *, unsigned, unsigned, const char *);
+
+/* busybox functions */
+static int get_uid_gid(int flag, const char *string);
+static void safe_memcpy(char * dest, const char * src, int len);
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr);
+static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr);
+
+/* Structs and vars */
+static struct config_entry_struct *first_config = NULL;
+static struct config_entry_struct *last_config = NULL;
+static char *mount_point = NULL;
+static volatile int caught_signal = FALSE;
+static volatile int caught_sighup = FALSE;
+static struct initial_symlink_struct {
+ const char *dest;
+ const char *name;
+} initial_symlinks[] = {
+ {"/proc/self/fd", "fd"},
+ {"fd/0", "stdin"},
+ {"fd/1", "stdout"},
+ {"fd/2", "stderr"},
+ {NULL, NULL},
+};
+
+static struct event_type {
+ unsigned int type; /* The DEVFSD_NOTIFY_* value */
+ const char *config_name; /* The name used in the config file */
+} event_types[] = {
+ {DEVFSD_NOTIFY_REGISTERED, "REGISTER"},
+ {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
+ {DEVFSD_NOTIFY_ASYNC_OPEN, "ASYNC_OPEN"},
+ {DEVFSD_NOTIFY_CLOSE, "CLOSE"},
+ {DEVFSD_NOTIFY_LOOKUP, "LOOKUP"},
+ {DEVFSD_NOTIFY_CHANGE, "CHANGE"},
+ {DEVFSD_NOTIFY_CREATE, "CREATE"},
+ {DEVFSD_NOTIFY_DELETE, "DELETE"},
+ {0xffffffff, NULL}
+};
+
+/* Busybox messages */
+
+static const char bb_msg_proto_rev[] ALIGN1 = "protocol revision";
+static const char bb_msg_bad_config[] ALIGN1 = "bad %s config file: %s";
+static const char bb_msg_small_buffer[] ALIGN1 = "buffer too small";
+static const char bb_msg_variable_not_found[] ALIGN1 = "variable: %s not found";
+
+/* Busybox stuff */
+#if ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG
+#define info_logger(p, fmt, args...) bb_info_msg(fmt, ## args)
+#define msg_logger(p, fmt, args...) bb_error_msg(fmt, ## args)
+#define msg_logger_and_die(p, fmt, args...) bb_error_msg_and_die(fmt, ## args)
+#define error_logger(p, fmt, args...) bb_perror_msg(fmt, ## args)
+#define error_logger_and_die(p, fmt, args...) bb_perror_msg_and_die(fmt, ## args)
+#else
+#define info_logger(p, fmt, args...)
+#define msg_logger(p, fmt, args...)
+#define msg_logger_and_die(p, fmt, args...) exit(EXIT_FAILURE)
+#define error_logger(p, fmt, args...)
+#define error_logger_and_die(p, fmt, args...) exit(EXIT_FAILURE)
+#endif
+
+static void safe_memcpy(char *dest, const char *src, int len)
+{
+ memcpy(dest , src, len);
+ dest[len] = '\0';
+}
+
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr)
+{
+ if (d[n - 4] == 'd' && d[n - 3] == 'i' && d[n - 2] == 's' && d[n - 1] == 'c')
+ return 2 + addendum;
+ if (d[n - 2] == 'c' && d[n - 1] == 'd')
+ return 3 + addendum;
+ if (ptr[0] == 'p' && ptr[1] == 'a' && ptr[2] == 'r' && ptr[3] == 't')
+ return 4 + addendum;
+ if (ptr[n - 2] == 'm' && ptr[n - 1] == 't')
+ return 5 + addendum;
+ return 0;
+}
+
+static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr)
+{
+ if (d[0] == 's' && d[1] == 'c' && d[2] == 's' && d[3] == 'i' && d[4] == '/') {
+ if (d[n - 7] == 'g' && d[n - 6] == 'e' && d[n - 5] == 'n'
+ && d[n - 4] == 'e' && d[n - 3] == 'r' && d[n - 2] == 'i' && d[n - 1] == 'c'
+ )
+ return 1;
+ return scan_dev_name_common(d, n, 0, ptr);
+ }
+ if (d[0] == 'i' && d[1] == 'd' && d[2] == 'e' && d[3] == '/'
+ && d[4] == 'h' && d[5] == 'o' && d[6] == 's' && d[7] == 't'
+ )
+ return scan_dev_name_common(d, n, 4, ptr);
+ if (d[0] == 's' && d[1] == 'b' && d[2] == 'p' && d[3] == '/')
+ return 10;
+ if (d[0] == 'v' && d[1] == 'c' && d[2] == 'c' && d[3] == '/')
+ return 11;
+ if (d[0] == 'p' && d[1] == 't' && d[2] == 'y' && d[3] == '/')
+ return 12;
+ return 0;
+}
+
+/* Public functions follow */
+
+int devfsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int devfsd_main(int argc, char **argv)
+{
+ int print_version = FALSE;
+ int do_daemon = TRUE;
+ int no_polling = FALSE;
+ int do_scan;
+ int fd, proto_rev, count;
+ unsigned long event_mask = 0;
+ struct sigaction new_action;
+ struct initial_symlink_struct *curr;
+
+ if (argc < 2)
+ bb_show_usage();
+
+ for (count = 2; count < argc; ++count) {
+ if (argv[count][0] == '-') {
+ if (argv[count][1] == 'v' && !argv[count][2]) /* -v */
+ print_version = TRUE;
+ else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'f'
+ && argv[count][2] == 'g' && !argv[count][3]) /* -fg */
+ do_daemon = FALSE;
+ else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'n'
+ && argv[count][2] == 'p' && !argv[count][3]) /* -np */
+ no_polling = TRUE;
+ else
+ bb_show_usage();
+ }
+ }
+
+ mount_point = bb_simplify_path(argv[1]);
+
+ xchdir(mount_point);
+
+ fd = xopen(".devfsd", O_RDONLY);
+ close_on_exec_on(fd);
+ xioctl(fd, DEVFSDIOC_GET_PROTO_REV, &proto_rev);
+
+ /*setup initial entries */
+ for (curr = initial_symlinks; curr->dest != NULL; ++curr)
+ symlink(curr->dest, curr->name);
+
+ /* NB: The check for CONFIG_FILE is done in read_config_file() */
+
+ if (print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)) {
+ printf("%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
+ applet_name, DEVFSD_VERSION, bb_msg_proto_rev,
+ DEVFSD_PROTOCOL_REVISION_DAEMON, bb_msg_proto_rev, proto_rev);
+ if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
+ bb_error_msg_and_die("%s mismatch!", bb_msg_proto_rev);
+ exit(EXIT_SUCCESS); /* -v */
+ }
+ /* Tell kernel we are special(i.e. we get to see hidden entries) */
+ xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, 0);
+
+ /* Set up SIGHUP and SIGUSR1 handlers */
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = 0;
+ new_action.sa_handler = signal_handler;
+ sigaction_set(SIGHUP, &new_action);
+ sigaction_set(SIGUSR1, &new_action);
+
+ printf("%s v%s started for %s\n", applet_name, DEVFSD_VERSION, mount_point);
+
+ /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */
+ umask(0);
+ read_config_file((char*)CONFIG_FILE, FALSE, &event_mask);
+ /* Do the scan before forking, so that boot scripts see the finished product */
+ dir_operation(SERVICE, mount_point, 0, NULL);
+
+ if (ENABLE_DEVFSD_FG_NP && no_polling)
+ exit(EXIT_SUCCESS);
+
+ if (ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG)
+ logmode = LOGMODE_BOTH;
+ else if (do_daemon == TRUE)
+ logmode = LOGMODE_SYSLOG;
+ /* This is the default */
+ /*else
+ logmode = LOGMODE_STDIO; */
+
+ if (do_daemon) {
+ /* Release so that the child can grab it */
+ xioctl(fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
+ bb_daemonize_or_rexec(0, argv);
+ } else if (ENABLE_DEVFSD_FG_NP) {
+ setpgid(0, 0); /* Become process group leader */
+ }
+
+ while (TRUE) {
+ do_scan = do_servicing(fd, event_mask);
+
+ free_config();
+ read_config_file((char*)CONFIG_FILE, FALSE, &event_mask);
+ if (do_scan)
+ dir_operation(SERVICE, mount_point, 0, NULL);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) free(mount_point);
+} /* End Function main */
+
+
+/* Private functions follow */
+
+static void read_config_file(char *path, int optional, unsigned long *event_mask)
+/* [SUMMARY] Read a configuration database.
+ <path> The path to read the database from. If this is a directory, all
+ entries in that directory will be read(except hidden entries).
+ <optional> If TRUE, the routine will silently ignore a missing config file.
+ <event_mask> The event mask is written here. This is not initialised.
+ [RETURNS] Nothing.
+*/
+{
+ struct stat statbuf;
+ FILE *fp;
+ char buf[STRING_LENGTH];
+ char *line = NULL;
+ char *p;
+
+ if (stat(path, &statbuf) == 0) {
+ /* Don't read 0 length files: ignored */
+ /*if (statbuf.st_size == 0)
+ return;*/
+ if (S_ISDIR(statbuf.st_mode)) {
+ p = bb_simplify_path(path);
+ dir_operation(READ_CONFIG, p, 0, event_mask);
+ free(p);
+ return;
+ }
+ fp = fopen_for_read(path);
+ if (fp != NULL) {
+ while (fgets(buf, STRING_LENGTH, fp) != NULL) {
+ /* Skip whitespace */
+ line = buf;
+ line = skip_whitespace(line);
+ if (line[0] == '\0' || line[0] == '#')
+ continue;
+ process_config_line(line, event_mask);
+ }
+ fclose(fp);
+ } else {
+ goto read_config_file_err;
+ }
+ } else {
+read_config_file_err:
+ if (optional == 0 && errno == ENOENT)
+ error_logger_and_die(LOG_ERR, "read config file: %s", path);
+ }
+} /* End Function read_config_file */
+
+static void process_config_line(const char *line, unsigned long *event_mask)
+/* [SUMMARY] Process a line from a configuration file.
+ <line> The configuration line.
+ <event_mask> The event mask is written here. This is not initialised.
+ [RETURNS] Nothing.
+*/
+{
+ int num_args, count;
+ struct config_entry_struct *new;
+ char p[MAX_ARGS][STRING_LENGTH];
+ char when[STRING_LENGTH], what[STRING_LENGTH];
+ char name[STRING_LENGTH];
+ const char *msg = "";
+ char *ptr;
+ int i;
+
+ /* !!!! Only Uppercase Keywords in devsfd.conf */
+ static const char options[] ALIGN1 =
+ "CLEAR_CONFIG\0""INCLUDE\0""OPTIONAL_INCLUDE\0"
+ "RESTORE\0""PERMISSIONS\0""MODLOAD\0""EXECUTE\0"
+ "COPY\0""IGNORE\0""MKOLDCOMPAT\0""MKNEWCOMPAT\0"
+ "RMOLDCOMPAT\0""RMNEWCOMPAT\0";
+
+ for (count = 0; count < MAX_ARGS; ++count)
+ p[count][0] = '\0';
+ num_args = sscanf(line, "%s %s %s %s %s %s %s %s %s %s",
+ when, name, what,
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
+
+ i = index_in_strings(options, when);
+
+ /* "CLEAR_CONFIG" */
+ if (i == 0) {
+ free_config();
+ *event_mask = 0;
+ return;
+ }
+
+ if (num_args < 2)
+ goto process_config_line_err;
+
+ /* "INCLUDE" & "OPTIONAL_INCLUDE" */
+ if (i == 1 || i == 2) {
+ st_expr_expand(name, STRING_LENGTH, name, get_variable, NULL);
+ info_logger(LOG_INFO, "%sinclude: %s", (toupper(when[0]) == 'I') ? "": "optional_", name);
+ read_config_file(name, (toupper(when[0]) == 'I') ? FALSE : TRUE, event_mask);
+ return;
+ }
+ /* "RESTORE" */
+ if (i == 3) {
+ dir_operation(RESTORE, name, strlen(name),NULL);
+ return;
+ }
+ if (num_args < 3)
+ goto process_config_line_err;
+
+ new = xzalloc(sizeof *new);
+
+ for (count = 0; event_types[count].config_name != NULL; ++count) {
+ if (strcasecmp(when, event_types[count].config_name) != 0)
+ continue;
+ new->action.when = event_types[count].type;
+ break;
+ }
+ if (event_types[count].config_name == NULL) {
+ msg = "WHEN in";
+ goto process_config_line_err;
+ }
+
+ i = index_in_strings(options, what);
+
+ switch (i) {
+ case 4: /* "PERMISSIONS" */
+ new->action.what = AC_PERMISSIONS;
+ /* Get user and group */
+ ptr = strchr(p[0], '.');
+ if (ptr == NULL) {
+ msg = "UID.GID";
+ goto process_config_line_err; /*"missing '.' in UID.GID"*/
+ }
+
+ *ptr++ = '\0';
+ new->u.permissions.uid = get_uid_gid(UID, p[0]);
+ new->u.permissions.gid = get_uid_gid(GID, ptr);
+ /* Get mode */
+ new->u.permissions.mode = get_mode(p[1]);
+ break;
+ case 5: /* MODLOAD */
+ /*This action will pass "/dev/$devname"(i.e. "/dev/" prefixed to
+ the device name) to the module loading facility. In addition,
+ the /etc/modules.devfs configuration file is used.*/
+ if (ENABLE_DEVFSD_MODLOAD)
+ new->action.what = AC_MODLOAD;
+ break;
+ case 6: /* EXECUTE */
+ new->action.what = AC_EXECUTE;
+ num_args -= 3;
+
+ for (count = 0; count < num_args; ++count)
+ new->u.execute.argv[count] = xstrdup(p[count]);
+
+ new->u.execute.argv[num_args] = NULL;
+ break;
+ case 7: /* COPY */
+ new->action.what = AC_COPY;
+ num_args -= 3;
+ if (num_args != 2)
+ goto process_config_line_err; /* missing path and function in line */
+
+ new->u.copy.source = xstrdup(p[0]);
+ new->u.copy.destination = xstrdup(p[1]);
+ break;
+ case 8: /* IGNORE */
+ /* FALLTROUGH */
+ case 9: /* MKOLDCOMPAT */
+ /* FALLTROUGH */
+ case 10: /* MKNEWCOMPAT */
+ /* FALLTROUGH */
+ case 11:/* RMOLDCOMPAT */
+ /* FALLTROUGH */
+ case 12: /* RMNEWCOMPAT */
+ /* AC_IGNORE 6
+ AC_MKOLDCOMPAT 7
+ AC_MKNEWCOMPAT 8
+ AC_RMOLDCOMPAT 9
+ AC_RMNEWCOMPAT 10*/
+ new->action.what = i - 2;
+ break;
+ default:
+ msg = "WHAT in";
+ goto process_config_line_err;
+ /*esac*/
+ } /* switch (i) */
+
+ xregcomp(&new->preg, name, REG_EXTENDED);
+
+ *event_mask |= 1 << new->action.when;
+ new->next = NULL;
+ if (first_config == NULL)
+ first_config = new;
+ else
+ last_config->next = new;
+ last_config = new;
+ return;
+
+ process_config_line_err:
+ msg_logger_and_die(LOG_ERR, bb_msg_bad_config, msg , line);
+} /* End Function process_config_line */
+
+static int do_servicing(int fd, unsigned long event_mask)
+/* [SUMMARY] Service devfs changes until a signal is received.
+ <fd> The open control file.
+ <event_mask> The event mask.
+ [RETURNS] TRUE if SIGHUP was caught, else FALSE.
+*/
+{
+ ssize_t bytes;
+ struct devfsd_notify_struct info;
+
+ /* (void*) cast is only in order to match prototype */
+ xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, (void*)event_mask);
+ while (!caught_signal) {
+ errno = 0;
+ bytes = read(fd,(char *) &info, sizeof info);
+ if (caught_signal)
+ break; /* Must test for this first */
+ if (errno == EINTR)
+ continue; /* Yes, the order is important */
+ if (bytes < 1)
+ break;
+ service_name(&info);
+ }
+ if (caught_signal) {
+ int c_sighup = caught_sighup;
+
+ caught_signal = FALSE;
+ caught_sighup = FALSE;
+ return c_sighup;
+ }
+ msg_logger_and_die(LOG_ERR, "read error on control file");
+} /* End Function do_servicing */
+
+static void service_name(const struct devfsd_notify_struct *info)
+/* [SUMMARY] Service a single devfs change.
+ <info> The devfs change.
+ [RETURNS] Nothing.
+*/
+{
+ unsigned int n;
+ regmatch_t mbuf[MAX_SUBEXPR];
+ struct config_entry_struct *entry;
+
+ if (ENABLE_DEBUG && info->overrun_count > 0)
+ msg_logger(LOG_ERR, "lost %u events", info->overrun_count);
+
+ /* Discard lookups on "/dev/log" and "/dev/initctl" */
+ if (info->type == DEVFSD_NOTIFY_LOOKUP
+ && ((info->devname[0] == 'l' && info->devname[1] == 'o'
+ && info->devname[2] == 'g' && !info->devname[3])
+ || (info->devname[0] == 'i' && info->devname[1] == 'n'
+ && info->devname[2] == 'i' && info->devname[3] == 't'
+ && info->devname[4] == 'c' && info->devname[5] == 't'
+ && info->devname[6] == 'l' && !info->devname[7]))
+ )
+ return;
+
+ for (entry = first_config; entry != NULL; entry = entry->next) {
+ /* First check if action matches the type, then check if name matches */
+ if (info->type != entry->action.when
+ || regexec(&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0)
+ continue;
+ for (n = 0;(n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
+ /* VOID */;
+
+ switch (entry->action.what) {
+ case AC_PERMISSIONS:
+ action_permissions(info, entry);
+ break;
+ case AC_MODLOAD:
+ if (ENABLE_DEVFSD_MODLOAD)
+ action_modload(info, entry);
+ break;
+ case AC_EXECUTE:
+ action_execute(info, entry, mbuf, n);
+ break;
+ case AC_COPY:
+ action_copy(info, entry, mbuf, n);
+ break;
+ case AC_IGNORE:
+ return;
+ /*break;*/
+ case AC_MKOLDCOMPAT:
+ case AC_MKNEWCOMPAT:
+ case AC_RMOLDCOMPAT:
+ case AC_RMNEWCOMPAT:
+ action_compat(info, entry->action.what);
+ break;
+ default:
+ msg_logger_and_die(LOG_ERR, "Unknown action");
+ }
+ }
+} /* End Function service_name */
+
+static void action_permissions(const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry)
+/* [SUMMARY] Update permissions for a device entry.
+ <info> The devfs change.
+ <entry> The config file entry.
+ [RETURNS] Nothing.
+*/
+{
+ struct stat statbuf;
+
+ if (stat(info->devname, &statbuf) != 0
+ || chmod(info->devname, (statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0
+ || chown(info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0
+ )
+ error_logger(LOG_ERR, "Can't chmod or chown: %s", info->devname);
+} /* End Function action_permissions */
+
+static void action_modload(const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry UNUSED_PARAM)
+/* [SUMMARY] Load a module.
+ <info> The devfs change.
+ <entry> The config file entry.
+ [RETURNS] Nothing.
+*/
+{
+ char *argv[6];
+
+ argv[0] = (char*)MODPROBE;
+ argv[1] = (char*)MODPROBE_SWITCH_1; /* "-k" */
+ argv[2] = (char*)MODPROBE_SWITCH_2; /* "-C" */
+ argv[3] = (char*)CONFIG_MODULES_DEVFS;
+ argv[4] = concat_path_file("/dev", info->devname); /* device */
+ argv[5] = NULL;
+
+ wait4pid(xspawn(argv));
+ free(argv[4]);
+} /* End Function action_modload */
+
+static void action_execute(const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry,
+ const regmatch_t *regexpr, unsigned int numexpr)
+/* [SUMMARY] Execute a programme.
+ <info> The devfs change.
+ <entry> The config file entry.
+ <regexpr> The number of subexpression(start, end) offsets within the
+ device name.
+ <numexpr> The number of elements within <<regexpr>>.
+ [RETURNS] Nothing.
+*/
+{
+ unsigned int count;
+ struct get_variable_info gv_info;
+ char *argv[MAX_ARGS + 1];
+ char largv[MAX_ARGS + 1][STRING_LENGTH];
+
+ gv_info.info = info;
+ gv_info.devname = info->devname;
+ snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname);
+ for (count = 0; entry->u.execute.argv[count] != NULL; ++count) {
+ expand_expression(largv[count], STRING_LENGTH,
+ entry->u.execute.argv[count],
+ get_variable, &gv_info,
+ gv_info.devname, regexpr, numexpr);
+ argv[count] = largv[count];
+ }
+ argv[count] = NULL;
+ wait4pid(spawn(argv));
+} /* End Function action_execute */
+
+
+static void action_copy(const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry,
+ const regmatch_t *regexpr, unsigned int numexpr)
+/* [SUMMARY] Copy permissions.
+ <info> The devfs change.
+ <entry> The config file entry.
+ <regexpr> This list of subexpression(start, end) offsets within the
+ device name.
+ <numexpr> The number of elements in <<regexpr>>.
+ [RETURNS] Nothing.
+*/
+{
+ mode_t new_mode;
+ struct get_variable_info gv_info;
+ struct stat source_stat, dest_stat;
+ char source[STRING_LENGTH], destination[STRING_LENGTH];
+ int ret = 0;
+
+ dest_stat.st_mode = 0;
+
+ if ((info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK(info->mode))
+ return;
+ gv_info.info = info;
+ gv_info.devname = info->devname;
+
+ snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname);
+ expand_expression(source, STRING_LENGTH, entry->u.copy.source,
+ get_variable, &gv_info, gv_info.devname,
+ regexpr, numexpr);
+
+ expand_expression(destination, STRING_LENGTH, entry->u.copy.destination,
+ get_variable, &gv_info, gv_info.devname,
+ regexpr, numexpr);
+
+ if (!make_dir_tree(destination) || lstat(source, &source_stat) != 0)
+ return;
+ lstat(destination, &dest_stat);
+ new_mode = source_stat.st_mode & ~S_ISVTX;
+ if (info->type == DEVFSD_NOTIFY_CREATE)
+ new_mode |= S_ISVTX;
+ else if ((info->type == DEVFSD_NOTIFY_CHANGE) &&(dest_stat.st_mode & S_ISVTX))
+ new_mode |= S_ISVTX;
+ ret = copy_inode(destination, &dest_stat, new_mode, source, &source_stat);
+ if (ENABLE_DEBUG && ret && (errno != EEXIST))
+ error_logger(LOG_ERR, "copy_inode: %s to %s", source, destination);
+} /* End Function action_copy */
+
+static void action_compat(const struct devfsd_notify_struct *info, unsigned int action)
+/* [SUMMARY] Process a compatibility request.
+ <info> The devfs change.
+ <action> The action to take.
+ [RETURNS] Nothing.
+*/
+{
+ int ret;
+ const char *compat_name = NULL;
+ const char *dest_name = info->devname;
+ const char *ptr;
+ char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
+ int mode, host, bus, target, lun;
+ unsigned int i;
+ char rewind_;
+ /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */
+ static const char *const fmt[] = {
+ NULL ,
+ "sg/c%db%dt%du%d", /* scsi/generic */
+ "sd/c%db%dt%du%d", /* scsi/disc */
+ "sr/c%db%dt%du%d", /* scsi/cd */
+ "sd/c%db%dt%du%dp%d", /* scsi/part */
+ "st/c%db%dt%du%dm%d%c", /* scsi/mt */
+ "ide/hd/c%db%dt%du%d", /* ide/host/disc */
+ "ide/cd/c%db%dt%du%d", /* ide/host/cd */
+ "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */
+ "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */
+ NULL
+ };
+
+ /* First construct compatibility name */
+ switch (action) {
+ case AC_MKOLDCOMPAT:
+ case AC_RMOLDCOMPAT:
+ compat_name = get_old_name(info->devname, info->namelen, compat_buf, info->major, info->minor);
+ break;
+ case AC_MKNEWCOMPAT:
+ case AC_RMNEWCOMPAT:
+ ptr = bb_basename(info->devname);
+ i = scan_dev_name(info->devname, info->namelen, ptr);
+
+ /* nothing found */
+ if (i == 0 || i > 9)
+ return;
+
+ sscanf(info->devname + ((i < 6) ? 5 : 4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
+ snprintf(dest_buf, sizeof(dest_buf), "../%s", info->devname + (( i > 5) ? 4 : 0));
+ dest_name = dest_buf;
+ compat_name = compat_buf;
+
+
+ /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
+ if (i == 1 || i == 2 || i == 3 || i == 6 || i ==7)
+ sprintf(compat_buf, fmt[i], host, bus, target, lun);
+
+ /* 4 == scsi/part 8 == ide/host/part */
+ if (i == 4 || i == 8)
+ sprintf(compat_buf, fmt[i], host, bus, target, lun, atoi(ptr + 4));
+
+ /* 5 == scsi/mt */
+ if (i == 5) {
+ rewind_ = info->devname[info->namelen - 1];
+ if (rewind_ != 'n')
+ rewind_ = '\0';
+ mode=0;
+ if (ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/)
+ mode = ptr[2] - 107; /* 1 or 2 */
+ if (ptr[2] == 'a')
+ mode = 3;
+ sprintf(compat_buf, fmt[i], host, bus, target, lun, mode, rewind_);
+ }
+
+ /* 9 == ide/host/mt */
+ if (i == 9)
+ snprintf(compat_buf, sizeof(compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
+ /* esac */
+ } /* switch (action) */
+
+ if (compat_name == NULL)
+ return;
+
+ /* Now decide what to do with it */
+ switch (action) {
+ case AC_MKOLDCOMPAT:
+ case AC_MKNEWCOMPAT:
+ mksymlink(dest_name, compat_name);
+ break;
+ case AC_RMOLDCOMPAT:
+ case AC_RMNEWCOMPAT:
+ ret = unlink(compat_name);
+ if (ENABLE_DEBUG && ret)
+ error_logger(LOG_ERR, "unlink: %s", compat_name);
+ break;
+ /*esac*/
+ } /* switch (action) */
+} /* End Function action_compat */
+
+static void restore(char *spath, struct stat source_stat, int rootlen)
+{
+ char *dpath;
+ struct stat dest_stat;
+
+ dest_stat.st_mode = 0;
+ dpath = concat_path_file(mount_point, spath + rootlen);
+ lstat(dpath, &dest_stat);
+ free(dpath);
+ if (S_ISLNK(source_stat.st_mode) || (source_stat.st_mode & S_ISVTX))
+ copy_inode(dpath, &dest_stat,(source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
+
+ if (S_ISDIR(source_stat.st_mode))
+ dir_operation(RESTORE, spath, rootlen,NULL);
+}
+
+
+static int copy_inode(const char *destpath, const struct stat *dest_stat,
+ mode_t new_mode,
+ const char *sourcepath, const struct stat *source_stat)
+/* [SUMMARY] Copy an inode.
+ <destpath> The destination path. An existing inode may be deleted.
+ <dest_stat> The destination stat(2) information.
+ <new_mode> The desired new mode for the destination.
+ <sourcepath> The source path.
+ <source_stat> The source stat(2) information.
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ int source_len, dest_len;
+ char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
+ int fd, val;
+ struct sockaddr_un un_addr;
+ char symlink_val[STRING_LENGTH];
+
+ if ((source_stat->st_mode & S_IFMT) ==(dest_stat->st_mode & S_IFMT)) {
+ /* Same type */
+ if (S_ISLNK(source_stat->st_mode)) {
+ source_len = readlink(sourcepath, source_link, STRING_LENGTH - 1);
+ if ((source_len < 0)
+ || (dest_len = readlink(destpath, dest_link, STRING_LENGTH - 1)) < 0
+ )
+ return FALSE;
+ source_link[source_len] = '\0';
+ dest_link[dest_len] = '\0';
+ if ((source_len != dest_len) || (strcmp(source_link, dest_link) != 0)) {
+ unlink(destpath);
+ symlink(source_link, destpath);
+ }
+ return TRUE;
+ } /* Else not a symlink */
+ chmod(destpath, new_mode & ~S_IFMT);
+ chown(destpath, source_stat->st_uid, source_stat->st_gid);
+ return TRUE;
+ }
+ /* Different types: unlink and create */
+ unlink(destpath);
+ switch (source_stat->st_mode & S_IFMT) {
+ case S_IFSOCK:
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ break;
+ un_addr.sun_family = AF_UNIX;
+ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s", destpath);
+ val = bind(fd,(struct sockaddr *) &un_addr,(int) sizeof un_addr);
+ close(fd);
+ if (val != 0 || chmod(destpath, new_mode & ~S_IFMT) != 0)
+ break;
+ goto do_chown;
+ case S_IFLNK:
+ val = readlink(sourcepath, symlink_val, STRING_LENGTH - 1);
+ if (val < 0)
+ break;
+ symlink_val[val] = '\0';
+ if (symlink(symlink_val, destpath) == 0)
+ return TRUE;
+ break;
+ case S_IFREG:
+ fd = open(destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT);
+ if (fd < 0)
+ break;
+ close(fd);
+ if (chmod(destpath, new_mode & ~S_IFMT) != 0)
+ break;
+ goto do_chown;
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ if (mknod(destpath, new_mode, source_stat->st_rdev) != 0)
+ break;
+ goto do_chown;
+ case S_IFDIR:
+ if (mkdir(destpath, new_mode & ~S_IFMT) != 0)
+ break;
+do_chown:
+ if (chown(destpath, source_stat->st_uid, source_stat->st_gid) == 0)
+ return TRUE;
+ /*break;*/
+ }
+ return FALSE;
+} /* End Function copy_inode */
+
+static void free_config(void)
+/* [SUMMARY] Free the configuration information.
+ [RETURNS] Nothing.
+*/
+{
+ struct config_entry_struct *c_entry;
+ void *next;
+
+ for (c_entry = first_config; c_entry != NULL; c_entry = next) {
+ unsigned int count;
+
+ next = c_entry->next;
+ regfree(&c_entry->preg);
+ if (c_entry->action.what == AC_EXECUTE) {
+ for (count = 0; count < MAX_ARGS; ++count) {
+ if (c_entry->u.execute.argv[count] == NULL)
+ break;
+ free(c_entry->u.execute.argv[count]);
+ }
+ }
+ free(c_entry);
+ }
+ first_config = NULL;
+ last_config = NULL;
+} /* End Function free_config */
+
+static int get_uid_gid(int flag, const char *string)
+/* [SUMMARY] Convert a string to a UID or GID value.
+ <flag> "UID" or "GID".
+ <string> The string.
+ [RETURNS] The UID or GID value.
+*/
+{
+ struct passwd *pw_ent;
+ struct group *grp_ent;
+ static const char *msg;
+
+ if (ENABLE_DEVFSD_VERBOSE)
+ msg = "user";
+
+ if (isdigit(string[0]) ||((string[0] == '-') && isdigit(string[1])))
+ return atoi(string);
+
+ if (flag == UID && (pw_ent = getpwnam(string)) != NULL)
+ return pw_ent->pw_uid;
+
+ if (flag == GID && (grp_ent = getgrnam(string)) != NULL)
+ return grp_ent->gr_gid;
+ else if (ENABLE_DEVFSD_VERBOSE)
+ msg = "group";
+
+ if (ENABLE_DEVFSD_VERBOSE)
+ msg_logger(LOG_ERR, "unknown %s: %s, defaulting to %cid=0", msg, string, msg[0]);
+ return 0;
+}/* End Function get_uid_gid */
+
+static mode_t get_mode(const char *string)
+/* [SUMMARY] Convert a string to a mode value.
+ <string> The string.
+ [RETURNS] The mode value.
+*/
+{
+ mode_t mode;
+ int i;
+
+ if (isdigit(string[0]))
+ return strtoul(string, NULL, 8);
+ if (strlen(string) != 9)
+ msg_logger_and_die(LOG_ERR, "bad mode: %s", string);
+
+ mode = 0;
+ i = S_IRUSR;
+ while (i > 0) {
+ if (string[0] == 'r' || string[0] == 'w' || string[0] == 'x')
+ mode += i;
+ i = i / 2;
+ string++;
+ }
+ return mode;
+} /* End Function get_mode */
+
+static void signal_handler(int sig)
+{
+ caught_signal = TRUE;
+ if (sig == SIGHUP)
+ caught_sighup = TRUE;
+
+ info_logger(LOG_INFO, "Caught signal %d", sig);
+} /* End Function signal_handler */
+
+static const char *get_variable(const char *variable, void *info)
+{
+ static char sbuf[sizeof(int)*3 + 2]; /* sign and NUL */
+ static char *hostname;
+
+ struct get_variable_info *gv_info = info;
+ const char *field_names[] = {
+ "hostname", "mntpt", "devpath", "devname",
+ "uid", "gid", "mode", hostname, mount_point,
+ gv_info->devpath, gv_info->devname, NULL
+ };
+ int i;
+
+ if (!hostname)
+ hostname = safe_gethostname();
+ /* index_in_str_array returns i>=0 */
+ i = index_in_str_array(field_names, variable);
+
+ if (i > 6 || i < 0 || (i > 1 && gv_info == NULL))
+ return NULL;
+ if (i >= 0 && i <= 3)
+ return field_names[i + 7];
+
+ if (i == 4)
+ sprintf(sbuf, "%u", gv_info->info->uid);
+ else if (i == 5)
+ sprintf(sbuf, "%u", gv_info->info->gid);
+ else if (i == 6)
+ sprintf(sbuf, "%o", gv_info->info->mode);
+ return sbuf;
+} /* End Function get_variable */
+
+static void service(struct stat statbuf, char *path)
+{
+ struct devfsd_notify_struct info;
+
+ memset(&info, 0, sizeof info);
+ info.type = DEVFSD_NOTIFY_REGISTERED;
+ info.mode = statbuf.st_mode;
+ info.major = major(statbuf.st_rdev);
+ info.minor = minor(statbuf.st_rdev);
+ info.uid = statbuf.st_uid;
+ info.gid = statbuf.st_gid;
+ snprintf(info.devname, sizeof(info.devname), "%s", path + strlen(mount_point) + 1);
+ info.namelen = strlen(info.devname);
+ service_name(&info);
+ if (S_ISDIR(statbuf.st_mode))
+ dir_operation(SERVICE, path, 0, NULL);
+}
+
+static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
+/* [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
+ <flag> To choose which function to perform
+ <dp> The directory pointer. This is closed upon completion.
+ <dir_name> The name of the directory.
+ <rootlen> string length parameter.
+ [RETURNS] Nothing.
+*/
+{
+ struct stat statbuf;
+ DIR *dp;
+ struct dirent *de;
+ char *path;
+
+ dp = warn_opendir(dir_name);
+ if (dp == NULL)
+ return;
+
+ while ((de = readdir(dp)) != NULL) {
+
+ if (de->d_name && DOT_OR_DOTDOT(de->d_name))
+ continue;
+ path = concat_path_file(dir_name, de->d_name);
+ if (lstat(path, &statbuf) == 0) {
+ switch (type) {
+ case SERVICE:
+ service(statbuf, path);
+ break;
+ case RESTORE:
+ restore(path, statbuf, var);
+ break;
+ case READ_CONFIG:
+ read_config_file(path, var, event_mask);
+ break;
+ }
+ }
+ free(path);
+ }
+ closedir(dp);
+} /* End Function do_scan_and_service */
+
+static int mksymlink(const char *oldpath, const char *newpath)
+/* [SUMMARY] Create a symlink, creating intervening directories as required.
+ <oldpath> The string contained in the symlink.
+ <newpath> The name of the new symlink.
+ [RETURNS] 0 on success, else -1.
+*/
+{
+ if (!make_dir_tree(newpath))
+ return -1;
+
+ if (symlink(oldpath, newpath) != 0) {
+ if (errno != EEXIST)
+ return -1;
+ }
+ return 0;
+} /* End Function mksymlink */
+
+
+static int make_dir_tree(const char *path)
+/* [SUMMARY] Creating intervening directories for a path as required.
+ <path> The full pathname(including the leaf node).
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ if (bb_make_directory(dirname((char *)path), -1, FILEUTILS_RECUR) == -1)
+ return FALSE;
+ return TRUE;
+} /* End Function make_dir_tree */
+
+static int expand_expression(char *output, unsigned int outsize,
+ const char *input,
+ const char *(*get_variable_func)(const char *variable, void *info),
+ void *info,
+ const char *devname,
+ const regmatch_t *ex, unsigned int numexp)
+/* [SUMMARY] Expand environment variables and regular subexpressions in string.
+ <output> The output expanded expression is written here.
+ <length> The size of the output buffer.
+ <input> The input expression. This may equal <<output>>.
+ <get_variable> A function which will be used to get variable values. If
+ this returns NULL, the environment is searched instead. If this is NULL,
+ only the environment is searched.
+ <info> An arbitrary pointer passed to <<get_variable>>.
+ <devname> Device name; specifically, this is the string that contains all
+ of the regular subexpressions.
+ <ex> Array of start / end offsets into info->devname for each subexpression
+ <numexp> Number of regular subexpressions found in <<devname>>.
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ char temp[STRING_LENGTH];
+
+ if (!st_expr_expand(temp, STRING_LENGTH, input, get_variable_func, info))
+ return FALSE;
+ expand_regexp(output, outsize, temp, devname, ex, numexp);
+ return TRUE;
+} /* End Function expand_expression */
+
+static void expand_regexp(char *output, size_t outsize, const char *input,
+ const char *devname,
+ const regmatch_t *ex, unsigned int numex)
+/* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
+ <output> The output expanded expression is written here.
+ <outsize> The size of the output buffer.
+ <input> The input expression. This may NOT equal <<output>>, because
+ supporting that would require yet another string-copy. However, it's not
+ hard to write a simple wrapper function to add this functionality for those
+ few cases that need it.
+ <devname> Device name; specifically, this is the string that contains all
+ of the regular subexpressions.
+ <ex> An array of start and end offsets into <<devname>>, one for each
+ subexpression
+ <numex> Number of subexpressions in the offset-array <<ex>>.
+ [RETURNS] Nothing.
+*/
+{
+ const char last_exp = '0' - 1 + numex;
+ int c = -1;
+
+ /* Guarantee NULL termination by writing an explicit '\0' character into
+ the very last byte */
+ if (outsize)
+ output[--outsize] = '\0';
+ /* Copy the input string into the output buffer, replacing '\\' with '\'
+ and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
+ codes are deleted */
+ while ((c != '\0') && (outsize != 0)) {
+ c = *input;
+ ++input;
+ if (c == '\\') {
+ c = *input;
+ ++input;
+ if (c != '\\') {
+ if ((c >= '0') && (c <= last_exp)) {
+ const regmatch_t *subexp = ex + (c - '0');
+ unsigned int sublen = subexp->rm_eo - subexp->rm_so;
+
+ /* Range checking */
+ if (sublen > outsize)
+ sublen = outsize;
+ strncpy(output, devname + subexp->rm_so, sublen);
+ output += sublen;
+ outsize -= sublen;
+ }
+ continue;
+ }
+ }
+ *output = c;
+ ++output;
+ --outsize;
+ } /* while */
+} /* End Function expand_regexp */
+
+
+/* from compat_name.c */
+
+struct translate_struct
+{
+ const char *match; /* The string to match to(up to length) */
+ const char *format; /* Format of output, "%s" takes data past match string,
+ NULL is effectively "%s"(just more efficient) */
+};
+
+static struct translate_struct translate_table[] =
+{
+ {"sound/", NULL},
+ {"printers/", "lp%s"},
+ {"v4l/", NULL},
+ {"parports/", "parport%s"},
+ {"fb/", "fb%s"},
+ {"netlink/", NULL},
+ {"loop/", "loop%s"},
+ {"floppy/", "fd%s"},
+ {"rd/", "ram%s"},
+ {"md/", "md%s"}, /* Meta-devices */
+ {"vc/", "tty%s"},
+ {"misc/", NULL},
+ {"isdn/", NULL},
+ {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
+ {"i2c/", "i2c-%s"},
+ {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */
+ {"tts/E", "ttyE%s"}, /* Stallion serial driver */
+ {"cua/E", "cue%s"}, /* Stallion serial driver callout */
+ {"tts/R", "ttyR%s"}, /* Rocketport serial driver */
+ {"cua/R", "cur%s"}, /* Rocketport serial driver callout */
+ {"ip2/", "ip2%s"}, /* Computone serial driver control */
+ {"tts/F", "ttyF%s"}, /* Computone serial driver */
+ {"cua/F", "cuf%s"}, /* Computone serial driver callout */
+ {"tts/C", "ttyC%s"}, /* Cyclades serial driver */
+ {"cua/C", "cub%s"}, /* Cyclades serial driver callout */
+ {"tts/", "ttyS%s"}, /* Generic serial: must be after others */
+ {"cua/", "cua%s"}, /* Generic serial: must be after others */
+ {"input/js", "js%s"}, /* Joystick driver */
+ {NULL, NULL}
+};
+
+const char *get_old_name(const char *devname, unsigned int namelen,
+ char *buffer, unsigned int major, unsigned int minor)
+/* [SUMMARY] Translate a kernel-supplied name into an old name.
+ <devname> The device name provided by the kernel.
+ <namelen> The length of the name.
+ <buffer> A buffer that may be used. This should be at least 128 bytes long.
+ <major> The major number for the device.
+ <minor> The minor number for the device.
+ [RETURNS] A pointer to the old name if known, else NULL.
+*/
+{
+ const char *compat_name = NULL;
+ const char *ptr;
+ struct translate_struct *trans;
+ unsigned int i;
+ char mode;
+ int indexx;
+ const char *pty1;
+ const char *pty2;
+ size_t len;
+ /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
+ static const char *const fmt[] = {
+ NULL ,
+ "sg%u", /* scsi/generic */
+ NULL, /* scsi/disc */
+ "sr%u", /* scsi/cd */
+ NULL, /* scsi/part */
+ "nst%u%c", /* scsi/mt */
+ "hd%c" , /* ide/host/disc */
+ "hd%c" , /* ide/host/cd */
+ "hd%c%s", /* ide/host/part */
+ "%sht%d", /* ide/host/mt */
+ "sbpcd%u", /* sbp/ */
+ "vcs%s", /* vcc/ */
+ "%cty%c%c", /* pty/ */
+ NULL
+ };
+
+ for (trans = translate_table; trans->match != NULL; ++trans) {
+ len = strlen(trans->match);
+
+ if (strncmp(devname, trans->match, len) == 0) {
+ if (trans->format == NULL)
+ return devname + len;
+ sprintf(buffer, trans->format, devname + len);
+ return buffer;
+ }
+ }
+
+ ptr = bb_basename(devname);
+ i = scan_dev_name(devname, namelen, ptr);
+
+ if (i > 0 && i < 13)
+ compat_name = buffer;
+ else
+ return NULL;
+
+ /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
+ if (i == 1 || i == 3 || i == 10)
+ sprintf(buffer, fmt[i], minor);
+
+ /* 2 ==scsi/disc, 4 == scsi/part */
+ if (i == 2 || i == 4)
+ compat_name = write_old_sd_name(buffer, major, minor,((i == 2) ? "" : (ptr + 4)));
+
+ /* 5 == scsi/mt */
+ if (i == 5) {
+ mode = ptr[2];
+ if (mode == 'n')
+ mode = '\0';
+ sprintf(buffer, fmt[i], minor & 0x1f, mode);
+ if (devname[namelen - 1] != 'n')
+ ++compat_name;
+ }
+ /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
+ if (i == 6 || i == 7 || i == 8)
+ /* last arg should be ignored for i == 6 or i== 7 */
+ sprintf(buffer, fmt[i] , get_old_ide_name(major, minor), ptr + 4);
+
+ /* 9 == ide/host/mt */
+ if (i == 9)
+ sprintf(buffer, fmt[i], ptr + 2, minor & 0x7f);
+
+ /* 11 == vcc/ */
+ if (i == 11) {
+ sprintf(buffer, fmt[i], devname + 4);
+ if (buffer[3] == '0')
+ buffer[3] = '\0';
+ }
+ /* 12 == pty/ */
+ if (i == 12) {
+ pty1 = "pqrstuvwxyzabcde";
+ pty2 = "0123456789abcdef";
+ indexx = atoi(devname + 5);
+ sprintf(buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
+ }
+ return compat_name;
+} /* End Function get_old_name */
+
+static char get_old_ide_name(unsigned int major, unsigned int minor)
+/* [SUMMARY] Get the old IDE name for a device.
+ <major> The major number for the device.
+ <minor> The minor number for the device.
+ [RETURNS] The drive letter.
+*/
+{
+ char letter = 'y'; /* 121 */
+ char c = 'a'; /* 97 */
+ int i = IDE0_MAJOR;
+
+ /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
+ do {
+ if (i == IDE0_MAJOR || i == IDE1_MAJOR || i == IDE2_MAJOR
+ || i == IDE3_MAJOR || i == IDE4_MAJOR || i == IDE5_MAJOR
+ || i == IDE6_MAJOR || i == IDE7_MAJOR || i == IDE8_MAJOR
+ || i == IDE9_MAJOR
+ ) {
+ if ((unsigned int)i == major) {
+ letter = c;
+ break;
+ }
+ c += 2;
+ }
+ i++;
+ } while (i <= IDE9_MAJOR);
+
+ if (minor > 63)
+ ++letter;
+ return letter;
+} /* End Function get_old_ide_name */
+
+static char *write_old_sd_name(char *buffer,
+ unsigned int major, unsigned int minor,
+ const char *part)
+/* [SUMMARY] Write the old SCSI disc name to a buffer.
+ <buffer> The buffer to write to.
+ <major> The major number for the device.
+ <minor> The minor number for the device.
+ <part> The partition string. Must be "" for a whole-disc entry.
+ [RETURNS] A pointer to the buffer on success, else NULL.
+*/
+{
+ unsigned int disc_index;
+
+ if (major == 8) {
+ sprintf(buffer, "sd%c%s", 'a' + (minor >> 4), part);
+ return buffer;
+ }
+ if ((major > 64) && (major < 72)) {
+ disc_index = ((major - 64) << 4) +(minor >> 4);
+ if (disc_index < 26)
+ sprintf(buffer, "sd%c%s", 'a' + disc_index, part);
+ else
+ sprintf(buffer, "sd%c%c%s", 'a' +(disc_index / 26) - 1, 'a' + disc_index % 26, part);
+ return buffer;
+ }
+ return NULL;
+} /* End Function write_old_sd_name */
+
+
+/* expression.c */
+
+/*EXPERIMENTAL_FUNCTION*/
+
+int st_expr_expand(char *output, unsigned int length, const char *input,
+ const char *(*get_variable_func)(const char *variable,
+ void *info),
+ void *info)
+/* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
+ <output> The output expanded expression is written here.
+ <length> The size of the output buffer.
+ <input> The input expression. This may equal <<output>>.
+ <get_variable> A function which will be used to get variable values. If
+ this returns NULL, the environment is searched instead. If this is NULL,
+ only the environment is searched.
+ <info> An arbitrary pointer passed to <<get_variable>>.
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ char ch;
+ unsigned int len;
+ unsigned int out_pos = 0;
+ const char *env;
+ const char *ptr;
+ struct passwd *pwent;
+ char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
+
+ if (length > BUFFER_SIZE)
+ length = BUFFER_SIZE;
+ for (; TRUE; ++input) {
+ switch (ch = *input) {
+ case '$':
+ /* Variable expansion */
+ input = expand_variable(buffer, length, &out_pos, ++input, get_variable_func, info);
+ if (input == NULL)
+ return FALSE;
+ break;
+ case '~':
+ /* Home directory expansion */
+ ch = input[1];
+ if (isspace(ch) ||(ch == '/') ||(ch == '\0')) {
+ /* User's own home directory: leave separator for next time */
+ env = getenv("HOME");
+ if (env == NULL) {
+ info_logger(LOG_INFO, bb_msg_variable_not_found, "HOME");
+ return FALSE;
+ }
+ len = strlen(env);
+ if (len + out_pos >= length)
+ goto st_expr_expand_out;
+ memcpy(buffer + out_pos, env, len + 1);
+ out_pos += len;
+ continue;
+ }
+ /* Someone else's home directory */
+ for (ptr = ++input; !isspace(ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
+ /* VOID */;
+ len = ptr - input;
+ if (len >= sizeof tmp)
+ goto st_expr_expand_out;
+ safe_memcpy(tmp, input, len);
+ input = ptr - 1;
+ pwent = getpwnam(tmp);
+ if (pwent == NULL) {
+ info_logger(LOG_INFO, "no pwent for: %s", tmp);
+ return FALSE;
+ }
+ len = strlen(pwent->pw_dir);
+ if (len + out_pos >= length)
+ goto st_expr_expand_out;
+ memcpy(buffer + out_pos, pwent->pw_dir, len + 1);
+ out_pos += len;
+ break;
+ case '\0':
+ /* Falltrough */
+ default:
+ if (out_pos >= length)
+ goto st_expr_expand_out;
+ buffer[out_pos++] = ch;
+ if (ch == '\0') {
+ memcpy(output, buffer, out_pos);
+ return TRUE;
+ }
+ break;
+ /* esac */
+ }
+ }
+ return FALSE;
+st_expr_expand_out:
+ info_logger(LOG_INFO, bb_msg_small_buffer);
+ return FALSE;
+} /* End Function st_expr_expand */
+
+
+/* Private functions follow */
+
+static const char *expand_variable(char *buffer, unsigned int length,
+ unsigned int *out_pos, const char *input,
+ const char *(*func)(const char *variable,
+ void *info),
+ void *info)
+/* [SUMMARY] Expand a variable.
+ <buffer> The buffer to write to.
+ <length> The length of the output buffer.
+ <out_pos> The current output position. This is updated.
+ <input> A pointer to the input character pointer.
+ <func> A function which will be used to get variable values. If this
+ returns NULL, the environment is searched instead. If this is NULL, only
+ the environment is searched.
+ <info> An arbitrary pointer passed to <<func>>.
+ <errfp> Diagnostic messages are written here.
+ [RETURNS] A pointer to the end of this subexpression on success, else NULL.
+*/
+{
+ char ch;
+ int len;
+ unsigned int open_braces;
+ const char *env, *ptr;
+ char tmp[STRING_LENGTH];
+
+ ch = input[0];
+ if (ch == '$') {
+ /* Special case for "$$": PID */
+ sprintf(tmp, "%d",(int) getpid());
+ len = strlen(tmp);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy(buffer + *out_pos, tmp, len + 1);
+ out_pos += len;
+ return input;
+ }
+ /* Ordinary variable expansion, possibly in braces */
+ if (ch != '{') {
+ /* Simple variable expansion */
+ for (ptr = input; isalnum(ch) || (ch == '_') || (ch == ':'); ch = *++ptr)
+ /* VOID */;
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy(tmp, input, len);
+ input = ptr - 1;
+ env = get_variable_v2(tmp, func, info);
+ if (env == NULL) {
+ info_logger(LOG_INFO, bb_msg_variable_not_found, tmp);
+ return NULL;
+ }
+ len = strlen(env);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy(buffer + *out_pos, env, len + 1);
+ *out_pos += len;
+ return input;
+ }
+ /* Variable in braces: check for ':' tricks */
+ ch = *++input;
+ for (ptr = input; isalnum(ch) || (ch == '_'); ch = *++ptr)
+ /* VOID */;
+ if (ch == '}') {
+ /* Must be simple variable expansion with "${var}" */
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy(tmp, input, len);
+ ptr = expand_variable(buffer, length, out_pos, tmp, func, info);
+ if (ptr == NULL)
+ return NULL;
+ return input + len;
+ }
+ if (ch != ':' || ptr[1] != '-') {
+ info_logger(LOG_INFO, "illegal char in var name");
+ return NULL;
+ }
+ /* It's that handy "${var:-word}" expression. Check if var is defined */
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy(tmp, input, len);
+ /* Move input pointer to ':' */
+ input = ptr;
+ /* First skip to closing brace, taking note of nested expressions */
+ ptr += 2;
+ ch = ptr[0];
+ for (open_braces = 1; open_braces > 0; ch = *++ptr) {
+ switch (ch) {
+ case '{':
+ ++open_braces;
+ break;
+ case '}':
+ --open_braces;
+ break;
+ case '\0':
+ info_logger(LOG_INFO, "\"}\" not found in: %s", input);
+ return NULL;
+ default:
+ break;
+ }
+ }
+ --ptr;
+ /* At this point ptr should point to closing brace of "${var:-word}" */
+ env = get_variable_v2(tmp, func, info);
+ if (env != NULL) {
+ /* Found environment variable, so skip the input to the closing brace
+ and return the variable */
+ input = ptr;
+ len = strlen(env);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy(buffer + *out_pos, env, len + 1);
+ *out_pos += len;
+ return input;
+ }
+ /* Environment variable was not found, so process word. Advance input
+ pointer to start of word in "${var:-word}" */
+ input += 2;
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy(tmp, input, len);
+ input = ptr;
+ if (!st_expr_expand(tmp, STRING_LENGTH, tmp, func, info))
+ return NULL;
+ len = strlen(tmp);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy(buffer + *out_pos, tmp, len + 1);
+ *out_pos += len;
+ return input;
+expand_variable_out:
+ info_logger(LOG_INFO, bb_msg_small_buffer);
+ return NULL;
+} /* End Function expand_variable */
+
+
+static const char *get_variable_v2(const char *variable,
+ const char *(*func)(const char *variable, void *info),
+ void *info)
+/* [SUMMARY] Get a variable from the environment or .
+ <variable> The variable name.
+ <func> A function which will be used to get the variable. If this returns
+ NULL, the environment is searched instead. If this is NULL, only the
+ environment is searched.
+ [RETURNS] The value of the variable on success, else NULL.
+*/
+{
+ const char *value;
+
+ if (func != NULL) {
+ value = (*func)(variable, info);
+ if (value != NULL)
+ return value;
+ }
+ return getenv(variable);
+} /* End Function get_variable */
+
+/* END OF CODE */
diff --git a/release/src/router/busybox/miscutils/devmem.c b/release/src/router/busybox/miscutils/devmem.c
new file mode 100644
index 00000000..e13dedc0
--- /dev/null
+++ b/release/src/router/busybox/miscutils/devmem.c
@@ -0,0 +1,128 @@
+/*
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Copyright (C) 2000, Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl)
+ * Copyright (C) 2008, BusyBox Team. -solar 4/26/08
+ */
+
+#include "libbb.h"
+
+int devmem_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int devmem_main(int argc UNUSED_PARAM, char **argv)
+{
+ void *map_base, *virt_addr;
+ uint64_t read_result;
+ uint64_t writeval = writeval; /* for compiler */
+ off_t target;
+ unsigned page_size = getpagesize();
+ int fd;
+ int width = 8 * sizeof(int);
+
+ /* devmem ADDRESS [WIDTH [VALUE]] */
+// TODO: options?
+// -r: read and output only the value in hex, with 0x prefix
+// -w: write only, no reads before or after, and no output
+// or make this behavior default?
+// Let's try this and see how users react.
+
+ /* ADDRESS */
+ if (!argv[1])
+ bb_show_usage();
+ errno = 0;
+ target = bb_strtoull(argv[1], NULL, 0); /* allows hex, oct etc */
+
+ /* WIDTH */
+ if (argv[2]) {
+ if (isdigit(argv[2][0]) || argv[2][1])
+ width = xatou(argv[2]);
+ else {
+ static const char bhwl[] ALIGN1 = "bhwl";
+ static const uint8_t sizes[] ALIGN1 = {
+ 8 * sizeof(char),
+ 8 * sizeof(short),
+ 8 * sizeof(int),
+ 8 * sizeof(long),
+ 0 /* bad */
+ };
+ width = strchrnul(bhwl, (argv[2][0] | 0x20)) - bhwl;
+ width = sizes[width];
+ }
+ /* VALUE */
+ if (argv[3])
+ writeval = bb_strtoull(argv[3], NULL, 0);
+ } else { /* argv[2] == NULL */
+ /* make argv[3] to be a valid thing to use */
+ argv--;
+ }
+ if (errno)
+ bb_show_usage(); /* bb_strtouXX failed */
+
+ fd = xopen("/dev/mem", argv[3] ? (O_RDWR | O_SYNC) : (O_RDONLY | O_SYNC));
+ map_base = mmap(NULL,
+ page_size * 2 /* in case value spans page */,
+ argv[3] ? (PROT_READ | PROT_WRITE) : PROT_READ,
+ MAP_SHARED,
+ fd,
+ target & ~(off_t)(page_size - 1));
+ if (map_base == MAP_FAILED)
+ bb_perror_msg_and_die("mmap");
+
+// printf("Memory mapped at address %p.\n", map_base);
+
+ virt_addr = (char*)map_base + (target & (page_size - 1));
+
+ if (!argv[3]) {
+ switch (width) {
+ case 8:
+ read_result = *(volatile uint8_t*)virt_addr;
+ break;
+ case 16:
+ read_result = *(volatile uint16_t*)virt_addr;
+ break;
+ case 32:
+ read_result = *(volatile uint32_t*)virt_addr;
+ break;
+ case 64:
+ read_result = *(volatile uint64_t*)virt_addr;
+ break;
+ default:
+ bb_error_msg_and_die("bad width");
+ }
+// printf("Value at address 0x%"OFF_FMT"X (%p): 0x%llX\n",
+// target, virt_addr,
+// (unsigned long long)read_result);
+ /* Zero-padded output shows the width of access just done */
+ printf("0x%0*llX\n", (width >> 2), (unsigned long long)read_result);
+ } else {
+ switch (width) {
+ case 8:
+ *(volatile uint8_t*)virt_addr = writeval;
+// read_result = *(volatile uint8_t*)virt_addr;
+ break;
+ case 16:
+ *(volatile uint16_t*)virt_addr = writeval;
+// read_result = *(volatile uint16_t*)virt_addr;
+ break;
+ case 32:
+ *(volatile uint32_t*)virt_addr = writeval;
+// read_result = *(volatile uint32_t*)virt_addr;
+ break;
+ case 64:
+ *(volatile uint64_t*)virt_addr = writeval;
+// read_result = *(volatile uint64_t*)virt_addr;
+ break;
+ default:
+ bb_error_msg_and_die("bad width");
+ }
+// printf("Written 0x%llX; readback 0x%llX\n",
+// (unsigned long long)writeval,
+// (unsigned long long)read_result);
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ if (munmap(map_base, page_size * 2) == -1)
+ bb_perror_msg_and_die("munmap");
+ close(fd);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/miscutils/dutmp.c b/release/src/router/busybox/miscutils/dutmp.c
deleted file mode 100644
index 86d7ce4b..00000000
--- a/release/src/router/busybox/miscutils/dutmp.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
- *
- * dutmp
- * Takes utmp formated file on stdin and dumps it's contents
- * out in colon delimited fields. Easy to 'cut' for shell based
- * versions of 'who', 'last', etc. IP Addr is output in hex,
- * little endian on x86.
- *
- */
-
-/* Mar 13, 2003 Manuel Novoa III
- *
- * 1) Added proper error checking.
- * 2) Allow '-' arg for stdin.
- * 3) For modern libcs, take into account that utmp char[] members
- * need not be nul-terminated.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <utmp.h>
-#include "busybox.h"
-
-/* Grr... utmp char[] members do not have to be nul-terminated.
- * Do what we can while still keeping this reasonably small.
- * Note: We are assuming the ut_id[] size is fixed at 4. */
-
-#if (UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256)
-#error struct utmp member char[] size(s) have changed!
-#endif
-
-extern int dutmp_main(int argc, char **argv)
-{
- int file = STDIN_FILENO;
- ssize_t n;
- struct utmp ut;
-
- if (argc > 2) {
- bb_show_usage();
- }
- ++argv;
- if ((argc == 2) && ((argv[0][0] != '-') || argv[0][1])) {
- file = bb_xopen(*argv, O_RDONLY);
- }
-
-
- while ((n = safe_read(file, (void*)&ut, sizeof(struct utmp))) != 0) {
-
- if (n != sizeof(struct utmp)) {
- bb_perror_msg_and_die("short read");
- }
-
- bb_printf("%d|%d|%.32s|%.4s|%.32s|%.256s|%d|%d|%ld|%ld|%ld|%x\n",
- ut.ut_type, ut.ut_pid, ut.ut_line,
- ut.ut_id, ut.ut_user, ut.ut_host,
- ut.ut_exit.e_termination, ut.ut_exit.e_exit,
- ut.ut_session,
- ut.ut_tv.tv_sec, ut.ut_tv.tv_usec,
- ut.ut_addr);
- }
-
- bb_fflush_stdout_and_exit(EXIT_SUCCESS);
-}
diff --git a/release/src/router/busybox/miscutils/eject.c b/release/src/router/busybox/miscutils/eject.c
new file mode 100644
index 00000000..ff3976eb
--- /dev/null
+++ b/release/src/router/busybox/miscutils/eject.c
@@ -0,0 +1,116 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * eject implementation for busybox
+ *
+ * Copyright (C) 2004 Peter Willis <psyphreak@phreaker.net>
+ * Copyright (C) 2005 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * This is a simple hack of eject based on something Erik posted in #uclibc.
+ * Most of the dirty work blatantly ripped off from cat.c =)
+ */
+
+#include "libbb.h"
+
+/* various defines swiped from linux/cdrom.h */
+#define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
+#define CDROMEJECT 0x5309 /* Ejects the cdrom media */
+#define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
+/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
+#define CDS_TRAY_OPEN 2
+
+#define dev_fd 3
+
+/* Code taken from the original eject (http://eject.sourceforge.net/),
+ * refactored it a bit for busybox (ne-bb@nicoerfurth.de) */
+
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+
+static void eject_scsi(const char *dev)
+{
+ static const char sg_commands[3][6] = {
+ { ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0 },
+ { START_STOP, 0, 0, 0, 1, 0 },
+ { START_STOP, 0, 0, 0, 2, 0 }
+ };
+
+ unsigned i;
+ unsigned char sense_buffer[32];
+ unsigned char inqBuff[2];
+ sg_io_hdr_t io_hdr;
+
+ if ((ioctl(dev_fd, SG_GET_VERSION_NUM, &i) < 0) || (i < 30000))
+ bb_error_msg_and_die("not a sg device or old sg driver");
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = 6;
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_NONE;
+ /* io_hdr.dxfer_len = 0; */
+ io_hdr.dxferp = inqBuff;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 2000;
+
+ for (i = 0; i < 3; i++) {
+ io_hdr.cmdp = (void *)sg_commands[i];
+ ioctl_or_perror_and_die(dev_fd, SG_IO, (void *)&io_hdr, "%s", dev);
+ }
+
+ /* force kernel to reread partition table when new disc is inserted */
+ ioctl(dev_fd, BLKRRPART);
+}
+
+#define FLAG_CLOSE 1
+#define FLAG_SMART 2
+#define FLAG_SCSI 4
+
+static void eject_cdrom(unsigned flags, const char *dev)
+{
+ int cmd = CDROMEJECT;
+
+ if (flags & FLAG_CLOSE
+ || (flags & FLAG_SMART && ioctl(dev_fd, CDROM_DRIVE_STATUS) == CDS_TRAY_OPEN)
+ ) {
+ cmd = CDROMCLOSETRAY;
+ }
+
+ ioctl_or_perror_and_die(dev_fd, cmd, NULL, "%s", dev);
+}
+
+int eject_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int eject_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned flags;
+ const char *device;
+
+ opt_complementary = "?1:t--T:T--t";
+ flags = getopt32(argv, "tT" USE_FEATURE_EJECT_SCSI("s"));
+ device = argv[optind] ? argv[optind] : "/dev/cdrom";
+
+ /* We used to do "umount <device>" here, but it was buggy
+ if something was mounted OVER cdrom and
+ if cdrom is mounted many times.
+
+ This works equally well (or better):
+ #!/bin/sh
+ umount /dev/cdrom
+ eject /dev/cdrom
+ */
+
+ xmove_fd(xopen(device, O_RDONLY|O_NONBLOCK), dev_fd);
+
+ if (ENABLE_FEATURE_EJECT_SCSI && (flags & FLAG_SCSI))
+ eject_scsi(device);
+ else
+ eject_cdrom(flags, device);
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(dev_fd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/miscutils/fbsplash.c b/release/src/router/busybox/miscutils/fbsplash.c
new file mode 100644
index 00000000..ec0f092d
--- /dev/null
+++ b/release/src/router/busybox/miscutils/fbsplash.c
@@ -0,0 +1,407 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2008 Michele Sanges <michele.sanges@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Usage:
+ * - use kernel option 'vga=xxx' or otherwise enable framebuffer device.
+ * - put somewhere fbsplash.cfg file and an image in .ppm format.
+ * - run applet: $ setsid fbsplash [params] &
+ * -c: hide cursor
+ * -d /dev/fbN: framebuffer device (if not /dev/fb0)
+ * -s path_to_image_file (can be "-" for stdin)
+ * -i path_to_cfg_file
+ * -f path_to_fifo (can be "-" for stdin)
+ * - if you want to run it only in presence of a kernel parameter
+ * (for example fbsplash=on), use:
+ * grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params]
+ * - commands for fifo:
+ * "NN" (ASCII decimal number) - percentage to show on progress bar.
+ * "exit" (or just close fifo) - well you guessed it.
+ */
+
+#include "libbb.h"
+#include <linux/fb.h>
+
+/* If you want logging messages on /tmp/fbsplash.log... */
+#define DEBUG 0
+
+#define BYTES_PER_PIXEL 2
+
+typedef unsigned short DATA;
+
+struct globals {
+#if DEBUG
+ bool bdebug_messages; // enable/disable logging
+ FILE *logfile_fd; // log file
+#endif
+ unsigned char *addr; // pointer to framebuffer memory
+ unsigned ns[7]; // n-parameters
+ const char *image_filename;
+ struct fb_var_screeninfo scr_var;
+ struct fb_fix_screeninfo scr_fix;
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+#define nbar_width ns[0] // progress bar width
+#define nbar_height ns[1] // progress bar height
+#define nbar_posx ns[2] // progress bar horizontal position
+#define nbar_posy ns[3] // progress bar vertical position
+#define nbar_colr ns[4] // progress bar color red component
+#define nbar_colg ns[5] // progress bar color green component
+#define nbar_colb ns[6] // progress bar color blue component
+
+#if DEBUG
+#define DEBUG_MESSAGE(strMessage, args...) \
+ if (G.bdebug_messages) { \
+ fprintf(G.logfile_fd, "[%s][%s] - %s\n", \
+ __FILE__, __FUNCTION__, strMessage); \
+ }
+#else
+#define DEBUG_MESSAGE(...) ((void)0)
+#endif
+
+
+/**
+ * Open and initialize the framebuffer device
+ * \param *strfb_device pointer to framebuffer device
+ */
+static void fb_open(const char *strfb_device)
+{
+ int fbfd = xopen(strfb_device, O_RDWR);
+
+ // framebuffer properties
+ xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var);
+ xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix);
+
+ if (G.scr_var.bits_per_pixel != 16)
+ bb_error_msg_and_die("only 16 bpp is supported");
+
+ // map the device in memory
+ G.addr = mmap(NULL,
+ G.scr_var.xres * G.scr_var.yres
+ * BYTES_PER_PIXEL /*(G.scr_var.bits_per_pixel / 8)*/ ,
+ PROT_WRITE, MAP_SHARED, fbfd, 0);
+ if (G.addr == MAP_FAILED)
+ bb_perror_msg_and_die("mmap");
+ close(fbfd);
+}
+
+
+/**
+ * Draw hollow rectangle on framebuffer
+ */
+static void fb_drawrectangle(void)
+{
+ int cnt;
+ DATA thispix;
+ DATA *ptr1, *ptr2;
+ unsigned char nred = G.nbar_colr/2;
+ unsigned char ngreen = G.nbar_colg/2;
+ unsigned char nblue = G.nbar_colb/2;
+
+ nred >>= 3; // 5-bit red
+ ngreen >>= 2; // 6-bit green
+ nblue >>= 3; // 5-bit blue
+ thispix = nblue + (ngreen << 5) + (nred << (5+6));
+
+ // horizontal lines
+ ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL);
+ ptr2 = (DATA*)(G.addr + ((G.nbar_posy + G.nbar_height - 1) * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL);
+ cnt = G.nbar_width - 1;
+ do {
+ *ptr1++ = thispix;
+ *ptr2++ = thispix;
+ } while (--cnt >= 0);
+
+ // vertical lines
+ ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL);
+ ptr2 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx + G.nbar_width - 1) * BYTES_PER_PIXEL);
+ cnt = G.nbar_height - 1 /* HUH?! G.nbar_posy + G.nbar_height - 1 - G.nbar_posy*/;
+ do {
+ *ptr1 = thispix; ptr1 += G.scr_var.xres;
+ *ptr2 = thispix; ptr2 += G.scr_var.xres;
+ } while (--cnt >= 0);
+}
+
+
+/**
+ * Draw filled rectangle on framebuffer
+ * \param nx1pos,ny1pos upper left position
+ * \param nx2pos,ny2pos down right position
+ * \param nred,ngreen,nblue rgb color
+ */
+static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos,
+ unsigned char nred, unsigned char ngreen, unsigned char nblue)
+{
+ int cnt1, cnt2, nypos;
+ DATA thispix;
+ DATA *ptr;
+
+ nred >>= 3; // 5-bit red
+ ngreen >>= 2; // 6-bit green
+ nblue >>= 3; // 5-bit blue
+ thispix = nblue + (ngreen << 5) + (nred << (5+6));
+
+ cnt1 = ny2pos - ny1pos;
+ nypos = ny1pos;
+ do {
+ ptr = (DATA*)(G.addr + (nypos * G.scr_var.xres + nx1pos) * BYTES_PER_PIXEL);
+ cnt2 = nx2pos - nx1pos;
+ do {
+ *ptr++ = thispix;
+ } while (--cnt2 >= 0);
+
+ nypos++;
+ } while (--cnt1 >= 0);
+}
+
+
+/**
+ * Draw a progress bar on framebuffer
+ * \param percent percentage of loading
+ */
+static void fb_drawprogressbar(unsigned percent)
+{
+ int i, left_x, top_y, width, height;
+
+ // outer box
+ left_x = G.nbar_posx;
+ top_y = G.nbar_posy;
+ width = G.nbar_width - 1;
+ height = G.nbar_height - 1;
+ if ((height | width) < 0)
+ return;
+ // NB: "width" of 1 actually makes rect with width of 2!
+ fb_drawrectangle();
+
+ // inner "empty" rectangle
+ left_x++;
+ top_y++;
+ width -= 2;
+ height -= 2;
+ if ((height | width) < 0)
+ return;
+ fb_drawfullrectangle(
+ left_x, top_y,
+ left_x + width, top_y + height,
+ G.nbar_colr, G.nbar_colg, G.nbar_colb);
+
+ if (percent > 0) {
+ // actual progress bar
+ width = width * percent / 100;
+ i = height;
+ if (height == 0)
+ height++; // divide by 0 is bad
+ while (i >= 0) {
+ // draw one-line thick "rectangle"
+ // top line will have gray lvl 200, bottom one 100
+ unsigned gray_level = 100 + i*100/height;
+ fb_drawfullrectangle(
+ left_x, top_y, left_x + width, top_y,
+ gray_level, gray_level, gray_level);
+ top_y++;
+ i--;
+ }
+ }
+}
+
+
+/**
+ * Draw image from PPM file
+ */
+static void fb_drawimage(void)
+{
+ char *head, *ptr;
+ FILE *theme_file;
+ unsigned char *pixline;
+ unsigned i, j, width, height, line_size;
+
+ theme_file = xfopen_stdin(G.image_filename);
+ head = xmalloc(256);
+
+ /* parse ppm header
+ * - A ppm image’s magic number is the two characters "P6".
+ * - Whitespace (blanks, TABs, CRs, LFs).
+ * - A width, formatted as ASCII characters in decimal.
+ * - Whitespace.
+ * - A height, again in ASCII decimal.
+ * - Whitespace.
+ * - The maximum color value (Maxval), again in ASCII decimal. Must be
+ * less than 65536.
+ * - Newline or other single whitespace character.
+ * - A raster of Width * Height pixels in triplets of rgb
+ * in pure binary by 1 (or not implemented 2) bytes.
+ */
+ while (1) {
+ if (fgets(head, 256, theme_file) == NULL
+ /* do not overrun the buffer */
+ || strlen(bb_common_bufsiz1) >= sizeof(bb_common_bufsiz1) - 256)
+ bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
+
+ ptr = memchr(skip_whitespace(head), '#', 256);
+ if (ptr != NULL)
+ *ptr = 0; /* ignore comments */
+ strcat(bb_common_bufsiz1, head);
+ // width, height, max_color_val
+ if (sscanf(bb_common_bufsiz1, "P6 %u %u %u", &width, &height, &i) == 3
+ && i <= 255)
+ break;
+ /* If we do not find a signature throughout the whole file then
+ we will diagnose this via EOF on read in the head of the loop. */
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(head);
+ if (width != G.scr_var.xres || height != G.scr_var.yres)
+ bb_error_msg_and_die("PPM %dx%d does not match screen %dx%d",
+ width, height, G.scr_var.xres, G.scr_var.yres);
+ line_size = width*3;
+ if (width > G.scr_var.xres)
+ width = G.scr_var.xres;
+ if (height > G.scr_var.yres)
+ height = G.scr_var.yres;
+
+ pixline = xmalloc(line_size);
+ for (j = 0; j < height; j++) {
+ unsigned char *pixel = pixline;
+ DATA *src = (DATA *)(G.addr + j * G.scr_fix.line_length);
+
+ if (fread(pixline, 1, line_size, theme_file) != line_size)
+ bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
+ for (i = 0; i < width; i++) {
+ unsigned thispix;
+ thispix = (((unsigned)pixel[0] << 8) & 0xf800)
+ | (((unsigned)pixel[1] << 3) & 0x07e0)
+ | (((unsigned)pixel[2] >> 3));
+ *src++ = thispix;
+ pixel += 3;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(pixline);
+ fclose(theme_file);
+}
+
+
+/**
+ * Parse configuration file
+ * \param *cfg_filename name of the configuration file
+ */
+static void init(const char *cfg_filename)
+{
+ static const char const param_names[] ALIGN1 =
+ "BAR_WIDTH\0" "BAR_HEIGHT\0"
+ "BAR_LEFT\0" "BAR_TOP\0"
+ "BAR_R\0" "BAR_G\0" "BAR_B\0"
+#if DEBUG
+ "DEBUG\0"
+#endif
+ ;
+ char *token[2];
+ parser_t *parser = config_open2(cfg_filename, xfopen_stdin);
+ while (config_read(parser, token, 2, 2, "#=",
+ (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
+ unsigned val = xatoi_u(token[1]);
+ int i = index_in_strings(param_names, token[0]);
+ if (i < 0)
+ bb_error_msg_and_die("syntax error: %s", token[0]);
+ if (i >= 0 && i < 7)
+ G.ns[i] = val;
+#if DEBUG
+ if (i == 7) {
+ G.bdebug_messages = val;
+ if (G.bdebug_messages)
+ G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log");
+ }
+#endif
+ }
+ config_close(parser);
+}
+
+
+int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fbsplash_main(int argc UNUSED_PARAM, char **argv)
+{
+ const char *fb_device, *cfg_filename, *fifo_filename;
+ FILE *fp = fp; // for compiler
+ char *num_buf;
+ unsigned num;
+ bool bCursorOff;
+
+ INIT_G();
+
+ // parse command line options
+ fb_device = "/dev/fb0";
+ cfg_filename = NULL;
+ fifo_filename = NULL;
+ bCursorOff = 1 & getopt32(argv, "cs:d:i:f:",
+ &G.image_filename, &fb_device, &cfg_filename, &fifo_filename);
+
+ // parse configuration file
+ if (cfg_filename)
+ init(cfg_filename);
+
+ // We must have -s IMG
+ if (!G.image_filename)
+ bb_show_usage();
+
+ fb_open(fb_device);
+
+ if (fifo_filename && bCursorOff) {
+ // hide cursor (BEFORE any fb ops)
+ full_write(STDOUT_FILENO, "\x1b" "[?25l", 6);
+ }
+
+ fb_drawimage();
+
+ if (!fifo_filename)
+ return EXIT_SUCCESS;
+
+ fp = xfopen_stdin(fifo_filename);
+ if (fp != stdin) {
+ // For named pipes, we want to support this:
+ // mkfifo cmd_pipe
+ // fbsplash -f cmd_pipe .... &
+ // ...
+ // echo 33 >cmd_pipe
+ // ...
+ // echo 66 >cmd_pipe
+ // This means that we don't want fbsplash to get EOF
+ // when last writer closes input end.
+ // The simplest way is to open fifo for writing too
+ // and become an additional writer :)
+ open(fifo_filename, O_WRONLY); // errors are ignored
+ }
+
+ fb_drawprogressbar(0);
+ // Block on read, waiting for some input.
+ // Use of <stdio.h> style I/O allows to correctly
+ // handle a case when we have many buffered lines
+ // already in the pipe
+ while ((num_buf = xmalloc_fgetline(fp)) != NULL) {
+ if (strncmp(num_buf, "exit", 4) == 0) {
+ DEBUG_MESSAGE("exit");
+ break;
+ }
+ num = atoi(num_buf);
+ if (isdigit(num_buf[0]) && (num <= 100)) {
+#if DEBUG
+ char strVal[10];
+ sprintf(strVal, "%d", num);
+ DEBUG_MESSAGE(strVal);
+#endif
+ fb_drawprogressbar(num);
+ }
+ free(num_buf);
+ }
+
+ if (bCursorOff) // restore cursor
+ full_write(STDOUT_FILENO, "\x1b" "[?25h", 6);
+
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/miscutils/fbsplash.cfg b/release/src/router/busybox/miscutils/fbsplash.cfg
new file mode 100644
index 00000000..b6cf607e
--- /dev/null
+++ b/release/src/router/busybox/miscutils/fbsplash.cfg
@@ -0,0 +1,9 @@
+# progress bar position
+BAR_LEFT=170
+BAR_TOP=300
+BAR_WIDTH=300
+BAR_HEIGHT=20
+# progress bar color
+BAR_R=80
+BAR_G=80
+BAR_B=130
diff --git a/release/src/router/busybox/miscutils/flash_eraseall.c b/release/src/router/busybox/miscutils/flash_eraseall.c
new file mode 100644
index 00000000..3e0c06ff
--- /dev/null
+++ b/release/src/router/busybox/miscutils/flash_eraseall.c
@@ -0,0 +1,194 @@
+/* vi: set sw=4 ts=4: */
+/* eraseall.c -- erase the whole of a MTD device
+ *
+ * Ported to busybox from mtd-utils.
+ *
+ * Copyright (C) 2000 Arcom Control System Ltd
+ *
+ * Renamed to flash_eraseall.c
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <mtd/mtd-user.h>
+#include <mtd/jffs2-user.h>
+
+#define OPTION_J (1 << 0)
+#define OPTION_Q (1 << 1)
+#define IS_NAND (1 << 2)
+#define BBTEST (1 << 3)
+
+struct globals {
+ /* This is used in the cpu_to_je/je_to_cpu macros in jffs2_user.h */
+ int tgt_endian;
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define target_endian (G.tgt_endian)
+#define INIT_G() do { \
+ target_endian = __BYTE_ORDER; \
+} while (0)
+
+static uint32_t crc32(uint32_t val, const void *ss, int len,
+ uint32_t *crc32_table)
+{
+ const unsigned char *s = ss;
+ while (--len >= 0)
+ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+ return val;
+}
+
+static void show_progress(mtd_info_t *meminfo, erase_info_t *erase)
+{
+ printf("\rErasing %d Kibyte @ %x -- %2llu %% complete.",
+ (unsigned)meminfo->erasesize / 1024, erase->start,
+ (unsigned long long) erase->start * 100 / meminfo->size);
+ fflush(stdout);
+}
+
+int flash_eraseall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
+{
+ struct jffs2_unknown_node cleanmarker;
+ mtd_info_t meminfo;
+ int fd, clmpos, clmlen;
+ erase_info_t erase;
+ struct stat st;
+ unsigned int flags;
+ char *mtd_name;
+
+ INIT_G();
+ opt_complementary = "=1";
+ flags = BBTEST | getopt32(argv, "jq");
+
+ mtd_name = argv[optind];
+ xstat(mtd_name, &st);
+ if (!S_ISCHR(st.st_mode))
+ bb_error_msg_and_die("%s: not a char device", mtd_name);
+
+ fd = xopen(mtd_name, O_RDWR);
+
+ xioctl(fd, MEMGETINFO, &meminfo);
+ erase.length = meminfo.erasesize;
+ if (meminfo.type == MTD_NANDFLASH)
+ flags |= IS_NAND;
+
+ clmpos = 0;
+ clmlen = 8;
+ if (flags & OPTION_J) {
+ uint32_t *crc32_table;
+
+ crc32_table = crc32_filltable(NULL, 0);
+
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+ if (!(flags & IS_NAND))
+ cleanmarker.totlen = cpu_to_je32(sizeof(struct jffs2_unknown_node));
+ else {
+ struct nand_oobinfo oobinfo;
+
+ xioctl(fd, MEMGETOOBSEL, &oobinfo);
+
+ /* Check for autoplacement */
+ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
+ /* Get the position of the free bytes */
+ clmpos = oobinfo.oobfree[0][0];
+ clmlen = oobinfo.oobfree[0][1];
+ if (clmlen > 8)
+ clmlen = 8;
+ if (clmlen == 0)
+ bb_error_msg_and_die("Autoplacement selected and no empty space in oob");
+ } else {
+ /* Legacy mode */
+ switch (meminfo.oobsize) {
+ case 8:
+ clmpos = 6;
+ clmlen = 2;
+ break;
+ case 16:
+ clmpos = 8;
+ /*clmlen = 8;*/
+ break;
+ case 64:
+ clmpos = 16;
+ /*clmlen = 8;*/
+ break;
+ }
+ }
+ cleanmarker.totlen = cpu_to_je32(8);
+ }
+
+ cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node) - 4,
+ crc32_table));
+ }
+
+ /* Don't want to destroy progress indicator by bb_error_msg's */
+ applet_name = xasprintf("\n%s: %s", applet_name, mtd_name);
+
+ for (erase.start = 0; erase.start < meminfo.size;
+ erase.start += meminfo.erasesize) {
+ if (flags & BBTEST) {
+ int ret;
+ loff_t offset = erase.start;
+
+ ret = ioctl(fd, MEMGETBADBLOCK, &offset);
+ if (ret > 0) {
+ if (!(flags & OPTION_Q))
+ bb_info_msg("\nSkipping bad block at 0x%08x", erase.start);
+ continue;
+ }
+ if (ret < 0) {
+ /* Black block table is not available on certain flash
+ * types e.g. NOR
+ */
+ if (errno == EOPNOTSUPP) {
+ flags &= ~BBTEST;
+ if (flags & IS_NAND)
+ bb_error_msg_and_die("bad block check not available");
+ } else {
+ bb_perror_msg_and_die("MEMGETBADBLOCK error");
+ }
+ }
+ }
+
+ if (!(flags & OPTION_Q))
+ show_progress(&meminfo, &erase);
+
+ xioctl(fd, MEMERASE, &erase);
+
+ /* format for JFFS2 ? */
+ if (!(flags & OPTION_J))
+ continue;
+
+ /* write cleanmarker */
+ if (flags & IS_NAND) {
+ struct mtd_oob_buf oob;
+
+ oob.ptr = (unsigned char *) &cleanmarker;
+ oob.start = erase.start + clmpos;
+ oob.length = clmlen;
+ xioctl(fd, MEMWRITEOOB, &oob);
+ } else {
+ xlseek(fd, erase.start, SEEK_SET);
+ /* if (lseek(fd, erase.start, SEEK_SET) < 0) {
+ bb_perror_msg("MTD %s failure", "seek");
+ continue;
+ } */
+ xwrite(fd, &cleanmarker, sizeof(cleanmarker));
+ /* if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) {
+ bb_perror_msg("MTD %s failure", "write");
+ continue;
+ } */
+ }
+ if (!(flags & OPTION_Q))
+ printf(" Cleanmarker written at %x.", erase.start);
+ }
+ if (!(flags & OPTION_Q)) {
+ show_progress(&meminfo, &erase);
+ bb_putchar('\n');
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/miscutils/hdparm.c b/release/src/router/busybox/miscutils/hdparm.c
index 1e34f4f4..de5d68a0 100644
--- a/release/src/router/busybox/miscutils/hdparm.c
+++ b/release/src/router/busybox/miscutils/hdparm.c
@@ -2,53 +2,18 @@
/*
* hdparm implementation for busybox
*
+ * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+ * Hacked by Tito <farmatito@tiscali.it> for size optimization.
*
- * Copyright (C) [2003] by [Matteo Croce] <pinnolo@tin.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA
- *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*
* This program is based on the source code of hdparm: see below...
* hdparm.c - Command line interface to get/set hard disk parameters
* - by Mark Lord (C) 1994-2002 -- freely distributable
*/
-
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <ctype.h>
-#include <endian.h>
-#include <sys/ioctl.h>
-#include <sys/shm.h>
-#include <sys/sysmacros.h>
-#include <sys/time.h>
-#include <sys/times.h>
-#include <sys/mount.h>
-#include "busybox.h"
-#include <linux/types.h>
-#include <linux/hdreg.h>
-#include <linux/major.h>
-#include <asm/byteorder.h>
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-#define __USE_XOPEN
-#endif
+#include "libbb.h"
+#include <linux/hdreg.h>
/* device types */
/* ------------ */
@@ -72,9 +37,9 @@
#define RW_LONG 22 /* extra bytes in R/W LONG cmd ( < ATA-4)*/
#define START_FW_REV 23 /* ASCII firmware revision */
#define LENGTH_FW_REV 4 /* 4 words (8 bytes or characters) */
-#define START_MODEL 27 /* ASCII model number */
-#define LENGTH_MODEL 20 /* 20 words (40 bytes or characters) */
-#define SECTOR_XFER_MAX 47 /* r/w multiple: max sectors xfered */
+#define START_MODEL 27 /* ASCII model number */
+#define LENGTH_MODEL 20 /* 20 words (40 bytes or characters) */
+#define SECTOR_XFER_MAX 47 /* r/w multiple: max sectors xfered */
#define DWORD_IO 48 /* can do double-word IO (ATA-1 only) */
#define CAPAB_0 49 /* capabilities */
#define CAPAB_1 50
@@ -83,7 +48,7 @@
#define WHATS_VALID 53 /* what fields are valid */
#define LCYLS_CUR 54 /* current logical cylinders */
#define LHEADS_CUR 55 /* current logical heads */
-#define LSECTS_CUR 56 /* current logical sectors/track */
+#define LSECTS_CUR 56 /* current logical sectors/track */
#define CAPACITY_LSB 57 /* current capacity in sectors */
#define CAPACITY_MSB 58
#define SECTOR_XFER_CUR 59 /* r/w multiple: current sectors xfered */
@@ -94,7 +59,7 @@
#define ADV_PIO_MODES 64 /* advanced PIO modes supported */
/* multiword DMA xfer cycle time: */
#define DMA_TIME_MIN 65 /* - minimum */
-#define DMA_TIME_NORM 66 /* - manufacturer's recommended */
+#define DMA_TIME_NORM 66 /* - manufacturer's recommended */
/* minimum PIO xfer cycle time: */
#define PIO_NO_FLOW 67 /* - without flow control */
#define PIO_FLOW 68 /* - with IORDY flow control */
@@ -116,15 +81,15 @@
#define ERASE_TIME 89 /* - ordinary */
#define ENH_ERASE_TIME 90 /* - enhanced */
#define ADV_PWR 91 /* current advanced power management level
- in low byte, 0x40 in high byte. */
-#define PSWD_CODE 92 /* master password revision code */
+ in low byte, 0x40 in high byte. */
+#define PSWD_CODE 92 /* master password revision code */
#define HWRST_RSLT 93 /* hardware reset result */
-#define ACOUSTIC 94 /* acoustic mgmt values ( >= ATA-6) */
+#define ACOUSTIC 94 /* acoustic mgmt values ( >= ATA-6) */
#define LBA_LSB 100 /* LBA: maximum. Currently only 48 */
#define LBA_MID 101 /* bits are used, but addr 103 */
#define LBA_48_MSB 102 /* has been reserved for LBA in */
#define LBA_64_MSB 103 /* the future. */
-#define RM_STAT 127 /* removable media status notification feature set support */
+#define RM_STAT 127 /* removable media status notification feature set support */
#define SECU_STATUS 128 /* security status */
#define CFA_PWR_MODE 160 /* CFA power mode 1 */
#define START_MEDIA 176 /* media serial number */
@@ -144,7 +109,7 @@
#define NOVAL_1 0xffff
/* word 0: gen_config */
-#define NOT_ATA 0x8000
+#define NOT_ATA 0x8000
#define NOT_ATAPI 0x4000 /* (check only if bit 15 == 1) */
#define MEDIA_REMOVABLE 0x0080
#define DRIVE_NOT_REMOVABLE 0x0040 /* bit obsoleted in ATA 6 */
@@ -162,71 +127,16 @@
#define CDROM 0x0005
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static const char *pkt_str[] = {
- "Direct-access device", /* word 0, bits 12-8 = 00 */
- "Sequential-access device", /* word 0, bits 12-8 = 01 */
- "Printer", /* word 0, bits 12-8 = 02 */
- "Processor", /* word 0, bits 12-8 = 03 */
- "Write-once device", /* word 0, bits 12-8 = 04 */
- "CD-ROM", /* word 0, bits 12-8 = 05 */
- "Scanner", /* word 0, bits 12-8 = 06 */
- "Optical memory", /* word 0, bits 12-8 = 07 */
- "Medium changer", /* word 0, bits 12-8 = 08 */
- "Communications device", /* word 0, bits 12-8 = 09 */
- "ACS-IT8 device", /* word 0, bits 12-8 = 0a */
- "ACS-IT8 device", /* word 0, bits 12-8 = 0b */
- "Array controller", /* word 0, bits 12-8 = 0c */
- "Enclosure services", /* word 0, bits 12-8 = 0d */
- "Reduced block command device", /* word 0, bits 12-8 = 0e */
- "Optical card reader/writer", /* word 0, bits 12-8 = 0f */
- "", /* word 0, bits 12-8 = 10 */
- "", /* word 0, bits 12-8 = 11 */
- "", /* word 0, bits 12-8 = 12 */
- "", /* word 0, bits 12-8 = 13 */
- "", /* word 0, bits 12-8 = 14 */
- "", /* word 0, bits 12-8 = 15 */
- "", /* word 0, bits 12-8 = 16 */
- "", /* word 0, bits 12-8 = 17 */
- "", /* word 0, bits 12-8 = 18 */
- "", /* word 0, bits 12-8 = 19 */
- "", /* word 0, bits 12-8 = 1a */
- "", /* word 0, bits 12-8 = 1b */
- "", /* word 0, bits 12-8 = 1c */
- "", /* word 0, bits 12-8 = 1d */
- "", /* word 0, bits 12-8 = 1e */
- "Unknown", /* word 0, bits 12-8 = 1f */
-};
-static const char *ata1_cfg_str[] = { /* word 0 in ATA-1 mode */
- "reserved", /* bit 0 */
- "hard sectored", /* bit 1 */
- "soft sectored", /* bit 2 */
- "not MFM encoded ", /* bit 3 */
- "head switch time > 15us", /* bit 4 */
- "spindle motor control option", /* bit 5 */
- "fixed drive", /* bit 6 */
- "removable drive", /* bit 7 */
- "disk xfer rate <= 5Mbs", /* bit 8 */
- "disk xfer rate > 5Mbs, <= 10Mbs", /* bit 9 */
- "disk xfer rate > 5Mbs", /* bit 10 */
- "rotational speed tol.", /* bit 11 */
- "data strobe offset option", /* bit 12 */
- "track offset option", /* bit 13 */
- "format speed tolerance gap reqd", /* bit 14 */
- "ATAPI" /* bit 14 */
-};
-#endif
-
/* word 1: number of logical cylinders */
#define LCYLS_MAX 0x3fff /* maximum allowable value */
-/* word 2: specific configureation
+/* word 2: specific configuration
* (a) require SET FEATURES to spin-up
* (b) require spin-up to fully reply to IDENTIFY DEVICE
*/
#define STBY_NID_VAL 0x37c8 /* (a) and (b) */
#define STBY_ID_VAL 0x738c /* (a) and not (b) */
-#define PWRD_NID_VAL 0x8c73 /* not (a) and (b) */
+#define PWRD_NID_VAL 0x8c73 /* not (a) and (b) */
#define PWRD_ID_VAL 0xc837 /* not (a) and not (b) */
/* words 47 & 59: sector_xfer_max & sector_xfer_cur */
@@ -234,8 +144,7 @@ static const char *ata1_cfg_str[] = { /* word 0 in ATA-1 mode */
#define MULTIPLE_SETTING_VALID 0x0100 /* 1=multiple sector setting is valid */
/* word 49: capabilities 0 */
-#define STD_STBY 0x2000 /* 1=standard values supported (ATA);
- 0=vendor specific values */
+#define STD_STBY 0x2000 /* 1=standard values supported (ATA); 0=vendor specific values */
#define IORDY_SUP 0x0800 /* 1=support; 0=may be supported */
#define IORDY_OFF 0x0400 /* 1=may be disabled */
#define LBA_SUP 0x0200 /* 1=Logical Block Address support */
@@ -252,7 +161,7 @@ static const char *ata1_cfg_str[] = { /* word 0 in ATA-1 mode */
#define MODE 0xff00 /* the mode is in the MSBs */
/* word 53: whats_valid */
-#define OK_W88 0x0004 /* the ultra_dma info is valid */
+#define OK_W88 0x0004 /* the ultra_dma info is valid */
#define OK_W64_70 0x0002 /* see above for word descriptions */
#define OK_W54_58 0x0001 /* current cyl, head, sector, cap. info valid */
@@ -272,818 +181,974 @@ static const char *ata1_cfg_str[] = { /* word 0 in ATA-1 mode */
/* NOVAL_0 or NOVAL_1 means device does not report version */
/* word 81: minor version number */
-#define MINOR_MAX 0x1C
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static const char *minor_str[] = { /* word 81 value: */
- "device does not report version", /* 0x0000 */
- "ATA-1 X3T9.2 781D prior to revision 4", /* 0x0001 */
- "ATA-1 published, ANSI X3.221-1994", /* 0x0002 */
- "ATA-1 X3T9.2 781D revision 4", /* 0x0003 */
- "ATA-2 published, ANSI X3.279-1996", /* 0x0004 */
- "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */
- "ATA-3 X3T10 2008D revision 1", /* 0x0006 */
- "ATA-2 X3T10 948D revision 2k", /* 0x0007 */
- "ATA-3 X3T10 2008D revision 0", /* 0x0008 */
- "ATA-2 X3T10 948D revision 3", /* 0x0009 */
- "ATA-3 published, ANSI X3.298-199x", /* 0x000a */
- "ATA-3 X3T10 2008D revision 6", /* 0x000b */
- "ATA-3 X3T13 2008D revision 7 and 7a", /* 0x000c */
- "ATA/ATAPI-4 X3T13 1153D revision 6", /* 0x000d */
- "ATA/ATAPI-4 T13 1153D revision 13", /* 0x000e */
- "ATA/ATAPI-4 X3T13 1153D revision 7", /* 0x000f */
- "ATA/ATAPI-4 T13 1153D revision 18", /* 0x0010 */
- "ATA/ATAPI-4 T13 1153D revision 15", /* 0x0011 */
- "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012 */
- "ATA/ATAPI-5 T13 1321D revision 3",
- "ATA/ATAPI-4 T13 1153D revision 14", /* 0x0014 */
- "ATA/ATAPI-5 T13 1321D revision 1", /* 0x0015 */
- "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016 */
- "ATA/ATAPI-4 T13 1153D revision 17", /* 0x0017 */
- "ATA/ATAPI-6 T13 1410D revision 0", /* 0x0018 */
- "ATA/ATAPI-6 T13 1410D revision 3a", /* 0x0019 */
- "Reserved", /* 0x001a */
- "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */
- "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */
- "reserved" /* 0x001d */
- "reserved" /* 0x001e */
- "reserved" /* 0x001f-0xfffe*/
-};
-#endif
-static const char actual_ver[] = {
- /* word 81 value: */
- 0, /* 0x0000 WARNING: */
- 1, /* 0x0001 WARNING: */
- 1, /* 0x0002 WARNING: */
- 1, /* 0x0003 WARNING: */
- 2, /* 0x0004 WARNING: This array */
- 2, /* 0x0005 WARNING: corresponds */
- 3, /* 0x0006 WARNING: *exactly* */
- 2, /* 0x0007 WARNING: to the ATA/ */
- 3, /* 0x0008 WARNING: ATAPI version */
- 2, /* 0x0009 WARNING: listed in */
- 3, /* 0x000a WARNING: the */
- 3, /* 0x000b WARNING: minor_str */
- 3, /* 0x000c WARNING: array */
- 4, /* 0x000d WARNING: above. */
- 4, /* 0x000e WARNING: */
- 4, /* 0x000f WARNING: if you change */
- 4, /* 0x0010 WARNING: that one, */
- 4, /* 0x0011 WARNING: change this one */
- 4, /* 0x0012 WARNING: too!!! */
- 5, /* 0x0013 WARNING: */
- 4, /* 0x0014 WARNING: */
- 5, /* 0x0015 WARNING: */
- 5, /* 0x0016 WARNING: */
- 4, /* 0x0017 WARNING: */
- 6, /* 0x0018 WARNING: */
- 6, /* 0x0019 WARNING: */
- 0, /* 0x001a WARNING: */
- 6, /* 0x001b WARNING: */
- 6, /* 0x001c WARNING: */
- 0 /* 0x001d-0xfffe */
-};
-
+#define MINOR_MAX 0x22
/* words 82-84: cmds/feats supported */
#define CMDS_W82 0x77ff /* word 82: defined command locations*/
#define CMDS_W83 0x3fff /* word 83: defined command locations*/
#define CMDS_W84 0x002f /* word 83: defined command locations*/
-#define SUPPORT_48_BIT 0x0400
+#define SUPPORT_48_BIT 0x0400
#define NUM_CMD_FEAT_STR 48
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static const char *cmd_feat_str[] = {
- "", /* word 82 bit 15: obsolete */
- "NOP cmd", /* word 82 bit 14 */
- "READ BUFFER cmd", /* word 82 bit 13 */
- "WRITE BUFFER cmd", /* word 82 bit 12 */
- "", /* word 82 bit 11: obsolete */
- "Host Protected Area feature set", /* word 82 bit 10 */
- "DEVICE RESET cmd", /* word 82 bit 9 */
- "SERVICE interrupt", /* word 82 bit 8 */
- "Release interrupt", /* word 82 bit 7 */
- "Look-ahead", /* word 82 bit 6 */
- "Write cache", /* word 82 bit 5 */
- "PACKET command feature set", /* word 82 bit 4 */
- "Power Management feature set", /* word 82 bit 3 */
- "Removable Media feature set", /* word 82 bit 2 */
- "Security Mode feature set", /* word 82 bit 1 */
- "SMART feature set", /* word 82 bit 0 */
- /* --------------*/
- "", /* word 83 bit 15: !valid bit */
- "", /* word 83 bit 14: valid bit */
- "FLUSH CACHE EXT command", /* word 83 bit 13 */
- "Mandatory FLUSH CACHE command ", /* word 83 bit 12 */
- "Device Configuration Overlay feature set ",
- "48-bit Address feature set ", /* word 83 bit 10 */
- "",
- "SET MAX security extension", /* word 83 bit 8 */
- "Address Offset Reserved Area Boot", /* word 83 bit 7 */
- "SET FEATURES subcommand required to spinup after power up",
- "Power-Up In Standby feature set", /* word 83 bit 5 */
- "Removable Media Status Notification feature set",
- "Advanced Power Management feature set",/* word 83 bit 3 */
- "CFA feature set", /* word 83 bit 2 */
- "READ/WRITE DMA QUEUED", /* word 83 bit 1 */
- "DOWNLOAD MICROCODE cmd", /* word 83 bit 0 */
- /* --------------*/
- "", /* word 84 bit 15: !valid bit */
- "", /* word 84 bit 14: valid bit */
- "", /* word 84 bit 13: reserved */
- "", /* word 84 bit 12: reserved */
- "", /* word 84 bit 11: reserved */
- "", /* word 84 bit 10: reserved */
- "", /* word 84 bit 9: reserved */
- "", /* word 84 bit 8: reserved */
- "", /* word 84 bit 7: reserved */
- "", /* word 84 bit 6: reserved */
- "General Purpose Logging feature set", /* word 84 bit 5 */
- "", /* word 84 bit 4: reserved */
- "Media Card Pass Through Command feature set ",
- "Media serial number ", /* word 84 bit 2 */
- "SMART self-test ", /* word 84 bit 1 */
- "SMART error logging " /* word 84 bit 0 */
-};
-#endif
-
-
/* words 85-87: cmds/feats enabled */
/* use cmd_feat_str[] to display what commands and features have
- * been enabled with words 85-87
+ * been enabled with words 85-87
*/
/* words 89, 90, SECU ERASE TIME */
-#define ERASE_BITS 0x00ff
+#define ERASE_BITS 0x00ff
/* word 92: master password revision */
/* NOVAL_0 or NOVAL_1 means no support for master password revision */
/* word 93: hw reset result */
-#define CBLID 0x2000 /* CBLID status */
-#define RST0 0x0001 /* 1=reset to device #0 */
-#define DEV_DET 0x0006 /* how device num determined */
-#define JUMPER_VAL 0x0002 /* device num determined by jumper */
-#define CSEL_VAL 0x0004 /* device num determined by CSEL_VAL */
+#define CBLID 0x2000 /* CBLID status */
+#define RST0 0x0001 /* 1=reset to device #0 */
+#define DEV_DET 0x0006 /* how device num determined */
+#define JUMPER_VAL 0x0002 /* device num determined by jumper */
+#define CSEL_VAL 0x0004 /* device num determined by CSEL_VAL */
/* word 127: removable media status notification feature set support */
-#define RM_STAT_BITS 0x0003
-#define RM_STAT_SUP 0x0001
-
+#define RM_STAT_BITS 0x0003
+#define RM_STAT_SUP 0x0001
+
/* word 128: security */
-#define SECU_ENABLED 0x0002
-#define SECU_LEVEL 0x0010
-#define NUM_SECU_STR 6
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static const char *secu_str[] = {
- "supported", /* word 128, bit 0 */
- "enabled", /* word 128, bit 1 */
- "locked", /* word 128, bit 2 */
- "frozen", /* word 128, bit 3 */
- "expired: security count", /* word 128, bit 4 */
- "supported: enhanced erase" /* word 128, bit 5 */
-};
-#endif
+#define SECU_ENABLED 0x0002
+#define SECU_LEVEL 0x0010
+#define NUM_SECU_STR 6
/* word 160: CFA power mode */
-#define VALID_W160 0x8000 /* 1=word valid */
-#define PWR_MODE_REQ 0x2000 /* 1=CFA power mode req'd by some cmds*/
-#define PWR_MODE_OFF 0x1000 /* 1=CFA power moded disabled */
-#define MAX_AMPS 0x0fff /* value = max current in ma */
+#define VALID_W160 0x8000 /* 1=word valid */
+#define PWR_MODE_REQ 0x2000 /* 1=CFA power mode req'd by some cmds*/
+#define PWR_MODE_OFF 0x1000 /* 1=CFA power moded disabled */
+#define MAX_AMPS 0x0fff /* value = max current in ma */
/* word 255: integrity */
-#define SIG 0x00ff /* signature location */
-#define SIG_VAL 0x00A5 /* signature value */
+#define SIG 0x00ff /* signature location */
+#define SIG_VAL 0x00a5 /* signature value */
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode) {
- uint16_t ii;
- uint8_t err_dma = 0;
- for(ii = 0; ii <= MODE_MAX; ii++) {
- if(mode_sel & 0x0001) {
- printf("*%cdma%u ",cc,ii);
- if(*have_mode) err_dma = 1;
- *have_mode = 1;
- } else if(mode_sup & 0x0001) {
- printf("%cdma%u ",cc,ii);
- }
- mode_sup >>=1; mode_sel >>=1;
+#define TIMING_BUF_MB 1
+#define TIMING_BUF_BYTES (TIMING_BUF_MB * 1024 * 1024)
+
+#undef DO_FLUSHCACHE /* under construction: force cache flush on -W0 */
+
+
+enum { fd = 3 };
+
+
+struct globals {
+ smallint get_identity, get_geom;
+ smallint do_flush;
+ smallint do_ctimings, do_timings;
+ smallint reread_partn;
+ smallint set_piomode, noisy_piomode;
+ smallint set_readahead, get_readahead;
+ smallint set_readonly, get_readonly;
+ smallint set_unmask, get_unmask;
+ smallint set_mult, get_mult;
+#ifdef HDIO_GET_QDMA
+ smallint get_dma_q;
+#ifdef HDIO_SET_QDMA
+ smallint set_dma_q;
+#endif
+#endif
+ smallint set_nowerr, get_nowerr;
+ smallint set_keep, get_keep;
+ smallint set_io32bit, get_io32bit;
+ int piomode;
+ unsigned long Xreadahead;
+ unsigned long readonly;
+ unsigned long unmask;
+ unsigned long mult;
+#ifdef HDIO_SET_QDMA
+ unsigned long dma_q;
+#endif
+ unsigned long nowerr;
+ unsigned long keep;
+ unsigned long io32bit;
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+ unsigned long dma;
+ smallint set_dma, get_dma;
+#endif
+#ifdef HDIO_DRIVE_CMD
+ smallint set_xfermode, get_xfermode;
+ smallint set_dkeep, get_dkeep;
+ smallint set_standby, get_standby;
+ smallint set_lookahead, get_lookahead;
+ smallint set_prefetch, get_prefetch;
+ smallint set_defects, get_defects;
+ smallint set_wcache, get_wcache;
+ smallint set_doorlock, get_doorlock;
+ smallint set_seagate, get_seagate;
+ smallint set_standbynow, get_standbynow;
+ smallint set_sleepnow, get_sleepnow;
+ smallint get_powermode;
+ smallint set_apmmode, get_apmmode;
+ int xfermode_requested;
+ unsigned long dkeep;
+ unsigned long standby_requested; /* 0..255 */
+ unsigned long lookahead;
+ unsigned long prefetch;
+ unsigned long defects;
+ unsigned long wcache;
+ unsigned long doorlock;
+ unsigned long apmmode;
+#endif
+ USE_FEATURE_HDPARM_GET_IDENTITY( smallint get_IDentity;)
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( smallint set_busstate, get_busstate;)
+ USE_FEATURE_HDPARM_HDIO_DRIVE_RESET( smallint perform_reset;)
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( smallint perform_tristate;)
+ USE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(smallint unregister_hwif;)
+ USE_FEATURE_HDPARM_HDIO_SCAN_HWIF( smallint scan_hwif;)
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( unsigned long busstate;)
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( unsigned long tristate;)
+ USE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(unsigned long hwif;)
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+ unsigned long hwif_data;
+ unsigned long hwif_ctrl;
+ unsigned long hwif_irq;
+#endif
+#ifdef DO_FLUSHCACHE
+ unsigned char flushcache[4] = { WIN_FLUSHCACHE, 0, 0, 0 };
+#endif
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+struct BUG_G_too_big {
+ char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+};
+#define get_identity (G.get_identity )
+#define get_geom (G.get_geom )
+#define do_flush (G.do_flush )
+#define do_ctimings (G.do_ctimings )
+#define do_timings (G.do_timings )
+#define reread_partn (G.reread_partn )
+#define set_piomode (G.set_piomode )
+#define noisy_piomode (G.noisy_piomode )
+#define set_readahead (G.set_readahead )
+#define get_readahead (G.get_readahead )
+#define set_readonly (G.set_readonly )
+#define get_readonly (G.get_readonly )
+#define set_unmask (G.set_unmask )
+#define get_unmask (G.get_unmask )
+#define set_mult (G.set_mult )
+#define get_mult (G.get_mult )
+#define set_dma_q (G.set_dma_q )
+#define get_dma_q (G.get_dma_q )
+#define set_nowerr (G.set_nowerr )
+#define get_nowerr (G.get_nowerr )
+#define set_keep (G.set_keep )
+#define get_keep (G.get_keep )
+#define set_io32bit (G.set_io32bit )
+#define get_io32bit (G.get_io32bit )
+#define piomode (G.piomode )
+#define Xreadahead (G.Xreadahead )
+#define readonly (G.readonly )
+#define unmask (G.unmask )
+#define mult (G.mult )
+#define dma_q (G.dma_q )
+#define nowerr (G.nowerr )
+#define keep (G.keep )
+#define io32bit (G.io32bit )
+#define dma (G.dma )
+#define set_dma (G.set_dma )
+#define get_dma (G.get_dma )
+#define set_xfermode (G.set_xfermode )
+#define get_xfermode (G.get_xfermode )
+#define set_dkeep (G.set_dkeep )
+#define get_dkeep (G.get_dkeep )
+#define set_standby (G.set_standby )
+#define get_standby (G.get_standby )
+#define set_lookahead (G.set_lookahead )
+#define get_lookahead (G.get_lookahead )
+#define set_prefetch (G.set_prefetch )
+#define get_prefetch (G.get_prefetch )
+#define set_defects (G.set_defects )
+#define get_defects (G.get_defects )
+#define set_wcache (G.set_wcache )
+#define get_wcache (G.get_wcache )
+#define set_doorlock (G.set_doorlock )
+#define get_doorlock (G.get_doorlock )
+#define set_seagate (G.set_seagate )
+#define get_seagate (G.get_seagate )
+#define set_standbynow (G.set_standbynow )
+#define get_standbynow (G.get_standbynow )
+#define set_sleepnow (G.set_sleepnow )
+#define get_sleepnow (G.get_sleepnow )
+#define get_powermode (G.get_powermode )
+#define set_apmmode (G.set_apmmode )
+#define get_apmmode (G.get_apmmode )
+#define xfermode_requested (G.xfermode_requested )
+#define dkeep (G.dkeep )
+#define standby_requested (G.standby_requested )
+#define lookahead (G.lookahead )
+#define prefetch (G.prefetch )
+#define defects (G.defects )
+#define wcache (G.wcache )
+#define doorlock (G.doorlock )
+#define apmmode (G.apmmode )
+#define get_IDentity (G.get_IDentity )
+#define set_busstate (G.set_busstate )
+#define get_busstate (G.get_busstate )
+#define perform_reset (G.perform_reset )
+#define perform_tristate (G.perform_tristate )
+#define unregister_hwif (G.unregister_hwif )
+#define scan_hwif (G.scan_hwif )
+#define busstate (G.busstate )
+#define tristate (G.tristate )
+#define hwif (G.hwif )
+#define hwif_data (G.hwif_data )
+#define hwif_ctrl (G.hwif_ctrl )
+#define hwif_irq (G.hwif_irq )
+
+
+/* Busybox messages and functions */
+#if ENABLE_IOCTL_HEX2STR_ERROR
+static int ioctl_alt_func(/*int fd,*/ int cmd, unsigned char *args, int alt, const char *string)
+{
+ if (!ioctl(fd, cmd, args))
+ return 0;
+ args[0] = alt;
+ return bb_ioctl_or_warn(fd, cmd, args, string);
+}
+#define ioctl_alt_or_warn(cmd,args,alt) ioctl_alt_func(cmd,args,alt,#cmd)
+#else
+static int ioctl_alt_func(/*int fd,*/ int cmd, unsigned char *args, int alt)
+{
+ if (!ioctl(fd, cmd, args))
+ return 0;
+ args[0] = alt;
+ return bb_ioctl_or_warn(fd, cmd, args);
+}
+#define ioctl_alt_or_warn(cmd,args,alt) ioctl_alt_func(cmd,args,alt)
+#endif
+
+static void on_off(int value)
+{
+ puts(value ? " (on)" : " (off)");
+}
+
+static void print_flag_on_off(int get_arg, const char *s, unsigned long arg)
+{
+ if (get_arg) {
+ printf(" setting %s to %ld", s, arg);
+ on_off(arg);
}
- return err_dma;
}
-static void print_ascii(uint16_t *p, uint8_t length) {
- uint8_t ii;
- char cl;
-
+
+static void print_value_on_off(const char *str, unsigned long argp)
+{
+ printf(" %s\t= %2ld", str, argp);
+ on_off(argp != 0);
+}
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static void print_ascii(const char *p, int length)
+{
+#if BB_BIG_ENDIAN
+#define LE_ONLY(x)
+ enum { ofs = 0 };
+#else
+#define LE_ONLY(x) x
+ /* every 16bit word is big-endian (i.e. inverted) */
+ /* accessing bytes in 1,0, 3,2, 5,4... sequence */
+ int ofs = 1;
+#endif
+
+ length *= 2;
/* find first non-space & print it */
- for(ii = 0; ii< length; ii++) {
- if(((char) 0x00ff&((*p)>>8)) != ' ') break;
- if((cl = (char) 0x00ff&(*p)) != ' ') {
- if(cl != '\0') printf("%c",cl);
- p++; ii++;
- break;
- }
+ while (length && p[ofs] != ' ') {
p++;
+ LE_ONLY(ofs = -ofs;)
+ length--;
}
- /* print the rest */
- for(; ii< length; ii++) {
- if(!(*p)) break; /* some older devices have NULLs */
- printf("%c%c",(char)0x00ff&((*p)>>8),(char)(*p)&0x00ff);
+ while (length && p[ofs]) {
+ bb_putchar(p[ofs]);
p++;
+ LE_ONLY(ofs = -ofs;)
+ length--;
}
- printf("\n");
+ bb_putchar('\n');
+#undef LE_ONLY
}
-/* identify() is the only extern function used across two source files. The
- others, though, were declared in hdparm.c with global scope; since other
- functions in that file have static (file) scope, I assume the difference is
- intentional. */
-static void identify (uint16_t *id_supplied, const char *devname)
+static void xprint_ascii(uint16_t *val, int i, const char *string, int n)
{
+ if (val[i]) {
+ printf("\t%-20s", string);
+ print_ascii((void*)&val[i], n);
+ }
+}
- char *id_file = NULL, fmt[]="/proc/ide/%s/identify";
- FILE *fl;
- uint16_t val[256], ii, jj, kk;
+static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode)
+{
+ uint16_t ii;
+ uint8_t err_dma = 0;
+
+ for (ii = 0; ii <= MODE_MAX; ii++) {
+ if (mode_sel & 0x0001) {
+ printf("*%cdma%u ", cc, ii);
+ if (*have_mode)
+ err_dma = 1;
+ *have_mode = 1;
+ } else if (mode_sup & 0x0001)
+ printf("%cdma%u ", cc, ii);
+
+ mode_sup >>= 1;
+ mode_sel >>= 1;
+ }
+ return err_dma;
+}
+
+static const char pkt_str[] ALIGN1 =
+ "Direct-access device" "\0" /* word 0, bits 12-8 = 00 */
+ "Sequential-access device" "\0" /* word 0, bits 12-8 = 01 */
+ "Printer" "\0" /* word 0, bits 12-8 = 02 */
+ "Processor" "\0" /* word 0, bits 12-8 = 03 */
+ "Write-once device" "\0" /* word 0, bits 12-8 = 04 */
+ "CD-ROM" "\0" /* word 0, bits 12-8 = 05 */
+ "Scanner" "\0" /* word 0, bits 12-8 = 06 */
+ "Optical memory" "\0" /* word 0, bits 12-8 = 07 */
+ "Medium changer" "\0" /* word 0, bits 12-8 = 08 */
+ "Communications device" "\0" /* word 0, bits 12-8 = 09 */
+ "ACS-IT8 device" "\0" /* word 0, bits 12-8 = 0a */
+ "ACS-IT8 device" "\0" /* word 0, bits 12-8 = 0b */
+ "Array controller" "\0" /* word 0, bits 12-8 = 0c */
+ "Enclosure services" "\0" /* word 0, bits 12-8 = 0d */
+ "Reduced block command device" "\0" /* word 0, bits 12-8 = 0e */
+ "Optical card reader/writer" "\0" /* word 0, bits 12-8 = 0f */
+;
+
+static const char ata1_cfg_str[] ALIGN1 = /* word 0 in ATA-1 mode */
+ "reserved" "\0" /* bit 0 */
+ "hard sectored" "\0" /* bit 1 */
+ "soft sectored" "\0" /* bit 2 */
+ "not MFM encoded " "\0" /* bit 3 */
+ "head switch time > 15us" "\0" /* bit 4 */
+ "spindle motor control option" "\0" /* bit 5 */
+ "fixed drive" "\0" /* bit 6 */
+ "removable drive" "\0" /* bit 7 */
+ "disk xfer rate <= 5Mbs" "\0" /* bit 8 */
+ "disk xfer rate > 5Mbs, <= 10Mbs" "\0" /* bit 9 */
+ "disk xfer rate > 5Mbs" "\0" /* bit 10 */
+ "rotational speed tol." "\0" /* bit 11 */
+ "data strobe offset option" "\0" /* bit 12 */
+ "track offset option" "\0" /* bit 13 */
+ "format speed tolerance gap reqd" "\0" /* bit 14 */
+ "ATAPI" /* bit 14 */
+;
+
+static const char minor_str[] ALIGN1 =
+ /* word 81 value: */
+ "Unspecified" "\0" /* 0x0000 */
+ "ATA-1 X3T9.2 781D prior to rev.4" "\0" /* 0x0001 */
+ "ATA-1 published, ANSI X3.221-1994" "\0" /* 0x0002 */
+ "ATA-1 X3T9.2 781D rev.4" "\0" /* 0x0003 */
+ "ATA-2 published, ANSI X3.279-1996" "\0" /* 0x0004 */
+ "ATA-2 X3T10 948D prior to rev.2k" "\0" /* 0x0005 */
+ "ATA-3 X3T10 2008D rev.1" "\0" /* 0x0006 */
+ "ATA-2 X3T10 948D rev.2k" "\0" /* 0x0007 */
+ "ATA-3 X3T10 2008D rev.0" "\0" /* 0x0008 */
+ "ATA-2 X3T10 948D rev.3" "\0" /* 0x0009 */
+ "ATA-3 published, ANSI X3.298-199x" "\0" /* 0x000a */
+ "ATA-3 X3T10 2008D rev.6" "\0" /* 0x000b */
+ "ATA-3 X3T13 2008D rev.7 and 7a" "\0" /* 0x000c */
+ "ATA/ATAPI-4 X3T13 1153D rev.6" "\0" /* 0x000d */
+ "ATA/ATAPI-4 T13 1153D rev.13" "\0" /* 0x000e */
+ "ATA/ATAPI-4 X3T13 1153D rev.7" "\0" /* 0x000f */
+ "ATA/ATAPI-4 T13 1153D rev.18" "\0" /* 0x0010 */
+ "ATA/ATAPI-4 T13 1153D rev.15" "\0" /* 0x0011 */
+ "ATA/ATAPI-4 published, ANSI INCITS 317-1998" "\0" /* 0x0012 */
+ "ATA/ATAPI-5 T13 1321D rev.3" "\0" /* 0x0013 */
+ "ATA/ATAPI-4 T13 1153D rev.14" "\0" /* 0x0014 */
+ "ATA/ATAPI-5 T13 1321D rev.1" "\0" /* 0x0015 */
+ "ATA/ATAPI-5 published, ANSI INCITS 340-2000" "\0" /* 0x0016 */
+ "ATA/ATAPI-4 T13 1153D rev.17" "\0" /* 0x0017 */
+ "ATA/ATAPI-6 T13 1410D rev.0" "\0" /* 0x0018 */
+ "ATA/ATAPI-6 T13 1410D rev.3a" "\0" /* 0x0019 */
+ "ATA/ATAPI-7 T13 1532D rev.1" "\0" /* 0x001a */
+ "ATA/ATAPI-6 T13 1410D rev.2" "\0" /* 0x001b */
+ "ATA/ATAPI-6 T13 1410D rev.1" "\0" /* 0x001c */
+ "ATA/ATAPI-7 published, ANSI INCITS 397-2005" "\0" /* 0x001d */
+ "ATA/ATAPI-7 T13 1532D rev.0" "\0" /* 0x001e */
+ "reserved" "\0" /* 0x001f */
+ "reserved" "\0" /* 0x0020 */
+ "ATA/ATAPI-7 T13 1532D rev.4a" "\0" /* 0x0021 */
+ "ATA/ATAPI-6 published, ANSI INCITS 361-2002" "\0" /* 0x0022 */
+ "reserved" /* 0x0023-0xfffe */
+;
+static const char actual_ver[MINOR_MAX + 2] ALIGN1 = {
+ /* word 81 value: */
+ 0, /* 0x0000 WARNING: actual_ver[] array */
+ 1, /* 0x0001 WARNING: corresponds */
+ 1, /* 0x0002 WARNING: *exactly* */
+ 1, /* 0x0003 WARNING: to the ATA/ */
+ 2, /* 0x0004 WARNING: ATAPI version */
+ 2, /* 0x0005 WARNING: listed in */
+ 3, /* 0x0006 WARNING: the */
+ 2, /* 0x0007 WARNING: minor_str */
+ 3, /* 0x0008 WARNING: array */
+ 2, /* 0x0009 WARNING: above. */
+ 3, /* 0x000a WARNING: */
+ 3, /* 0x000b WARNING: If you change */
+ 3, /* 0x000c WARNING: that one, */
+ 4, /* 0x000d WARNING: change this one */
+ 4, /* 0x000e WARNING: too!!! */
+ 4, /* 0x000f */
+ 4, /* 0x0010 */
+ 4, /* 0x0011 */
+ 4, /* 0x0012 */
+ 5, /* 0x0013 */
+ 4, /* 0x0014 */
+ 5, /* 0x0015 */
+ 5, /* 0x0016 */
+ 4, /* 0x0017 */
+ 6, /* 0x0018 */
+ 6, /* 0x0019 */
+ 7, /* 0x001a */
+ 6, /* 0x001b */
+ 6, /* 0x001c */
+ 7, /* 0x001d */
+ 7, /* 0x001e */
+ 0, /* 0x001f */
+ 0, /* 0x0020 */
+ 7, /* 0x0021 */
+ 6, /* 0x0022 */
+ 0 /* 0x0023-0xfffe */
+};
+
+static const char cmd_feat_str[] ALIGN1 =
+ "" "\0" /* word 82 bit 15: obsolete */
+ "NOP cmd" "\0" /* word 82 bit 14 */
+ "READ BUFFER cmd" "\0" /* word 82 bit 13 */
+ "WRITE BUFFER cmd" "\0" /* word 82 bit 12 */
+ "" "\0" /* word 82 bit 11: obsolete */
+ "Host Protected Area feature set" "\0" /* word 82 bit 10 */
+ "DEVICE RESET cmd" "\0" /* word 82 bit 9 */
+ "SERVICE interrupt" "\0" /* word 82 bit 8 */
+ "Release interrupt" "\0" /* word 82 bit 7 */
+ "Look-ahead" "\0" /* word 82 bit 6 */
+ "Write cache" "\0" /* word 82 bit 5 */
+ "PACKET command feature set" "\0" /* word 82 bit 4 */
+ "Power Management feature set" "\0" /* word 82 bit 3 */
+ "Removable Media feature set" "\0" /* word 82 bit 2 */
+ "Security Mode feature set" "\0" /* word 82 bit 1 */
+ "SMART feature set" "\0" /* word 82 bit 0 */
+ /* -------------- */
+ "" "\0" /* word 83 bit 15: !valid bit */
+ "" "\0" /* word 83 bit 14: valid bit */
+ "FLUSH CACHE EXT cmd" "\0" /* word 83 bit 13 */
+ "Mandatory FLUSH CACHE cmd " "\0" /* word 83 bit 12 */
+ "Device Configuration Overlay feature set " "\0"
+ "48-bit Address feature set " "\0" /* word 83 bit 10 */
+ "" "\0"
+ "SET MAX security extension" "\0" /* word 83 bit 8 */
+ "Address Offset Reserved Area Boot" "\0" /* word 83 bit 7 */
+ "SET FEATURES subcommand required to spinup after power up" "\0"
+ "Power-Up In Standby feature set" "\0" /* word 83 bit 5 */
+ "Removable Media Status Notification feature set" "\0"
+ "Adv. Power Management feature set" "\0" /* word 83 bit 3 */
+ "CFA feature set" "\0" /* word 83 bit 2 */
+ "READ/WRITE DMA QUEUED" "\0" /* word 83 bit 1 */
+ "DOWNLOAD MICROCODE cmd" "\0" /* word 83 bit 0 */
+ /* -------------- */
+ "" "\0" /* word 84 bit 15: !valid bit */
+ "" "\0" /* word 84 bit 14: valid bit */
+ "" "\0" /* word 84 bit 13: reserved */
+ "" "\0" /* word 84 bit 12: reserved */
+ "" "\0" /* word 84 bit 11: reserved */
+ "" "\0" /* word 84 bit 10: reserved */
+ "" "\0" /* word 84 bit 9: reserved */
+ "" "\0" /* word 84 bit 8: reserved */
+ "" "\0" /* word 84 bit 7: reserved */
+ "" "\0" /* word 84 bit 6: reserved */
+ "General Purpose Logging feature set" "\0" /* word 84 bit 5 */
+ "" "\0" /* word 84 bit 4: reserved */
+ "Media Card Pass Through Command feature set " "\0"
+ "Media serial number " "\0" /* word 84 bit 2 */
+ "SMART self-test " "\0" /* word 84 bit 1 */
+ "SMART error logging " /* word 84 bit 0 */
+;
+
+static const char secu_str[] ALIGN1 =
+ "supported" "\0" /* word 128, bit 0 */
+ "enabled" "\0" /* word 128, bit 1 */
+ "locked" "\0" /* word 128, bit 2 */
+ "frozen" "\0" /* word 128, bit 3 */
+ "expired: security count" "\0" /* word 128, bit 4 */
+ "supported: enhanced erase" /* word 128, bit 5 */
+;
+
+// Parse 512 byte disk identification block and print much crap.
+static void identify(uint16_t *val) NORETURN;
+static void identify(uint16_t *val)
+{
+ uint16_t ii, jj, kk;
uint16_t like_std = 1, std = 0, min_std = 0xffff;
uint16_t dev = NO_DEV, eqpt = NO_DEV;
uint8_t have_mode = 0, err_dma = 0;
uint8_t chksum = 0;
uint32_t ll, mm, nn, oo;
- __u64 bbbig; /* (:) */
-
- if (id_supplied) {
-#if __BYTE_ORDER == __BIG_ENDIAN
- swab(id_supplied, val, sizeof(val));
-#else
- memcpy(val, id_supplied, sizeof(val));
+ uint64_t bbbig; /* (:) */
+ const char *strng;
+#if BB_BIG_ENDIAN
+ uint16_t buf[256];
+
+ // Adjust for endianness
+ swab(val, buf, sizeof(buf));
+ val = buf;
#endif
- } else {
- id_file = xcalloc(1, strlen(devname)+1+strlen(fmt));
- sprintf(id_file, fmt, devname);
-
- /* open the file, read in all the info and close it */
- if (id_file == NULL) {
- fl = stdin;
- } else if(NULL == (fl = fopen(id_file, "r")))
- bb_perror_msg_and_die(id_file);
- /* calculate checksum over all bytes */
- for(ii = GEN_CONFIG; ii<=INTEGRITY; ii++) {
- unsigned int scratch;
- if(1 != fscanf(fl,"%04x",&scratch)) break;
- val[ii] = (uint16_t)scratch;
- chksum += val[ii] + (val[ii] >> 8);
- }
- fclose(fl);
- if(ii < (INTEGRITY+1))
- bb_error_msg_and_die("Input file wrong format or length");
- }
- chksum &= 0xff;
-
/* check if we recognise the device type */
- printf("\n");
- if(!(val[GEN_CONFIG] & NOT_ATA)) {
+ bb_putchar('\n');
+ if (!(val[GEN_CONFIG] & NOT_ATA)) {
dev = ATA_DEV;
printf("ATA device, with ");
- } else if(val[GEN_CONFIG]==CFA_SUPPORT_VAL) {
+ } else if (val[GEN_CONFIG]==CFA_SUPPORT_VAL) {
dev = ATA_DEV;
like_std = 4;
printf("CompactFlash ATA device, with ");
- } else if(!(val[GEN_CONFIG] & NOT_ATAPI)) {
+ } else if (!(val[GEN_CONFIG] & NOT_ATAPI)) {
dev = ATAPI_DEV;
eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT;
- printf("ATAPI %s, with ", pkt_str[eqpt]);
+ printf("ATAPI %s, with ", eqpt <= 0xf ? nth_string(pkt_str, eqpt) : "unknown");
like_std = 3;
- } else {
- bb_error_msg_and_die("Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n");
- }
- if(!(val[GEN_CONFIG] & MEDIA_REMOVABLE))
- printf("non-");
- printf("removable media\n");
-
+ } else
+ /* "Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n" */
+ bb_error_msg_and_die("unknown device type");
+ printf("%sremovable media\n", !(val[GEN_CONFIG] & MEDIA_REMOVABLE) ? "non-" : "");
/* Info from the specific configuration word says whether or not the
* ID command completed correctly. It is only defined, however in
- * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior
+ * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior
* standards. Since the values allowed for this word are extremely
* specific, it should be safe to check it now, even though we don't
* know yet what standard this device is using.
*/
- if((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL) ||
- (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL) ) {
- like_std = 5;
- if((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL))
+ if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)
+ || (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL)
+ ) {
+ like_std = 5;
+ if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL))
printf("powers-up in standby; SET FEATURES subcmd spins-up.\n");
- if(((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) &&
- (val[GEN_CONFIG] & INCOMPLETE))
- printf("\n\tWARNING: ID response incomplete.\n\tWARNING: Following data may be incorrect.\n\n");
+ if (((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE))
+ printf("\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n");
}
/* output the model and serial numbers and the fw revision */
- if(val[START_MODEL]) {
- printf("\t%-20s","Model Number:");
- print_ascii(&val[START_MODEL], LENGTH_MODEL);
- }
- if(val[START_SERIAL]) {
- printf("\t%-20s","Serial Number:");
- print_ascii( &val[START_SERIAL], LENGTH_SERIAL);
- }
- if(val[START_FW_REV]) {
- printf("\t%-20s","Firmware Revision:");
- print_ascii(&val[START_FW_REV], LENGTH_FW_REV);
- }
- if(val[START_MEDIA]) {
- printf("\t%-20s","Media Serial Num:");
- print_ascii(&val[START_MEDIA], LENGTH_MEDIA);
- }
- if(val[START_MANUF]) {
- printf("\t%-20s","Media Manufacturer:");
- print_ascii(&val[START_MANUF], LENGTH_MANUF);
- }
+ xprint_ascii(val, START_MODEL, "Model Number:", LENGTH_MODEL);
+ xprint_ascii(val, START_SERIAL, "Serial Number:", LENGTH_SERIAL);
+ xprint_ascii(val, START_FW_REV, "Firmware Revision:", LENGTH_FW_REV);
+ xprint_ascii(val, START_MEDIA, "Media Serial Num:", LENGTH_MEDIA);
+ xprint_ascii(val, START_MANUF, "Media Manufacturer:", LENGTH_MANUF);
/* major & minor standards version number (Note: these words were not
* defined until ATA-3 & the CDROM std uses different words.) */
printf("Standards:");
- if(eqpt != CDROM) {
- if(val[MINOR] && (val[MINOR] <= MINOR_MAX)){
- if(like_std < 3) like_std = 3;
+ if (eqpt != CDROM) {
+ if (val[MINOR] && (val[MINOR] <= MINOR_MAX)) {
+ if (like_std < 3) like_std = 3;
std = actual_ver[val[MINOR]];
- if(std) {
- printf("\n\tUsed: ");
- printf("%s ",minor_str[val[MINOR]]);
- }
+ if (std) printf("\n\tUsed: %s ", nth_string(minor_str, val[MINOR]));
+
}
/* looks like when they up-issue the std, they obsolete one;
- * thus, only the newest 4 issues need be supported. (That's
+ * thus, only the newest 4 issues need be supported. (That's
* what "kk" and "min_std" are all about.) */
- if(val[MAJOR] && (val[MAJOR] !=NOVAL_1)) {
+ if (val[MAJOR] && (val[MAJOR] != NOVAL_1)) {
printf("\n\tSupported: ");
jj = val[MAJOR] << 1;
kk = like_std >4 ? like_std-4: 0;
- for(ii = 14; (ii >0)&&(ii>kk); ii--) {
- if(jj & 0x8000) {
+ for (ii = 14; (ii >0)&&(ii>kk); ii--) {
+ if (jj & 0x8000) {
printf("%u ", ii);
- if(like_std < ii) {
+ if (like_std < ii) {
like_std = ii;
kk = like_std >4 ? like_std-4: 0;
}
- if(min_std > ii) min_std = ii;
+ if (min_std > ii) min_std = ii;
}
jj <<= 1;
}
- if(like_std < 3) like_std = 3;
+ if (like_std < 3) like_std = 3;
}
/* Figure out what standard the device is using if it hasn't told
* us. If we know the std, check if the device is using any of
* the words from the next level up. It happens.
*/
- if(like_std < std) like_std = std;
- if(((std == 5) || (!std && (like_std < 6))) &&
- ( (((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
- ((val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) ||
- (((val[CMDS_SUPP_2] & VALID) == VALID_VAL) &&
- (val[CMDS_SUPP_2] & CMDS_W84) ) ) ) {
+ if (like_std < std) like_std = std;
+
+ if (((std == 5) || (!std && (like_std < 6))) &&
+ ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+ (( val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) ||
+ ((( val[CMDS_SUPP_2] & VALID) == VALID_VAL) &&
+ ( val[CMDS_SUPP_2] & CMDS_W84) ) )
+ ) {
like_std = 6;
- } else if(((std == 4) || (!std && (like_std < 5))) &&
- ((((val[INTEGRITY] & SIG) == SIG_VAL) && !chksum) ||
- ((val[HWRST_RSLT] & VALID) == VALID_VAL) ||
- (((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
- ((val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) ) {
+ } else if (((std == 4) || (!std && (like_std < 5))) &&
+ ((((val[INTEGRITY] & SIG) == SIG_VAL) && !chksum) ||
+ (( val[HWRST_RSLT] & VALID) == VALID_VAL) ||
+ ((( val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+ (( val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) )
+ {
like_std = 5;
- } else if(((std == 3) || (!std && (like_std < 4))) &&
- ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
- (((val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) ||
- ((val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) ||
- ((val[CAPAB_1] & VALID) == VALID_VAL) ||
- ((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) ||
- ((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) ) ) {
+ } else if (((std == 3) || (!std && (like_std < 4))) &&
+ ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+ ((( val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) ||
+ (( val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) ||
+ (( val[CAPAB_1] & VALID) == VALID_VAL) ||
+ (( val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) ||
+ (( val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) )
+ ) {
like_std = 4;
- } else if(((std == 2) || (!std && (like_std < 3))) &&
- ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) ) {
+ } else if (((std == 2) || (!std && (like_std < 3)))
+ && ((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
+ ) {
like_std = 3;
- } else if(((std == 1) || (!std && (like_std < 2))) &&
- ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) ||
- (val[WHATS_VALID] & OK_W64_70)) ) {
+ } else if (((std == 1) || (!std && (like_std < 2))) &&
+ ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) ||
+ (val[WHATS_VALID] & OK_W64_70)) )
+ {
like_std = 2;
}
- if(!std) {
- printf("\n\tLikely used: %u\n",like_std);
- } else if(like_std > std) {
- printf("& some of %u\n",like_std);
- } else printf("\n");
+
+ if (!std)
+ printf("\n\tLikely used: %u\n", like_std);
+ else if (like_std > std)
+ printf("& some of %u\n", like_std);
+ else
+ bb_putchar('\n');
} else {
/* TBD: do CDROM stuff more thoroughly. For now... */
- kk = 0;
- if(val[CDR_MINOR] == 9) {
+ kk = 0;
+ if (val[CDR_MINOR] == 9) {
kk = 1;
printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5");
}
- if(val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1)) {
+ if (val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1)) {
kk = 1;
printf("\n\tSupported: CD-ROM ATAPI");
jj = val[CDR_MAJOR] >> 1;
- for(ii = 1; ii <15; ii++) {
- if(jj & 0x0001) {
- printf("-%u ", ii);
- }
+ for (ii = 1; ii < 15; ii++) {
+ if (jj & 0x0001) printf("-%u ", ii);
jj >>= 1;
}
}
- if(!kk) printf("\n\tLikely used CD-ROM ATAPI-1\n");
- else printf("\n");
+ puts(kk ? "" : "\n\tLikely used CD-ROM ATAPI-1");
/* the cdrom stuff is more like ATA-2 than anything else, so: */
like_std = 2;
}
- if(min_std == 0xffff) min_std = like_std > 4 ? like_std - 3 : 1;
+ if (min_std == 0xffff)
+ min_std = like_std > 4 ? like_std - 3 : 1;
printf("Configuration:\n");
/* more info from the general configuration word */
- if((eqpt != CDROM) && (like_std == 1)) {
+ if ((eqpt != CDROM) && (like_std == 1)) {
jj = val[GEN_CONFIG] >> 1;
- for(ii = 1; ii < 15; ii++) {
- if(jj & 0x0001) printf("\t%s\n",ata1_cfg_str[ii]);
+ for (ii = 1; ii < 15; ii++) {
+ if (jj & 0x0001)
+ printf("\t%s\n", nth_string(ata1_cfg_str, ii));
jj >>=1;
}
}
- if(dev == ATAPI_DEV) {
- printf("\tDRQ response: "); /* Data Request (DRQ) */
- switch(val[GEN_CONFIG] & DRQ_RESPONSE_TIME) {
- case DRQ_3MS_VAL : printf("3ms.\n"); break;
- case DRQ_INTR_VAL : printf("<=10ms with INTRQ\n"); break;
- case DRQ_50US_VAL : printf("50us.\n"); break;
- default : printf("unknown.\n"); break;
- }
- printf("\tPacket size: ");
- switch(val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) {
- case PKT_SIZE_12_VAL : printf("12 bytes\n"); break;
- case PKT_SIZE_16_VAL : printf("16 bytes\n"); break;
- default : printf("Unknown\n"); break;
- }
+ if (dev == ATAPI_DEV) {
+ if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_3MS_VAL)
+ strng = "3ms";
+ else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_INTR_VAL)
+ strng = "<=10ms with INTRQ";
+ else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_50US_VAL)
+ strng ="50us";
+ else
+ strng = "unknown";
+ printf("\tDRQ response: %s\n\tPacket size: ", strng); /* Data Request (DRQ) */
+
+ if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_12_VAL)
+ strng = "12 bytes";
+ else if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_16_VAL)
+ strng = "16 bytes";
+ else
+ strng = "unknown";
+ puts(strng);
} else {
- /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */
+ /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */
ll = (uint32_t)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB];
mm = 0; bbbig = 0;
- if ( (ll > 0x00FBFC10) && (!val[LCYLS])) {
- printf("\tCHS addressing not supported\n");
- } else {
+ if ((ll > 0x00FBFC10) && (!val[LCYLS]))
+ printf("\tCHS addressing not supported\n");
+ else {
jj = val[WHATS_VALID] & OK_W54_58;
- printf("\tLogical\t\tmax\tcurrent\n");
- printf("\tcylinders\t%u\t%u\n",val[LCYLS],jj?val[LCYLS_CUR]:0);
- printf("\theads\t\t%u\t%u\n",val[LHEADS],jj?val[LHEADS_CUR]:0);
- printf("\tsectors/track\t%u\t%u\n",val[LSECTS],jj?val[LSECTS_CUR]:0);
- printf("\t--\n");
- if((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES])) {
- printf("\tbytes/track: %u",val[TRACK_BYTES]);
- printf("\tbytes/sector: %u\n",val[SECT_BYTES]);
- }
- if(jj) {
+ printf("\tLogical\t\tmax\tcurrent\n\tcylinders\t%u\t%u\n\theads\t\t%u\t%u\n\tsectors/track\t%u\t%u\n\t--\n",
+ val[LCYLS],jj?val[LCYLS_CUR]:0, val[LHEADS],jj?val[LHEADS_CUR]:0, val[LSECTS],jj?val[LSECTS_CUR]:0);
+
+ if ((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES]))
+ printf("\tbytes/track: %u\tbytes/sector: %u\n", val[TRACK_BYTES], val[SECT_BYTES]);
+
+ if (jj) {
mm = (uint32_t)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB];
- if(like_std < 3) { /* check Endian of capacity bytes */
+ if (like_std < 3) {
+ /* check Endian of capacity bytes */
nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR];
oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB];
- if(abs(mm - nn) > abs(oo - nn))
+ if (abs(mm - nn) > abs(oo - nn))
mm = oo;
}
- printf("\tCHS current addressable sectors:%11u\n",mm);
- }
+ printf("\tCHS current addressable sectors:%11u\n", mm);
+ }
}
/* LBA addressing */
- printf("\tLBA user addressable sectors:%11u\n",ll);
- if( ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
- (val[CMDS_SUPP_1] & SUPPORT_48_BIT) ) {
- bbbig = (__u64)val[LBA_64_MSB] << 48 |
- (__u64)val[LBA_48_MSB] << 32 |
- (__u64)val[LBA_MID] << 16 |
- val[LBA_LSB] ;
- printf("\tLBA48 user addressable sectors:%11llu\n",bbbig);
- }
- if (!bbbig) bbbig = (__u64)(ll>mm ? ll : mm); /* # 512 byte blocks */
- printf("\tdevice size with M = 1024*1024: %11llu MBytes\n",bbbig>>11);
- bbbig = (bbbig<<9)/1000000;
- printf("\tdevice size with M = 1000*1000: %11llu MBytes ",bbbig);
- if(bbbig > 1000) printf("(%llu GB)\n",bbbig/1000);
- else printf("\n");
-
+ printf("\tLBA user addressable sectors:%11u\n", ll);
+ if (((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
+ && (val[CMDS_SUPP_1] & SUPPORT_48_BIT)
+ ) {
+ bbbig = (uint64_t)val[LBA_64_MSB] << 48 |
+ (uint64_t)val[LBA_48_MSB] << 32 |
+ (uint64_t)val[LBA_MID] << 16 |
+ val[LBA_LSB];
+ printf("\tLBA48 user addressable sectors:%11"PRIu64"\n", bbbig);
+ }
+
+ if (!bbbig)
+ bbbig = (uint64_t)(ll>mm ? ll : mm); /* # 512 byte blocks */
+ printf("\tdevice size with M = 1024*1024: %11"PRIu64" MBytes\n", bbbig>>11);
+ bbbig = (bbbig << 9) / 1000000;
+ printf("\tdevice size with M = 1000*1000: %11"PRIu64" MBytes ", bbbig);
+
+ if (bbbig > 1000)
+ printf("(%"PRIu64" GB)\n", bbbig/1000);
+ else
+ bb_putchar('\n');
}
/* hw support of commands (capabilities) */
- printf("Capabilities:\n");
- printf("\t");
- if(dev == ATAPI_DEV) {
- if(eqpt != CDROM) {
- if(val[CAPAB_0] & CMD_Q_SUP) printf("Cmd queuing, ");
- }
- if(val[CAPAB_0] & OVLP_SUP) printf("Cmd overlap, ");
- }
- if(val[CAPAB_0] & LBA_SUP) printf("LBA, ");
- if(like_std != 1) {
- printf("IORDY");
- if(!(val[CAPAB_0] & IORDY_SUP)) printf("(may be)");
- if(val[CAPAB_0] & IORDY_OFF) printf("(can");
- else printf("(cannot");
- printf(" be disabled)");
- } else printf("no IORDY");
- printf("\n");
- if((like_std == 1) && val[BUF_TYPE]) {
- kk = val[BUF_TYPE];
- printf("\tBuffer type: %04x: ",kk);
- if (kk < 2) printf("single port, single-sector");
- else printf("dual port, multi-sector");
- if (kk > 2) printf(" with read caching ability");
- printf("\n");
- }
- jj = 0;
- if((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1))) {
- printf("\tBuffer size: %.1fkB",(float)val[BUFFER__SIZE]/2);
- jj = 1;
- }
- if((min_std < 4) && (val[RW_LONG])) {
- printf("\tbytes avail on r/w long: %u",val[RW_LONG]);
- jj = 1;
- }
- if((eqpt != CDROM) && (like_std > 3)) {
- printf("\tQueue depth: %u",(val[QUEUE_DEPTH] & DEPTH_BITS)+1);
- jj = 1;
- }
- if(jj) printf("\n");
- if(dev == ATA_DEV) {
- if(like_std == 1) {
- printf("\tCan");
- if(!val[DWORD_IO]) printf("not");
- printf(" perform double-word IO\n");
- } else {
- printf("\tStandby timer values: spec'd by ");
- if(val[CAPAB_0] & STD_STBY) printf("Standard");
- else printf("Vendor");
- if((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL)) {
- if(val[CAPAB_1] & MIN_STANDBY_TIMER) printf(", with ");
- else printf(", no ");
- printf("device specific minimum\n");
- } else printf("\n");
+ printf("Capabilities:\n\t");
+
+ if (dev == ATAPI_DEV) {
+ if (eqpt != CDROM && (val[CAPAB_0] & CMD_Q_SUP)) printf("Cmd queuing, ");
+ if (val[CAPAB_0] & OVLP_SUP) printf("Cmd overlap, ");
+ }
+ if (val[CAPAB_0] & LBA_SUP) printf("LBA, ");
+
+ if (like_std != 1) {
+ printf("IORDY%s(can%s be disabled)\n",
+ !(val[CAPAB_0] & IORDY_SUP) ? "(may be)" : "",
+ (val[CAPAB_0] & IORDY_OFF) ? "" :"not");
+ } else
+ printf("no IORDY\n");
+
+ if ((like_std == 1) && val[BUF_TYPE]) {
+ printf("\tBuffer type: %04x: %s%s\n", val[BUF_TYPE],
+ (val[BUF_TYPE] < 2) ? "single port, single-sector" : "dual port, multi-sector",
+ (val[BUF_TYPE] > 2) ? " with read caching ability" : "");
+ }
+
+ if ((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1))) {
+ printf("\tBuffer size: %.1fkB\n", (float)val[BUFFER__SIZE]/2);
+ }
+ if ((min_std < 4) && (val[RW_LONG])) {
+ printf("\tbytes avail on r/w long: %u\n", val[RW_LONG]);
+ }
+ if ((eqpt != CDROM) && (like_std > 3)) {
+ printf("\tQueue depth: %u\n", (val[QUEUE_DEPTH] & DEPTH_BITS) + 1);
+ }
+
+ if (dev == ATA_DEV) {
+ if (like_std == 1)
+ printf("\tCan%s perform double-word IO\n", (!val[DWORD_IO]) ? "not" : "");
+ else {
+ printf("\tStandby timer values: spec'd by %s", (val[CAPAB_0] & STD_STBY) ? "Standard" : "Vendor");
+ if ((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL))
+ printf(", %s device specific minimum\n", (val[CAPAB_1] & MIN_STANDBY_TIMER) ? "with" : "no");
+ else
+ bb_putchar('\n');
}
printf("\tR/W multiple sector transfer: ");
- if((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER)) {
+ if ((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER))
printf("not supported\n");
- } else {
- printf("Max = %u\t",val[SECTOR_XFER_MAX] & SECTOR_XFER);
- printf("Current = ");
- if(val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID)
- printf("%u\n",val[SECTOR_XFER_CUR] & SECTOR_XFER);
- else printf("?\n");
+ else {
+ printf("Max = %u\tCurrent = ", val[SECTOR_XFER_MAX] & SECTOR_XFER);
+ if (val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID)
+ printf("%u\n", val[SECTOR_XFER_CUR] & SECTOR_XFER);
+ else
+ printf("?\n");
}
- if((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) {
+ if ((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) {
/* We print out elsewhere whether the APM feature is enabled or
not. If it's not enabled, let's not repeat the info; just print
nothing here. */
- printf("\tAdvanced power management level: ");
- if ( (val[ADV_PWR] & 0xFF00) == 0x4000 ) {
+ printf("\tAdvancedPM level: ");
+ if ((val[ADV_PWR] & 0xFF00) == 0x4000) {
uint8_t apm_level = val[ADV_PWR] & 0x00FF;
-
printf("%u (0x%x)\n", apm_level, apm_level);
- } else {
- printf("unknown setting (0x%04x)\n", val[ADV_PWR]);
- }
- }
- if(like_std > 5) {
- if(val[ACOUSTIC]) {
- printf("\tRecommended acoustic management value: %u, current value: %u\n", (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff);
}
+ else
+ printf("unknown setting (0x%04x)\n", val[ADV_PWR]);
}
- } else { /* ATAPI */
- if(eqpt != CDROM) {
- if(val[CAPAB_0] & SWRST_REQ) printf("\tATA sw reset required\n");
+ if (like_std > 5 && val[ACOUSTIC]) {
+ printf("\tRecommended acoustic management value: %u, current value: %u\n",
+ (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff);
}
- if(val[PKT_REL] || val[SVC_NBSY]) {
+ } else {
+ /* ATAPI */
+ if (eqpt != CDROM && (val[CAPAB_0] & SWRST_REQ))
+ printf("\tATA sw reset required\n");
+
+ if (val[PKT_REL] || val[SVC_NBSY]) {
printf("\tOverlap support:");
- if(val[PKT_REL]) printf(" %uus to release bus.",val[PKT_REL]);
- if(val[SVC_NBSY]) printf(" %uus to clear BSY after SERVICE cmd.",val[SVC_NBSY]);
- printf("\n");
+ if (val[PKT_REL]) printf(" %uus to release bus.", val[PKT_REL]);
+ if (val[SVC_NBSY]) printf(" %uus to clear BSY after SERVICE cmd.", val[SVC_NBSY]);
+ bb_putchar('\n');
}
}
/* DMA stuff. Check that only one DMA mode is selected. */
printf("\tDMA: ");
- if(!(val[CAPAB_0] & DMA_SUP)) {
+ if (!(val[CAPAB_0] & DMA_SUP))
printf("not supported\n");
- } else {
- if(val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA])
- printf(" sdma%u\n",(val[DMA_MODE] & MODE) >> 8);
- if(val[SINGLE_DMA]) {
- jj = val[SINGLE_DMA]; kk = val[SINGLE_DMA] >> 8;
- err_dma += mode_loop(jj,kk,'s',&have_mode);
+ else {
+ if (val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA])
+ printf(" sdma%u\n", (val[DMA_MODE] & MODE) >> 8);
+ if (val[SINGLE_DMA]) {
+ jj = val[SINGLE_DMA];
+ kk = val[SINGLE_DMA] >> 8;
+ err_dma += mode_loop(jj, kk, 's', &have_mode);
}
- if(val[MULTI_DMA]) {
- jj = val[MULTI_DMA]; kk = val[MULTI_DMA] >> 8;
- err_dma += mode_loop(jj,kk,'m',&have_mode);
+ if (val[MULTI_DMA]) {
+ jj = val[MULTI_DMA];
+ kk = val[MULTI_DMA] >> 8;
+ err_dma += mode_loop(jj, kk, 'm', &have_mode);
}
- if((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) {
- jj = val[ULTRA_DMA]; kk = val[ULTRA_DMA] >> 8;
- err_dma += mode_loop(jj,kk,'u',&have_mode);
+ if ((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) {
+ jj = val[ULTRA_DMA];
+ kk = val[ULTRA_DMA] >> 8;
+ err_dma += mode_loop(jj, kk, 'u', &have_mode);
}
- if(err_dma || !have_mode) printf("(?)");
- printf("\n");
-
- if((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP))
- printf("\t Interleaved DMA support\n");
-
- if((val[WHATS_VALID] & OK_W64_70) &&
- (val[DMA_TIME_MIN] || val[DMA_TIME_NORM])) {
- printf("\t Cycle time:");
- if(val[DMA_TIME_MIN])
- printf(" min=%uns",val[DMA_TIME_MIN]);
- if(val[DMA_TIME_NORM])
- printf(" recommended=%uns",val[DMA_TIME_NORM]);
- printf("\n");
+ if (err_dma || !have_mode) printf("(?)");
+ bb_putchar('\n');
+
+ if ((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP))
+ printf("\t\tInterleaved DMA support\n");
+
+ if ((val[WHATS_VALID] & OK_W64_70)
+ && (val[DMA_TIME_MIN] || val[DMA_TIME_NORM])
+ ) {
+ printf("\t\tCycle time:");
+ if (val[DMA_TIME_MIN]) printf(" min=%uns", val[DMA_TIME_MIN]);
+ if (val[DMA_TIME_NORM]) printf(" recommended=%uns", val[DMA_TIME_NORM]);
+ bb_putchar('\n');
}
}
/* Programmed IO stuff */
printf("\tPIO: ");
- /* If a drive supports mode n (e.g. 3), it also supports all modes less
+ /* If a drive supports mode n (e.g. 3), it also supports all modes less
* than n (e.g. 3, 2, 1 and 0). Print all the modes. */
- if((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP)) {
+ if ((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP)) {
jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007;
- for(ii = 0; ii <= PIO_MODE_MAX ; ii++) {
- if(jj & 0x0001)
- printf("pio%d ",ii);
+ for (ii = 0; ii <= PIO_MODE_MAX; ii++) {
+ if (jj & 0x0001) printf("pio%d ", ii);
jj >>=1;
}
- printf("\n");
- } else if(((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE) ) {
- for(ii = 0; ii <= val[PIO_MODE]>>8; ii++) {
- printf("pio%d ",ii);
- }
- printf("\n");
- } else printf("unknown\n");
- if(val[WHATS_VALID] & OK_W64_70) {
- if(val[PIO_NO_FLOW] || val[PIO_FLOW]) {
- printf("\t Cycle time:");
- if(val[PIO_NO_FLOW])
- printf(" no flow control=%uns", val[PIO_NO_FLOW]);
- if(val[PIO_FLOW])
- printf(" IORDY flow control=%uns", val[PIO_FLOW]);
- printf("\n");
+ bb_putchar('\n');
+ } else if (((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE)) {
+ for (ii = 0; ii <= val[PIO_MODE]>>8; ii++)
+ printf("pio%d ", ii);
+ bb_putchar('\n');
+ } else
+ puts("unknown");
+
+ if (val[WHATS_VALID] & OK_W64_70) {
+ if (val[PIO_NO_FLOW] || val[PIO_FLOW]) {
+ printf("\t\tCycle time:");
+ if (val[PIO_NO_FLOW]) printf(" no flow control=%uns", val[PIO_NO_FLOW]);
+ if (val[PIO_FLOW]) printf(" IORDY flow control=%uns", val[PIO_FLOW]);
+ bb_putchar('\n');
}
}
- if((val[CMDS_SUPP_1] & VALID) == VALID_VAL){
+ if ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) {
printf("Commands/features:\n\tEnabled\tSupported:\n");
jj = val[CMDS_SUPP_0];
kk = val[CMDS_EN_0];
- for(ii = 0; ii < NUM_CMD_FEAT_STR; ii++) {
- if((jj & 0x8000) && (*cmd_feat_str[ii] != '\0')) {
- if(kk & 0x8000) printf("\t *");
- else printf("\t");
- printf("\t%s\n",cmd_feat_str[ii]);
+ for (ii = 0; ii < NUM_CMD_FEAT_STR; ii++) {
+ const char *feat_str = nth_string(cmd_feat_str, ii);
+ if ((jj & 0x8000) && (*feat_str != '\0')) {
+ printf("\t%s\t%s\n", (kk & 0x8000) ? " *" : "", feat_str);
}
- jj <<=1; kk<<=1;
- if(ii%16 == 15) {
+ jj <<= 1;
+ kk <<= 1;
+ if (ii % 16 == 15) {
jj = val[CMDS_SUPP_0+1+(ii/16)];
kk = val[CMDS_EN_0+1+(ii/16)];
}
- if(ii == 31) {
- if((val[CMDS_SUPP_2] & VALID) != VALID_VAL) ii +=16;
+ if (ii == 31) {
+ if ((val[CMDS_SUPP_2] & VALID) != VALID_VAL)
+ ii +=16;
}
}
}
- if((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP)
- printf("\tRemovable Media Status Notification feature set supported\n");
-
-
+ /* Removable Media Status Notification feature set */
+ if ((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP)
+ printf("\t%s supported\n", nth_string(cmd_feat_str, 27));
+
/* security */
- if((eqpt != CDROM) && (like_std > 3) &&
- (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME])) {
- printf("Security: \n");
- if(val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1))
- printf("\tMaster password revision code = %u\n",val[PSWD_CODE]);
+ if ((eqpt != CDROM) && (like_std > 3)
+ && (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME])
+ ) {
+ printf("Security:\n");
+ if (val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1))
+ printf("\tMaster password revision code = %u\n", val[PSWD_CODE]);
jj = val[SECU_STATUS];
- if(jj) {
- for(ii = 0; ii < NUM_SECU_STR; ii++) {
- if(!(jj & 0x0001)) printf("\tnot\t");
- else printf("\t\t");
- printf("%s\n",secu_str[ii]);
+ if (jj) {
+ for (ii = 0; ii < NUM_SECU_STR; ii++) {
+ printf("\t%s\t%s\n", (!(jj & 0x0001)) ? "not" : "", nth_string(secu_str, ii));
jj >>=1;
}
- if(val[SECU_STATUS] & SECU_ENABLED) {
- printf("\tSecurity level ");
- if(val[SECU_STATUS] & SECU_LEVEL) printf("maximum\n");
- else printf("high\n");
+ if (val[SECU_STATUS] & SECU_ENABLED) {
+ printf("\tSecurity level %s\n", (val[SECU_STATUS] & SECU_LEVEL) ? "maximum" : "high");
}
}
jj = val[ERASE_TIME] & ERASE_BITS;
kk = val[ENH_ERASE_TIME] & ERASE_BITS;
- if(jj || kk) {
- printf("\t");
- if(jj) printf("%umin for SECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1);
- if(kk) printf("%umin for ENHANCED SECURITY ERASE UNIT.", kk==ERASE_BITS ? 508 : kk<<1);
- printf("\n");
+ if (jj || kk) {
+ bb_putchar('\t');
+ if (jj) printf("%umin for %sSECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1, "");
+ if (kk) printf("%umin for %sSECURITY ERASE UNIT. ", kk==ERASE_BITS ? 508 : kk<<1, "ENHANCED ");
+ bb_putchar('\n');
}
}
/* reset result */
- if((val[HWRST_RSLT] & VALID) == VALID_VAL) {
- printf("HW reset results:\n");
- if(val[HWRST_RSLT] & CBLID) printf("\tCBLID- above Vih\n");
- else printf("\tCBLID- below Vih\n");
- if(val[HWRST_RSLT] & RST0) {
- printf("\tDevice num = 0");
- jj = val[HWRST_RSLT];
- } else {
- printf("\tDevice num = 1");
- jj = val[HWRST_RSLT] >> 8;
- }
- if((jj & DEV_DET) == JUMPER_VAL)
- printf(" determined by the jumper");
- else if((jj & DEV_DET) == CSEL_VAL)
- printf(" determined by CSEL");
- printf("\n");
+ jj = val[HWRST_RSLT];
+ if ((jj & VALID) == VALID_VAL) {
+ oo = (jj & RST0);
+ if (!oo)
+ jj >>= 8;
+ if ((jj & DEV_DET) == JUMPER_VAL)
+ strng = " determined by the jumper";
+ else if ((jj & DEV_DET) == CSEL_VAL)
+ strng = " determined by CSEL";
+ else
+ strng = "";
+ printf("HW reset results:\n\tCBLID- %s Vih\n\tDevice num = %i%s\n",
+ (val[HWRST_RSLT] & CBLID) ? "above" : "below", !(oo), strng);
}
/* more stuff from std 5 */
- if((like_std > 4) && (eqpt != CDROM)) {
- if(val[CFA_PWR_MODE] & VALID_W160) {
- printf("CFA power mode 1:\n\t");
- if(val[CFA_PWR_MODE] & PWR_MODE_OFF) printf("dis");
- else printf("en");
- printf("abled");
- if(val[CFA_PWR_MODE] & PWR_MODE_REQ)
- printf(" and required by some commands");
- printf("\n");
- if(val[CFA_PWR_MODE] & MAX_AMPS)
- printf("\tMaximum current = %uma\n",val[CFA_PWR_MODE] & MAX_AMPS);
+ if ((like_std > 4) && (eqpt != CDROM)) {
+ if (val[CFA_PWR_MODE] & VALID_W160) {
+ printf("CFA power mode 1:\n\t%s%s\n", (val[CFA_PWR_MODE] & PWR_MODE_OFF) ? "disabled" : "enabled",
+ (val[CFA_PWR_MODE] & PWR_MODE_REQ) ? " and required by some commands" : "");
+
+ if (val[CFA_PWR_MODE] & MAX_AMPS)
+ printf("\tMaximum current = %uma\n", val[CFA_PWR_MODE] & MAX_AMPS);
}
- if((val[INTEGRITY] & SIG) == SIG_VAL) {
- printf("Checksum: ");
- if(chksum) printf("in");
- printf("correct\n");
+ if ((val[INTEGRITY] & SIG) == SIG_VAL) {
+ printf("Checksum: %scorrect\n", chksum ? "in" : "");
}
}
- exit(0);
+ exit(EXIT_SUCCESS);
}
#endif
-#define VERSION "v5.4"
-
-#undef DO_FLUSHCACHE /* under construction: force cache flush on -W0 */
-#define TIMING_BUF_MB 2
-#define TIMING_BUF_BYTES (TIMING_BUF_MB * 1024 * 1024)
-#define BUFCACHE_FACTOR 2
-
-static int verbose = 0, get_identity = 0, get_geom = 0, noisy = 1, quiet = 0;
-static int flagcount = 0, do_flush = 0, is_scsi_hd = 0, is_xt_hd = 0;
-static int do_ctimings, do_timings = 0;
-
-static unsigned long set_readahead= 0, get_readahead= 0, readahead= 0;
-static unsigned long set_readonly = 0, get_readonly = 0, readonly = 0;
-static unsigned long set_unmask = 0, get_unmask = 0, unmask = 0;
-static unsigned long set_mult = 0, get_mult = 0, mult = 0;
-static unsigned long set_dma = 0, get_dma = 0, dma = 0;
-static unsigned long set_dma_q = 0, get_dma_q = 0, dma_q = 0;
-static unsigned long set_nowerr = 0, get_nowerr = 0, nowerr = 0;
-static unsigned long set_keep = 0, get_keep = 0, keep = 0;
-static unsigned long set_io32bit = 0, get_io32bit = 0, io32bit = 0;
-static unsigned long set_piomode = 0, noisy_piomode= 0;
-static int piomode = 0;
-#ifdef HDIO_DRIVE_CMD
-static unsigned long set_dkeep = 0, get_dkeep = 0, dkeep = 0;
-static unsigned long set_standby = 0, get_standby = 0, standby_requested= 0;
-static unsigned long set_xfermode = 0, get_xfermode = 0;
-static int xfermode_requested= 0;
-static unsigned long set_lookahead= 0, get_lookahead= 0, lookahead= 0;
-static unsigned long set_prefetch = 0, get_prefetch = 0, prefetch = 0;
-static unsigned long set_defects = 0, get_defects = 0, defects = 0;
-static unsigned long set_wcache = 0, get_wcache = 0, wcache = 0;
-static unsigned long set_doorlock = 0, get_doorlock = 0, doorlock = 0;
-static unsigned long set_seagate = 0, get_seagate = 0;
-static unsigned long set_standbynow = 0, get_standbynow = 0;
-static unsigned long set_sleepnow = 0, get_sleepnow = 0;
-static unsigned long get_powermode = 0;
-static unsigned long set_apmmode = 0, get_apmmode= 0, apmmode = 0;
-#endif
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static int get_IDentity = 0;
-#endif
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
-static int unregister_hwif = 0;
-static int hwif = 0;
-#endif
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
-static int scan_hwif = 0;
-static int hwif_data = 0;
-static int hwif_ctrl = 0;
-static int hwif_irq = 0;
-#endif
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
-static int set_busstate = 0, get_busstate = 0, busstate = 0;
-#endif
-static int reread_partn = 0;
-
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
-static int perform_reset = 0;
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
-static int perform_tristate = 0, tristate = 0;
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
-
// Historically, if there was no HDIO_OBSOLETE_IDENTITY, then
// then the HDIO_GET_IDENTITY only returned 142 bytes.
// Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes,
@@ -1095,881 +1160,529 @@ static int perform_tristate = 0, tristate = 0;
// On a really old system, it will not, and we will be confused.
// Too bad, really.
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static const char *cfg_str[] =
-{ "", " HardSect", " SoftSect", " NotMFM",
- " HdSw>15uSec", " SpinMotCtl", " Fixed", " Removeable",
- " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs", " RotSpdTol>.5%",
- " dStbOff", " TrkOff", " FmtGapReq", " nonMagnetic"
-};
-
-static const char *BuffType[] = {"unknown", "1Sect", "DualPort", "DualPortCache"};
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static const char cfg_str[] ALIGN1 =
+ """\0" "HardSect""\0" "SoftSect""\0" "NotMFM""\0"
+ "HdSw>15uSec""\0" "SpinMotCtl""\0" "Fixed""\0" "Removeable""\0"
+ "DTR<=5Mbs""\0" "DTR>5Mbs""\0" "DTR>10Mbs""\0" "RotSpdTol>.5%""\0"
+ "dStbOff""\0" "TrkOff""\0" "FmtGapReq""\0" "nonMagnetic"
+;
-#define YN(b) (((b)==0)?"no":"yes")
+static const char BuffType[] ALIGN1 =
+ "unknown""\0" "1Sect""\0" "DualPort""\0" "DualPortCache"
+;
-static void dmpstr (const char *prefix, unsigned int i, const char *s[], unsigned int maxi)
-{
- if (i > maxi)
- printf("%s%u", prefix, i);
- else
- printf("%s%s", prefix, s[i]);
-}
-
-static void dump_identity (const struct hd_driveid *id)
+static void dump_identity(const struct hd_driveid *id)
{
int i;
- char pmodes[64] = {0,}, dmodes[128]={0,}, umodes[128]={0,};
- const unsigned short int *id_regs= (const void*) id;
- unsigned long capacity;
-
- printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s",
- id->model, id->fw_rev, id->serial_no);
- printf("\n Config={");
- for (i=0; i<=15; i++) {
+ const unsigned short *id_regs = (const void*) id;
+
+ printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n Config={",
+ id->model, id->fw_rev, id->serial_no);
+ for (i = 0; i <= 15; i++) {
if (id->config & (1<<i))
- printf("%s", cfg_str[i]);
- }
- printf(" }\n");
- printf(" RawCHS=%u/%u/%u, TrkSize=%u, SectSize=%u, ECCbytes=%u\n",
- id->cyls, id->heads, id->sectors,
- id->track_bytes, id->sector_bytes, id->ecc_bytes);
- dmpstr(" BuffType=",id->buf_type,BuffType,3);
- printf(", BuffSize=%ukB, MaxMultSect=%u", id->buf_size/2, id->max_multsect);
+ printf(" %s", nth_string(cfg_str, i));
+ }
+ printf(" }\n RawCHS=%u/%u/%u, TrkSize=%u, SectSize=%u, ECCbytes=%u\n"
+ " BuffType=(%u) %s, BuffSize=%ukB, MaxMultSect=%u",
+ id->cyls, id->heads, id->sectors, id->track_bytes,
+ id->sector_bytes, id->ecc_bytes,
+ id->buf_type, nth_string(BuffType, (id->buf_type > 3) ? 0 : id->buf_type),
+ id->buf_size/2, id->max_multsect);
if (id->max_multsect) {
printf(", MultSect=");
- if (!(id->multsect_valid&1))
+ if (!(id->multsect_valid & 1))
printf("?%u?", id->multsect);
else if (id->multsect)
printf("%u", id->multsect);
else
printf("off");
}
- putchar('\n');
- if (id->tPIO <= 5) {
- strcat(pmodes, "pio0 ");
- if (id->tPIO >= 1) strcat(pmodes, "pio1 ");
- if (id->tPIO >= 2) strcat(pmodes, "pio2 ");
- }
- if (!(id->field_valid&1))
+ bb_putchar('\n');
+
+ if (!(id->field_valid & 1))
printf(" (maybe):");
-#if __BYTE_ORDER == __BIG_ENDIAN
- capacity = (id->cur_capacity0 << 16) | id->cur_capacity1;
-#else
- capacity = (id->cur_capacity1 << 16) | id->cur_capacity0;
-#endif
- printf(" CurCHS=%u/%u/%u, CurSects=%lu", id->cur_cyls, id->cur_heads, id->cur_sectors, capacity);
- printf(", LBA=%s", YN(id->capability&2));
- if (id->capability&2)
- printf(", LBAsects=%u", id->lba_capacity);
- if (id->capability&1) {
+ printf(" CurCHS=%u/%u/%u, CurSects=%lu, LBA=%s", id->cur_cyls, id->cur_heads,
+ id->cur_sectors,
+ (BB_BIG_ENDIAN) ?
+ (unsigned long)(id->cur_capacity0 << 16) | id->cur_capacity1 :
+ (unsigned long)(id->cur_capacity1 << 16) | id->cur_capacity0,
+ ((id->capability&2) == 0) ? "no" : "yes");
+
+ if (id->capability & 2)
+ printf(", LBAsects=%u", id->lba_capacity);
+
+ printf("\n IORDY=%s", (id->capability & 8) ? (id->capability & 4) ? "on/off" : "yes" : "no");
+
+ if (((id->capability & 8) || (id->field_valid & 2)) && (id->field_valid & 2))
+ printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy);
+
+ if ((id->capability & 1) && (id->field_valid & 2))
+ printf(", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time);
+
+ printf("\n PIO modes: ");
+ if (id->tPIO <= 5) {
+ printf("pio0 ");
+ if (id->tPIO >= 1) printf("pio1 ");
+ if (id->tPIO >= 2) printf("pio2 ");
+ }
+ if (id->field_valid & 2) {
+ static const masks_labels_t pio_modes = {
+ .masks = { 1, 2, ~3 },
+ .labels = "pio3 \0""pio4 \0""pio? \0",
+ };
+ print_flags(&pio_modes, id->eide_pio_modes);
+ }
+ if (id->capability & 1) {
if (id->dma_1word | id->dma_mword) {
- if (id->dma_1word & 0x100) strcat(dmodes,"*");
- if (id->dma_1word & 1) strcat(dmodes,"sdma0 ");
- if (id->dma_1word & 0x200) strcat(dmodes,"*");
- if (id->dma_1word & 2) strcat(dmodes,"sdma1 ");
- if (id->dma_1word & 0x400) strcat(dmodes,"*");
- if (id->dma_1word & 4) strcat(dmodes,"sdma2 ");
- if (id->dma_1word & 0xf800) strcat(dmodes,"*");
- if (id->dma_1word & 0xf8) strcat(dmodes,"sdma? ");
- if (id->dma_mword & 0x100) strcat(dmodes,"*");
- if (id->dma_mword & 1) strcat(dmodes,"mdma0 ");
- if (id->dma_mword & 0x200) strcat(dmodes,"*");
- if (id->dma_mword & 2) strcat(dmodes,"mdma1 ");
- if (id->dma_mword & 0x400) strcat(dmodes,"*");
- if (id->dma_mword & 4) strcat(dmodes,"mdma2 ");
- if (id->dma_mword & 0xf800) strcat(dmodes,"*");
- if (id->dma_mword & 0xf8) strcat(dmodes,"mdma? ");
- }
- }
- printf("\n IORDY=");
- if (id->capability&8)
- printf((id->capability&4) ? "on/off" : "yes");
- else
- printf("no");
- if ((id->capability&8) || (id->field_valid&2)) {
- if (id->field_valid&2) {
- printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy);
- if (id->eide_pio_modes & 1) strcat(pmodes, "pio3 ");
- if (id->eide_pio_modes & 2) strcat(pmodes, "pio4 ");
- if (id->eide_pio_modes &~3) strcat(pmodes, "pio? ");
- }
- if (id->field_valid&4) {
- if (id->dma_ultra & 0x100) strcat(umodes,"*");
- if (id->dma_ultra & 0x001) strcat(umodes,"udma0 ");
- if (id->dma_ultra & 0x200) strcat(umodes,"*");
- if (id->dma_ultra & 0x002) strcat(umodes,"udma1 ");
- if (id->dma_ultra & 0x400) strcat(umodes,"*");
- if (id->dma_ultra & 0x004) strcat(umodes,"udma2 ");
+ static const int dma_wmode_masks[] = { 0x100, 1, 0x200, 2, 0x400, 4, 0xf800, 0xf8 };
+ printf("\n DMA modes: ");
+ print_flags_separated(dma_wmode_masks,
+ "*\0""sdma0 \0""*\0""sdma1 \0""*\0""sdma2 \0""*\0""sdma? \0",
+ id->dma_1word, NULL);
+ print_flags_separated(dma_wmode_masks,
+ "*\0""mdma0\0""*\0""mdma1\0""*\0""mdma2\0""*\0""mdma?\0",
+ id->dma_mword, NULL);
+ }
+ }
+ if (((id->capability & 8) || (id->field_valid & 2)) && id->field_valid & 4) {
+ static const masks_labels_t ultra_modes1 = {
+ .masks = { 0x100, 0x001, 0x200, 0x002, 0x400, 0x004 },
+ .labels = "*\0""udma0 \0""*\0""udma1 \0""*\0""udma2 \0",
+ };
+
+ printf("\n UDMA modes: ");
+ print_flags(&ultra_modes1, id->dma_ultra);
#ifdef __NEW_HD_DRIVE_ID
- if (id->hw_config & 0x2000) {
+ if (id->hw_config & 0x2000) {
#else /* !__NEW_HD_DRIVE_ID */
- if (id->word93 & 0x2000) {
+ if (id->word93 & 0x2000) {
#endif /* __NEW_HD_DRIVE_ID */
- if (id->dma_ultra & 0x0800) strcat(umodes,"*");
- if (id->dma_ultra & 0x0008) strcat(umodes,"udma3 ");
- if (id->dma_ultra & 0x1000) strcat(umodes,"*");
- if (id->dma_ultra & 0x0010) strcat(umodes,"udma4 ");
- if (id->dma_ultra & 0x2000) strcat(umodes,"*");
- if (id->dma_ultra & 0x0020) strcat(umodes,"udma5 ");
- if (id->dma_ultra & 0x4000) strcat(umodes,"*");
- if (id->dma_ultra & 0x0040) strcat(umodes,"udma6 ");
- if (id->dma_ultra & 0x8000) strcat(umodes,"*");
- if (id->dma_ultra & 0x0080) strcat(umodes,"udma7 ");
- }
- }
+ static const masks_labels_t ultra_modes2 = {
+ .masks = { 0x0800, 0x0008, 0x1000, 0x0010,
+ 0x2000, 0x0020, 0x4000, 0x0040,
+ 0x8000, 0x0080 },
+ .labels = "*\0""udma3 \0""*\0""udma4 \0"
+ "*\0""udma5 \0""*\0""udma6 \0"
+ "*\0""udma7 \0"
+ };
+ print_flags(&ultra_modes2, id->dma_ultra);
+ }
+ }
+ printf("\n AdvancedPM=%s", (!(id_regs[83] & 8)) ? "no" : "yes");
+ if (id_regs[83] & 8) {
+ if (!(id_regs[86] & 8))
+ printf(": disabled (255)");
+ else if ((id_regs[91] & 0xFF00) != 0x4000)
+ printf(": unknown setting");
+ else
+ printf(": mode=0x%02X (%u)", id_regs[91] & 0xFF, id_regs[91] & 0xFF);
}
- if ((id->capability&1) && (id->field_valid&2))
- printf(", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time);
- printf("\n PIO modes: %s", pmodes);
- if (*dmodes)
- printf("\n DMA modes: %s", dmodes);
- if (*umodes)
- printf("\n UDMA modes: %s", umodes);
-
- printf("\n AdvancedPM=%s",YN(id_regs[83]&8));
- if (id_regs[83] & 8) {
- if (!(id_regs[86]&8))
- printf(": disabled (255)");
- else if ((id_regs[91]&0xFF00)!=0x4000)
- printf(": unknown setting");
- else
- printf(": mode=0x%02X (%u)",id_regs[91]&0xFF,id_regs[91]&0xFF);
- }
- if (id_regs[82]&0x20)
- printf(" WriteCache=%s",(id_regs[85]&0x20) ? "enabled" : "disabled");
+ if (id_regs[82] & 0x20)
+ printf(" WriteCache=%s", (id_regs[85] & 0x20) ? "enabled" : "disabled");
#ifdef __NEW_HD_DRIVE_ID
- if ((id->minor_rev_num && id->minor_rev_num <= 31) || (id->major_rev_num && id->minor_rev_num <= 31)) {
- printf("\n Drive conforms to: ");
- if (id->minor_rev_num <= 31)
- printf("%s: ", minor_str[id->minor_rev_num]);
- else
- printf("unknown: ");
- if (id->major_rev_num < 31) {
- for (i=0; i <= 15; i++) {
+ if ((id->minor_rev_num && id->minor_rev_num <= 31)
+ || (id->major_rev_num && id->minor_rev_num <= 31)
+ ) {
+ printf("\n Drive conforms to: %s: ", (id->minor_rev_num <= 31) ? nth_string(minor_str, id->minor_rev_num) : "unknown");
+ if (id->major_rev_num != 0x0000 && /* NOVAL_0 */
+ id->major_rev_num != 0xFFFF) { /* NOVAL_1 */
+ for (i = 0; i <= 15; i++) {
if (id->major_rev_num & (1<<i))
- printf(" %u", i);
+ printf(" ATA/ATAPI-%u", i);
}
}
}
#endif /* __NEW_HD_DRIVE_ID */
- printf("\n");
- printf("\n * signifies the current active mode\n");
- printf("\n");
+ printf("\n\n * current active mode\n\n");
}
#endif
-static void flush_buffer_cache (int fd)
+static void flush_buffer_cache(/*int fd*/ void)
{
- fsync (fd); /* flush buffers */
- if (ioctl(fd, BLKFLSBUF, NULL)) /* do it again, big time */
- bb_perror_msg("BLKFLSBUF failed");
+ fsync(fd); /* flush buffers */
+ ioctl_or_warn(fd, BLKFLSBUF, NULL); /* do it again, big time */
#ifdef HDIO_DRIVE_CMD
- if (is_scsi_hd || is_xt_hd) {
- sleep(1);
- } else {
- if (ioctl(fd, HDIO_DRIVE_CMD, NULL) && errno != EINVAL) /* await completion */
- bb_perror_msg("HDIO_DRIVE_CMD(null) (wait for flush complete) failed");
+ sleep(1);
+ if (ioctl(fd, HDIO_DRIVE_CMD, NULL) && errno != EINVAL) { /* await completion */
+ if (ENABLE_IOCTL_HEX2STR_ERROR) /* To be coherent with ioctl_or_warn */
+ bb_perror_msg("HDIO_DRIVE_CMD");
+ else
+ bb_perror_msg("ioctl %#x failed", HDIO_DRIVE_CMD);
}
#endif
}
-static int seek_to_zero (int fd)
+static void seek_to_zero(/*int fd*/ void)
{
- if (lseek(fd, (off_t) 0, SEEK_SET)) {
- bb_perror_msg("lseek() failed");
- return 1;
- }
- return 0;
+ xlseek(fd, (off_t) 0, SEEK_SET);
}
-static int read_big_block (int fd, char *buf)
+static void read_big_block(/*int fd,*/ char *buf)
{
- int i, rc;
- if ((rc = read(fd, buf, TIMING_BUF_BYTES)) != TIMING_BUF_BYTES) {
- if (rc) {
- if (rc == -1)
- bb_perror_msg("read() failed");
- else
- bb_error_msg("read(%u) returned %u bytes", TIMING_BUF_BYTES, rc);
- } else {
- fputs ("read() hit EOF - device too small\n", stderr);
- }
- return 1;
- }
+ int i;
+
+ xread(fd, buf, TIMING_BUF_BYTES);
/* access all sectors of buf to ensure the read fully completed */
for (i = 0; i < TIMING_BUF_BYTES; i += 512)
buf[i] &= 1;
- return 0;
}
-static void time_cache (int fd)
+static unsigned dev_size_mb(/*int fd*/ void)
{
- char *buf;
- struct itimerval e1, e2;
- int shmid;
- double elapsed, elapsed2;
- unsigned int iterations, total_MB;
-
- if ((shmid = shmget(IPC_PRIVATE, TIMING_BUF_BYTES, 0600)) == -1) {
- bb_perror_msg ("could not allocate sharedmem buf");
- return;
- }
- if (shmctl(shmid, SHM_LOCK, NULL) == -1) {
- bb_perror_msg ("could not lock sharedmem buf");
- (void) shmctl(shmid, IPC_RMID, NULL);
- return;
- }
- if ((buf = shmat(shmid, (char *) 0, 0)) == (char *) -1) {
- bb_perror_msg ("could not attach sharedmem buf");
- (void) shmctl(shmid, IPC_RMID, NULL);
- return;
- }
- if (shmctl(shmid, IPC_RMID, NULL) == -1)
- bb_perror_msg ("shmctl(,IPC_RMID,) failed");
-
- /* Clear out the device request queues & give them time to complete */
- sync();
- sleep(3);
-
- /*
- * getitimer() is used rather than gettimeofday() because
- * it is much more consistent (on my machine, at least).
- */
- setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL);
- if (seek_to_zero (fd)) return;
- if (read_big_block (fd, buf)) return;
- printf(" Timing buffer-cache reads: ");
- fflush(stdout);
-
- /* Clear out the device request queues & give them time to complete */
- sync();
- sleep(1);
-
- /* Now do the timing */
- iterations = 0;
- getitimer(ITIMER_REAL, &e1);
- do {
- ++iterations;
- if (seek_to_zero (fd) || read_big_block (fd, buf))
- goto quit;
- getitimer(ITIMER_REAL, &e2);
- elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec)
- + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
- } while (elapsed < 2.0);
- total_MB = iterations * TIMING_BUF_MB;
-
- elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec)
- + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
-
- /* Now remove the lseek() and getitimer() overheads from the elapsed time */
- getitimer(ITIMER_REAL, &e1);
- do {
- if (seek_to_zero (fd))
- goto quit;
- getitimer(ITIMER_REAL, &e2);
- elapsed2 = (e1.it_value.tv_sec - e2.it_value.tv_sec)
- + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
- } while (--iterations);
-
- elapsed -= elapsed2;
-
- if ((BUFCACHE_FACTOR * total_MB) >= elapsed) /* more than 1MB/s */
- printf("%3u MB in %5.2f seconds = %6.2f MB/sec\n",
- (BUFCACHE_FACTOR * total_MB), elapsed,
- (BUFCACHE_FACTOR * total_MB) / elapsed);
- else
- printf("%3u MB in %5.2f seconds = %6.2f kB/sec\n",
- (BUFCACHE_FACTOR * total_MB), elapsed,
- (BUFCACHE_FACTOR * total_MB) / elapsed * 1024);
+ union {
+ unsigned long long blksize64;
+ unsigned blksize32;
+ } u;
- flush_buffer_cache(fd);
- sleep(1);
-quit:
- if (-1 == shmdt(buf))
- bb_perror_msg ("could not detach sharedmem buf");
+ if (0 == ioctl(fd, BLKGETSIZE64, &u.blksize64)) { // bytes
+ u.blksize64 /= (1024 * 1024);
+ } else {
+ xioctl(fd, BLKGETSIZE, &u.blksize32); // sectors
+ u.blksize64 = u.blksize32 / (2 * 1024);
+ }
+ if (u.blksize64 > UINT_MAX)
+ return UINT_MAX;
+ return u.blksize64;
}
-static void time_device (int fd)
+static void print_timing(unsigned m, unsigned elapsed_us)
{
- char *buf;
- double elapsed;
- struct itimerval e1, e2;
- int shmid;
- unsigned int max_iterations = 1024, total_MB, iterations;
- static long parm;
-
- //
- // get device size
- //
- if (do_ctimings || do_timings) {
- if (ioctl(fd, BLKGETSIZE, &parm))
- bb_perror_msg(" BLKGETSIZE failed");
- else
- max_iterations = parm / (2 * 1024) / TIMING_BUF_MB;
- }
-
- if ((shmid = shmget(IPC_PRIVATE, TIMING_BUF_BYTES, 0600)) == -1) {
- bb_perror_msg ("could not allocate sharedmem buf");
- return;
- }
- if (shmctl(shmid, SHM_LOCK, NULL) == -1) {
- bb_perror_msg ("could not lock sharedmem buf");
- (void) shmctl(shmid, IPC_RMID, NULL);
- return;
- }
- if ((buf = shmat(shmid, (char *) 0, 0)) == (char *) -1) {
- bb_perror_msg ("could not attach sharedmem buf");
- (void) shmctl(shmid, IPC_RMID, NULL);
- return;
- }
- if (shmctl(shmid, IPC_RMID, NULL) == -1)
- bb_perror_msg ("shmctl(,IPC_RMID,) failed");
+ unsigned sec = elapsed_us / 1000000;
+ unsigned hs = (elapsed_us % 1000000) / 10000;
+
+ printf("%5u MB in %u.%02u seconds = %u kB/s\n",
+ m, sec, hs,
+ /* "| 1" prevents div-by-0 */
+ (unsigned) ((unsigned long long)m * (1024 * 1000000) / (elapsed_us | 1))
+ // ~= (m * 1024) / (elapsed_us / 1000000)
+ // = kb / elapsed_sec
+ );
+}
- /* Clear out the device request queues & give them time to complete */
+static void do_time(int cache /*,int fd*/)
+/* cache=1: time cache: repeatedly read N MB at offset 0
+ * cache=0: time device: linear read, starting at offset 0
+ */
+{
+ unsigned max_iterations, iterations;
+ unsigned start; /* doesn't need to be long long */
+ unsigned elapsed, elapsed2;
+ unsigned total_MB;
+ char *buf = xmalloc(TIMING_BUF_BYTES);
+
+ if (mlock(buf, TIMING_BUF_BYTES))
+ bb_perror_msg_and_die("mlock");
+
+ /* Clear out the device request queues & give them time to complete.
+ * NB: *small* delay. User is expected to have a clue and to not run
+ * heavy io in parallel with measurements. */
sync();
- sleep(3);
-
- printf(" Timing buffered disk reads: ");
+ sleep(1);
+ if (cache) { /* Time cache */
+ seek_to_zero();
+ read_big_block(buf);
+ printf("Timing buffer-cache reads: ");
+ } else { /* Time device */
+ printf("Timing buffered disk reads:");
+ }
fflush(stdout);
- /*
- * getitimer() is used rather than gettimeofday() because
- * it is much more consistent (on my machine, at least).
- */
- setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL);
-
- /* Now do the timings for real */
+ /* Now do the timing */
iterations = 0;
- getitimer(ITIMER_REAL, &e1);
+ /* Max time to run (small for cache, avoids getting
+ * huge total_MB which can overlow unsigned type) */
+ elapsed2 = 510000; /* cache */
+ max_iterations = UINT_MAX;
+ if (!cache) {
+ elapsed2 = 3000000; /* not cache */
+ /* Don't want to read past the end! */
+ max_iterations = dev_size_mb() / TIMING_BUF_MB;
+ }
+ start = monotonic_us();
do {
+ if (cache)
+ seek_to_zero();
+ read_big_block(buf);
+ elapsed = (unsigned)monotonic_us() - start;
++iterations;
- if (read_big_block (fd, buf))
- goto quit;
- getitimer(ITIMER_REAL, &e2);
- elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec)
- + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
- } while (elapsed < 3.0 && iterations < max_iterations);
-
+ } while (elapsed < elapsed2 && iterations < max_iterations);
total_MB = iterations * TIMING_BUF_MB;
- if ((total_MB / elapsed) > 1.0) /* more than 1MB/s */
- printf("%3u MB in %5.2f seconds = %6.2f MB/sec\n",
- total_MB, elapsed, total_MB / elapsed);
- else
- printf("%3u MB in %5.2f seconds = %6.2f kB/sec\n",
- total_MB, elapsed, total_MB / elapsed * 1024);
-quit:
- if (-1 == shmdt(buf))
- bb_perror_msg ("could not detach sharedmem buf");
+ //printf(" elapsed:%u iterations:%u ", elapsed, iterations);
+ if (cache) {
+ /* Cache: remove lseek() and monotonic_us() overheads
+ * from elapsed */
+ start = monotonic_us();
+ do {
+ seek_to_zero();
+ elapsed2 = (unsigned)monotonic_us() - start;
+ } while (--iterations);
+ //printf(" elapsed2:%u ", elapsed2);
+ elapsed -= elapsed2;
+ total_MB *= 2; // BUFCACHE_FACTOR (why?)
+ flush_buffer_cache();
+ }
+ print_timing(total_MB, elapsed);
+ munlock(buf, TIMING_BUF_BYTES);
+ free(buf);
}
-static void no_scsi (void)
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static void bus_state_value(unsigned value)
{
- if (is_scsi_hd) {
- bb_error_msg_and_die(" operation not supported on SCSI disks\n");
- }
-}
-
-static void no_xt (void)
-{
- if (is_xt_hd) {
- bb_error_msg_and_die (" operation not supported on XT disks\n");
- }
-}
-
-static void on_off (unsigned int value)
-{
- printf(value ? " (on)\n" : " (off)\n");
-}
-
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
-static void bus_state_value (unsigned int value)
-{
- const char *string;
-
- switch (value) {
- case BUSSTATE_ON:
- string = " (on)\n";
- break;
- case BUSSTATE_OFF:
- string = " (off)\n";
- break;
- case BUSSTATE_TRISTATE:
- string = " (tristate)\n";
- break;
- default:
- string = " (unknown: %d)\n";
- break;
- }
- printf(string, value);
+ if (value == BUSSTATE_ON)
+ on_off(1);
+ else if (value == BUSSTATE_OFF)
+ on_off(0);
+ else if (value == BUSSTATE_TRISTATE)
+ printf(" (tristate)\n");
+ else
+ printf(" (unknown: %d)\n", value);
}
#endif
#ifdef HDIO_DRIVE_CMD
-static void interpret_standby (unsigned int standby)
+static void interpret_standby(uint8_t standby)
{
printf(" (");
- switch(standby) {
- case 0: printf("off");
- break;
- case 252: printf("21 minutes");
- break;
- case 253: printf("vendor-specific");
- break;
- case 254: printf("?reserved");
- break;
- case 255: printf("21 minutes + 15 seconds");
- break;
- default:
- if (standby <= 240) {
- unsigned int secs = standby * 5;
- unsigned int mins = secs / 60;
- secs %= 60;
- if (mins) printf("%u minutes", mins);
- if (mins && secs) printf(" + ");
- if (secs) printf("%u seconds", secs);
- } else if (standby <= 251) {
- unsigned int mins = (standby - 240) * 30;
- unsigned int hrs = mins / 60;
- mins %= 60;
- if (hrs) printf("%u hours", hrs);
- if (hrs && mins) printf(" + ");
- if (mins) printf("%u minutes", mins);
- } else
- printf("illegal value");
- break;
- }
+ if (standby == 0) {
+ printf("off");
+ } else if (standby <= 240 || standby == 252 || standby == 255) {
+ /* standby is in 5 sec units */
+ printf("%u minutes %u seconds", standby / 12, (standby*5) % 60);
+ } else if (standby <= 251) {
+ unsigned t = (standby - 240); /* t is in 30 min units */;
+ printf("%u.%c hours", t / 2, (t & 1) ? '0' : '5');
+ }
+ if (standby == 253)
+ printf("vendor-specific");
+ if (standby == 254)
+ printf("reserved");
printf(")\n");
}
-struct xfermode_entry {
- int val;
- const char *name;
+static const uint8_t xfermode_val[] ALIGN1 = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 64, 65, 66, 67, 68, 69, 70, 71
};
-
-static const struct xfermode_entry xfermode_table[] = {
- { 8, "pio0" },
- { 9, "pio1" },
- { 10, "pio2" },
- { 11, "pio3" },
- { 12, "pio4" },
- { 13, "pio5" },
- { 14, "pio6" },
- { 15, "pio7" },
- { 16, "sdma0" },
- { 17, "sdma1" },
- { 18, "sdma2" },
- { 19, "sdma3" },
- { 20, "sdma4" },
- { 21, "sdma5" },
- { 22, "sdma6" },
- { 23, "sdma7" },
- { 32, "mdma0" },
- { 33, "mdma1" },
- { 34, "mdma2" },
- { 35, "mdma3" },
- { 36, "mdma4" },
- { 37, "mdma5" },
- { 38, "mdma6" },
- { 39, "mdma7" },
- { 64, "udma0" },
- { 65, "udma1" },
- { 66, "udma2" },
- { 67, "udma3" },
- { 68, "udma4" },
- { 69, "udma5" },
- { 70, "udma6" },
- { 71, "udma7" },
- { 0, NULL }
+/* NB: we save size by _not_ storing terninating NUL! */
+static const char xfermode_name[][5] ALIGN1 = {
+ "pio0", "pio1", "pio2", "pio3", "pio4", "pio5", "pio6", "pio7",
+ "sdma0","sdma1","sdma2","sdma3","sdma4","sdma5","sdma6","sdma7",
+ "mdma0","mdma1","mdma2","mdma3","mdma4","mdma5","mdma6","mdma7",
+ "udma0","udma1","udma2","udma3","udma4","udma5","udma6","udma7"
};
-static int translate_xfermode(char * name)
+static int translate_xfermode(const char *name)
{
- const struct xfermode_entry *tmp;
- char *endptr;
- int val = -1;
-
-
- for (tmp = xfermode_table; tmp->name != NULL; ++tmp) {
- if (!strcmp(name, tmp->name))
- return tmp->val;
- }
-
- val = strtol(name, &endptr, 10);
- if (*endptr == '\0')
- return val;
-
- return -1;
+ int val;
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(xfermode_val); i++) {
+ if (!strncmp(name, xfermode_name[i], 5))
+ if (strlen(name) <= 5)
+ return xfermode_val[i];
+ }
+ /* Negative numbers are invalid and are caught later */
+ val = bb_strtoi(name, NULL, 10);
+ if (!errno)
+ return val;
+ return -1;
}
-static void interpret_xfermode (unsigned int xfermode)
+static void interpret_xfermode(unsigned xfermode)
{
printf(" (");
- switch(xfermode) {
- case 0: printf("default PIO mode");
- break;
- case 1: printf("default PIO mode, disable IORDY");
- break;
- case 8:
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15: printf("PIO flow control mode%u", xfermode-8);
- break;
- case 16:
- case 17:
- case 18:
- case 19:
- case 20:
- case 21:
- case 22:
- case 23: printf("singleword DMA mode%u", xfermode-16);
- break;
- case 32:
- case 33:
- case 34:
- case 35:
- case 36:
- case 37:
- case 38:
- case 39: printf("multiword DMA mode%u", xfermode-32);
- break;
- case 64:
- case 65:
- case 66:
- case 67:
- case 68:
- case 69:
- case 70:
- case 71: printf("UltraDMA mode%u", xfermode-64);
- break;
- default:
- printf("unknown, probably not valid");
- break;
- }
+ if (xfermode == 0)
+ printf("default PIO mode");
+ else if (xfermode == 1)
+ printf("default PIO mode, disable IORDY");
+ else if (xfermode >= 8 && xfermode <= 15)
+ printf("PIO flow control mode%u", xfermode - 8);
+ else if (xfermode >= 16 && xfermode <= 23)
+ printf("singleword DMA mode%u", xfermode - 16);
+ else if (xfermode >= 32 && xfermode <= 39)
+ printf("multiword DMA mode%u", xfermode - 32);
+ else if (xfermode >= 64 && xfermode <= 71)
+ printf("UltraDMA mode%u", xfermode - 64);
+ else
+ printf("unknown");
printf(")\n");
}
#endif /* HDIO_DRIVE_CMD */
-#ifndef VXVM_MAJOR
-#define VXVM_MAJOR 199
-#endif
-
-#ifndef CCISS_MAJOR
-#define CCISS_MAJOR 104
-#endif
+static void print_flag(int flag, const char *s, unsigned long value)
+{
+ if (flag)
+ printf(" setting %s to %ld\n", s, value);
+}
-static void process_dev (char *devname)
+static void process_dev(char *devname)
{
- int fd;
- static long parm, multcount;
- struct stat stat_buf;
+ /*int fd;*/
+ long parm, multcount;
#ifndef HDIO_DRIVE_CMD
int force_operation = 0;
#endif
- if (stat(devname,&stat_buf))
- bb_perror_msg_and_die(devname);
-
- switch(major(stat_buf.st_rdev)) {
-#ifdef SCSI_DISK0_MAJOR
- case (SCSI_DISK0_MAJOR):
- case (SCSI_DISK1_MAJOR):
- case (SCSI_DISK2_MAJOR):
- case (SCSI_DISK3_MAJOR):
- case (SCSI_DISK4_MAJOR):
- case (SCSI_DISK5_MAJOR):
- case (SCSI_DISK6_MAJOR):
- case (SCSI_DISK7_MAJOR):
-#else
- case (SCSI_DISK_MAJOR):
-#endif
-#ifdef MD_MAJOR
- case (MD_MAJOR):
-#endif
- case (VXVM_MAJOR):
-#ifdef LVM_BLK_MAJOR
- case (LVM_BLK_MAJOR):
-#endif
- case (CCISS_MAJOR):
- is_scsi_hd = 1;
- break;
-#ifdef XT_DISK_MAJOR
- case (XT_DISK_MAJOR):
- is_xt_hd = 1;
- break;
-#endif
- case IDE0_MAJOR:
- case IDE1_MAJOR:
-#ifdef IDE2_MAJOR
- case IDE2_MAJOR:
-#endif
-#ifdef IDE3_MAJOR
- case IDE3_MAJOR:
-#endif
-#ifdef IDE4_MAJOR
- case IDE4_MAJOR:
-#endif
-#ifdef IDE5_MAJOR
- case IDE5_MAJOR:
-#endif
-#ifdef IDE6_MAJOR
- case IDE6_MAJOR:
-#endif
-#ifdef IDE7_MAJOR
- case IDE7_MAJOR:
-#endif
-#ifdef IDE8_MAJOR
- case IDE8_MAJOR:
-#endif
-#ifdef IDE9_MAJOR
- case IDE9_MAJOR:
-#endif
- break; /* do nothing */
- default:
- bb_error_msg_and_die("%s not supported by hdparm",devname);
- }
+ /* Please restore args[n] to these values after each ioctl
+ except for args[2] */
+ unsigned char args[4] = { WIN_SETFEATURES, 0, 0, 0 };
+ const char *fmt = " %s\t= %2ld";
- fd = open (devname, O_RDONLY|O_NONBLOCK);
- if (fd < 0)
- bb_perror_msg_and_die(devname);
- if (!quiet)
- printf("\n%s:\n", devname);
+ /*fd = xopen(devname, O_RDONLY | O_NONBLOCK);*/
+ xmove_fd(xopen(devname, O_RDONLY | O_NONBLOCK), fd);
+ printf("\n%s:\n", devname);
if (set_readahead) {
- if (get_readahead)
- printf(" setting fs readahead to %ld\n", readahead);
- if (ioctl(fd, BLKRASET, readahead))
- bb_perror_msg(" BLKRASET failed");
+ print_flag(get_readahead, "fs readahead", Xreadahead);
+ ioctl_or_warn(fd, BLKRASET, (int *)Xreadahead);
}
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+#if ENABLE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
if (unregister_hwif) {
- no_scsi();
- printf(" attempting to unregister hwif#%u\n", hwif);
- if (ioctl(fd, HDIO_UNREGISTER_HWIF, hwif))
- bb_perror_msg(" HDIO_UNREGISTER_HWIF failed");
+ printf(" attempting to unregister hwif#%lu\n", hwif);
+ ioctl_or_warn(fd, HDIO_UNREGISTER_HWIF, (int *)(unsigned long)hwif);
}
#endif
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
if (scan_hwif) {
- int args[3];
- no_scsi();
- printf(" attempting to scan hwif (0x%x, 0x%x, %u)\n", hwif_data, hwif_ctrl, hwif_irq);
+ printf(" attempting to scan hwif (0x%lx, 0x%lx, %lu)\n", hwif_data, hwif_ctrl, hwif_irq);
args[0] = hwif_data;
args[1] = hwif_ctrl;
args[2] = hwif_irq;
- if (ioctl(fd, HDIO_SCAN_HWIF, args))
- bb_perror_msg(" HDIO_SCAN_HWIF failed");
+ ioctl_or_warn(fd, HDIO_SCAN_HWIF, args);
+ args[0] = WIN_SETFEATURES;
+ args[1] = 0;
}
#endif
if (set_piomode) {
- no_scsi();
- no_xt();
if (noisy_piomode) {
+ printf(" attempting to ");
if (piomode == 255)
- printf(" attempting to auto-tune PIO mode\n");
+ printf("auto-tune PIO mode\n");
else if (piomode < 100)
- printf(" attempting to set PIO mode to %d\n", piomode);
+ printf("set PIO mode to %d\n", piomode);
else if (piomode < 200)
- printf(" attempting to set MDMA mode to %d\n", (piomode-100));
+ printf("set MDMA mode to %d\n", (piomode-100));
else
- printf(" attempting to set UDMA mode to %d\n", (piomode-200));
+ printf("set UDMA mode to %d\n", (piomode-200));
}
- if (ioctl(fd, HDIO_SET_PIO_MODE, piomode))
- bb_perror_msg(" HDIO_SET_PIO_MODE failed");
+ ioctl_or_warn(fd, HDIO_SET_PIO_MODE, (int *)(unsigned long)piomode);
}
if (set_io32bit) {
- no_scsi();
- no_xt();
- if (get_io32bit)
- printf(" setting 32-bit IO_support flag to %ld\n", io32bit);
- if (ioctl(fd, HDIO_SET_32BIT, io32bit))
- bb_perror_msg(" HDIO_SET_32BIT failed");
+ print_flag(get_io32bit, "32-bit IO_support flag", io32bit);
+ ioctl_or_warn(fd, HDIO_SET_32BIT, (int *)io32bit);
}
if (set_mult) {
- no_scsi();
- no_xt();
- if (get_mult)
- printf(" setting multcount to %ld\n", mult);
- if (ioctl(fd, HDIO_SET_MULTCOUNT, mult))
- bb_perror_msg(" HDIO_SET_MULTCOUNT failed");
-#ifndef HDIO_DRIVE_CMD
- else force_operation = 1;
+ print_flag(get_mult, "multcount", mult);
+#ifdef HDIO_DRIVE_CMD
+ ioctl_or_warn(fd, HDIO_SET_MULTCOUNT, (void *)mult);
+#else
+ force_operation |= (!ioctl_or_warn(fd, HDIO_SET_MULTCOUNT, (void *)mult));
#endif
}
if (set_readonly) {
- if (get_readonly) {
- printf(" setting readonly to %ld", readonly);
- on_off(readonly);
- }
- if (ioctl(fd, BLKROSET, &readonly))
- bb_perror_msg(" BLKROSET failed");
+ print_flag_on_off(get_readonly, "readonly", readonly);
+ ioctl_or_warn(fd, BLKROSET, &readonly);
}
if (set_unmask) {
- no_scsi();
- no_xt();
- if (get_unmask) {
- printf(" setting unmaskirq to %ld", unmask);
- on_off(unmask);
- }
- if (ioctl(fd, HDIO_SET_UNMASKINTR, unmask))
- bb_perror_msg(" HDIO_SET_UNMASKINTR failed");
+ print_flag_on_off(get_unmask, "unmaskirq", unmask);
+ ioctl_or_warn(fd, HDIO_SET_UNMASKINTR, (int *)unmask);
}
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
if (set_dma) {
- no_scsi();
- if (get_dma) {
- printf(" setting using_dma to %ld", dma);
- on_off(dma);
- }
- if (ioctl(fd, HDIO_SET_DMA, dma))
- bb_perror_msg(" HDIO_SET_DMA failed");
+ print_flag_on_off(get_dma, "using_dma", dma);
+ ioctl_or_warn(fd, HDIO_SET_DMA, (int *)dma);
}
+#endif /* FEATURE_HDPARM_HDIO_GETSET_DMA */
+#ifdef HDIO_SET_QDMA
if (set_dma_q) {
- no_scsi();
- if (get_dma_q) {
- printf(" setting DMA queue_depth to %ld", dma_q);
- on_off(dma_q);
- }
- if (ioctl(fd, HDIO_SET_QDMA, dma_q))
- bb_perror_msg(" HDIO_SET_QDMA failed");
+ print_flag_on_off(get_dma_q, "DMA queue_depth", dma_q);
+ ioctl_or_warn(fd, HDIO_SET_QDMA, (int *)dma_q);
}
+#endif
if (set_nowerr) {
- no_scsi();
- no_xt();
- if (get_nowerr) {
- printf(" setting nowerr to %ld", nowerr);
- on_off(nowerr);
- }
- if (ioctl(fd, HDIO_SET_NOWERR, nowerr))
- bb_perror_msg(" HDIO_SET_NOWERR failed");
+ print_flag_on_off(get_nowerr, "nowerr", nowerr);
+ ioctl_or_warn(fd, HDIO_SET_NOWERR, (int *)nowerr);
}
if (set_keep) {
- no_scsi();
- no_xt();
- if (get_keep) {
- printf(" setting keep_settings to %ld", keep);
- on_off(keep);
- }
- if (ioctl(fd, HDIO_SET_KEEPSETTINGS, keep))
- bb_perror_msg(" HDIO_SET_KEEPSETTINGS failed");
+ print_flag_on_off(get_keep, "keep_settings", keep);
+ ioctl_or_warn(fd, HDIO_SET_KEEPSETTINGS, (int *)keep);
}
#ifdef HDIO_DRIVE_CMD
if (set_doorlock) {
- unsigned char args[4] = {0,0,0,0};
- no_scsi();
- no_xt();
args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK;
- if (get_doorlock) {
- printf(" setting drive doorlock to %ld", doorlock);
- on_off(doorlock);
- }
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(doorlock) failed");
+ args[2] = 0;
+ print_flag_on_off(get_doorlock, "drive doorlock", doorlock);
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+ args[0] = WIN_SETFEATURES;
}
if (set_dkeep) {
/* lock/unlock the drive's "feature" settings */
- unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
- no_scsi();
- no_xt();
- if (get_dkeep) {
- printf(" setting drive keep features to %ld", dkeep);
- on_off(dkeep);
- }
+ print_flag_on_off(get_dkeep, "drive keep features", dkeep);
args[2] = dkeep ? 0x66 : 0xcc;
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(keepsettings) failed");
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
}
if (set_defects) {
- unsigned char args[4] = {WIN_SETFEATURES,0,0x04,0};
- no_scsi();
args[2] = defects ? 0x04 : 0x84;
- if (get_defects)
- printf(" setting drive defect-mgmt to %ld\n", defects);
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(defectmgmt) failed");
+ print_flag(get_defects, "drive defect-mgmt", defects);
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
}
if (set_prefetch) {
- unsigned char args[4] = {WIN_SETFEATURES,0,0xab,0};
- no_scsi();
- no_xt();
args[1] = prefetch;
- if (get_prefetch)
- printf(" setting drive prefetch to %ld\n", prefetch);
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(setprefetch) failed");
+ args[2] = 0xab;
+ print_flag(get_prefetch, "drive prefetch", prefetch);
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+ args[1] = 0;
}
if (set_xfermode) {
- unsigned char args[4] = {WIN_SETFEATURES,0,3,0};
- no_scsi();
- no_xt();
args[1] = xfermode_requested;
+ args[2] = 3;
if (get_xfermode) {
- printf(" setting xfermode to %d", xfermode_requested);
+ print_flag(1, "xfermode", xfermode_requested);
interpret_xfermode(xfermode_requested);
}
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(setxfermode) failed");
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+ args[1] = 0;
}
if (set_lookahead) {
- unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
- no_scsi();
- no_xt();
args[2] = lookahead ? 0xaa : 0x55;
- if (get_lookahead) {
- printf(" setting drive read-lookahead to %ld", lookahead);
- on_off(lookahead);
- }
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(setreadahead) failed");
+ print_flag_on_off(get_lookahead, "drive read-lookahead", lookahead);
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
}
if (set_apmmode) {
- unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
- no_scsi();
- if (apmmode<1) apmmode=1;
- if (apmmode>255) apmmode=255;
- if (get_apmmode)
- printf(" setting Advanced Power Management level to");
- if (apmmode==255) {
- /* disable Advanced Power Management */
- args[2] = 0x85; /* feature register */
- if (get_apmmode) printf(" disabled\n");
- } else {
- /* set Advanced Power Management mode */
- args[2] = 0x05; /* feature register */
- args[1] = apmmode; /* sector count register */
- if (get_apmmode) printf(" 0x%02lX (%ld)\n",apmmode,apmmode);
- }
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD failed");
- }
- if (set_wcache) {
+ args[2] = (apmmode == 255) ? 0x85 /* disable */ : 0x05 /* set */; /* feature register */
+ args[1] = apmmode; /* sector count register 1-255 */
+ if (get_apmmode)
+ printf(" setting APM level to %s 0x%02lX (%ld)\n", (apmmode == 255) ? "disabled" : "", apmmode, apmmode);
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+ args[1] = 0;
+ }
+ if (set_wcache) {
#ifdef DO_FLUSHCACHE
#ifndef WIN_FLUSHCACHE
#define WIN_FLUSHCACHE 0xe7
#endif
- unsigned char flushcache[4] = {WIN_FLUSHCACHE,0,0,0};
#endif /* DO_FLUSHCACHE */
- unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
- no_scsi();
- no_xt();
args[2] = wcache ? 0x02 : 0x82;
- if (get_wcache) {
- printf(" setting drive write-caching to %ld", wcache);
- on_off(wcache);
- }
+ print_flag_on_off(get_wcache, "drive write-caching", wcache);
#ifdef DO_FLUSHCACHE
- if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache))
- bb_perror_msg (" HDIO_DRIVE_CMD(flushcache) failed");
+ if (!wcache)
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &flushcache);
#endif /* DO_FLUSHCACHE */
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(setcache) failed");
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
#ifdef DO_FLUSHCACHE
- if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache))
- bb_perror_msg (" HDIO_DRIVE_CMD(flushcache) failed");
+ if (!wcache)
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &flushcache);
#endif /* DO_FLUSHCACHE */
}
+
+ /* In code below, we do not preserve args[0], but the rest
+ is preserved, including args[2] */
+ args[2] = 0;
+
if (set_standbynow) {
#ifndef WIN_STANDBYNOW1
#define WIN_STANDBYNOW1 0xE0
@@ -1977,14 +1690,9 @@ static void process_dev (char *devname)
#ifndef WIN_STANDBYNOW2
#define WIN_STANDBYNOW2 0x94
#endif
- unsigned char args1[4] = {WIN_STANDBYNOW1,0,0,0};
- unsigned char args2[4] = {WIN_STANDBYNOW2,0,0,0};
- no_scsi();
- if (get_standbynow)
- printf(" issuing standby command\n");
- if (ioctl(fd, HDIO_DRIVE_CMD, &args1)
- && ioctl(fd, HDIO_DRIVE_CMD, &args2))
- bb_perror_msg(" HDIO_DRIVE_CMD(standby) failed");
+ if (get_standbynow) printf(" issuing standby command\n");
+ args[0] = WIN_STANDBYNOW1;
+ ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_STANDBYNOW2);
}
if (set_sleepnow) {
#ifndef WIN_SLEEPNOW1
@@ -1993,166 +1701,111 @@ static void process_dev (char *devname)
#ifndef WIN_SLEEPNOW2
#define WIN_SLEEPNOW2 0x99
#endif
- unsigned char args1[4] = {WIN_SLEEPNOW1,0,0,0};
- unsigned char args2[4] = {WIN_SLEEPNOW2,0,0,0};
- no_scsi();
- if (get_sleepnow)
- printf(" issuing sleep command\n");
- if (ioctl(fd, HDIO_DRIVE_CMD, &args1)
- && ioctl(fd, HDIO_DRIVE_CMD, &args2))
- bb_perror_msg(" HDIO_DRIVE_CMD(sleep) failed");
+ if (get_sleepnow) printf(" issuing sleep command\n");
+ args[0] = WIN_SLEEPNOW1;
+ ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_SLEEPNOW2);
}
if (set_seagate) {
- unsigned char args[4] = {0xfb,0,0,0};
- no_scsi();
- no_xt();
- if (get_seagate)
- printf(" disabling Seagate auto powersaving mode\n");
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(seagatepwrsave) failed");
+ args[0] = 0xfb;
+ if (get_seagate) printf(" disabling Seagate auto powersaving mode\n");
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
}
if (set_standby) {
- unsigned char args[4] = {WIN_SETIDLE1,standby_requested,0,0};
- no_scsi();
- no_xt();
+ args[0] = WIN_SETIDLE1;
+ args[1] = standby_requested;
if (get_standby) {
- printf(" setting standby to %lu", standby_requested);
+ print_flag(1, "standby", standby_requested);
interpret_standby(standby_requested);
}
- if (ioctl(fd, HDIO_DRIVE_CMD, &args))
- bb_perror_msg(" HDIO_DRIVE_CMD(setidle1) failed");
+ ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+ args[1] = 0;
}
#else /* HDIO_DRIVE_CMD */
if (force_operation) {
char buf[512];
- flush_buffer_cache(fd);
+ flush_buffer_cache();
if (-1 == read(fd, buf, sizeof(buf)))
- bb_perror_msg(" access failed");
+ bb_perror_msg("read(%d bytes) failed (rc=-1)", sizeof(buf));
}
#endif /* HDIO_DRIVE_CMD */
- if (!flagcount)
- verbose = 1;
-
- if ((verbose && !is_scsi_hd && !is_xt_hd) || get_mult || get_identity) {
- no_scsi();
+ if (get_mult || get_identity) {
multcount = -1;
if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount)) {
- if ((verbose && !is_xt_hd) || get_mult)
- bb_perror_msg(" HDIO_GET_MULTCOUNT failed");
- } else if (verbose | get_mult) {
- printf(" multcount = %2ld", multcount);
- on_off(multcount);
- }
- }
- if ((verbose && !is_scsi_hd && !is_xt_hd) || get_io32bit) {
- no_scsi();
- no_xt();
- if (ioctl(fd, HDIO_GET_32BIT, &parm))
- bb_perror_msg(" HDIO_GET_32BIT failed");
- else {
- printf(" IO_support =%3ld (", parm);
- switch (parm) {
- case 0: printf("default ");
- case 2: printf("16-bit)\n");
- break;
- case 1: printf("32-bit)\n");
- break;
- case 3: printf("32-bit w/sync)\n");
- break;
- case 8: printf("Request-Queue-Bypass)\n");
- break;
- default:printf("\?\?\?)\n");
- }
+ if (get_mult && ENABLE_IOCTL_HEX2STR_ERROR) /* To be coherent with ioctl_or_warn. */
+ bb_perror_msg("HDIO_GET_MULTCOUNT");
+ else
+ bb_perror_msg("ioctl %#x failed", HDIO_GET_MULTCOUNT);
+ } else if (get_mult) {
+ printf(fmt, "multcount", multcount);
+ on_off(multcount != 0);
+ }
+ }
+ if (get_io32bit) {
+ if (!ioctl_or_warn(fd, HDIO_GET_32BIT, &parm)) {
+ printf(" IO_support\t=%3ld (", parm);
+ if (parm == 0)
+ printf("default 16-bit)\n");
+ else if (parm == 2)
+ printf("16-bit)\n");
+ else if (parm == 1)
+ printf("32-bit)\n");
+ else if (parm == 3)
+ printf("32-bit w/sync)\n");
+ else if (parm == 8)
+ printf("Request-Queue-Bypass)\n");
+ else
+ printf("\?\?\?)\n");
}
}
- if ((verbose && !is_scsi_hd && !is_xt_hd) || get_unmask) {
- no_scsi();
- no_xt();
- if (ioctl(fd, HDIO_GET_UNMASKINTR, &parm))
- bb_perror_msg(" HDIO_GET_UNMASKINTR failed");
- else {
- printf(" unmaskirq = %2ld", parm);
- on_off(parm);
- }
+ if (get_unmask) {
+ if (!ioctl_or_warn(fd, HDIO_GET_UNMASKINTR, &parm))
+ print_value_on_off("unmaskirq", parm);
}
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_GET_DMA
- if ((verbose && !is_scsi_hd) || get_dma) {
- no_scsi();
- if (ioctl(fd, HDIO_GET_DMA, &parm))
- bb_perror_msg(" HDIO_GET_DMA failed");
- else {
- printf(" using_dma = %2ld", parm);
+
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+ if (get_dma) {
+ if (!ioctl_or_warn(fd, HDIO_GET_DMA, &parm)) {
+ printf(fmt, "using_dma", parm);
if (parm == 8)
printf(" (DMA-Assisted-PIO)\n");
else
- on_off(parm);
+ on_off(parm != 0);
}
}
#endif
+#ifdef HDIO_GET_QDMA
if (get_dma_q) {
- no_scsi();
- if(ioctl(fd, HDIO_GET_QDMA, &parm)) {
- bb_perror_msg(" HDIO_GET_QDMA failed");
- } else {
- printf(" queue_depth = %2ld", parm);
- on_off(parm);
- }
+ if (!ioctl_or_warn(fd, HDIO_GET_QDMA, &parm))
+ print_value_on_off("queue_depth", parm);
}
- if ((verbose && !is_scsi_hd && !is_xt_hd) || get_keep) {
- no_scsi();
- no_xt();
- if (ioctl(fd, HDIO_GET_KEEPSETTINGS, &parm))
- bb_perror_msg(" HDIO_GET_KEEPSETTINGS failed");
- else {
- printf(" keepsettings = %2ld", parm);
- on_off(parm);
- }
+#endif
+ if (get_keep) {
+ if (!ioctl_or_warn(fd, HDIO_GET_KEEPSETTINGS, &parm))
+ print_value_on_off("keepsettings", parm);
}
if (get_nowerr) {
- no_scsi();
- no_xt();
- if (ioctl(fd, HDIO_GET_NOWERR, &parm))
- bb_perror_msg(" HDIO_GET_NOWERR failed");
- else {
- printf(" nowerr = %2ld", parm);
- on_off(parm);
- }
+ if (!ioctl_or_warn(fd, HDIO_GET_NOWERR, &parm))
+ print_value_on_off("nowerr", parm);
}
- if (verbose || get_readonly) {
- if (ioctl(fd, BLKROGET, &parm))
- bb_perror_msg(" BLKROGET failed");
- else {
- printf(" readonly = %2ld", parm);
- on_off(parm);
- }
+ if (get_readonly) {
+ if (!ioctl_or_warn(fd, BLKROGET, &parm))
+ print_value_on_off("readonly", parm);
}
- if ((verbose && !is_scsi_hd) || get_readahead) {
- if (ioctl(fd, BLKRAGET, &parm))
- bb_perror_msg(" BLKRAGET failed");
- else {
- printf(" readahead = %2ld", parm);
- on_off(parm);
- }
+ if (get_readahead) {
+ if (!ioctl_or_warn(fd, BLKRAGET, &parm))
+ print_value_on_off("readahead", parm);
}
- if (verbose || get_geom) {
- static const char msg[] = " geometry = %u/%u/%u, sectors = %ld, start = %ld\n";
- static struct hd_geometry g;
-#ifdef HDIO_GETGEO_BIG
- static struct hd_big_geometry bg;
-#endif
+ if (get_geom) {
+ if (!ioctl_or_warn(fd, BLKGETSIZE, &parm)) {
+ struct hd_geometry g;
- if (ioctl(fd, BLKGETSIZE, &parm))
- bb_perror_msg(" BLKGETSIZE failed");
-#ifdef HDIO_GETGEO_BIG
- else if (!ioctl(fd, HDIO_GETGEO_BIG, &bg))
- printf(msg, bg.cylinders, bg.heads, bg.sectors, parm, bg.start);
-#endif
- else if (ioctl(fd, HDIO_GETGEO, &g))
- bb_perror_msg(" HDIO_GETGEO failed");
- else printf(msg, g.cylinders, g.heads, g.sectors, parm, g.start);
+ if (!ioctl_or_warn(fd, HDIO_GETGEO, &g))
+ printf(" geometry\t= %u/%u/%u, sectors = %ld, start = %ld\n",
+ g.cylinders, g.heads, g.sectors, parm, g.start);
+ }
}
#ifdef HDIO_DRIVE_CMD
if (get_powermode) {
@@ -2162,47 +1815,38 @@ static void process_dev (char *devname)
#ifndef WIN_CHECKPOWERMODE2
#define WIN_CHECKPOWERMODE2 0x98
#endif
- unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
const char *state;
- no_scsi();
- if (ioctl(fd, HDIO_DRIVE_CMD, &args)
- && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
- && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
+
+ args[0] = WIN_CHECKPOWERMODE1;
+ if (ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_CHECKPOWERMODE2)) {
if (errno != EIO || args[0] != 0 || args[1] != 0)
state = "unknown";
else
state = "sleeping";
- } else {
+ } else
state = (args[2] == 255) ? "active/idle" : "standby";
- }
+ args[1] = args[2] = 0;
+
printf(" drive state is: %s\n", state);
}
#endif
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
+#if ENABLE_FEATURE_HDPARM_HDIO_DRIVE_RESET
if (perform_reset) {
- no_scsi();
- no_xt();
- if (ioctl(fd, HDIO_DRIVE_RESET, NULL))
- bb_perror_msg(" HDIO_DRIVE_RESET failed");
+ ioctl_or_warn(fd, HDIO_DRIVE_RESET, NULL);
}
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+#endif /* FEATURE_HDPARM_HDIO_DRIVE_RESET */
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
if (perform_tristate) {
- unsigned char args[4] = {0,tristate,0,0};
- no_scsi();
- no_xt();
- if (ioctl(fd, HDIO_TRISTATE_HWIF, &args))
- bb_perror_msg(" HDIO_TRISTATE_HWIF failed");
- }
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+ args[0] = 0;
+ args[1] = tristate;
+ ioctl_or_warn(fd, HDIO_TRISTATE_HWIF, &args);
+ }
+#endif /* FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
if (get_identity) {
- static struct hd_driveid id;
+ struct hd_driveid id;
- no_scsi();
- no_xt();
-
- if (!ioctl(fd, HDIO_GET_IDENTITY, &id)) {
+ if (!ioctl(fd, HDIO_GET_IDENTITY, &id)) {
if (multcount != -1) {
id.multsect = multcount;
id.multsect_valid |= 1;
@@ -2211,455 +1855,209 @@ static void process_dev (char *devname)
dump_identity(&id);
} else if (errno == -ENOMSG)
printf(" no identification info available\n");
+ else if (ENABLE_IOCTL_HEX2STR_ERROR) /* To be coherent with ioctl_or_warn */
+ bb_perror_msg("HDIO_GET_IDENTITY");
else
- bb_perror_msg(" HDIO_GET_IDENTITY failed");
+ bb_perror_msg("ioctl %#x failed", HDIO_GET_IDENTITY);
}
+
if (get_IDentity) {
- unsigned char args[4+512] = {WIN_IDENTIFY,0,0,1,};
- unsigned i;
- no_scsi();
- no_xt();
- if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
- args[0] = WIN_PIDENTIFY;
- if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
- bb_perror_msg(" HDIO_DRIVE_CMD(identify) failed");
- goto identify_abort;
- }
- }
- for(i=0; i<(sizeof args)/2; i+=2) {
- __le16_to_cpus((uint16_t *)(&args[i]));
- }
- identify((void *)&args[4], NULL);
-identify_abort: ;
+ unsigned char args1[4+512]; /* = { ... } will eat 0.5k of rodata! */
+
+ memset(args1, 0, sizeof(args1));
+ args1[0] = WIN_IDENTIFY;
+ args1[3] = 1;
+ if (!ioctl_alt_or_warn(HDIO_DRIVE_CMD, args1, WIN_PIDENTIFY))
+ identify((void *)(args1 + 4));
}
#endif
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
if (set_busstate) {
- no_scsi();
if (get_busstate) {
- printf(" setting bus state to %d", busstate);
+ print_flag(1, "bus state", busstate);
bus_state_value(busstate);
}
- if (ioctl(fd, HDIO_SET_BUSSTATE, busstate))
- bb_perror_msg(" HDIO_SET_BUSSTATE failed");
+ ioctl_or_warn(fd, HDIO_SET_BUSSTATE, (int *)(unsigned long)busstate);
}
-#endif
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
if (get_busstate) {
- no_scsi();
- if (ioctl(fd, HDIO_GET_BUSSTATE, &parm))
- bb_perror_msg(" HDIO_GET_BUSSTATE failed");
- else {
- printf(" busstate = %2ld", parm);
+ if (!ioctl_or_warn(fd, HDIO_GET_BUSSTATE, &parm)) {
+ printf(fmt, "bus state", parm);
bus_state_value(parm);
}
}
#endif
- if (reread_partn) {
- if (ioctl(fd, BLKRRPART, NULL)) {
- bb_perror_msg(" BLKRRPART failed");
- }
- }
+ if (reread_partn)
+ ioctl_or_warn(fd, BLKRRPART, NULL);
if (do_ctimings)
- time_cache (fd);
+ do_time(1 /*,fd*/); /* time cache */
if (do_timings)
- time_device (fd);
+ do_time(0 /*,fd*/); /* time device */
if (do_flush)
- flush_buffer_cache (fd);
- close (fd);
+ flush_buffer_cache();
+ close(fd);
}
-#define GET_NUMBER(flag,num) num = 0; \
- if (!*p && argc && isdigit(**argv)) \
- p = *argv++, --argc; \
- while (isdigit(*p)) { \
- flag = 1; \
- num = (num * 10) + (*p++ - '0'); \
- }
-
-#define GET_STRING(flag, num) tmpstr = name; \
- tmpstr[0] = '\0'; \
- if (!*p && argc && isalnum(**argv)) \
- p = *argv++, --argc; \
- while (isalnum(*p) && (tmpstr - name) < 31) { \
- tmpstr[0] = *p++; \
- tmpstr[1] = '\0'; \
- ++tmpstr; \
- } \
- num = translate_xfermode(name); \
- if (num == -1) \
- flag = 0; \
- else \
- flag = 1;
-
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
-static int fromhex (unsigned char c)
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static int fromhex(unsigned char c)
{
- if (c >= 'a' && c <= 'f')
- return 10 + (c - 'a');
- if (c >= '0' && c <= '9')
+ if (isdigit(c))
return (c - '0');
+ if (c >= 'a' && c <= 'f')
+ return (c - ('a' - 10));
bb_error_msg_and_die("bad char: '%c' 0x%02x", c, c);
}
-static int identify_from_stdin (void)
+static void identify_from_stdin(void) NORETURN;
+static void identify_from_stdin(void)
{
- unsigned short sbuf[800];
- unsigned char buf[1600], *b = (unsigned char *)buf;
- int i, count = read(0, buf, 1280);
-
- if (count != 1280)
- bb_error_msg_and_die("read(1280 bytes) failed (rc=%d)", count);
- for (i = 0; count >= 4; ++i) {
- sbuf[i] = (fromhex(b[0]) << 12) | (fromhex(b[1]) << 8) | (fromhex(b[2]) << 4) | fromhex(b[3]);
- __le16_to_cpus((uint16_t *)(&sbuf[i]));
- b += 5;
- count -= 5;
- }
- identify(sbuf, NULL);
- return 0;
+ uint16_t sbuf[256];
+ unsigned char buf[1280];
+ unsigned char *b = (unsigned char *)buf;
+ int i;
+
+ xread(STDIN_FILENO, buf, 1280);
+
+ // Convert the newline-separated hex data into an identify block.
+
+ for (i = 0; i < 256; i++) {
+ int j;
+ for (j = 0; j < 4; j++)
+ sbuf[i] = (sbuf[i] << 4) + fromhex(*(b++));
+ }
+
+ // Parse the data.
+
+ identify(sbuf);
}
+#else
+void identify_from_stdin(void);
#endif
-/* our main() routine: */
-int hdparm_main(int argc, char **argv)
+/* busybox specific stuff */
+static void parse_opts(smallint *get, smallint *set, unsigned long *value, int min, int max)
{
- char c, *p;
- char *tmpstr;
- char name[32];
- int neg;
+ if (get) {
+ *get = 1;
+ }
+ if (optarg) {
+ *set = 1;
+ *value = xatol_range(optarg, min, max);
+ }
+}
- ++argv;
- if (!--argc)
- bb_show_usage();
- while (argc--) {
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
- if (!strcmp("-Istdin", *argv)) {
- exit(identify_from_stdin());
+static void parse_xfermode(int flag, smallint *get, smallint *set, int *value)
+{
+ if (flag) {
+ *get = 1;
+ if (optarg) {
+ *value = translate_xfermode(optarg);
+ *set = (*value > -1);
}
+ }
+}
+
+/*------- getopt short options --------*/
+static const char hdparm_options[] ALIGN1 =
+ "gfu::n::p:r::m::c::k::a::B:tT"
+ USE_FEATURE_HDPARM_GET_IDENTITY("iI")
+ USE_FEATURE_HDPARM_HDIO_GETSET_DMA("d::")
+#ifdef HDIO_DRIVE_CMD
+ "S:D:P:X:K:A:L:W:CyYzZ"
+#endif
+ USE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF("U:")
+#ifdef HDIO_GET_QDMA
+#ifdef HDIO_SET_QDMA
+ "Q:"
+#else
+ "Q"
#endif
- p = *argv++;
- if (*p == '-') {
- if (!*++p)
- bb_show_usage();
- while ((c = *p++)) {
- ++flagcount;
- switch (c) {
- case 'V':
- bb_error_msg_and_die("%s", VERSION);
- case 'v':
- verbose = 1;
- break;
-#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
- case 'I':
- get_IDentity = 1;
- break;
- case 'i':
- get_identity = 1;
- break;
#endif
- case 'g':
- get_geom = 1;
- break;
- case 'f':
- do_flush = 1;
- break;
- case 'q':
- quiet = 1;
- noisy = 0;
- break;
- case 'u':
- get_unmask = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_unmask = 1;
- unmask = *p++ - '0';
- }
- break;
- case 'd':
- get_dma = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p >= '0' && *p <= '9') {
- set_dma = 1;
- dma = *p++ - '0';
- }
- break;
- case 'n':
- get_nowerr = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_nowerr = 1;
- nowerr = *p++ - '0';
- }
- break;
- case 'p':
- noisy_piomode = noisy;
- noisy = 1;
- GET_STRING(set_piomode,piomode);
- break;
- case 'r':
- get_readonly = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_readonly = 1;
- readonly = *p++ - '0';
- }
- break;
- case 'm':
- get_mult = noisy;
- noisy = 1;
- GET_NUMBER(set_mult,mult);
- break;
- case 'c':
- get_io32bit = noisy;
- noisy = 1;
- GET_NUMBER(set_io32bit,io32bit);
- break;
+ USE_FEATURE_HDPARM_HDIO_DRIVE_RESET("w")
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF("x::b:")
+ USE_FEATURE_HDPARM_HDIO_SCAN_HWIF("R:");
+/*-------------------------------------*/
+
+/* our main() routine: */
+int hdparm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hdparm_main(int argc, char **argv)
+{
+ int c;
+ int flagcount = 0;
+
+ while ((c = getopt(argc, argv, hdparm_options)) >= 0) {
+ flagcount++;
+ USE_FEATURE_HDPARM_GET_IDENTITY(get_IDentity |= (c == 'I'));
+ USE_FEATURE_HDPARM_GET_IDENTITY(get_identity |= (c == 'i'));
+ get_geom |= (c == 'g');
+ do_flush |= (c == 'f');
+ if (c == 'u') parse_opts(&get_unmask, &set_unmask, &unmask, 0, 1);
+ USE_FEATURE_HDPARM_HDIO_GETSET_DMA(if (c == 'd') parse_opts(&get_dma, &set_dma, &dma, 0, 9));
+ if (c == 'n') parse_opts(&get_nowerr, &set_nowerr, &nowerr, 0, 1);
+ parse_xfermode((c == 'p'), &noisy_piomode, &set_piomode, &piomode);
+ if (c == 'r') parse_opts(&get_readonly, &set_readonly, &readonly, 0, 1);
+ if (c == 'm') parse_opts(&get_mult, &set_mult, &mult, 0, INT_MAX /*32*/);
+ if (c == 'c') parse_opts(&get_io32bit, &set_io32bit, &io32bit, 0, INT_MAX /*8*/);
+ if (c == 'k') parse_opts(&get_keep, &set_keep, &keep, 0, 1);
+ if (c == 'a') parse_opts(&get_readahead, &set_readahead, &Xreadahead, 0, INT_MAX);
+ if (c == 'B') parse_opts(&get_apmmode, &set_apmmode, &apmmode, 1, 255);
+ do_flush |= do_timings |= (c == 't');
+ do_flush |= do_ctimings |= (c == 'T');
#ifdef HDIO_DRIVE_CMD
- case 'S':
- get_standby = noisy;
- noisy = 1;
- GET_NUMBER(set_standby,standby_requested);
- if (!set_standby)
- bb_error_msg("-S: missing value");
- break;
-
- case 'D':
- get_defects = noisy;
- noisy = 1;
- GET_NUMBER(set_defects,defects);
- if (!set_defects)
- bb_error_msg("-D: missing value");
- break;
- case 'P':
- get_prefetch = noisy;
- noisy = 1;
- GET_NUMBER(set_prefetch,prefetch);
- if (!set_prefetch)
- bb_error_msg("-P: missing value");
- break;
-
- case 'X':
- get_xfermode = noisy;
- noisy = 1;
- GET_STRING(set_xfermode,xfermode_requested);
- if (!set_xfermode)
- bb_error_msg("-X: missing value");
- break;
-
- case 'K':
- get_dkeep = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_dkeep = 1;
- dkeep = *p++ - '0';
- } else
- bb_error_msg("-K: missing value (0/1)");
- break;
-
- case 'A':
- get_lookahead = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_lookahead = 1;
- lookahead = *p++ - '0';
- } else
- bb_error_msg("-A: missing value (0/1)");
- break;
-
- case 'L':
- get_doorlock = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_doorlock = 1;
- doorlock = *p++ - '0';
- } else
- bb_error_msg("-L: missing value (0/1)");
- break;
-
- case 'W':
- get_wcache = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_wcache = 1;
- wcache = *p++ - '0';
- } else
- bb_error_msg("-W: missing value (0/1)");
- break;
-
- case 'C':
- get_powermode = noisy;
- noisy = 1;
- break;
-
- case 'y':
- get_standbynow = noisy;
- noisy = 1;
- set_standbynow = 1;
- break;
-
- case 'Y':
- get_sleepnow = noisy;
- noisy = 1;
- set_sleepnow = 1;
- break;
-
- case 'z':
- reread_partn = 1;
- break;
-
- case 'Z':
- get_seagate = noisy;
- noisy = 1;
- set_seagate = 1;
- break;
-#endif /* HDIO_DRIVE_CMD */
- case 'k':
- get_keep = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- set_keep = 1;
- keep = *p++ - '0';
- }
- break;
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
- case 'U':
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
-
- if(! p) {
- bb_error_msg_and_die("expected hwif_nr");
- }
-
- sscanf(p++, "%i", &hwif);
-
- unregister_hwif = 1;
- break;
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF */
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
- case 'R':
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
-
- if(! p) {
- bb_error_msg_and_die("expected hwif_data");
- }
-
- sscanf(p++, "%i", &hwif_data);
-
- if (argc && isdigit(**argv))
- p = *argv++, --argc;
- else {
- bb_error_msg_and_die("expected hwif_ctrl");
- }
-
- sscanf(p, "%i", &hwif_ctrl);
-
- if (argc && isdigit(**argv))
- p = *argv++, --argc;
- else {
- bb_error_msg_and_die("expected hwif_irq");
- }
-
- sscanf(p, "%i", &hwif_irq);
-
- *p = '\0';
-
- scan_hwif = 1;
- break;
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF */
- case 'Q':
- get_dma_q = noisy;
- noisy = 1;
- neg = 0;
- GET_NUMBER(set_dma_q, dma_q);
- if (neg)
- dma_q = -dma_q;
- break;
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
- case 'w':
- perform_reset = 1;
- break;
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
- case 'x':
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- if (*p == '0' || *p == '1') {
- perform_tristate = 1;
- tristate = *p++ - '0';
- } else
- bb_error_msg("-x: missing value (0/1)");
- break;
-
-#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
- case 'a':
- get_readahead = noisy;
- noisy = 1;
- GET_NUMBER(set_readahead,readahead);
- break;
- case 'B':
- get_apmmode = noisy;
- noisy = 1;
- GET_NUMBER(set_apmmode,apmmode);
- if (!set_apmmode)
- printf("-B: missing value (1-255)");
- break;
- case 't':
- do_timings = 1;
- do_flush = 1;
- break;
- case 'T':
- do_ctimings = 1;
- do_flush = 1;
- break;
-#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
- case 'b':
- get_busstate = noisy;
- noisy = 1;
- if (!*p && argc && isdigit(**argv))
- p = *argv++, --argc;
- switch (*p) {
- case '0':
- case '1':
- case '2':
- set_busstate = 1;
- busstate = *p++ - '0';
- break;
- default:
- break;
- }
- break;
+ if (c == 'S') parse_opts(&get_standby, &set_standby, &standby_requested, 0, 255);
+ if (c == 'D') parse_opts(&get_defects, &set_defects, &defects, 0, INT_MAX);
+ if (c == 'P') parse_opts(&get_prefetch, &set_prefetch, &prefetch, 0, INT_MAX);
+ parse_xfermode((c == 'X'), &get_xfermode, &set_xfermode, &xfermode_requested);
+ if (c == 'K') parse_opts(&get_dkeep, &set_dkeep, &prefetch, 0, 1);
+ if (c == 'A') parse_opts(&get_lookahead, &set_lookahead, &lookahead, 0, 1);
+ if (c == 'L') parse_opts(&get_doorlock, &set_doorlock, &doorlock, 0, 1);
+ if (c == 'W') parse_opts(&get_wcache, &set_wcache, &wcache, 0, 1);
+ get_powermode |= (c == 'C');
+ get_standbynow = set_standbynow |= (c == 'y');
+ get_sleepnow = set_sleepnow |= (c == 'Y');
+ reread_partn |= (c == 'z');
+ get_seagate = set_seagate |= (c == 'Z');
#endif
- case 'h':
- default:
- bb_show_usage();
- }
- }
- if (!argc)
- bb_show_usage();
- } else {
- process_dev (p);
+ USE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(if (c == 'U') parse_opts(NULL, &unregister_hwif, &hwif, 0, INT_MAX));
+#ifdef HDIO_GET_QDMA
+ if (c == 'Q') {
+#ifdef HDIO_SET_QDMA
+ parse_opts(&get_dma_q, &set_dma_q, &dma_q, 0, INT_MAX);
+#else
+ parse_opts(&get_dma_q, NULL, NULL, 0, 0);
+#endif
+ }
+#endif
+ USE_FEATURE_HDPARM_HDIO_DRIVE_RESET(perform_reset = (c == 'r'));
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'x') parse_opts(NULL, &perform_tristate, &tristate, 0, 1));
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'b') parse_opts(&get_busstate, &set_busstate, &busstate, 0, 2));
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+ if (c == 'R') {
+ parse_opts(NULL, &scan_hwif, &hwif_data, 0, INT_MAX);
+ hwif_ctrl = xatoi_u((argv[optind]) ? argv[optind] : "");
+ hwif_irq = xatoi_u((argv[optind+1]) ? argv[optind+1] : "");
+ /* Move past the 2 additional arguments */
+ argv += 2;
+ argc -= 2;
}
+#endif
+ }
+ /* When no flags are given (flagcount = 0), -acdgkmnru is assumed. */
+ if (!flagcount) {
+ get_mult = get_io32bit = get_unmask = get_keep = get_readonly = get_readahead = get_geom = 1;
+ USE_FEATURE_HDPARM_HDIO_GETSET_DMA(get_dma = 1);
}
- return 0 ;
+ argv += optind;
+
+ if (!*argv) {
+ if (ENABLE_FEATURE_HDPARM_GET_IDENTITY && !isatty(STDIN_FILENO))
+ identify_from_stdin(); /* EXIT */
+ bb_show_usage();
+ }
+
+ do {
+ process_dev(*argv++);
+ } while (*argv);
+
+ return EXIT_SUCCESS;
}
diff --git a/release/src/router/busybox/miscutils/inotifyd.c b/release/src/router/busybox/miscutils/inotifyd.c
new file mode 100644
index 00000000..d6b5d246
--- /dev/null
+++ b/release/src/router/busybox/miscutils/inotifyd.c
@@ -0,0 +1,176 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * simple inotify daemon
+ * reports filesystem changes via userspace agent
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * Use as follows:
+ * # inotifyd /user/space/agent dir/or/file/being/watched[:mask] ...
+ *
+ * When a filesystem event matching the specified mask is occured on specified file (or directory)
+ * a userspace agent is spawned and given the following parameters:
+ * $1. actual event(s)
+ * $2. file (or directory) name
+ * $3. name of subfile (if any), in case of watching a directory
+ *
+ * E.g. inotifyd ./dev-watcher /dev:n
+ *
+ * ./dev-watcher can be, say:
+ * #!/bin/sh
+ * echo "We have new device in here! Hello, $3!"
+ *
+ * See below for mask names explanation.
+ */
+
+#include "libbb.h"
+#include <sys/inotify.h>
+
+static const char mask_names[] ALIGN1 =
+ "a" // 0x00000001 File was accessed
+ "c" // 0x00000002 File was modified
+ "e" // 0x00000004 Metadata changed
+ "w" // 0x00000008 Writtable file was closed
+ "0" // 0x00000010 Unwrittable file closed
+ "r" // 0x00000020 File was opened
+ "m" // 0x00000040 File was moved from X
+ "y" // 0x00000080 File was moved to Y
+ "n" // 0x00000100 Subfile was created
+ "d" // 0x00000200 Subfile was deleted
+ "D" // 0x00000400 Self was deleted
+ "M" // 0x00000800 Self was moved
+ "\0" // 0x00001000 (unused)
+ // Kernel events, always reported:
+ "u" // 0x00002000 Backing fs was unmounted
+ "o" // 0x00004000 Event queued overflowed
+ "x" // 0x00008000 File is no longer watched (usually deleted)
+;
+enum {
+ MASK_BITS = sizeof(mask_names) - 1
+};
+
+int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int inotifyd_main(int argc, char **argv)
+{
+ int n;
+ unsigned mask;
+ struct pollfd pfd;
+ char **watches; // names of files being watched
+ const char *args[5];
+
+ // sanity check: agent and at least one watch must be given
+ if (!argv[1] || !argv[2])
+ bb_show_usage();
+
+ argv++;
+ // inotify_add_watch will number watched files
+ // starting from 1, thus watches[0] is unimportant,
+ // and 1st file name is watches[1].
+ watches = argv;
+ args[0] = *argv;
+ args[4] = NULL;
+ argc -= 2; // number of files we watch
+
+ // open inotify
+ pfd.fd = inotify_init();
+ if (pfd.fd < 0)
+ bb_perror_msg_and_die("no kernel support");
+
+ // setup watches
+ while (*++argv) {
+ char *path = *argv;
+ char *masks = strchr(path, ':');
+
+ mask = 0x0fff; // assuming we want all non-kernel events
+ // if mask is specified ->
+ if (masks) {
+ *masks = '\0'; // split path and mask
+ // convert mask names to mask bitset
+ mask = 0;
+ while (*++masks) {
+ const char *found;
+ found = memchr(mask_names, *masks, MASK_BITS);
+ if (found)
+ mask |= (1 << (found - mask_names));
+ }
+ }
+ // add watch
+ n = inotify_add_watch(pfd.fd, path, mask);
+ if (n < 0)
+ bb_perror_msg_and_die("add watch (%s) failed", path);
+ //bb_error_msg("added %d [%s]:%4X", n, path, mask);
+ }
+
+ // setup signals
+ bb_signals(BB_FATAL_SIGS, record_signo);
+
+ // do watch
+ pfd.events = POLLIN;
+ while (1) {
+ int len;
+ void *buf;
+ struct inotify_event *ie;
+ again:
+ if (bb_got_signal)
+ break;
+ n = poll(&pfd, 1, -1);
+ // Signal interrupted us?
+ if (n < 0 && errno == EINTR)
+ goto again;
+ // Under Linux, above if() is not necessary.
+ // Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL,
+ // are not interrupting poll().
+ // Thus we can just break if n <= 0 (see below),
+ // because EINTR will happen only on SIGTERM et al.
+ // But this might be not true under other Unixes,
+ // and is generally way too subtle to depend on.
+ if (n <= 0) // strange error?
+ break;
+
+ // read out all pending events
+ // (NB: len must be int, not ssize_t or long!)
+ xioctl(pfd.fd, FIONREAD, &len);
+#define eventbuf bb_common_bufsiz1
+ ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len);
+ len = full_read(pfd.fd, buf, len);
+ // process events. N.B. events may vary in length
+ while (len > 0) {
+ int i;
+ // cache relevant events mask
+ unsigned m = ie->mask & ((1 << MASK_BITS) - 1);
+ if (m) {
+ char events[MASK_BITS + 1];
+ char *s = events;
+ for (i = 0; i < MASK_BITS; ++i, m >>= 1) {
+ if ((m & 1) && (mask_names[i] != '\0'))
+ *s++ = mask_names[i];
+ }
+ *s = '\0';
+// bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
+// ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
+ args[1] = events;
+ args[2] = watches[ie->wd];
+ args[3] = ie->len ? ie->name : NULL;
+ wait4pid(xspawn((char **)args));
+ // we are done if all files got final x event
+ if (ie->mask & 0x8000) {
+ if (--argc <= 0)
+ goto done;
+ inotify_rm_watch(pfd.fd, ie->wd);
+ }
+ }
+ // next event
+ i = sizeof(struct inotify_event) + ie->len;
+ len -= i;
+ ie = (void*)((char*)ie + i);
+ }
+ if (eventbuf != buf)
+ free(buf);
+ } // while (1)
+ done:
+ return bb_got_signal;
+}
diff --git a/release/src/router/busybox/miscutils/ionice.c b/release/src/router/busybox/miscutils/ionice.c
new file mode 100644
index 00000000..361c141b
--- /dev/null
+++ b/release/src/router/busybox/miscutils/ionice.c
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ionice implementation for busybox based on linux-utils-ng 2.14
+ *
+ * Copyright (C) 2008 by <u173034@informatik.uni-oldenburg.de>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/syscall.h>
+#include <asm/unistd.h>
+#include "libbb.h"
+
+static int ioprio_set(int which, int who, int ioprio)
+{
+ return syscall(SYS_ioprio_set, which, who, ioprio);
+}
+
+static int ioprio_get(int which, int who)
+{
+ return syscall(SYS_ioprio_get, which, who);
+}
+
+enum {
+ IOPRIO_WHO_PROCESS = 1,
+ IOPRIO_WHO_PGRP,
+ IOPRIO_WHO_USER
+};
+
+enum {
+ IOPRIO_CLASS_NONE,
+ IOPRIO_CLASS_RT,
+ IOPRIO_CLASS_BE,
+ IOPRIO_CLASS_IDLE
+};
+
+static const char to_prio[] = "none\0realtime\0best-effort\0idle";
+
+#define IOPRIO_CLASS_SHIFT 13
+
+int ionice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ionice_main(int argc UNUSED_PARAM, char **argv)
+{
+ /* Defaults */
+ int ioclass = 0;
+ int pri = 0;
+ int pid = 0; /* affect own porcess */
+ int opt;
+ enum {
+ OPT_n = 1,
+ OPT_c = 2,
+ OPT_p = 4,
+ };
+
+ /* Numeric params */
+ opt_complementary = "n+:c+:p+";
+ /* '+': stop at first non-option */
+ opt = getopt32(argv, "+n:c:p:", &pri, &ioclass, &pid);
+ argv += optind;
+
+ if (opt & OPT_c) {
+ if (ioclass > 3)
+ bb_error_msg_and_die("bad class %d", ioclass);
+// Do we need this (compat?)?
+// if (ioclass == IOPRIO_CLASS_NONE)
+// ioclass = IOPRIO_CLASS_BE;
+// if (ioclass == IOPRIO_CLASS_IDLE) {
+// //if (opt & OPT_n)
+// // bb_error_msg("ignoring priority for idle class");
+// pri = 7;
+// }
+ }
+
+ if (!(opt & (OPT_n|OPT_c))) {
+ if (!(opt & OPT_p) && *argv)
+ pid = xatoi_u(*argv);
+
+ pri = ioprio_get(IOPRIO_WHO_PROCESS, pid);
+ if (pri == -1)
+ bb_perror_msg_and_die("ioprio_%cet", 'g');
+
+ ioclass = (pri >> IOPRIO_CLASS_SHIFT) & 0x3;
+ pri &= 0xff;
+ printf((ioclass == IOPRIO_CLASS_IDLE) ? "%s\n" : "%s: prio %d\n",
+ nth_string(to_prio, ioclass), pri);
+ } else {
+//printf("pri=%d class=%d val=%x\n",
+//pri, ioclass, pri | (ioclass << IOPRIO_CLASS_SHIFT));
+ pri |= (ioclass << IOPRIO_CLASS_SHIFT);
+ if (ioprio_set(IOPRIO_WHO_PROCESS, pid, pri) == -1)
+ bb_perror_msg_and_die("ioprio_%cet", 's');
+ if (*argv) {
+ BB_EXECVP(*argv, argv);
+ bb_simple_perror_msg_and_die(*argv);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/miscutils/last.c b/release/src/router/busybox/miscutils/last.c
new file mode 100644
index 00000000..f8c30139
--- /dev/null
+++ b/release/src/router/busybox/miscutils/last.c
@@ -0,0 +1,133 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * last implementation for busybox
+ *
+ * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+#include <utmp.h>
+
+/* NB: ut_name and ut_user are the same field, use only one name (ut_user)
+ * to reduce confusion */
+
+#ifndef SHUTDOWN_TIME
+# define SHUTDOWN_TIME 254
+#endif
+
+/* Grr... utmp char[] members do not have to be nul-terminated.
+ * Do what we can while still keeping this reasonably small.
+ * Note: We are assuming the ut_id[] size is fixed at 4. */
+
+#if defined UT_LINESIZE \
+ && ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256))
+#error struct utmp member char[] size(s) have changed!
+#elif defined __UT_LINESIZE \
+ && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256))
+#error struct utmp member char[] size(s) have changed!
+#endif
+
+#if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \
+ OLD_TIME != 4
+#error Values for the ut_type field of struct utmp changed
+#endif
+
+int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int last_main(int argc, char **argv UNUSED_PARAM)
+{
+ struct utmp ut;
+ int n, file = STDIN_FILENO;
+ time_t t_tmp;
+ off_t pos;
+ static const char _ut_usr[] ALIGN1 =
+ "runlevel\0" "reboot\0" "shutdown\0";
+ static const char _ut_lin[] ALIGN1 =
+ "~\0" "{\0" "|\0" /* "LOGIN\0" "date\0" */;
+ enum {
+ TYPE_RUN_LVL = RUN_LVL, /* 1 */
+ TYPE_BOOT_TIME = BOOT_TIME, /* 2 */
+ TYPE_SHUTDOWN_TIME = SHUTDOWN_TIME
+ };
+ enum {
+ _TILDE = EMPTY, /* 0 */
+ TYPE_NEW_TIME, /* NEW_TIME, 3 */
+ TYPE_OLD_TIME /* OLD_TIME, 4 */
+ };
+
+ if (argc > 1) {
+ bb_show_usage();
+ }
+ file = xopen(bb_path_wtmp_file, O_RDONLY);
+
+ printf("%-10s %-14s %-18s %-12.12s %s\n",
+ "USER", "TTY", "HOST", "LOGIN", "TIME");
+ /* yikes. We reverse over the file and that is a not too elegant way */
+ pos = xlseek(file, 0, SEEK_END);
+ pos = lseek(file, pos - sizeof(ut), SEEK_SET);
+ while ((n = full_read(file, &ut, sizeof(ut))) > 0) {
+ if (n != sizeof(ut)) {
+ bb_perror_msg_and_die("short read");
+ }
+ n = index_in_strings(_ut_lin, ut.ut_line);
+ if (n == _TILDE) { /* '~' */
+#if 1
+/* do we really need to be cautious here? */
+ n = index_in_strings(_ut_usr, ut.ut_user);
+ if (++n > 0)
+ ut.ut_type = n != 3 ? n : SHUTDOWN_TIME;
+#else
+ if (strncmp(ut.ut_user, "shutdown", 8) == 0)
+ ut.ut_type = SHUTDOWN_TIME;
+ else if (strncmp(ut.ut_user, "reboot", 6) == 0)
+ ut.ut_type = BOOT_TIME;
+ else if (strncmp(ut.ut_user, "runlevel", 8) == 0)
+ ut.ut_type = RUN_LVL;
+#endif
+ } else {
+ if (ut.ut_user[0] == '\0' || strcmp(ut.ut_user, "LOGIN") == 0) {
+ /* Don't bother. This means we can't find how long
+ * someone was logged in for. Oh well. */
+ goto next;
+ }
+ if (ut.ut_type != DEAD_PROCESS
+ && ut.ut_user[0] && ut.ut_line[0]
+ ) {
+ ut.ut_type = USER_PROCESS;
+ }
+ if (strcmp(ut.ut_user, "date") == 0) {
+ if (n == TYPE_OLD_TIME) { /* '|' */
+ ut.ut_type = OLD_TIME;
+ }
+ if (n == TYPE_NEW_TIME) { /* '{' */
+ ut.ut_type = NEW_TIME;
+ }
+ }
+ }
+
+ if (ut.ut_type != USER_PROCESS) {
+ switch (ut.ut_type) {
+ case OLD_TIME:
+ case NEW_TIME:
+ case RUN_LVL:
+ case SHUTDOWN_TIME:
+ goto next;
+ case BOOT_TIME:
+ strcpy(ut.ut_line, "system boot");
+ }
+ }
+ /* manpages say ut_tv.tv_sec *is* time_t,
+ * but some systems have it wrong */
+ t_tmp = (time_t)ut.ut_tv.tv_sec;
+ printf("%-10s %-14s %-18s %-12.12s\n",
+ ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t_tmp) + 4);
+ next:
+ pos -= sizeof(ut);
+ if (pos <= 0)
+ break; /* done. */
+ xlseek(file, pos, SEEK_SET);
+ }
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/release/src/router/busybox/miscutils/last_fancy.c b/release/src/router/busybox/miscutils/last_fancy.c
new file mode 100644
index 00000000..f3ea0375
--- /dev/null
+++ b/release/src/router/busybox/miscutils/last_fancy.c
@@ -0,0 +1,297 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * (sysvinit like) last implementation
+ *
+ * Copyright (C) 2008 by Patricia Muscalu <patricia.muscalu@axis.com>
+ *
+ * Licensed under the GPLv2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+#include <utmp.h>
+
+/* NB: ut_name and ut_user are the same field, use only one name (ut_user)
+ * to reduce confusion */
+
+#ifndef SHUTDOWN_TIME
+# define SHUTDOWN_TIME 254
+#endif
+
+#define HEADER_FORMAT "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n"
+#define HEADER_LINE "USER", "TTY", \
+ INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
+#define HEADER_LINE_WIDE "USER", "TTY", \
+ INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
+
+enum {
+ NORMAL,
+ LOGGED,
+ DOWN,
+ REBOOT,
+ CRASH,
+ GONE
+};
+
+enum {
+ LAST_OPT_W = (1 << 0), /* -W wide */
+ LAST_OPT_f = (1 << 1), /* -f input file */
+ LAST_OPT_H = (1 << 2), /* -H header */
+};
+
+#define show_wide (option_mask32 & LAST_OPT_W)
+
+static void show_entry(struct utmp *ut, int state, time_t dur_secs)
+{
+ unsigned days, hours, mins;
+ char duration[32];
+ char login_time[17];
+ char logout_time[8];
+ const char *logout_str;
+ const char *duration_str;
+ time_t tmp;
+
+ /* manpages say ut_tv.tv_sec *is* time_t,
+ * but some systems have it wrong */
+ tmp = ut->ut_tv.tv_sec;
+ safe_strncpy(login_time, ctime(&tmp), 17);
+ snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11);
+
+ dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
+ /* unsigned int is easier to divide than time_t (which may be signed long) */
+ mins = dur_secs / 60;
+ days = mins / (24*60);
+ mins = mins % (24*60);
+ hours = mins / 60;
+ mins = mins % 60;
+
+// if (days) {
+ sprintf(duration, "(%u+%02u:%02u)", days, hours, mins);
+// } else {
+// sprintf(duration, " (%02u:%02u)", hours, mins);
+// }
+
+ logout_str = logout_time;
+ duration_str = duration;
+ switch (state) {
+ case NORMAL:
+ break;
+ case LOGGED:
+ logout_str = " still";
+ duration_str = "logged in";
+ break;
+ case DOWN:
+ logout_str = "- down ";
+ break;
+ case REBOOT:
+ break;
+ case CRASH:
+ logout_str = "- crash";
+ break;
+ case GONE:
+ logout_str = " gone";
+ duration_str = "- no logout";
+ break;
+ }
+
+ printf(HEADER_FORMAT,
+ ut->ut_user,
+ ut->ut_line,
+ show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
+ show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
+ ut->ut_host,
+ login_time,
+ logout_str,
+ duration_str);
+}
+
+static int get_ut_type(struct utmp *ut)
+{
+ if (ut->ut_line[0] == '~') {
+ if (strcmp(ut->ut_user, "shutdown") == 0) {
+ return SHUTDOWN_TIME;
+ }
+ if (strcmp(ut->ut_user, "reboot") == 0) {
+ return BOOT_TIME;
+ }
+ if (strcmp(ut->ut_user, "runlevel") == 0) {
+ return RUN_LVL;
+ }
+ return ut->ut_type;
+ }
+
+ if (ut->ut_user[0] == 0) {
+ return DEAD_PROCESS;
+ }
+
+ if ((ut->ut_type != DEAD_PROCESS)
+ && (strcmp(ut->ut_user, "LOGIN") != 0)
+ && ut->ut_user[0]
+ && ut->ut_line[0]
+ ) {
+ ut->ut_type = USER_PROCESS;
+ }
+
+ if (strcmp(ut->ut_user, "date") == 0) {
+ if (ut->ut_line[0] == '|') {
+ return OLD_TIME;
+ }
+ if (ut->ut_line[0] == '{') {
+ return NEW_TIME;
+ }
+ }
+ return ut->ut_type;
+}
+
+static int is_runlevel_shutdown(struct utmp *ut)
+{
+ if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int last_main(int argc UNUSED_PARAM, char **argv)
+{
+ struct utmp ut;
+ const char *filename = _PATH_WTMP;
+ llist_t *zlist;
+ off_t pos;
+ time_t start_time;
+ time_t boot_time;
+ time_t down_time;
+ int file;
+ unsigned opt;
+ smallint going_down;
+ smallint boot_down;
+
+ opt = getopt32(argv, "Wf:" /* "H" */, &filename);
+#ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
+ if (opt & LAST_OPT_H) {
+ /* Print header line */
+ if (opt & LAST_OPT_W) {
+ printf(HEADER_FORMAT, HEADER_LINE_WIDE);
+ } else {
+ printf(HEADER_FORMAT, HEADER_LINE);
+ }
+ }
+#endif
+
+ file = xopen(filename, O_RDONLY);
+ {
+ /* in case the file is empty... */
+ struct stat st;
+ fstat(file, &st);
+ start_time = st.st_ctime;
+ }
+
+ time(&down_time);
+ going_down = 0;
+ boot_down = NORMAL; /* 0 */
+ zlist = NULL;
+ boot_time = 0;
+ /* get file size, rounding down to last full record */
+ pos = xlseek(file, 0, SEEK_END) / sizeof(ut) * sizeof(ut);
+ for (;;) {
+ pos -= (off_t)sizeof(ut);
+ if (pos < 0) {
+ /* Beyond the beginning of the file boundary =>
+ * the whole file has been read. */
+ break;
+ }
+ xlseek(file, pos, SEEK_SET);
+ xread(file, &ut, sizeof(ut));
+ /* rewritten by each record, eventially will have
+ * first record's ut_tv.tv_sec: */
+ start_time = ut.ut_tv.tv_sec;
+
+ switch (get_ut_type(&ut)) {
+ case SHUTDOWN_TIME:
+ down_time = ut.ut_tv.tv_sec;
+ boot_down = DOWN;
+ going_down = 1;
+ break;
+ case RUN_LVL:
+ if (is_runlevel_shutdown(&ut)) {
+ down_time = ut.ut_tv.tv_sec;
+ going_down = 1;
+ boot_down = DOWN;
+ }
+ break;
+ case BOOT_TIME:
+ strcpy(ut.ut_line, "system boot");
+ show_entry(&ut, REBOOT, down_time);
+ boot_down = CRASH;
+ going_down = 1;
+ break;
+ case DEAD_PROCESS:
+ if (!ut.ut_line[0]) {
+ break;
+ }
+ /* add_entry */
+ llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
+ break;
+ case USER_PROCESS: {
+ int show;
+
+ if (!ut.ut_line[0]) {
+ break;
+ }
+ /* find_entry */
+ show = 1;
+ {
+ llist_t *el, *next;
+ for (el = zlist; el; el = next) {
+ struct utmp *up = (struct utmp *)el->data;
+ next = el->link;
+ if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) {
+ if (show) {
+ show_entry(&ut, NORMAL, up->ut_tv.tv_sec);
+ show = 0;
+ }
+ llist_unlink(&zlist, el);
+ free(el->data);
+ free(el);
+ }
+ }
+ }
+
+ if (show) {
+ int state = boot_down;
+
+ if (boot_time == 0) {
+ state = LOGGED;
+ /* Check if the process is alive */
+ if ((ut.ut_pid > 0)
+ && (kill(ut.ut_pid, 0) != 0)
+ && (errno == ESRCH)) {
+ state = GONE;
+ }
+ }
+ show_entry(&ut, state, boot_time);
+ }
+ /* add_entry */
+ llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
+ break;
+ }
+ }
+
+ if (going_down) {
+ boot_time = ut.ut_tv.tv_sec;
+ llist_free(zlist, free);
+ zlist = NULL;
+ going_down = 0;
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ llist_free(zlist, free);
+ }
+
+ printf("\nwtmp begins %s", ctime(&start_time));
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(file);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/release/src/router/busybox/miscutils/less.c b/release/src/router/busybox/miscutils/less.c
new file mode 100644
index 00000000..27855bbe
--- /dev/null
+++ b/release/src/router/busybox/miscutils/less.c
@@ -0,0 +1,1799 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini less implementation for busybox
+ *
+ * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * TODO:
+ * - Add more regular expression support - search modifiers, certain matches, etc.
+ * - Add more complex bracket searching - currently, nested brackets are
+ * not considered.
+ * - Add support for "F" as an input. This causes less to act in
+ * a similar way to tail -f.
+ * - Allow horizontal scrolling.
+ *
+ * Notes:
+ * - the inp file pointer is used so that keyboard input works after
+ * redirected input has been read from stdin
+ */
+
+#include <sched.h> /* sched_yield() */
+
+#include "libbb.h"
+#if ENABLE_FEATURE_LESS_REGEXP
+#include "xregex.h"
+#endif
+
+/* The escape codes for highlighted and normal text */
+#define HIGHLIGHT "\033[7m"
+#define NORMAL "\033[0m"
+/* The escape code to clear the screen */
+#define CLEAR "\033[H\033[J"
+/* The escape code to clear to end of line */
+#define CLEAR_2_EOL "\033[K"
+
+enum {
+/* Absolute max of lines eaten */
+ MAXLINES = CONFIG_FEATURE_LESS_MAXLINES,
+/* This many "after the end" lines we will show (at max) */
+ TILDES = 1,
+};
+
+/* Command line options */
+enum {
+ FLAG_E = 1 << 0,
+ FLAG_M = 1 << 1,
+ FLAG_m = 1 << 2,
+ FLAG_N = 1 << 3,
+ FLAG_TILDE = 1 << 4,
+ FLAG_I = 1 << 5,
+ FLAG_S = (1 << 6) * ENABLE_FEATURE_LESS_DASHCMD,
+/* hijack command line options variable for internal state vars */
+ LESS_STATE_MATCH_BACKWARDS = 1 << 15,
+};
+
+#if !ENABLE_FEATURE_LESS_REGEXP
+enum { pattern_valid = 0 };
+#endif
+
+struct globals {
+ int cur_fline; /* signed */
+ int kbd_fd; /* fd to get input from */
+ int less_gets_pos;
+/* last position in last line, taking into account tabs */
+ size_t last_line_pos;
+ unsigned max_fline;
+ unsigned max_lineno; /* this one tracks linewrap */
+ unsigned max_displayed_line;
+ unsigned width;
+#if ENABLE_FEATURE_LESS_WINCH
+ unsigned winch_counter;
+#endif
+ ssize_t eof_error; /* eof if 0, error if < 0 */
+ ssize_t readpos;
+ ssize_t readeof; /* must be signed */
+ const char **buffer;
+ const char **flines;
+ const char *empty_line_marker;
+ unsigned num_files;
+ unsigned current_file;
+ char *filename;
+ char **files;
+#if ENABLE_FEATURE_LESS_MARKS
+ unsigned num_marks;
+ unsigned mark_lines[15][2];
+#endif
+#if ENABLE_FEATURE_LESS_REGEXP
+ unsigned *match_lines;
+ int match_pos; /* signed! */
+ int wanted_match; /* signed! */
+ int num_matches;
+ regex_t pattern;
+ smallint pattern_valid;
+#endif
+ smallint terminated;
+ smalluint kbd_input_size;
+ struct termios term_orig, term_less;
+ char kbd_input[KEYCODE_BUFFER_SIZE];
+};
+#define G (*ptr_to_globals)
+#define cur_fline (G.cur_fline )
+#define kbd_fd (G.kbd_fd )
+#define less_gets_pos (G.less_gets_pos )
+#define last_line_pos (G.last_line_pos )
+#define max_fline (G.max_fline )
+#define max_lineno (G.max_lineno )
+#define max_displayed_line (G.max_displayed_line)
+#define width (G.width )
+#define winch_counter (G.winch_counter )
+/* This one is 100% not cached by compiler on read access */
+#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter)
+#define eof_error (G.eof_error )
+#define readpos (G.readpos )
+#define readeof (G.readeof )
+#define buffer (G.buffer )
+#define flines (G.flines )
+#define empty_line_marker (G.empty_line_marker )
+#define num_files (G.num_files )
+#define current_file (G.current_file )
+#define filename (G.filename )
+#define files (G.files )
+#define num_marks (G.num_marks )
+#define mark_lines (G.mark_lines )
+#if ENABLE_FEATURE_LESS_REGEXP
+#define match_lines (G.match_lines )
+#define match_pos (G.match_pos )
+#define num_matches (G.num_matches )
+#define wanted_match (G.wanted_match )
+#define pattern (G.pattern )
+#define pattern_valid (G.pattern_valid )
+#endif
+#define terminated (G.terminated )
+#define term_orig (G.term_orig )
+#define term_less (G.term_less )
+#define kbd_input_size (G.kbd_input_size )
+#define kbd_input (G.kbd_input )
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+ less_gets_pos = -1; \
+ empty_line_marker = "~"; \
+ num_files = 1; \
+ current_file = 1; \
+ eof_error = 1; \
+ terminated = 1; \
+ USE_FEATURE_LESS_REGEXP(wanted_match = -1;) \
+} while (0)
+
+/* flines[] are lines read from stdin, each in malloc'ed buffer.
+ * Line numbers are stored as uint32_t prepended to each line.
+ * Pointer is adjusted so that flines[i] points directly past
+ * line number. Accesor: */
+#define MEMPTR(p) ((char*)(p) - 4)
+#define LINENO(p) (*(uint32_t*)((p) - 4))
+
+
+/* Reset terminal input to normal */
+static void set_tty_cooked(void)
+{
+ fflush(stdout);
+ tcsetattr(kbd_fd, TCSANOW, &term_orig);
+}
+
+/* Move the cursor to a position (x,y), where (0,0) is the
+ top-left corner of the console */
+static void move_cursor(int line, int row)
+{
+ printf("\033[%u;%uH", line, row);
+}
+
+static void clear_line(void)
+{
+ printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
+}
+
+static void print_hilite(const char *str)
+{
+ printf(HIGHLIGHT"%s"NORMAL, str);
+}
+
+static void print_statusline(const char *str)
+{
+ clear_line();
+ printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
+}
+
+/* Exit the program gracefully */
+static void less_exit(int code)
+{
+ set_tty_cooked();
+ clear_line();
+ if (code < 0)
+ kill_myself_with_sig(- code); /* does not return */
+ exit(code);
+}
+
+#if (ENABLE_FEATURE_LESS_DASHCMD && ENABLE_FEATURE_LESS_LINENUMS) \
+ || ENABLE_FEATURE_LESS_WINCH
+static void re_wrap(void)
+{
+ int w = width;
+ int new_line_pos;
+ int src_idx;
+ int dst_idx;
+ int new_cur_fline = 0;
+ uint32_t lineno;
+ char linebuf[w + 1];
+ const char **old_flines = flines;
+ const char *s;
+ char **new_flines = NULL;
+ char *d;
+
+ if (option_mask32 & FLAG_N)
+ w -= 8;
+
+ src_idx = 0;
+ dst_idx = 0;
+ s = old_flines[0];
+ lineno = LINENO(s);
+ d = linebuf;
+ new_line_pos = 0;
+ while (1) {
+ *d = *s;
+ if (*d != '\0') {
+ new_line_pos++;
+ if (*d == '\t') /* tab */
+ new_line_pos += 7;
+ s++;
+ d++;
+ if (new_line_pos >= w) {
+ int sz;
+ /* new line is full, create next one */
+ *d = '\0';
+ next_new:
+ sz = (d - linebuf) + 1; /* + 1: NUL */
+ d = ((char*)xmalloc(sz + 4)) + 4;
+ LINENO(d) = lineno;
+ memcpy(d, linebuf, sz);
+ new_flines = xrealloc_vector(new_flines, 8, dst_idx);
+ new_flines[dst_idx] = d;
+ dst_idx++;
+ if (new_line_pos < w) {
+ /* if we came here thru "goto next_new" */
+ if (src_idx > max_fline)
+ break;
+ lineno = LINENO(s);
+ }
+ d = linebuf;
+ new_line_pos = 0;
+ }
+ continue;
+ }
+ /* *d == NUL: old line ended, go to next old one */
+ free(MEMPTR(old_flines[src_idx]));
+ /* btw, convert cur_fline... */
+ if (cur_fline == src_idx)
+ new_cur_fline = dst_idx;
+ src_idx++;
+ /* no more lines? finish last new line (and exit the loop) */
+ if (src_idx > max_fline)
+ goto next_new;
+ s = old_flines[src_idx];
+ if (lineno != LINENO(s)) {
+ /* this is not a continuation line!
+ * create next _new_ line too */
+ goto next_new;
+ }
+ }
+
+ free(old_flines);
+ flines = (const char **)new_flines;
+
+ max_fline = dst_idx - 1;
+ last_line_pos = new_line_pos;
+ cur_fline = new_cur_fline;
+ /* max_lineno is screen-size independent */
+#if ENABLE_FEATURE_LESS_REGEXP
+ pattern_valid = 0;
+#endif
+}
+#endif
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void fill_match_lines(unsigned pos);
+#else
+#define fill_match_lines(pos) ((void)0)
+#endif
+
+/* Devilishly complex routine.
+ *
+ * Has to deal with EOF and EPIPE on input,
+ * with line wrapping, with last line not ending in '\n'
+ * (possibly not ending YET!), with backspace and tabs.
+ * It reads input again if last time we got an EOF (thus supporting
+ * growing files) or EPIPE (watching output of slow process like make).
+ *
+ * Variables used:
+ * flines[] - array of lines already read. Linewrap may cause
+ * one source file line to occupy several flines[n].
+ * flines[max_fline] - last line, possibly incomplete.
+ * terminated - 1 if flines[max_fline] is 'terminated'
+ * (if there was '\n' [which isn't stored itself, we just remember
+ * that it was seen])
+ * max_lineno - last line's number, this one doesn't increment
+ * on line wrap, only on "real" new lines.
+ * readbuf[0..readeof-1] - small preliminary buffer.
+ * readbuf[readpos] - next character to add to current line.
+ * last_line_pos - screen line position of next char to be read
+ * (takes into account tabs and backspaces)
+ * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
+ */
+static void read_lines(void)
+{
+#define readbuf bb_common_bufsiz1
+ char *current_line, *p;
+ int w = width;
+ char last_terminated = terminated;
+#if ENABLE_FEATURE_LESS_REGEXP
+ unsigned old_max_fline = max_fline;
+ time_t last_time = 0;
+ unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */
+#endif
+
+ if (option_mask32 & FLAG_N)
+ w -= 8;
+
+ USE_FEATURE_LESS_REGEXP(again0:)
+
+ p = current_line = ((char*)xmalloc(w + 4)) + 4;
+ max_fline += last_terminated;
+ if (!last_terminated) {
+ const char *cp = flines[max_fline];
+ strcpy(p, cp);
+ p += strlen(current_line);
+ free(MEMPTR(flines[max_fline]));
+ /* last_line_pos is still valid from previous read_lines() */
+ } else {
+ last_line_pos = 0;
+ }
+
+ while (1) { /* read lines until we reach cur_fline or wanted_match */
+ *p = '\0';
+ terminated = 0;
+ while (1) { /* read chars until we have a line */
+ char c;
+ /* if no unprocessed chars left, eat more */
+ if (readpos >= readeof) {
+ ndelay_on(0);
+ eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf));
+ ndelay_off(0);
+ readpos = 0;
+ readeof = eof_error;
+ if (eof_error <= 0)
+ goto reached_eof;
+ }
+ c = readbuf[readpos];
+ /* backspace? [needed for manpages] */
+ /* <tab><bs> is (a) insane and */
+ /* (b) harder to do correctly, so we refuse to do it */
+ if (c == '\x8' && last_line_pos && p[-1] != '\t') {
+ readpos++; /* eat it */
+ last_line_pos--;
+ /* was buggy (p could end up <= current_line)... */
+ *--p = '\0';
+ continue;
+ }
+ {
+ size_t new_last_line_pos = last_line_pos + 1;
+ if (c == '\t') {
+ new_last_line_pos += 7;
+ new_last_line_pos &= (~7);
+ }
+ if ((int)new_last_line_pos >= w)
+ break;
+ last_line_pos = new_last_line_pos;
+ }
+ /* ok, we will eat this char */
+ readpos++;
+ if (c == '\n') {
+ terminated = 1;
+ last_line_pos = 0;
+ break;
+ }
+ /* NUL is substituted by '\n'! */
+ if (c == '\0') c = '\n';
+ *p++ = c;
+ *p = '\0';
+ } /* end of "read chars until we have a line" loop */
+ /* Corner case: linewrap with only "" wrapping to next line */
+ /* Looks ugly on screen, so we do not store this empty line */
+ if (!last_terminated && !current_line[0]) {
+ last_terminated = 1;
+ max_lineno++;
+ continue;
+ }
+ reached_eof:
+ last_terminated = terminated;
+ flines = xrealloc_vector(flines, 8, max_fline);
+
+ flines[max_fline] = (char*)xrealloc(MEMPTR(current_line), strlen(current_line) + 1 + 4) + 4;
+ LINENO(flines[max_fline]) = max_lineno;
+ if (terminated)
+ max_lineno++;
+
+ if (max_fline >= MAXLINES) {
+ eof_error = 0; /* Pretend we saw EOF */
+ break;
+ }
+ if (!(option_mask32 & FLAG_S)
+ ? (max_fline > cur_fline + max_displayed_line)
+ : (max_fline >= cur_fline
+ && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
+ ) {
+#if !ENABLE_FEATURE_LESS_REGEXP
+ break;
+#else
+ if (wanted_match >= num_matches) { /* goto_match called us */
+ fill_match_lines(old_max_fline);
+ old_max_fline = max_fline;
+ }
+ if (wanted_match < num_matches)
+ break;
+#endif
+ }
+ if (eof_error <= 0) {
+ if (eof_error < 0) {
+ if (errno == EAGAIN) {
+ /* not yet eof or error, reset flag (or else
+ * we will hog CPU - select() will return
+ * immediately */
+ eof_error = 1;
+ } else {
+ print_statusline("read error");
+ }
+ }
+#if !ENABLE_FEATURE_LESS_REGEXP
+ break;
+#else
+ if (wanted_match < num_matches) {
+ break;
+ } else { /* goto_match called us */
+ time_t t = time(NULL);
+ if (t != last_time) {
+ last_time = t;
+ if (--seconds_p1 == 0)
+ break;
+ }
+ sched_yield();
+ goto again0; /* go loop again (max 2 seconds) */
+ }
+#endif
+ }
+ max_fline++;
+ current_line = ((char*)xmalloc(w + 4)) + 4;
+ p = current_line;
+ last_line_pos = 0;
+ } /* end of "read lines until we reach cur_fline" loop */
+ fill_match_lines(old_max_fline);
+#if ENABLE_FEATURE_LESS_REGEXP
+ /* prevent us from being stuck in search for a match */
+ wanted_match = -1;
+#endif
+#undef readbuf
+}
+
+#if ENABLE_FEATURE_LESS_FLAGS
+/* Interestingly, writing calc_percent as a function saves around 32 bytes
+ * on my build. */
+static int calc_percent(void)
+{
+ unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1);
+ return p <= 100 ? p : 100;
+}
+
+/* Print a status line if -M was specified */
+static void m_status_print(void)
+{
+ int percentage;
+
+ if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
+ return;
+
+ clear_line();
+ printf(HIGHLIGHT"%s", filename);
+ if (num_files > 1)
+ printf(" (file %i of %i)", current_file, num_files);
+ printf(" lines %i-%i/%i ",
+ cur_fline + 1, cur_fline + max_displayed_line + 1,
+ max_fline + 1);
+ if (cur_fline >= (int)(max_fline - max_displayed_line)) {
+ printf("(END)"NORMAL);
+ if (num_files > 1 && current_file != num_files)
+ printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
+ return;
+ }
+ percentage = calc_percent();
+ printf("%i%%"NORMAL, percentage);
+}
+#endif
+
+/* Print the status line */
+static void status_print(void)
+{
+ const char *p;
+
+ if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
+ return;
+
+ /* Change the status if flags have been set */
+#if ENABLE_FEATURE_LESS_FLAGS
+ if (option_mask32 & (FLAG_M|FLAG_m)) {
+ m_status_print();
+ return;
+ }
+ /* No flags set */
+#endif
+
+ clear_line();
+ if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) {
+ bb_putchar(':');
+ return;
+ }
+ p = "(END)";
+ if (!cur_fline)
+ p = filename;
+ if (num_files > 1) {
+ printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
+ p, current_file, num_files);
+ return;
+ }
+ print_hilite(p);
+}
+
+static void cap_cur_fline(int nlines)
+{
+ int diff;
+ if (cur_fline < 0)
+ cur_fline = 0;
+ if (cur_fline + max_displayed_line > max_fline + TILDES) {
+ cur_fline -= nlines;
+ if (cur_fline < 0)
+ cur_fline = 0;
+ diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
+ /* As the number of lines requested was too large, we just move
+ to the end of the file */
+ if (diff > 0)
+ cur_fline += diff;
+ }
+}
+
+static const char controls[] ALIGN1 =
+ /* NUL: never encountered; TAB: not converted */
+ /**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+ "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
+static const char ctrlconv[] ALIGN1 =
+ /* '\n': it's a former NUL - subst with '@', not 'J' */
+ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
+ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
+
+static void lineno_str(char *nbuf9, const char *line)
+{
+ nbuf9[0] = '\0';
+ if (option_mask32 & FLAG_N) {
+ const char *fmt;
+ unsigned n;
+
+ if (line == empty_line_marker) {
+ memset(nbuf9, ' ', 8);
+ nbuf9[8] = '\0';
+ return;
+ }
+ /* Width of 7 preserves tab spacing in the text */
+ fmt = "%7u ";
+ n = LINENO(line) + 1;
+ if (n > 9999999) {
+ n %= 10000000;
+ fmt = "%07u ";
+ }
+ sprintf(nbuf9, fmt, n);
+ }
+}
+
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void print_found(const char *line)
+{
+ int match_status;
+ int eflags;
+ char *growline;
+ regmatch_t match_structs;
+
+ char buf[width];
+ char nbuf9[9];
+ const char *str = line;
+ char *p = buf;
+ size_t n;
+
+ while (*str) {
+ n = strcspn(str, controls);
+ if (n) {
+ if (!str[n]) break;
+ memcpy(p, str, n);
+ p += n;
+ str += n;
+ }
+ n = strspn(str, controls);
+ memset(p, '.', n);
+ p += n;
+ str += n;
+ }
+ strcpy(p, str);
+
+ /* buf[] holds quarantined version of str */
+
+ /* Each part of the line that matches has the HIGHLIGHT
+ and NORMAL escape sequences placed around it.
+ NB: we regex against line, but insert text
+ from quarantined copy (buf[]) */
+ str = buf;
+ growline = NULL;
+ eflags = 0;
+ goto start;
+
+ while (match_status == 0) {
+ char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
+ growline ? : "",
+ match_structs.rm_so, str,
+ match_structs.rm_eo - match_structs.rm_so,
+ str + match_structs.rm_so);
+ free(growline);
+ growline = new;
+ str += match_structs.rm_eo;
+ line += match_structs.rm_eo;
+ eflags = REG_NOTBOL;
+ start:
+ /* Most of the time doesn't find the regex, optimize for that */
+ match_status = regexec(&pattern, line, 1, &match_structs, eflags);
+ /* if even "" matches, treat it as "not a match" */
+ if (match_structs.rm_so >= match_structs.rm_eo)
+ match_status = 1;
+ }
+
+ lineno_str(nbuf9, line);
+ if (!growline) {
+ printf(CLEAR_2_EOL"%s%s\n", nbuf9, str);
+ return;
+ }
+ printf(CLEAR_2_EOL"%s%s%s\n", nbuf9, growline, str);
+ free(growline);
+}
+#else
+void print_found(const char *line);
+#endif
+
+static void print_ascii(const char *str)
+{
+ char buf[width];
+ char nbuf9[9];
+ char *p;
+ size_t n;
+
+ lineno_str(nbuf9, str);
+ printf(CLEAR_2_EOL"%s", nbuf9);
+
+ while (*str) {
+ n = strcspn(str, controls);
+ if (n) {
+ if (!str[n]) break;
+ printf("%.*s", (int) n, str);
+ str += n;
+ }
+ n = strspn(str, controls);
+ p = buf;
+ do {
+ if (*str == 0x7f)
+ *p++ = '?';
+ else if (*str == (char)0x9b)
+ /* VT100's CSI, aka Meta-ESC. Who's inventor? */
+ /* I want to know who committed this sin */
+ *p++ = '{';
+ else
+ *p++ = ctrlconv[(unsigned char)*str];
+ str++;
+ } while (--n);
+ *p = '\0';
+ print_hilite(buf);
+ }
+ puts(str);
+}
+
+/* Print the buffer */
+static void buffer_print(void)
+{
+ unsigned i;
+
+ move_cursor(0, 0);
+ for (i = 0; i <= max_displayed_line; i++)
+ if (pattern_valid)
+ print_found(buffer[i]);
+ else
+ print_ascii(buffer[i]);
+ status_print();
+}
+
+static void buffer_fill_and_print(void)
+{
+ unsigned i;
+#if ENABLE_FEATURE_LESS_DASHCMD
+ int fpos = cur_fline;
+
+ if (option_mask32 & FLAG_S) {
+ /* Go back to the beginning of this line */
+ while (fpos && LINENO(flines[fpos]) == LINENO(flines[fpos-1]))
+ fpos--;
+ }
+
+ i = 0;
+ while (i <= max_displayed_line && fpos <= max_fline) {
+ int lineno = LINENO(flines[fpos]);
+ buffer[i] = flines[fpos];
+ i++;
+ do {
+ fpos++;
+ } while ((fpos <= max_fline)
+ && (option_mask32 & FLAG_S)
+ && lineno == LINENO(flines[fpos])
+ );
+ }
+#else
+ for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
+ buffer[i] = flines[cur_fline + i];
+ }
+#endif
+ for (; i <= max_displayed_line; i++) {
+ buffer[i] = empty_line_marker;
+ }
+ buffer_print();
+}
+
+/* Move the buffer up and down in the file in order to scroll */
+static void buffer_down(int nlines)
+{
+ cur_fline += nlines;
+ read_lines();
+ cap_cur_fline(nlines);
+ buffer_fill_and_print();
+}
+
+static void buffer_up(int nlines)
+{
+ cur_fline -= nlines;
+ if (cur_fline < 0) cur_fline = 0;
+ read_lines();
+ buffer_fill_and_print();
+}
+
+static void buffer_line(int linenum)
+{
+ if (linenum < 0)
+ linenum = 0;
+ cur_fline = linenum;
+ read_lines();
+ if (linenum + max_displayed_line > max_fline)
+ linenum = max_fline - max_displayed_line + TILDES;
+ if (linenum < 0)
+ linenum = 0;
+ cur_fline = linenum;
+ buffer_fill_and_print();
+}
+
+static void open_file_and_read_lines(void)
+{
+ if (filename) {
+ xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
+ } else {
+ /* "less" with no arguments in argv[] */
+ /* For status line only */
+ filename = xstrdup(bb_msg_standard_input);
+ }
+ readpos = 0;
+ readeof = 0;
+ last_line_pos = 0;
+ terminated = 1;
+ read_lines();
+}
+
+/* Reinitialize everything for a new file - free the memory and start over */
+static void reinitialize(void)
+{
+ unsigned i;
+
+ if (flines) {
+ for (i = 0; i <= max_fline; i++)
+ free(MEMPTR(flines[i]));
+ free(flines);
+ flines = NULL;
+ }
+
+ max_fline = -1;
+ cur_fline = 0;
+ max_lineno = 0;
+ open_file_and_read_lines();
+ buffer_fill_and_print();
+}
+
+static ssize_t getch_nowait(void)
+{
+ int rd;
+ struct pollfd pfd[2];
+
+ pfd[0].fd = STDIN_FILENO;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = kbd_fd;
+ pfd[1].events = POLLIN;
+ again:
+ tcsetattr(kbd_fd, TCSANOW, &term_less);
+ /* NB: select/poll returns whenever read will not block. Therefore:
+ * if eof is reached, select/poll will return immediately
+ * because read will immediately return 0 bytes.
+ * Even if select/poll says that input is available, read CAN block
+ * (switch fd into O_NONBLOCK'ed mode to avoid it)
+ */
+ rd = 1;
+ /* Are we interested in stdin? */
+//TODO: reuse code for determining this
+ if (!(option_mask32 & FLAG_S)
+ ? !(max_fline > cur_fline + max_displayed_line)
+ : !(max_fline >= cur_fline
+ && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
+ ) {
+ if (eof_error > 0) /* did NOT reach eof yet */
+ rd = 0; /* yes, we are interested in stdin */
+ }
+ /* Position cursor if line input is done */
+ if (less_gets_pos >= 0)
+ move_cursor(max_displayed_line + 2, less_gets_pos + 1);
+ fflush(stdout);
+
+ if (kbd_input_size == 0) {
+#if ENABLE_FEATURE_LESS_WINCH
+ while (1) {
+ int r;
+ /* NB: SIGWINCH interrupts poll() */
+ r = poll(pfd + rd, 2 - rd, -1);
+ if (/*r < 0 && errno == EINTR &&*/ winch_counter)
+ return '\\'; /* anything which has no defined function */
+ if (r) break;
+ }
+#else
+ safe_poll(pfd + rd, 2 - rd, -1);
+#endif
+ }
+
+ /* We have kbd_fd in O_NONBLOCK mode, read inside read_key()
+ * would not block even if there is no input available */
+ rd = read_key(kbd_fd, &kbd_input_size, kbd_input);
+ if (rd == -1) {
+ if (errno == EAGAIN) {
+ /* No keyboard input available. Since poll() did return,
+ * we should have input on stdin */
+ read_lines();
+ buffer_fill_and_print();
+ goto again;
+ }
+ /* EOF/error (ssh session got killed etc) */
+ less_exit(0);
+ }
+ set_tty_cooked();
+ return rd;
+}
+
+/* Grab a character from input without requiring the return key. If the
+ * character is ASCII \033, get more characters and assign certain sequences
+ * special return codes. Note that this function works best with raw input. */
+static int less_getch(int pos)
+{
+ int i;
+
+ again:
+ less_gets_pos = pos;
+ i = getch_nowait();
+ less_gets_pos = -1;
+
+ /* Discard Ctrl-something chars */
+ if (i >= 0 && i < ' ' && i != 0x0d && i != 8)
+ goto again;
+ return i;
+}
+
+static char* less_gets(int sz)
+{
+ int c;
+ unsigned i = 0;
+ char *result = xzalloc(1);
+
+ while (1) {
+ c = '\0';
+ less_gets_pos = sz + i;
+ c = getch_nowait();
+ if (c == 0x0d) {
+ result[i] = '\0';
+ less_gets_pos = -1;
+ return result;
+ }
+ if (c == 0x7f)
+ c = 8;
+ if (c == 8 && i) {
+ printf("\x8 \x8");
+ i--;
+ }
+ if (c < ' ') /* filters out KEYCODE_xxx too (<0) */
+ continue;
+ if (i >= width - sz - 1)
+ continue; /* len limit */
+ bb_putchar(c);
+ result[i++] = c;
+ result = xrealloc(result, i+1);
+ }
+}
+
+static void examine_file(void)
+{
+ char *new_fname;
+
+ print_statusline("Examine: ");
+ new_fname = less_gets(sizeof("Examine: ") - 1);
+ if (!new_fname[0]) {
+ status_print();
+ err:
+ free(new_fname);
+ return;
+ }
+ if (access(new_fname, R_OK) != 0) {
+ print_statusline("Cannot read this file");
+ goto err;
+ }
+ free(filename);
+ filename = new_fname;
+ /* files start by = argv. why we assume that argv is infinitely long??
+ files[num_files] = filename;
+ current_file = num_files + 1;
+ num_files++; */
+ files[0] = filename;
+ num_files = current_file = 1;
+ reinitialize();
+}
+
+/* This function changes the file currently being paged. direction can be one of the following:
+ * -1: go back one file
+ * 0: go to the first file
+ * 1: go forward one file */
+static void change_file(int direction)
+{
+ if (current_file != ((direction > 0) ? num_files : 1)) {
+ current_file = direction ? current_file + direction : 1;
+ free(filename);
+ filename = xstrdup(files[current_file - 1]);
+ reinitialize();
+ } else {
+ print_statusline(direction > 0 ? "No next file" : "No previous file");
+ }
+}
+
+static void remove_current_file(void)
+{
+ unsigned i;
+
+ if (num_files < 2)
+ return;
+
+ if (current_file != 1) {
+ change_file(-1);
+ for (i = 3; i <= num_files; i++)
+ files[i - 2] = files[i - 1];
+ num_files--;
+ } else {
+ change_file(1);
+ for (i = 2; i <= num_files; i++)
+ files[i - 2] = files[i - 1];
+ num_files--;
+ current_file--;
+ }
+}
+
+static void colon_process(void)
+{
+ int keypress;
+
+ /* Clear the current line and print a prompt */
+ print_statusline(" :");
+
+ keypress = less_getch(2);
+ switch (keypress) {
+ case 'd':
+ remove_current_file();
+ break;
+ case 'e':
+ examine_file();
+ break;
+#if ENABLE_FEATURE_LESS_FLAGS
+ case 'f':
+ m_status_print();
+ break;
+#endif
+ case 'n':
+ change_file(1);
+ break;
+ case 'p':
+ change_file(-1);
+ break;
+ case 'q':
+ less_exit(EXIT_SUCCESS);
+ break;
+ case 'x':
+ change_file(0);
+ break;
+ }
+}
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void normalize_match_pos(int match)
+{
+ if (match >= num_matches)
+ match = num_matches - 1;
+ if (match < 0)
+ match = 0;
+ match_pos = match;
+}
+
+static void goto_match(int match)
+{
+ if (!pattern_valid)
+ return;
+ if (match < 0)
+ match = 0;
+ /* Try to find next match if eof isn't reached yet */
+ if (match >= num_matches && eof_error > 0) {
+ wanted_match = match; /* "I want to read until I see N'th match" */
+ read_lines();
+ }
+ if (num_matches) {
+ normalize_match_pos(match);
+ buffer_line(match_lines[match_pos]);
+ } else {
+ print_statusline("No matches found");
+ }
+}
+
+static void fill_match_lines(unsigned pos)
+{
+ if (!pattern_valid)
+ return;
+ /* Run the regex on each line of the current file */
+ while (pos <= max_fline) {
+ /* If this line matches */
+ if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
+ /* and we didn't match it last time */
+ && !(num_matches && match_lines[num_matches-1] == pos)
+ ) {
+ match_lines = xrealloc_vector(match_lines, 4, num_matches);
+ match_lines[num_matches++] = pos;
+ }
+ pos++;
+ }
+}
+
+static void regex_process(void)
+{
+ char *uncomp_regex, *err;
+
+ /* Reset variables */
+ free(match_lines);
+ match_lines = NULL;
+ match_pos = 0;
+ num_matches = 0;
+ if (pattern_valid) {
+ regfree(&pattern);
+ pattern_valid = 0;
+ }
+
+ /* Get the uncompiled regular expression from the user */
+ clear_line();
+ bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
+ uncomp_regex = less_gets(1);
+ if (!uncomp_regex[0]) {
+ free(uncomp_regex);
+ buffer_print();
+ return;
+ }
+
+ /* Compile the regex and check for errors */
+ err = regcomp_or_errmsg(&pattern, uncomp_regex,
+ (option_mask32 & FLAG_I) ? REG_ICASE : 0);
+ free(uncomp_regex);
+ if (err) {
+ print_statusline(err);
+ free(err);
+ return;
+ }
+
+ pattern_valid = 1;
+ match_pos = 0;
+ fill_match_lines(0);
+ while (match_pos < num_matches) {
+ if ((int)match_lines[match_pos] > cur_fline)
+ break;
+ match_pos++;
+ }
+ if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
+ match_pos--;
+
+ /* It's possible that no matches are found yet.
+ * goto_match() will read input looking for match,
+ * if needed */
+ goto_match(match_pos);
+}
+#endif
+
+static void number_process(int first_digit)
+{
+ unsigned i;
+ int num;
+ int keypress;
+ char num_input[sizeof(int)*4]; /* more than enough */
+
+ num_input[0] = first_digit;
+
+ /* Clear the current line, print a prompt, and then print the digit */
+ clear_line();
+ printf(":%c", first_digit);
+
+ /* Receive input until a letter is given */
+ i = 1;
+ while (i < sizeof(num_input)-1) {
+ keypress = less_getch(i + 1);
+ if ((unsigned)keypress > 255 || !isdigit(num_input[i]))
+ break;
+ num_input[i] = keypress;
+ bb_putchar(keypress);
+ i++;
+ }
+
+ num_input[i] = '\0';
+ num = bb_strtou(num_input, NULL, 10);
+ /* on format error, num == -1 */
+ if (num < 1 || num > MAXLINES) {
+ buffer_print();
+ return;
+ }
+
+ /* We now know the number and the letter entered, so we process them */
+ switch (keypress) {
+ case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
+ buffer_down(num);
+ break;
+ case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u':
+ buffer_up(num);
+ break;
+ case 'g': case '<': case 'G': case '>':
+ cur_fline = num + max_displayed_line;
+ read_lines();
+ buffer_line(num - 1);
+ break;
+ case 'p': case '%':
+ num = num * (max_fline / 100); /* + max_fline / 2; */
+ cur_fline = num + max_displayed_line;
+ read_lines();
+ buffer_line(num);
+ break;
+#if ENABLE_FEATURE_LESS_REGEXP
+ case 'n':
+ goto_match(match_pos + num);
+ break;
+ case '/':
+ option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+ case '?':
+ option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+#endif
+ }
+}
+
+#if ENABLE_FEATURE_LESS_DASHCMD
+static void flag_change(void)
+{
+ int keypress;
+
+ clear_line();
+ bb_putchar('-');
+ keypress = less_getch(1);
+
+ switch (keypress) {
+ case 'M':
+ option_mask32 ^= FLAG_M;
+ break;
+ case 'm':
+ option_mask32 ^= FLAG_m;
+ break;
+ case 'E':
+ option_mask32 ^= FLAG_E;
+ break;
+ case '~':
+ option_mask32 ^= FLAG_TILDE;
+ break;
+ case 'S':
+ option_mask32 ^= FLAG_S;
+ buffer_fill_and_print();
+ break;
+#if ENABLE_FEATURE_LESS_LINENUMS
+ case 'N':
+ option_mask32 ^= FLAG_N;
+ re_wrap();
+ buffer_fill_and_print();
+ break;
+#endif
+ }
+}
+
+#ifdef BLOAT
+static void show_flag_status(void)
+{
+ int keypress;
+ int flag_val;
+
+ clear_line();
+ bb_putchar('_');
+ keypress = less_getch(1);
+
+ switch (keypress) {
+ case 'M':
+ flag_val = option_mask32 & FLAG_M;
+ break;
+ case 'm':
+ flag_val = option_mask32 & FLAG_m;
+ break;
+ case '~':
+ flag_val = option_mask32 & FLAG_TILDE;
+ break;
+ case 'N':
+ flag_val = option_mask32 & FLAG_N;
+ break;
+ case 'E':
+ flag_val = option_mask32 & FLAG_E;
+ break;
+ default:
+ flag_val = 0;
+ break;
+ }
+
+ clear_line();
+ printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
+}
+#endif
+
+#endif /* ENABLE_FEATURE_LESS_DASHCMD */
+
+static void save_input_to_file(void)
+{
+ const char *msg = "";
+ char *current_line;
+ unsigned i;
+ FILE *fp;
+
+ print_statusline("Log file: ");
+ current_line = less_gets(sizeof("Log file: ")-1);
+ if (current_line[0]) {
+ fp = fopen_for_write(current_line);
+ if (!fp) {
+ msg = "Error opening log file";
+ goto ret;
+ }
+ for (i = 0; i <= max_fline; i++)
+ fprintf(fp, "%s\n", flines[i]);
+ fclose(fp);
+ msg = "Done";
+ }
+ ret:
+ print_statusline(msg);
+ free(current_line);
+}
+
+#if ENABLE_FEATURE_LESS_MARKS
+static void add_mark(void)
+{
+ int letter;
+
+ print_statusline("Mark: ");
+ letter = less_getch(sizeof("Mark: ") - 1);
+
+ if (isalpha(letter)) {
+ /* If we exceed 15 marks, start overwriting previous ones */
+ if (num_marks == 14)
+ num_marks = 0;
+
+ mark_lines[num_marks][0] = letter;
+ mark_lines[num_marks][1] = cur_fline;
+ num_marks++;
+ } else {
+ print_statusline("Invalid mark letter");
+ }
+}
+
+static void goto_mark(void)
+{
+ int letter;
+ int i;
+
+ print_statusline("Go to mark: ");
+ letter = less_getch(sizeof("Go to mark: ") - 1);
+ clear_line();
+
+ if (isalpha(letter)) {
+ for (i = 0; i <= num_marks; i++)
+ if (letter == mark_lines[i][0]) {
+ buffer_line(mark_lines[i][1]);
+ break;
+ }
+ if (num_marks == 14 && letter != mark_lines[14][0])
+ print_statusline("Mark not set");
+ } else
+ print_statusline("Invalid mark letter");
+}
+#endif
+
+#if ENABLE_FEATURE_LESS_BRACKETS
+static char opp_bracket(char bracket)
+{
+ switch (bracket) {
+ case '{': case '[': /* '}' == '{' + 2. Same for '[' */
+ bracket++;
+ case '(': /* ')' == '(' + 1 */
+ bracket++;
+ break;
+ case '}': case ']':
+ bracket--;
+ case ')':
+ bracket--;
+ break;
+ };
+ return bracket;
+}
+
+static void match_right_bracket(char bracket)
+{
+ unsigned i;
+
+ if (strchr(flines[cur_fline], bracket) == NULL) {
+ print_statusline("No bracket in top line");
+ return;
+ }
+ bracket = opp_bracket(bracket);
+ for (i = cur_fline + 1; i < max_fline; i++) {
+ if (strchr(flines[i], bracket) != NULL) {
+ buffer_line(i);
+ return;
+ }
+ }
+ print_statusline("No matching bracket found");
+}
+
+static void match_left_bracket(char bracket)
+{
+ int i;
+
+ if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
+ print_statusline("No bracket in bottom line");
+ return;
+ }
+
+ bracket = opp_bracket(bracket);
+ for (i = cur_fline + max_displayed_line; i >= 0; i--) {
+ if (strchr(flines[i], bracket) != NULL) {
+ buffer_line(i);
+ return;
+ }
+ }
+ print_statusline("No matching bracket found");
+}
+#endif /* FEATURE_LESS_BRACKETS */
+
+static void keypress_process(int keypress)
+{
+ switch (keypress) {
+ case KEYCODE_DOWN: case 'e': case 'j': case 0x0d:
+ buffer_down(1);
+ break;
+ case KEYCODE_UP: case 'y': case 'k':
+ buffer_up(1);
+ break;
+ case KEYCODE_PAGEDOWN: case ' ': case 'z': case 'f':
+ buffer_down(max_displayed_line + 1);
+ break;
+ case KEYCODE_PAGEUP: case 'w': case 'b':
+ buffer_up(max_displayed_line + 1);
+ break;
+ case 'd':
+ buffer_down((max_displayed_line + 1) / 2);
+ break;
+ case 'u':
+ buffer_up((max_displayed_line + 1) / 2);
+ break;
+ case KEYCODE_HOME: case 'g': case 'p': case '<': case '%':
+ buffer_line(0);
+ break;
+ case KEYCODE_END: case 'G': case '>':
+ cur_fline = MAXLINES;
+ read_lines();
+ buffer_line(cur_fline);
+ break;
+ case 'q': case 'Q':
+ less_exit(EXIT_SUCCESS);
+ break;
+#if ENABLE_FEATURE_LESS_MARKS
+ case 'm':
+ add_mark();
+ buffer_print();
+ break;
+ case '\'':
+ goto_mark();
+ buffer_print();
+ break;
+#endif
+ case 'r': case 'R':
+ buffer_print();
+ break;
+ /*case 'R':
+ full_repaint();
+ break;*/
+ case 's':
+ save_input_to_file();
+ break;
+ case 'E':
+ examine_file();
+ break;
+#if ENABLE_FEATURE_LESS_FLAGS
+ case '=':
+ m_status_print();
+ break;
+#endif
+#if ENABLE_FEATURE_LESS_REGEXP
+ case '/':
+ option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+ case 'n':
+ goto_match(match_pos + 1);
+ break;
+ case 'N':
+ goto_match(match_pos - 1);
+ break;
+ case '?':
+ option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+#endif
+#if ENABLE_FEATURE_LESS_DASHCMD
+ case '-':
+ flag_change();
+ buffer_print();
+ break;
+#ifdef BLOAT
+ case '_':
+ show_flag_status();
+ break;
+#endif
+#endif
+#if ENABLE_FEATURE_LESS_BRACKETS
+ case '{': case '(': case '[':
+ match_right_bracket(keypress);
+ break;
+ case '}': case ')': case ']':
+ match_left_bracket(keypress);
+ break;
+#endif
+ case ':':
+ colon_process();
+ break;
+ }
+
+ if (isdigit(keypress))
+ number_process(keypress);
+}
+
+static void sig_catcher(int sig)
+{
+ less_exit(- sig);
+}
+
+#if ENABLE_FEATURE_LESS_WINCH
+static void sigwinch_handler(int sig UNUSED_PARAM)
+{
+ winch_counter++;
+}
+#endif
+
+int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int less_main(int argc, char **argv)
+{
+ int keypress;
+
+ INIT_G();
+
+ /* TODO: -x: do not interpret backspace, -xx: tab also */
+ /* -xxx: newline also */
+ /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
+ getopt32(argv, "EMmN~I" USE_FEATURE_LESS_DASHCMD("S"));
+ argc -= optind;
+ argv += optind;
+ num_files = argc;
+ files = argv;
+
+ /* Another popular pager, most, detects when stdout
+ * is not a tty and turns into cat. This makes sense. */
+ if (!isatty(STDOUT_FILENO))
+ return bb_cat(argv);
+
+ if (!num_files) {
+ if (isatty(STDIN_FILENO)) {
+ /* Just "less"? No args and no redirection? */
+ bb_error_msg("missing filename");
+ bb_show_usage();
+ }
+ } else {
+ filename = xstrdup(files[0]);
+ }
+
+ if (option_mask32 & FLAG_TILDE)
+ empty_line_marker = "";
+
+ kbd_fd = open(CURRENT_TTY, O_RDONLY);
+ if (kbd_fd < 0)
+ return bb_cat(argv);
+ ndelay_on(kbd_fd);
+
+ tcgetattr(kbd_fd, &term_orig);
+ term_less = term_orig;
+ term_less.c_lflag &= ~(ICANON | ECHO);
+ term_less.c_iflag &= ~(IXON | ICRNL);
+ /*term_less.c_oflag &= ~ONLCR;*/
+ term_less.c_cc[VMIN] = 1;
+ term_less.c_cc[VTIME] = 0;
+
+ get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
+ /* 20: two tabstops + 4 */
+ if (width < 20 || max_displayed_line < 3)
+ return bb_cat(argv);
+ max_displayed_line -= 2;
+
+ /* We want to restore term_orig on exit */
+ bb_signals(BB_FATAL_SIGS, sig_catcher);
+#if ENABLE_FEATURE_LESS_WINCH
+ signal(SIGWINCH, sigwinch_handler);
+#endif
+
+ buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
+ reinitialize();
+ while (1) {
+#if ENABLE_FEATURE_LESS_WINCH
+ while (WINCH_COUNTER) {
+ again:
+ winch_counter--;
+ get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
+ /* 20: two tabstops + 4 */
+ if (width < 20)
+ width = 20;
+ if (max_displayed_line < 3)
+ max_displayed_line = 3;
+ max_displayed_line -= 2;
+ free(buffer);
+ buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
+ /* Avoid re-wrap and/or redraw if we already know
+ * we need to do it again. These ops are expensive */
+ if (WINCH_COUNTER)
+ goto again;
+ re_wrap();
+ if (WINCH_COUNTER)
+ goto again;
+ buffer_fill_and_print();
+ /* This took some time. Loop back and check,
+ * were there another SIGWINCH? */
+ }
+#endif
+ keypress = less_getch(-1); /* -1: do not position cursor */
+ keypress_process(keypress);
+ }
+}
+
+/*
+Help text of less version 418 is below.
+If you are implementing something, keeping
+key and/or command line switch compatibility is a good idea:
+
+
+ SUMMARY OF LESS COMMANDS
+
+ Commands marked with * may be preceded by a number, N.
+ Notes in parentheses indicate the behavior if N is given.
+ h H Display this help.
+ q :q Q :Q ZZ Exit.
+ ---------------------------------------------------------------------------
+ MOVING
+ e ^E j ^N CR * Forward one line (or N lines).
+ y ^Y k ^K ^P * Backward one line (or N lines).
+ f ^F ^V SPACE * Forward one window (or N lines).
+ b ^B ESC-v * Backward one window (or N lines).
+ z * Forward one window (and set window to N).
+ w * Backward one window (and set window to N).
+ ESC-SPACE * Forward one window, but don't stop at end-of-file.
+ d ^D * Forward one half-window (and set half-window to N).
+ u ^U * Backward one half-window (and set half-window to N).
+ ESC-) RightArrow * Left one half screen width (or N positions).
+ ESC-( LeftArrow * Right one half screen width (or N positions).
+ F Forward forever; like "tail -f".
+ r ^R ^L Repaint screen.
+ R Repaint screen, discarding buffered input.
+ ---------------------------------------------------
+ Default "window" is the screen height.
+ Default "half-window" is half of the screen height.
+ ---------------------------------------------------------------------------
+ SEARCHING
+ /pattern * Search forward for (N-th) matching line.
+ ?pattern * Search backward for (N-th) matching line.
+ n * Repeat previous search (for N-th occurrence).
+ N * Repeat previous search in reverse direction.
+ ESC-n * Repeat previous search, spanning files.
+ ESC-N * Repeat previous search, reverse dir. & spanning files.
+ ESC-u Undo (toggle) search highlighting.
+ ---------------------------------------------------
+ Search patterns may be modified by one or more of:
+ ^N or ! Search for NON-matching lines.
+ ^E or * Search multiple files (pass thru END OF FILE).
+ ^F or @ Start search at FIRST file (for /) or last file (for ?).
+ ^K Highlight matches, but don't move (KEEP position).
+ ^R Don't use REGULAR EXPRESSIONS.
+ ---------------------------------------------------------------------------
+ JUMPING
+ g < ESC-< * Go to first line in file (or line N).
+ G > ESC-> * Go to last line in file (or line N).
+ p % * Go to beginning of file (or N percent into file).
+ t * Go to the (N-th) next tag.
+ T * Go to the (N-th) previous tag.
+ { ( [ * Find close bracket } ) ].
+ } ) ] * Find open bracket { ( [.
+ ESC-^F <c1> <c2> * Find close bracket <c2>.
+ ESC-^B <c1> <c2> * Find open bracket <c1>
+ ---------------------------------------------------
+ Each "find close bracket" command goes forward to the close bracket
+ matching the (N-th) open bracket in the top line.
+ Each "find open bracket" command goes backward to the open bracket
+ matching the (N-th) close bracket in the bottom line.
+ m<letter> Mark the current position with <letter>.
+ '<letter> Go to a previously marked position.
+ '' Go to the previous position.
+ ^X^X Same as '.
+ ---------------------------------------------------
+ A mark is any upper-case or lower-case letter.
+ Certain marks are predefined:
+ ^ means beginning of the file
+ $ means end of the file
+ ---------------------------------------------------------------------------
+ CHANGING FILES
+ :e [file] Examine a new file.
+ ^X^V Same as :e.
+ :n * Examine the (N-th) next file from the command line.
+ :p * Examine the (N-th) previous file from the command line.
+ :x * Examine the first (or N-th) file from the command line.
+ :d Delete the current file from the command line list.
+ = ^G :f Print current file name.
+ ---------------------------------------------------------------------------
+ MISCELLANEOUS COMMANDS
+ -<flag> Toggle a command line option [see OPTIONS below].
+ --<name> Toggle a command line option, by name.
+ _<flag> Display the setting of a command line option.
+ __<name> Display the setting of an option, by name.
+ +cmd Execute the less cmd each time a new file is examined.
+ !command Execute the shell command with $SHELL.
+ |Xcommand Pipe file between current pos & mark X to shell command.
+ v Edit the current file with $VISUAL or $EDITOR.
+ V Print version number of "less".
+ ---------------------------------------------------------------------------
+ OPTIONS
+ Most options may be changed either on the command line,
+ or from within less by using the - or -- command.
+ Options may be given in one of two forms: either a single
+ character preceded by a -, or a name preceeded by --.
+ -? ........ --help
+ Display help (from command line).
+ -a ........ --search-skip-screen
+ Forward search skips current screen.
+ -b [N] .... --buffers=[N]
+ Number of buffers.
+ -B ........ --auto-buffers
+ Don't automatically allocate buffers for pipes.
+ -c ........ --clear-screen
+ Repaint by clearing rather than scrolling.
+ -d ........ --dumb
+ Dumb terminal.
+ -D [xn.n] . --color=xn.n
+ Set screen colors. (MS-DOS only)
+ -e -E .... --quit-at-eof --QUIT-AT-EOF
+ Quit at end of file.
+ -f ........ --force
+ Force open non-regular files.
+ -F ........ --quit-if-one-screen
+ Quit if entire file fits on first screen.
+ -g ........ --hilite-search
+ Highlight only last match for searches.
+ -G ........ --HILITE-SEARCH
+ Don't highlight any matches for searches.
+ -h [N] .... --max-back-scroll=[N]
+ Backward scroll limit.
+ -i ........ --ignore-case
+ Ignore case in searches that do not contain uppercase.
+ -I ........ --IGNORE-CASE
+ Ignore case in all searches.
+ -j [N] .... --jump-target=[N]
+ Screen position of target lines.
+ -J ........ --status-column
+ Display a status column at left edge of screen.
+ -k [file] . --lesskey-file=[file]
+ Use a lesskey file.
+ -L ........ --no-lessopen
+ Ignore the LESSOPEN environment variable.
+ -m -M .... --long-prompt --LONG-PROMPT
+ Set prompt style.
+ -n -N .... --line-numbers --LINE-NUMBERS
+ Don't use line numbers.
+ -o [file] . --log-file=[file]
+ Copy to log file (standard input only).
+ -O [file] . --LOG-FILE=[file]
+ Copy to log file (unconditionally overwrite).
+ -p [pattern] --pattern=[pattern]
+ Start at pattern (from command line).
+ -P [prompt] --prompt=[prompt]
+ Define new prompt.
+ -q -Q .... --quiet --QUIET --silent --SILENT
+ Quiet the terminal bell.
+ -r -R .... --raw-control-chars --RAW-CONTROL-CHARS
+ Output "raw" control characters.
+ -s ........ --squeeze-blank-lines
+ Squeeze multiple blank lines.
+ -S ........ --chop-long-lines
+ Chop long lines.
+ -t [tag] .. --tag=[tag]
+ Find a tag.
+ -T [tagsfile] --tag-file=[tagsfile]
+ Use an alternate tags file.
+ -u -U .... --underline-special --UNDERLINE-SPECIAL
+ Change handling of backspaces.
+ -V ........ --version
+ Display the version number of "less".
+ -w ........ --hilite-unread
+ Highlight first new line after forward-screen.
+ -W ........ --HILITE-UNREAD
+ Highlight first new line after any forward movement.
+ -x [N[,...]] --tabs=[N[,...]]
+ Set tab stops.
+ -X ........ --no-init
+ Don't use termcap init/deinit strings.
+ --no-keypad
+ Don't use termcap keypad init/deinit strings.
+ -y [N] .... --max-forw-scroll=[N]
+ Forward scroll limit.
+ -z [N] .... --window=[N]
+ Set size of window.
+ -" [c[c]] . --quotes=[c[c]]
+ Set shell quote characters.
+ -~ ........ --tilde
+ Don't display tildes after end of file.
+ -# [N] .... --shift=[N]
+ Horizontal scroll amount (0 = one half screen width)
+
+ ---------------------------------------------------------------------------
+ LINE EDITING
+ These keys can be used to edit text being entered
+ on the "command line" at the bottom of the screen.
+ RightArrow ESC-l Move cursor right one character.
+ LeftArrow ESC-h Move cursor left one character.
+ CNTL-RightArrow ESC-RightArrow ESC-w Move cursor right one word.
+ CNTL-LeftArrow ESC-LeftArrow ESC-b Move cursor left one word.
+ HOME ESC-0 Move cursor to start of line.
+ END ESC-$ Move cursor to end of line.
+ BACKSPACE Delete char to left of cursor.
+ DELETE ESC-x Delete char under cursor.
+ CNTL-BACKSPACE ESC-BACKSPACE Delete word to left of cursor.
+ CNTL-DELETE ESC-DELETE ESC-X Delete word under cursor.
+ CNTL-U ESC (MS-DOS only) Delete entire line.
+ UpArrow ESC-k Retrieve previous command line.
+ DownArrow ESC-j Retrieve next command line.
+ TAB Complete filename & cycle.
+ SHIFT-TAB ESC-TAB Complete filename & reverse cycle.
+ CNTL-L Complete filename, list all.
+*/
diff --git a/release/src/router/busybox/miscutils/makedevs.c b/release/src/router/busybox/miscutils/makedevs.c
index 308d651a..be080552 100644
--- a/release/src/router/busybox/miscutils/makedevs.c
+++ b/release/src/router/busybox/miscutils/makedevs.c
@@ -1,39 +1,48 @@
/* vi: set sw=4 ts=4: */
/*
* public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
- *
+ *
* makedevs
- * Make ranges of device files quickly.
+ * Make ranges of device files quickly.
* known bugs: can't deal with alpha ranges
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include "busybox.h"
+#include "libbb.h"
+#if ENABLE_FEATURE_MAKEDEVS_LEAF
+/*
+makedevs NAME TYPE MAJOR MINOR FIRST LAST [s]
+TYPEs:
+b Block device
+c Character device
+f FIFO
+
+FIRST..LAST specify numbers appended to NAME.
+If 's' is the last argument, the base device is created as well.
+Examples:
+ makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63
+ makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8
+*/
+int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int makedevs_main(int argc, char **argv)
{
mode_t mode;
- char *basedev, *type, *nodname, buf[255];
- int major, Sminor, S, E;
+ char *basedev, *type, *nodname, *buf;
+ int Smajor, Sminor, S, E;
- if (argc < 7 || *argv[1]=='-')
+ if (argc < 7 || argv[1][0] == '-')
bb_show_usage();
basedev = argv[1];
+ buf = xasprintf("%s%u", argv[1], (unsigned)-1);
type = argv[2];
- major = atoi(argv[3]) << 8; /* correcting param to mknod() */
- Sminor = atoi(argv[4]);
- S = atoi(argv[5]);
- E = atoi(argv[6]);
- nodname = argc == 8 ? basedev : buf;
+ Smajor = xatoi_u(argv[3]);
+ Sminor = xatoi_u(argv[4]);
+ S = xatoi_u(argv[5]);
+ E = xatoi_u(argv[6]);
+ nodname = argv[7] ? basedev : buf;
mode = 0660;
-
switch (type[0]) {
case 'c':
mode |= S_IFCHR;
@@ -49,18 +58,14 @@ int makedevs_main(int argc, char **argv)
}
while (S <= E) {
- int sz;
-
- sz = snprintf(buf, sizeof(buf), "%s%d", basedev, S);
- if(sz<0 || sz>=sizeof(buf)) /* libc different */
- bb_error_msg_and_die("%s too large", basedev);
-
- /* if mode != S_IFCHR and != S_IFBLK third param in mknod() ignored */
+ sprintf(buf, "%s%u", basedev, S);
- if (mknod(nodname, mode, major | Sminor))
- bb_error_msg("Failed to create: %s", nodname);
+ /* if mode != S_IFCHR and != S_IFBLK,
+ * third param in mknod() ignored */
+ if (mknod(nodname, mode, makedev(Smajor, Sminor)))
+ bb_perror_msg("can't create %s", nodname);
- if (nodname == basedev) /* ex. /dev/hda - to /dev/hda1 ... */
+ /*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */
nodname = buf;
S++;
Sminor++;
@@ -69,24 +74,136 @@ int makedevs_main(int argc, char **argv)
return 0;
}
-/*
-And this is what this program replaces. The shell is too slow!
-
-makedev () {
-local basedev=$1; local S=$2; local E=$3
-local major=$4; local Sminor=$5; local type=$6
-local sbase=$7
-
- if [ ! "$sbase" = "" ]; then
- mknod "$basedev" $type $major $Sminor
- S=`expr $S + 1`
- Sminor=`expr $Sminor + 1`
- fi
-
- while [ $S -le $E ]; do
- mknod "$basedev$S" $type $major $Sminor
- S=`expr $S + 1`
- Sminor=`expr $Sminor + 1`
- done
+#elif ENABLE_FEATURE_MAKEDEVS_TABLE
+
+/* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. */
+
+int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int makedevs_main(int argc UNUSED_PARAM, char **argv)
+{
+ parser_t *parser;
+ char *line = (char *)"-";
+ int ret = EXIT_SUCCESS;
+
+ opt_complementary = "=1"; /* exactly one param */
+ getopt32(argv, "d:", &line);
+ argv += optind;
+
+ xchdir(*argv); /* ensure root dir exists */
+
+ umask(0);
+
+ printf("rootdir=%s\ntable=", *argv);
+ if (NOT_LONE_DASH(line)) {
+ printf("'%s'\n", line);
+ } else {
+ puts("<stdin>");
+ }
+
+ parser = config_open(line);
+ while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
+ int linenum;
+ char type;
+ unsigned mode = 0755;
+ unsigned major = 0;
+ unsigned minor = 0;
+ unsigned count = 0;
+ unsigned increment = 0;
+ unsigned start = 0;
+ char name[41];
+ char user[41];
+ char group[41];
+ char *full_name = name;
+ uid_t uid;
+ gid_t gid;
+
+ linenum = parser->lineno;
+
+ if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u",
+ name, &type, &mode, user, group,
+ &major, &minor, &start, &increment, &count))
+ || ((unsigned)(major | minor | start | count | increment) > 255)
+ ) {
+ bb_error_msg("invalid line %d: '%s'", linenum, line);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+
+ gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
+ uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
+ /* We are already in the right root dir,
+ * so make absolute paths relative */
+ if ('/' == *full_name)
+ full_name++;
+
+ if (type == 'd') {
+ bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
+ if (chown(full_name, uid, gid) == -1) {
+ chown_fail:
+ bb_perror_msg("line %d: can't chown %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+ if (chmod(full_name, mode) < 0) {
+ chmod_fail:
+ bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+ } else if (type == 'f') {
+ struct stat st;
+ if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
+ bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+ if (chown(full_name, uid, gid) < 0)
+ goto chown_fail;
+ if (chmod(full_name, mode) < 0)
+ goto chmod_fail;
+ } else {
+ dev_t rdev;
+ unsigned i;
+ char *full_name_inc;
+
+ if (type == 'p') {
+ mode |= S_IFIFO;
+ } else if (type == 'c') {
+ mode |= S_IFCHR;
+ } else if (type == 'b') {
+ mode |= S_IFBLK;
+ } else {
+ bb_error_msg("line %d: unsupported file type %c", linenum, type);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+
+ full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2);
+ if (count)
+ count--;
+ for (i = start; i <= start + count; i++) {
+ sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i);
+ rdev = makedev(major, minor + (i - start) * increment);
+ if (mknod(full_name_inc, mode, rdev) < 0) {
+ bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc);
+ ret = EXIT_FAILURE;
+ } else if (chown(full_name_inc, uid, gid) < 0) {
+ bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc);
+ ret = EXIT_FAILURE;
+ } else if (chmod(full_name_inc, mode) < 0) {
+ bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc);
+ ret = EXIT_FAILURE;
+ }
+ }
+ free(full_name_inc);
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ config_close(parser);
+
+ return ret;
}
-*/
+
+#else
+# error makedevs configuration error, either leaf or table must be selected
+#endif
diff --git a/release/src/router/busybox/miscutils/man.c b/release/src/router/busybox/miscutils/man.c
new file mode 100644
index 00000000..672ddb1c
--- /dev/null
+++ b/release/src/router/busybox/miscutils/man.c
@@ -0,0 +1,278 @@
+/* mini man implementation for busybox
+ * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+enum {
+ OPT_a = 1, /* all */
+ OPT_w = 2, /* print path */
+};
+
+/* This is what I see on my desktop system being executed:
+
+(
+echo ".ll 12.4i"
+echo ".nr LL 12.4i"
+echo ".pl 1100i"
+gunzip -c '/usr/man/man1/bzip2.1.gz'
+echo ".\\\""
+echo ".pl \n(nlu+10"
+) | gtbl | nroff -Tlatin1 -mandoc | less
+
+*/
+
+#if ENABLE_FEATURE_SEAMLESS_LZMA
+#define Z_SUFFIX ".lzma"
+#elif ENABLE_FEATURE_SEAMLESS_BZ2
+#define Z_SUFFIX ".bz2"
+#elif ENABLE_FEATURE_SEAMLESS_GZ
+#define Z_SUFFIX ".gz"
+#else
+#define Z_SUFFIX ""
+#endif
+
+static int show_manpage(const char *pager, char *man_filename, int man, int level);
+
+static int run_pipe(const char *pager, char *man_filename, int man, int level)
+{
+ char *cmd;
+
+ /* Prevent man page link loops */
+ if (level > 10)
+ return 0;
+
+ if (access(man_filename, R_OK) != 0)
+ return 0;
+
+ if (option_mask32 & OPT_w) {
+ puts(man_filename);
+ return 1;
+ }
+
+ if (man) { /* man page, not cat page */
+ /* Is this a link to another manpage? */
+ /* The link has the following on the first line: */
+ /* ".so another_man_page" */
+
+ struct stat sb;
+ char *line;
+ char *linkname, *p;
+
+ /* On my system:
+ * man1/genhostid.1.gz: 203 bytes - smallest real manpage
+ * man2/path_resolution.2.gz: 114 bytes - largest link
+ */
+ xstat(man_filename, &sb);
+ if (sb.st_size > 300) /* err on the safe side */
+ goto ordinary_manpage;
+
+ line = xmalloc_open_zipped_read_close(man_filename, NULL);
+ if (!line || strncmp(line, ".so ", 4) != 0) {
+ free(line);
+ goto ordinary_manpage;
+ }
+ /* Example: man2/path_resolution.2.gz contains
+ * ".so man7/path_resolution.7\n<junk>"
+ */
+ *strchrnul(line, '\n') = '\0';
+ linkname = skip_whitespace(&line[4]);
+
+ /* If link has no slashes, we just replace man page name.
+ * If link has slashes (however many), we go back *once*.
+ * ".so zzz/ggg/page.3" does NOT go back two levels. */
+ p = strrchr(man_filename, '/');
+ if (!p)
+ goto ordinary_manpage;
+ *p = '\0';
+ if (strchr(linkname, '/')) {
+ p = strrchr(man_filename, '/');
+ if (!p)
+ goto ordinary_manpage;
+ *p = '\0';
+ }
+
+ /* Links do not have .gz extensions, even if manpage
+ * is compressed */
+ man_filename = xasprintf("%s/%s" Z_SUFFIX, man_filename, linkname);
+ free(line);
+ /* Note: we leak "new" man_filename string as well... */
+ if (show_manpage(pager, man_filename, man, level + 1))
+ return 1;
+ /* else: show the link, it's better than nothing */
+ }
+
+ ordinary_manpage:
+ close(STDIN_FILENO);
+ open_zipped(man_filename); /* guaranteed to use fd 0 (STDIN_FILENO) */
+ /* "2>&1" is added so that nroff errors are shown in pager too.
+ * Otherwise it may show just empty screen */
+ cmd = xasprintf(
+ man ? "gtbl | nroff -Tlatin1 -mandoc 2>&1 | %s"
+ : "%s",
+ pager);
+ system(cmd);
+ free(cmd);
+ return 1;
+}
+
+/* man_filename is of the form "/dir/dir/dir/name.s" Z_SUFFIX */
+static int show_manpage(const char *pager, char *man_filename, int man, int level)
+{
+#if ENABLE_FEATURE_SEAMLESS_LZMA
+ if (run_pipe(pager, man_filename, man, level))
+ return 1;
+#endif
+
+#if ENABLE_FEATURE_SEAMLESS_BZ2
+#if ENABLE_FEATURE_SEAMLESS_LZMA
+ strcpy(strrchr(man_filename, '.') + 1, "bz2");
+#endif
+ if (run_pipe(pager, man_filename, man, level))
+ return 1;
+#endif
+
+#if ENABLE_FEATURE_SEAMLESS_GZ
+#if ENABLE_FEATURE_SEAMLESS_LZMA || ENABLE_FEATURE_SEAMLESS_BZ2
+ strcpy(strrchr(man_filename, '.') + 1, "gz");
+#endif
+ if (run_pipe(pager, man_filename, man, level))
+ return 1;
+#endif
+
+#if ENABLE_FEATURE_SEAMLESS_LZMA || ENABLE_FEATURE_SEAMLESS_BZ2 || ENABLE_FEATURE_SEAMLESS_GZ
+ *strrchr(man_filename, '.') = '\0';
+#endif
+ if (run_pipe(pager, man_filename, man, level))
+ return 1;
+
+ return 0;
+}
+
+int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int man_main(int argc UNUSED_PARAM, char **argv)
+{
+ parser_t *parser;
+ const char *pager;
+ char **man_path_list;
+ char *sec_list;
+ char *cur_path, *cur_sect;
+ int count_mp, cur_mp;
+ int opt, not_found;
+ char *token[2];
+
+ opt_complementary = "-1"; /* at least one argument */
+ opt = getopt32(argv, "+aw");
+ argv += optind;
+
+ sec_list = xstrdup("1:2:3:4:5:6:7:8:9");
+ /* Last valid man_path_list[] is [0x10] */
+ count_mp = 0;
+ man_path_list = xzalloc(0x11 * sizeof(man_path_list[0]));
+ man_path_list[0] = getenv("MANPATH");
+ if (!man_path_list[0]) /* default, may be overridden by /etc/man.conf */
+ man_path_list[0] = (char*)"/usr/man";
+ else
+ count_mp++;
+ pager = getenv("MANPAGER");
+ if (!pager) {
+ pager = getenv("PAGER");
+ if (!pager)
+ pager = "more";
+ }
+
+ /* Parse man.conf */
+ parser = config_open2("/etc/man.conf", fopen_for_read);
+ while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) {
+ if (!token[1])
+ continue;
+ if (strcmp("MANPATH", token[0]) == 0) {
+ char *path = token[1];
+ while (*path) {
+ char *next_path;
+ char **path_element;
+
+ next_path = strchr(path, ':');
+ if (next_path) {
+ *next_path = '\0';
+ if (next_path++ == path) /* "::"? */
+ goto next;
+ }
+ /* Do we already have path? */
+ path_element = man_path_list;
+ while (*path_element) {
+ if (strcmp(*path_element, path) == 0)
+ goto skip;
+ path_element++;
+ }
+ man_path_list = xrealloc_vector(man_path_list, 4, count_mp);
+ man_path_list[count_mp] = xstrdup(path);
+ count_mp++;
+ /* man_path_list is NULL terminated */
+ /*man_path_list[count_mp] = NULL; - xrealloc_vector did it */
+ skip:
+ if (!next_path)
+ break;
+ next:
+ path = next_path;
+ }
+ }
+ if (strcmp("MANSECT", token[0]) == 0) {
+ free(sec_list);
+ sec_list = xstrdup(token[1]);
+ }
+ }
+ config_close(parser);
+
+ not_found = 0;
+ do { /* for each argv[] */
+ int found = 0;
+ cur_mp = 0;
+
+ if (strchr(*argv, '/')) {
+ found = show_manpage(pager, *argv, /*man:*/ 1, 0);
+ goto check_found;
+ }
+ while ((cur_path = man_path_list[cur_mp++]) != NULL) {
+ /* for each MANPATH */
+ cur_sect = sec_list;
+ do { /* for each section */
+ char *next_sect = strchrnul(cur_sect, ':');
+ int sect_len = next_sect - cur_sect;
+ char *man_filename;
+ int cat0man1 = 0;
+
+ /* Search for cat, then man page */
+ while (cat0man1 < 2) {
+ int found_here;
+ man_filename = xasprintf("%s/%s%.*s/%s.%.*s" Z_SUFFIX,
+ cur_path,
+ "cat\0man" + (cat0man1 * 4),
+ sect_len, cur_sect,
+ *argv,
+ sect_len, cur_sect);
+ found_here = show_manpage(pager, man_filename, cat0man1, 0);
+ found |= found_here;
+ cat0man1 += found_here + 1;
+ free(man_filename);
+ }
+
+ if (found && !(opt & OPT_a))
+ goto next_arg;
+ cur_sect = next_sect;
+ while (*cur_sect == ':')
+ cur_sect++;
+ } while (*cur_sect);
+ }
+ check_found:
+ if (!found) {
+ bb_error_msg("no manual entry for '%s'", *argv);
+ not_found = 1;
+ }
+ next_arg:
+ argv++;
+ } while (*argv);
+
+ return not_found;
+}
diff --git a/release/src/router/busybox/miscutils/microcom.c b/release/src/router/busybox/miscutils/microcom.c
new file mode 100644
index 00000000..a322197b
--- /dev/null
+++ b/release/src/router/busybox/miscutils/microcom.c
@@ -0,0 +1,171 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bare bones 'talk to modem' program - similar to 'cu -l $device'
+ * inspired by mgetty's microcom
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
+
+// set raw tty mode
+static void xget1(int fd, struct termios *t, struct termios *oldt)
+{
+ tcgetattr(fd, oldt);
+ *t = *oldt;
+ cfmakeraw(t);
+// t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
+// t->c_iflag &= ~(BRKINT|IXON|ICRNL);
+// t->c_oflag &= ~(ONLCR);
+// t->c_cc[VMIN] = 1;
+// t->c_cc[VTIME] = 0;
+}
+
+static int xset1(int fd, struct termios *tio, const char *device)
+{
+ int ret = tcsetattr(fd, TCSAFLUSH, tio);
+
+ if (ret) {
+ bb_perror_msg("can't tcsetattr for %s", device);
+ }
+ return ret;
+}
+
+int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int microcom_main(int argc UNUSED_PARAM, char **argv)
+{
+ int sfd;
+ int nfd;
+ struct pollfd pfd[2];
+ struct termios tio0, tiosfd, tio;
+ char *device_lock_file;
+ enum {
+ OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@
+ OPT_s = 1 << 1, // baudrate
+ OPT_d = 1 << 2, // wait for device response, ms
+ OPT_t = 1 << 3, // timeout, ms
+ };
+ speed_t speed = 9600;
+ int delay = -1;
+ int timeout = -1;
+ unsigned opts;
+
+ // fetch options
+ opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options
+ opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout);
+// argc -= optind;
+ argv += optind;
+
+ // try to create lock file in /var/lock
+ device_lock_file = (char *)bb_basename(argv[0]);
+ device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
+ sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
+ if (sfd < 0) {
+ // device already locked -> bail out
+ if (errno == EEXIST)
+ bb_perror_msg_and_die("can't create %s", device_lock_file);
+ // can't create lock -> don't care
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(device_lock_file);
+ device_lock_file = NULL;
+ } else {
+ // %4d to make concurrent mgetty (if any) happy.
+ // Mgetty treats 4-bytes lock files as binary,
+ // not text, PID. Making 5+ char file. Brrr...
+ fdprintf(sfd, "%4d\n", getpid());
+ close(sfd);
+ }
+
+ // setup signals
+ bb_signals(0
+ + (1 << SIGHUP)
+ + (1 << SIGINT)
+ + (1 << SIGTERM)
+ + (1 << SIGPIPE)
+ , record_signo);
+
+ // error exit code if we fail to open the device
+ bb_got_signal = 1;
+
+ // open device
+ sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (sfd < 0)
+ goto done;
+ fcntl(sfd, F_SETFL, 0);
+
+ // put device to "raw mode"
+ xget1(sfd, &tio, &tiosfd);
+ // set device speed
+ cfsetspeed(&tio, tty_value_to_baud(speed));
+ if (xset1(sfd, &tio, argv[0]))
+ goto done;
+
+ // put stdin to "raw mode" (if stdin is a TTY),
+ // handle one character at a time
+ if (isatty(STDIN_FILENO)) {
+ xget1(STDIN_FILENO, &tio, &tio0);
+ if (xset1(STDIN_FILENO, &tio, "stdin"))
+ goto done;
+ }
+
+ // main loop: check with poll(), then read/write bytes across
+ pfd[0].fd = sfd;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = STDIN_FILENO;
+ pfd[1].events = POLLIN;
+
+ bb_got_signal = 0;
+ nfd = 2;
+ while (!bb_got_signal && safe_poll(pfd, nfd, timeout) > 0) {
+ if (nfd > 1 && pfd[1].revents) {
+ char c;
+ // read from stdin -> write to device
+ if (safe_read(STDIN_FILENO, &c, 1) < 1) {
+ // don't poll stdin anymore if we got EOF/error
+ nfd--;
+ goto skip_write;
+ }
+ // do we need special processing?
+ if (!(opts & OPT_X)) {
+ // ^@ sends Break
+ if (VINTR == c) {
+ tcsendbreak(sfd, 0);
+ goto skip_write;
+ }
+ // ^X exits
+ if (24 == c)
+ break;
+ }
+ write(sfd, &c, 1);
+ if (delay >= 0)
+ safe_poll(pfd, 1, delay);
+skip_write: ;
+ }
+ if (pfd[0].revents) {
+#define iobuf bb_common_bufsiz1
+ ssize_t len;
+ // read from device -> write to stdout
+ len = safe_read(sfd, iobuf, sizeof(iobuf));
+ if (len > 0)
+ full_write(STDOUT_FILENO, iobuf, len);
+ else {
+ // EOF/error -> bail out
+ bb_got_signal = SIGHUP;
+ break;
+ }
+ }
+ }
+
+ // restore device mode
+ tcsetattr(sfd, TCSAFLUSH, &tiosfd);
+
+ if (isatty(STDIN_FILENO))
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
+
+done:
+ if (device_lock_file)
+ unlink(device_lock_file);
+
+ return bb_got_signal;
+}
diff --git a/release/src/router/busybox/miscutils/mountpoint.c b/release/src/router/busybox/miscutils/mountpoint.c
new file mode 100644
index 00000000..b541ce28
--- /dev/null
+++ b/release/src/router/busybox/miscutils/mountpoint.c
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mountpoint implementation for busybox
+ *
+ * Copyright (C) 2005 Bernhard Reutner-Fischer
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Based on sysvinit's mountpoint
+ */
+
+#include "libbb.h"
+
+int mountpoint_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mountpoint_main(int argc UNUSED_PARAM, char **argv)
+{
+ struct stat st;
+ const char *msg;
+ char *arg;
+ int rc, opt;
+
+ opt_complementary = "=1"; /* must have one argument */
+ opt = getopt32(argv, "qdxn");
+#define OPT_q (1)
+#define OPT_d (2)
+#define OPT_x (4)
+#define OPT_n (8)
+ arg = argv[optind];
+ msg = "%s";
+
+ rc = (opt & OPT_x) ? stat(arg, &st) : lstat(arg, &st);
+ if (rc != 0)
+ goto err;
+
+ if (opt & OPT_x) {
+ if (S_ISBLK(st.st_mode)) {
+ printf("%u:%u\n", major(st.st_rdev),
+ minor(st.st_rdev));
+ return EXIT_SUCCESS;
+ }
+ errno = 0; /* make perror_msg work as error_msg */
+ msg = "%s: not a block device";
+ goto err;
+ }
+
+ errno = ENOTDIR;
+ if (S_ISDIR(st.st_mode)) {
+ dev_t st_dev = st.st_dev;
+ ino_t st_ino = st.st_ino;
+ char *p = xasprintf("%s/..", arg);
+
+ if (stat(p, &st) == 0) {
+ //int is_mnt = (st_dev != st.st_dev) || (st_dev == st.st_dev && st_ino == st.st_ino);
+ int is_not_mnt = (st_dev == st.st_dev) && (st_ino != st.st_ino);
+
+ if (opt & OPT_d)
+ printf("%u:%u\n", major(st_dev), minor(st_dev));
+ if (opt & OPT_n)
+ printf("%s %s\n", find_block_device(arg), arg);
+ if (!(opt & (OPT_q | OPT_d | OPT_n)))
+ printf("%s is %sa mountpoint\n", arg, is_not_mnt ? "not " : "");
+ return is_not_mnt;
+ }
+ arg = p;
+ /* else: stat had set errno, just fall through */
+ }
+
+ err:
+ if (!(opt & OPT_q))
+ bb_perror_msg(msg, arg);
+ return EXIT_FAILURE;
+}
diff --git a/release/src/router/busybox/miscutils/mt.c b/release/src/router/busybox/miscutils/mt.c
index e7995455..586373d1 100644
--- a/release/src/router/busybox/miscutils/mt.c
+++ b/release/src/router/busybox/miscutils/mt.c
@@ -1,94 +1,116 @@
/* vi: set sw=4 ts=4: */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mtio.h>
-#include <sys/fcntl.h>
-#include "busybox.h"
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
-struct mt_opcodes {
- char *name;
- short value;
-};
+#include "libbb.h"
+#include <sys/mtio.h>
/* missing: eod/seod, stoptions, stwrthreshold, densities */
-static const struct mt_opcodes opcodes[] = {
- {"bsf", MTBSF},
- {"bsfm", MTBSFM},
- {"bsr", MTBSR},
- {"bss", MTBSS},
- {"datacompression", MTCOMPRESSION},
- {"eom", MTEOM},
- {"erase", MTERASE},
- {"fsf", MTFSF},
- {"fsfm", MTFSFM},
- {"fsr", MTFSR},
- {"fss", MTFSS},
- {"load", MTLOAD},
- {"lock", MTLOCK},
- {"mkpart", MTMKPART},
- {"nop", MTNOP},
- {"offline", MTOFFL},
- {"rewoffline", MTOFFL},
- {"ras1", MTRAS1},
- {"ras2", MTRAS2},
- {"ras3", MTRAS3},
- {"reset", MTRESET},
- {"retension", MTRETEN},
- {"rewind", MTREW},
- {"seek", MTSEEK},
- {"setblk", MTSETBLK},
- {"setdensity", MTSETDENSITY},
- {"drvbuffer", MTSETDRVBUFFER},
- {"setpart", MTSETPART},
- {"tell", MTTELL},
- {"wset", MTWSM},
- {"unload", MTUNLOAD},
- {"unlock", MTUNLOCK},
- {"eof", MTWEOF},
- {"weof", MTWEOF},
- {0, 0}
+static const short opcode_value[] = {
+ MTBSF,
+ MTBSFM,
+ MTBSR,
+ MTBSS,
+ MTCOMPRESSION,
+ MTEOM,
+ MTERASE,
+ MTFSF,
+ MTFSFM,
+ MTFSR,
+ MTFSS,
+ MTLOAD,
+ MTLOCK,
+ MTMKPART,
+ MTNOP,
+ MTOFFL,
+ MTOFFL,
+ MTRAS1,
+ MTRAS2,
+ MTRAS3,
+ MTRESET,
+ MTRETEN,
+ MTREW,
+ MTSEEK,
+ MTSETBLK,
+ MTSETDENSITY,
+ MTSETDRVBUFFER,
+ MTSETPART,
+ MTTELL,
+ MTWSM,
+ MTUNLOAD,
+ MTUNLOCK,
+ MTWEOF,
+ MTWEOF
};
-extern int mt_main(int argc, char **argv)
+static const char opcode_name[] ALIGN1 =
+ "bsf" "\0"
+ "bsfm" "\0"
+ "bsr" "\0"
+ "bss" "\0"
+ "datacompression" "\0"
+ "eom" "\0"
+ "erase" "\0"
+ "fsf" "\0"
+ "fsfm" "\0"
+ "fsr" "\0"
+ "fss" "\0"
+ "load" "\0"
+ "lock" "\0"
+ "mkpart" "\0"
+ "nop" "\0"
+ "offline" "\0"
+ "rewoffline" "\0"
+ "ras1" "\0"
+ "ras2" "\0"
+ "ras3" "\0"
+ "reset" "\0"
+ "retension" "\0"
+ "rewind" "\0"
+ "seek" "\0"
+ "setblk" "\0"
+ "setdensity" "\0"
+ "drvbuffer" "\0"
+ "setpart" "\0"
+ "tell" "\0"
+ "wset" "\0"
+ "unload" "\0"
+ "unlock" "\0"
+ "eof" "\0"
+ "weof" "\0";
+
+int mt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mt_main(int argc UNUSED_PARAM, char **argv)
{
const char *file = "/dev/tape";
- const struct mt_opcodes *code = opcodes;
struct mtop op;
struct mtpos position;
- int fd, mode;
-
- if (argc < 2) {
+ int fd, mode, idx;
+
+ if (!argv[1]) {
bb_show_usage();
}
if (strcmp(argv[1], "-f") == 0) {
- if (argc < 4) {
+ if (!argv[2] || !argv[3])
bb_show_usage();
- }
file = argv[2];
argv += 2;
- argc -= 2;
}
- while (code->name != 0) {
- if (strcmp(code->name, argv[1]) == 0)
- break;
- code++;
- }
+ idx = index_in_strings(opcode_name, argv[1]);
- if (code->name == 0) {
- bb_error_msg("unrecognized opcode %s.", argv[1]);
- return EXIT_FAILURE;
- }
+ if (idx < 0)
+ bb_error_msg_and_die("unrecognized opcode %s", argv[1]);
- op.mt_op = code->value;
- if (argc >= 3)
- op.mt_count = atoi(argv[2]);
+ op.mt_op = opcode_value[idx];
+ if (argv[2])
+ op.mt_count = xatoi_u(argv[2]);
else
op.mt_count = 1; /* One, not zero, right? */
- switch (code->value) {
+ switch (opcode_value[idx]) {
case MTWEOF:
case MTERASE:
case MTWSM:
@@ -101,19 +123,16 @@ extern int mt_main(int argc, char **argv)
break;
}
- if ((fd = open(file, mode, 0)) < 0)
- bb_perror_msg_and_die("%s", file);
+ fd = xopen(file, mode);
- switch (code->value) {
+ switch (opcode_value[idx]) {
case MTTELL:
- if (ioctl(fd, MTIOCPOS, &position) < 0)
- bb_perror_msg_and_die("%s", file);
- printf ("At block %d.\n", (int) position.mt_blkno);
+ ioctl_or_perror_and_die(fd, MTIOCPOS, &position, "%s", file);
+ printf("At block %d\n", (int) position.mt_blkno);
break;
default:
- if (ioctl(fd, MTIOCTOP, &op) != 0)
- bb_perror_msg_and_die("%s", file);
+ ioctl_or_perror_and_die(fd, MTIOCTOP, &op, "%s", file);
break;
}
diff --git a/release/src/router/busybox/miscutils/raidautorun.c b/release/src/router/busybox/miscutils/raidautorun.c
new file mode 100644
index 00000000..a2a852bb
--- /dev/null
+++ b/release/src/router/busybox/miscutils/raidautorun.c
@@ -0,0 +1,25 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * raidautorun implementation for busybox
+ *
+ * Copyright (C) 2006 Bernhard Reutner-Fischer
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ */
+
+#include "libbb.h"
+
+#include <linux/major.h>
+#include <linux/raid/md_u.h>
+
+int raidautorun_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int raidautorun_main(int argc, char **argv)
+{
+ if (argc != 2)
+ bb_show_usage();
+
+ xioctl(xopen(argv[1], O_RDONLY), RAID_AUTORUN, NULL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/miscutils/readahead.c b/release/src/router/busybox/miscutils/readahead.c
new file mode 100644
index 00000000..fb71ce85
--- /dev/null
+++ b/release/src/router/busybox/miscutils/readahead.c
@@ -0,0 +1,40 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * readahead implementation for busybox
+ *
+ * Preloads the given files in RAM, to reduce access time.
+ * Does this by calling the readahead(2) system call.
+ *
+ * Copyright (C) 2006 Michael Opdenacker <michael@free-electrons.com>
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#include "libbb.h"
+
+int readahead_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int readahead_main(int argc, char **argv)
+{
+ int retval = EXIT_SUCCESS;
+
+ if (argc == 1) bb_show_usage();
+
+ while (*++argv) {
+ int fd = open_or_warn(*argv, O_RDONLY);
+ if (fd >= 0) {
+ off_t len;
+ int r;
+
+ /* fdlength was reported to be unreliable - use seek */
+ len = xlseek(fd, 0, SEEK_END);
+ xlseek(fd, 0, SEEK_SET);
+ r = readahead(fd, 0, len);
+ close(fd);
+ if (r >= 0)
+ continue;
+ }
+ retval = EXIT_FAILURE;
+ }
+
+ return retval;
+}
diff --git a/release/src/router/busybox/miscutils/runlevel.c b/release/src/router/busybox/miscutils/runlevel.c
new file mode 100644
index 00000000..6e10d9cb
--- /dev/null
+++ b/release/src/router/busybox/miscutils/runlevel.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * runlevel Prints out the previous and the current runlevel.
+ *
+ * Version: @(#)runlevel 1.20 16-Apr-1997 MvS
+ *
+ * This file is part of the sysvinit suite,
+ * Copyright 1991-1997 Miquel van Smoorenburg.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * initially busyboxified by Bernhard Reutner-Fischer
+ */
+
+#include <utmp.h>
+#include "libbb.h"
+
+int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int runlevel_main(int argc, char **argv)
+{
+ struct utmp *ut;
+ char prev;
+
+ if (argc > 1) utmpname(argv[1]);
+
+ setutent();
+ while ((ut = getutent()) != NULL) {
+ if (ut->ut_type == RUN_LVL) {
+ prev = ut->ut_pid / 256;
+ if (prev == 0) prev = 'N';
+ printf("%c %c\n", prev, ut->ut_pid % 256);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ endutent();
+ return 0;
+ }
+ }
+
+ puts("unknown");
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ endutent();
+ return 1;
+}
diff --git a/release/src/router/busybox/miscutils/rx.c b/release/src/router/busybox/miscutils/rx.c
new file mode 100644
index 00000000..94eb4522
--- /dev/null
+++ b/release/src/router/busybox/miscutils/rx.c
@@ -0,0 +1,254 @@
+/* vi: set sw=4 ts=4: */
+/*-------------------------------------------------------------------------
+ * Filename: xmodem.c
+ * Copyright: Copyright (C) 2001, Hewlett-Packard Company
+ * Author: Christopher Hoover <ch@hpl.hp.com>
+ * Description: xmodem functionality for uploading of kernels
+ * and the like
+ * Created at: Thu Dec 20 01:58:08 PST 2001
+ *-----------------------------------------------------------------------*/
+/*
+ * xmodem.c: xmodem functionality for uploading of kernels and
+ * the like
+ *
+ * Copyright (C) 2001 Hewlett-Packard Laboratories
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * This was originally written for blob and then adapted for busybox.
+ */
+
+#include "libbb.h"
+
+#define SOH 0x01
+#define STX 0x02
+#define EOT 0x04
+#define ACK 0x06
+#define NAK 0x15
+#define BS 0x08
+
+/*
+Cf:
+ http://www.textfiles.com/apple/xmodem
+ http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
+ http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
+ http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
+*/
+
+#define TIMEOUT 1
+#define TIMEOUT_LONG 10
+#define MAXERRORS 10
+
+#define read_fd STDIN_FILENO
+#define write_fd STDOUT_FILENO
+
+static int read_byte(unsigned timeout)
+{
+ char buf[1];
+ int n;
+
+ alarm(timeout);
+ /* NOT safe_read! We want ALRM to interrupt us */
+ n = read(read_fd, buf, 1);
+ alarm(0);
+ if (n == 1)
+ return (unsigned char)buf[0];
+ return -1;
+}
+
+static int receive(/*int read_fd, */int file_fd)
+{
+ unsigned char blockBuf[1024];
+ unsigned errors = 0;
+ unsigned wantBlockNo = 1;
+ unsigned length = 0;
+ int do_crc = 1;
+ char nak = 'C';
+ unsigned timeout = TIMEOUT_LONG;
+
+ /* Flush pending input */
+ tcflush(read_fd, TCIFLUSH);
+
+ /* Ask for CRC; if we get errors, we will go with checksum */
+ full_write(write_fd, &nak, 1);
+
+ for (;;) {
+ int blockBegin;
+ int blockNo, blockNoOnesCompl;
+ int blockLength;
+ int cksum_crc; /* cksum OR crc */
+ int expected;
+ int i,j;
+
+ blockBegin = read_byte(timeout);
+ if (blockBegin < 0)
+ goto timeout;
+
+ timeout = TIMEOUT;
+ nak = NAK;
+
+ switch (blockBegin) {
+ case SOH:
+ case STX:
+ break;
+
+ case EOT:
+ nak = ACK;
+ full_write(write_fd, &nak, 1);
+ return length;
+
+ default:
+ goto error;
+ }
+
+ /* block no */
+ blockNo = read_byte(TIMEOUT);
+ if (blockNo < 0)
+ goto timeout;
+
+ /* block no one's compliment */
+ blockNoOnesCompl = read_byte(TIMEOUT);
+ if (blockNoOnesCompl < 0)
+ goto timeout;
+
+ if (blockNo != (255 - blockNoOnesCompl)) {
+ bb_error_msg("bad block ones compl");
+ goto error;
+ }
+
+ blockLength = (blockBegin == SOH) ? 128 : 1024;
+
+ for (i = 0; i < blockLength; i++) {
+ int cc = read_byte(TIMEOUT);
+ if (cc < 0)
+ goto timeout;
+ blockBuf[i] = cc;
+ }
+
+ if (do_crc) {
+ cksum_crc = read_byte(TIMEOUT);
+ if (cksum_crc < 0)
+ goto timeout;
+ cksum_crc = (cksum_crc << 8) | read_byte(TIMEOUT);
+ if (cksum_crc < 0)
+ goto timeout;
+ } else {
+ cksum_crc = read_byte(TIMEOUT);
+ if (cksum_crc < 0)
+ goto timeout;
+ }
+
+ if (blockNo == ((wantBlockNo - 1) & 0xff)) {
+ /* a repeat of the last block is ok, just ignore it. */
+ /* this also ignores the initial block 0 which is */
+ /* meta data. */
+ goto next;
+ }
+ if (blockNo != (wantBlockNo & 0xff)) {
+ bb_error_msg("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
+ goto error;
+ }
+
+ expected = 0;
+ if (do_crc) {
+ for (i = 0; i < blockLength; i++) {
+ expected = expected ^ blockBuf[i] << 8;
+ for (j = 0; j < 8; j++) {
+ if (expected & 0x8000)
+ expected = expected << 1 ^ 0x1021;
+ else
+ expected = expected << 1;
+ }
+ }
+ expected &= 0xffff;
+ } else {
+ for (i = 0; i < blockLength; i++)
+ expected += blockBuf[i];
+ expected &= 0xff;
+ }
+ if (cksum_crc != expected) {
+ bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x"
+ : "checksum error, expected 0x%02x, got 0x%02x",
+ expected, cksum_crc);
+ goto error;
+ }
+
+ wantBlockNo++;
+ length += blockLength;
+
+ errno = 0;
+ if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
+ bb_perror_msg("can't write to file");
+ goto fatal;
+ }
+ next:
+ errors = 0;
+ nak = ACK;
+ full_write(write_fd, &nak, 1);
+ continue;
+ error:
+ timeout:
+ errors++;
+ if (errors == MAXERRORS) {
+ /* Abort */
+
+ /* if were asking for crc, try again w/o crc */
+ if (nak == 'C') {
+ nak = NAK;
+ errors = 0;
+ do_crc = 0;
+ goto timeout;
+ }
+ bb_error_msg("too many errors; giving up");
+ fatal:
+ /* 5 CAN followed by 5 BS. Don't try too hard... */
+ safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10);
+ return -1;
+ }
+
+ /* Flush pending input */
+ tcflush(read_fd, TCIFLUSH);
+
+ full_write(write_fd, &nak, 1);
+ } /* for (;;) */
+}
+
+static void sigalrm_handler(int UNUSED_PARAM signum)
+{
+}
+
+int rx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rx_main(int argc, char **argv)
+{
+ struct termios tty, orig_tty;
+ int termios_err;
+ int file_fd;
+ int n;
+
+ if (argc != 2)
+ bb_show_usage();
+
+ /* Disabled by vda:
+ * why we can't receive from stdin? Why we *require*
+ * controlling tty?? */
+ /*read_fd = xopen(CURRENT_TTY, O_RDWR);*/
+ file_fd = xopen(argv[1], O_RDWR|O_CREAT|O_TRUNC);
+
+ termios_err = tcgetattr(read_fd, &tty);
+ if (termios_err == 0) {
+ orig_tty = tty;
+ cfmakeraw(&tty);
+ tcsetattr(read_fd, TCSAFLUSH, &tty);
+ }
+
+ /* No SA_RESTART: we want ALRM to interrupt read() */
+ signal_no_SA_RESTART_empty_mask(SIGALRM, sigalrm_handler);
+
+ n = receive(file_fd);
+
+ if (termios_err == 0)
+ tcsetattr(read_fd, TCSAFLUSH, &orig_tty);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(file_fd);
+ fflush_stdout_and_exit(n >= 0);
+}
diff --git a/release/src/router/busybox/miscutils/setsid.c b/release/src/router/busybox/miscutils/setsid.c
new file mode 100644
index 00000000..d7de1f14
--- /dev/null
+++ b/release/src/router/busybox/miscutils/setsid.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setsid.c -- execute a command in a new session
+ * Rick Sladkey <jrs@world.std.com>
+ * In the public domain.
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2001-01-18 John Fremlin <vii@penguinpowered.com>
+ * - fork in case we are process group leader
+ *
+ * 2004-11-12 Paul Fox
+ * - busyboxed
+ */
+
+#include "libbb.h"
+
+int setsid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setsid_main(int argc UNUSED_PARAM, char **argv)
+{
+ if (!argv[1])
+ bb_show_usage();
+
+ /* setsid() is allowed only when we are not a process group leader.
+ * Otherwise our PID serves as PGID of some existing process group
+ * and cannot be used as PGID of a new process group. */
+ if (getpgrp() == getpid())
+ if (fork_or_rexec(argv))
+ exit(EXIT_SUCCESS); /* parent */
+
+ setsid(); /* no error possible */
+
+ BB_EXECVP(argv[1], argv + 1);
+ bb_simple_perror_msg_and_die(argv[1]);
+}
diff --git a/release/src/router/busybox/miscutils/strings.c b/release/src/router/busybox/miscutils/strings.c
index c914338d..fea9edbe 100644
--- a/release/src/router/busybox/miscutils/strings.c
+++ b/release/src/router/busybox/miscutils/strings.c
@@ -2,159 +2,82 @@
/*
* strings implementation for busybox
*
- * Copyright (c) 1980, 1987
- * The Regents of the University of California. All rights reserved.
+ * Copyright 2003 Tito Ragusa <farmatito@tiscali.it>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Original copyright notice is retained at the end of this file.
- *
- * Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
- * Badly hacked by Tito Ragusa <farmatito@tiscali.it>
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <ctype.h>
-#include "busybox.h"
+#include "libbb.h"
-#define ISSTR(ch) (isprint(ch) || ch == '\t')
+#define WHOLE_FILE 1
+#define PRINT_NAME 2
+#define PRINT_OFFSET 4
+#define SIZE 8
-int strings_main(int argc, char **argv)
+int strings_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int strings_main(int argc UNUSED_PARAM, char **argv)
{
- int n=4, c, i, opt=0, a=0, status=EXIT_SUCCESS;
- long t=0, count;
+ int n, c, status = EXIT_SUCCESS;
+ unsigned count;
+ off_t offset;
FILE *file;
- char *string=NULL;
-
- while ((i = getopt(argc, argv, "afon:")) > 0)
- switch(i)
- {
- case 'a':
- break;
- case 'f':
- opt+=1;
- break;
- case 'o':
- opt+=2;
- break;
- case 'n':
- n = bb_xgetlarg(optarg, 10, 1, INT_MAX);
- break;
- default:
- bb_show_usage();
- }
+ char *string;
+ const char *fmt = "%s: ";
+ const char *n_arg = "4";
- argc -= optind;
+ getopt32(argv, "afon:", &n_arg);
+ /* -a is our default behaviour */
+ /*argc -= optind;*/
argv += optind;
- i=0;
-
- string=xmalloc(n+1);
- string[n]='\0';
- n-=1;
+ n = xatou_range(n_arg, 1, INT_MAX);
+ string = xzalloc(n + 1);
+ n--;
- if(!argc )
- {
- file = stdin;
- goto pipe;
+ if (!*argv) {
+ fmt = "{%s}: ";
+ *--argv = (char *)bb_msg_standard_input;
}
- for(a=0;a<argc;a++)
- {
- if((file=fopen(argv[a],"r")))
- {
-pipe:
-
- count=0;
- do
- {
- c=fgetc(file);
- if(ISSTR(c))
- {
- if(i==0)
- t=count;
- if(i<=n)
- string[i]=c;
- if(i==n)
- {
- if(opt == 1 || opt == 3 )
- printf("%s: ", (!argv[a])? "{stdin}" : argv[a]);
- if(opt >= 2 )
- printf("%7lo ", t);
- printf("%s", string);
+ do {
+ file = fopen_or_warn_stdin(*argv);
+ if (!file) {
+ status = EXIT_FAILURE;
+ continue;
+ }
+ offset = 0;
+ count = 0;
+ do {
+ c = fgetc(file);
+ if (isprint(c) || c == '\t') {
+ if (count > n) {
+ bb_putchar(c);
+ } else {
+ string[count] = c;
+ if (count == n) {
+ if (option_mask32 & PRINT_NAME) {
+ printf(fmt, *argv);
+ }
+ if (option_mask32 & PRINT_OFFSET) {
+ printf("%7"OFF_FMT"o ", offset - n);
+ }
+ fputs(string, stdout);
}
- if(i>n)
- putchar(c);
- i++;
+ count++;
}
- else
- {
- if(i>n)
- puts("");
- i=0;
+ } else {
+ if (count > n) {
+ bb_putchar('\n');
}
- count++;
+ count = 0;
}
- while(c!=EOF);
+ offset++;
+ } while (c != EOF);
+ fclose_if_not_stdin(file);
+ } while (*++argv);
- if(file!=stdin)
- fclose(file);
- }
- else
- {
- bb_perror_msg("%s",argv[a]);
- status=EXIT_FAILURE;
- }
- }
- free(string);
- exit(status);
-}
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(string);
-/*
- * Copyright (c) 1980, 1987
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
- *
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
+ fflush_stdout_and_exit(status);
+}
diff --git a/release/src/router/busybox/miscutils/taskset.c b/release/src/router/busybox/miscutils/taskset.c
new file mode 100644
index 00000000..a0bbf0aa
--- /dev/null
+++ b/release/src/router/busybox/miscutils/taskset.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * taskset - retrieve or set a processes' CPU affinity
+ * Copyright (c) 2006 Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sched.h>
+#include "libbb.h"
+
+#if ENABLE_FEATURE_TASKSET_FANCY
+#define TASKSET_PRINTF_MASK "%s"
+/* craft a string from the mask */
+static char *from_cpuset(cpu_set_t *mask)
+{
+ int i;
+ char *ret = NULL;
+ char *str = xzalloc((CPU_SETSIZE / 4) + 1); /* we will leak it */
+
+ for (i = CPU_SETSIZE - 4; i >= 0; i -= 4) {
+ int val = 0;
+ int off;
+ for (off = 0; off <= 3; ++off)
+ if (CPU_ISSET(i + off, mask))
+ val |= 1 << off;
+ if (!ret && val)
+ ret = str;
+ *str++ = bb_hexdigits_upcase[val] | 0x20;
+ }
+ return ret;
+}
+#else
+#define TASKSET_PRINTF_MASK "%llx"
+static unsigned long long from_cpuset(cpu_set_t *mask)
+{
+ struct BUG_CPU_SETSIZE_is_too_small {
+ char BUG_CPU_SETSIZE_is_too_small[
+ CPU_SETSIZE < sizeof(int) ? -1 : 1];
+ };
+ char *p = (void*)mask;
+
+ /* Take the least significant bits. Careful!
+ * Consider both CPU_SETSIZE=4 and CPU_SETSIZE=1024 cases
+ */
+#if BB_BIG_ENDIAN
+ /* For big endian, it means LAST bits */
+ if (CPU_SETSIZE < sizeof(long))
+ p += CPU_SETSIZE - sizeof(int);
+ else if (CPU_SETSIZE < sizeof(long long))
+ p += CPU_SETSIZE - sizeof(long);
+ else
+ p += CPU_SETSIZE - sizeof(long long);
+#endif
+ if (CPU_SETSIZE < sizeof(long))
+ return *(unsigned*)p;
+ if (CPU_SETSIZE < sizeof(long long))
+ return *(unsigned long*)p;
+ return *(unsigned long long*)p;
+}
+#endif
+
+
+int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int taskset_main(int argc UNUSED_PARAM, char **argv)
+{
+ cpu_set_t mask;
+ pid_t pid = 0;
+ unsigned opt_p;
+ const char *current_new;
+ char *pid_str;
+ char *aff = aff; /* for compiler */
+
+ /* NB: we mimic util-linux's taskset: -p does not take
+ * an argument, i.e., "-pN" is NOT valid, only "-p N"!
+ * Indeed, util-linux-2.13-pre7 uses:
+ * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
+
+ opt_complementary = "-1"; /* at least 1 arg */
+ opt_p = getopt32(argv, "+p");
+ argv += optind;
+
+ if (opt_p) {
+ pid_str = *argv++;
+ if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */
+ aff = pid_str;
+ pid_str = *argv; /* NB: *argv != NULL in this case */
+ }
+ /* else it was just "-p <pid>", and *argv == NULL */
+ pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
+ } else {
+ aff = *argv++; /* <aff> <cmd...> */
+ if (!*argv)
+ bb_show_usage();
+ }
+
+ current_new = "current\0new";
+ if (opt_p) {
+ print_aff:
+ if (sched_getaffinity(pid, sizeof(mask), &mask) < 0)
+ bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid);
+ printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
+ pid, current_new, from_cpuset(&mask));
+ if (!*argv) {
+ /* Either it was just "-p <pid>",
+ * or it was "-p <aff> <pid>" and we came here
+ * for the second time (see goto below) */
+ return EXIT_SUCCESS;
+ }
+ *argv = NULL;
+ current_new += 8; /* "new" */
+ }
+
+ { /* Affinity was specified, translate it into cpu_set_t */
+ unsigned i;
+ /* Do not allow zero mask: */
+ unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
+ enum { CNT_BIT = CPU_SETSIZE < sizeof(m)*8 ? CPU_SETSIZE : sizeof(m)*8 };
+
+ CPU_ZERO(&mask);
+ for (i = 0; i < CNT_BIT; i++) {
+ unsigned long long bit = (1ULL << i);
+ if (bit & m)
+ CPU_SET(i, &mask);
+ }
+ }
+
+ /* Set pid's or our own (pid==0) affinity */
+ if (sched_setaffinity(pid, sizeof(mask), &mask))
+ bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid);
+
+ if (!*argv) /* "-p <aff> <pid> [...ignored...]" */
+ goto print_aff; /* print new affinity and exit */
+
+ BB_EXECVP(*argv, argv);
+ bb_simple_perror_msg_and_die(*argv);
+}
diff --git a/release/src/router/busybox/miscutils/time.c b/release/src/router/busybox/miscutils/time.c
index c30ef431..30298fe3 100644
--- a/release/src/router/busybox/miscutils/time.c
+++ b/release/src/router/busybox/miscutils/time.c
@@ -1,147 +1,85 @@
-/* `time' utility to display resource usage of processes.
+/* vi: set sw=4 ts=4: */
+/* 'time' utility to display resource usage of processes.
Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
-
+ Licensed under GPL version 2, see file LICENSE in this tarball for details.
+*/
/* Originally written by David Keppel <pardo@cs.washington.edu>.
- Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
+ Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <signal.h>
-#include <errno.h>
-#include <getopt.h>
-#include <string.h>
-#include <limits.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <sys/types.h> /* For pid_t. */
-#include <sys/wait.h>
-#include <sys/param.h> /* For getpagesize, maybe. */
-
-#define TV_MSEC tv_usec / 1000
-#include <sys/resource.h>
-#include "busybox.h"
+*/
+
+#include "libbb.h"
/* Information on the resources used by a child process. */
-typedef struct
-{
- int waitstatus;
- struct rusage ru;
- struct timeval start, elapsed; /* Wallclock time of process. */
+typedef struct {
+ int waitstatus;
+ struct rusage ru;
+ unsigned elapsed_ms; /* Wallclock time of process. */
} resource_t;
/* msec = milliseconds = 1/1,000 (1*10e-3) second.
usec = microseconds = 1/1,000,000 (1*10e-6) second. */
-#ifndef TICKS_PER_SEC
-#define TICKS_PER_SEC 100
-#endif
-
-/* The number of milliseconds in one `tick' used by the `rusage' structure. */
-#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
-
-/* Return the number of clock ticks that occur in M milliseconds. */
-#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
-
#define UL unsigned long
-static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
+static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
/* The output format for the -p option .*/
-static const char *const posix_format = "real %e\nuser %U\nsys %S";
-
+static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
/* Format string for printing all statistics verbosely.
Keep this output to 24 lines so users on terminals can see it all.*/
-static const char *const long_format =
- "\tCommand being timed: \"%C\"\n"
- "\tUser time (seconds): %U\n"
- "\tSystem time (seconds): %S\n"
- "\tPercent of CPU this job got: %P\n"
- "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
- "\tAverage shared text size (kbytes): %X\n"
- "\tAverage unshared data size (kbytes): %D\n"
- "\tAverage stack size (kbytes): %p\n"
- "\tAverage total size (kbytes): %K\n"
- "\tMaximum resident set size (kbytes): %M\n"
- "\tAverage resident set size (kbytes): %t\n"
- "\tMajor (requiring I/O) page faults: %F\n"
- "\tMinor (reclaiming a frame) page faults: %R\n"
- "\tVoluntary context switches: %w\n"
- "\tInvoluntary context switches: %c\n"
- "\tSwaps: %W\n"
- "\tFile system inputs: %I\n"
- "\tFile system outputs: %O\n"
- "\tSocket messages sent: %s\n"
- "\tSocket messages received: %r\n"
- "\tSignals delivered: %k\n"
- "\tPage size (bytes): %Z\n"
- "\tExit status: %x";
-
-
- /* Wait for and fill in data on child process PID.
+static const char long_format[] ALIGN1 =
+ "\tCommand being timed: \"%C\"\n"
+ "\tUser time (seconds): %U\n"
+ "\tSystem time (seconds): %S\n"
+ "\tPercent of CPU this job got: %P\n"
+ "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
+ "\tAverage shared text size (kbytes): %X\n"
+ "\tAverage unshared data size (kbytes): %D\n"
+ "\tAverage stack size (kbytes): %p\n"
+ "\tAverage total size (kbytes): %K\n"
+ "\tMaximum resident set size (kbytes): %M\n"
+ "\tAverage resident set size (kbytes): %t\n"
+ "\tMajor (requiring I/O) page faults: %F\n"
+ "\tMinor (reclaiming a frame) page faults: %R\n"
+ "\tVoluntary context switches: %w\n"
+ "\tInvoluntary context switches: %c\n"
+ "\tSwaps: %W\n"
+ "\tFile system inputs: %I\n"
+ "\tFile system outputs: %O\n"
+ "\tSocket messages sent: %s\n"
+ "\tSocket messages received: %r\n"
+ "\tSignals delivered: %k\n"
+ "\tPage size (bytes): %Z\n"
+ "\tExit status: %x";
+
+/* Wait for and fill in data on child process PID.
Return 0 on error, 1 if ok. */
-
/* pid_t is short on BSDI, so don't try to promote it. */
-static int resuse_end (pid_t pid, resource_t *resp)
+static void resuse_end(pid_t pid, resource_t *resp)
{
- int status;
-
- pid_t caught;
-
- /* Ignore signals, but don't ignore the children. When wait3
- returns the child process, set the time the command finished. */
- while ((caught = wait3 (&status, 0, &resp->ru)) != pid)
- {
- if (caught == -1)
- return 0;
- }
-
- gettimeofday (&resp->elapsed, (struct timezone *) 0);
- resp->elapsed.tv_sec -= resp->start.tv_sec;
- if (resp->elapsed.tv_usec < resp->start.tv_usec)
- {
- /* Manually carry a one from the seconds field. */
- resp->elapsed.tv_usec += 1000000;
- --resp->elapsed.tv_sec;
- }
- resp->elapsed.tv_usec -= resp->start.tv_usec;
-
- resp->waitstatus = status;
-
- return 1;
+ pid_t caught;
+
+ /* Ignore signals, but don't ignore the children. When wait3
+ returns the child process, set the time the command finished. */
+ while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
+ if (caught == -1 && errno != EINTR) {
+ bb_perror_msg("wait");
+ return;
+ }
+ }
+ resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms;
}
-/* Print ARGV to FP, with each entry in ARGV separated by FILLER. */
-static void fprintargv (FILE *fp, char *const *argv, const char *filler)
+static void printargv(char *const *argv)
{
- char *const *av;
-
- av = argv;
- fputs (*av, fp);
- while (*++av)
- {
- fputs (filler, fp);
- fputs (*av, fp);
- }
- if (ferror (fp))
- bb_error_msg_and_die("write error");
+ const char *fmt = " %s" + 1;
+ do {
+ printf(fmt, *argv);
+ fmt = " %s";
+ } while (*++argv);
}
/* Return the number of kilobytes corresponding to a number of pages PAGES.
@@ -151,34 +89,23 @@ static void fprintargv (FILE *fp, char *const *argv, const char *filler)
This is funky since the pagesize could be less than 1K.
Note: Some machines express getrusage statistics in terms of K,
others in terms of pages. */
-
-static unsigned long ptok (unsigned long pages)
+static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
{
- static unsigned long ps = 0;
- unsigned long tmp;
- static long size = LONG_MAX;
-
- /* Initialization. */
- if (ps == 0)
- ps = (long) getpagesize ();
-
- /* Conversion. */
- if (pages > (LONG_MAX / ps))
- { /* Could overflow. */
- tmp = pages / 1024; /* Smaller first, */
- size = tmp * ps; /* then larger. */
- }
- else
- { /* Could underflow. */
- tmp = pages * ps; /* Larger first, */
- size = tmp / 1024; /* then smaller. */
- }
- return size;
+ unsigned long tmp;
+
+ /* Conversion. */
+ if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */
+ tmp = pages / 1024; /* Smaller first, */
+ return tmp * pagesize; /* then larger. */
+ }
+ /* Could underflow. */
+ tmp = pages * pagesize; /* Larger first, */
+ return tmp / 1024; /* then smaller. */
}
/* summarize: Report on the system use of a command.
- Copy the FMT argument to FP except that `%' sequences
+ Print the FMT argument except that `%' sequences
have special meaning, and `\n' and `\t' are translated into
newline and tab, respectively, and `\\' is translated into `\'.
@@ -216,287 +143,287 @@ static unsigned long ptok (unsigned long pages)
to kbytes by multiplying by the page size, dividing by 1024,
and dividing by elapsed real time.
- FP is the stream to print to.
FMT is the format string, interpreted as described above.
COMMAND is the command and args that are being summarized.
RESP is resource information on the command. */
-static void summarize (FILE *fp, const char *fmt, char **command, resource_t *resp)
+#ifndef TICKS_PER_SEC
+#define TICKS_PER_SEC 100
+#endif
+
+static void summarize(const char *fmt, char **command, resource_t *resp)
{
- unsigned long r; /* Elapsed real milliseconds. */
- unsigned long v; /* Elapsed virtual (CPU) milliseconds. */
-
- if (WIFSTOPPED (resp->waitstatus))
- fprintf (fp, "Command stopped by signal %d\n", WSTOPSIG (resp->waitstatus));
- else if (WIFSIGNALED (resp->waitstatus))
- fprintf (fp, "Command terminated by signal %d\n", WTERMSIG (resp->waitstatus));
- else if (WIFEXITED (resp->waitstatus) && WEXITSTATUS (resp->waitstatus))
- fprintf (fp, "Command exited with non-zero status %d\n", WEXITSTATUS (resp->waitstatus));
-
- /* Convert all times to milliseconds. Occasionally, one of these values
- comes out as zero. Dividing by zero causes problems, so we first
- check the time value. If it is zero, then we take `evasive action'
- instead of calculating a value. */
-
- r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
-
- v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
- resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
-
- while (*fmt)
- {
- switch (*fmt)
- {
- case '%':
- switch (*++fmt)
- {
- case '%': /* Literal '%'. */
- putc ('%', fp);
- break;
- case 'C': /* The command that got timed. */
- fprintargv (fp, command, " ");
- break;
- case 'D': /* Average unshared data size. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
- ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
- break;
- case 'E': /* Elapsed real (wall clock) time. */
- if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
- fprintf (fp, "%ldh %ldm %02lds",
- resp->elapsed.tv_sec / 3600,
- (resp->elapsed.tv_sec % 3600) / 60,
- resp->elapsed.tv_sec % 60);
- else
- fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */
- resp->elapsed.tv_sec / 60,
- resp->elapsed.tv_sec % 60,
- resp->elapsed.tv_usec / 10000);
- break;
- case 'F': /* Major page faults. */
- fprintf (fp, "%ld", resp->ru.ru_majflt);
- break;
- case 'I': /* Inputs. */
- fprintf (fp, "%ld", resp->ru.ru_inblock);
- break;
- case 'K': /* Average mem usage == data+stack+text. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
- ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v) +
- ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
- break;
- case 'M': /* Maximum resident set size. */
- fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss));
- break;
- case 'O': /* Outputs. */
- fprintf (fp, "%ld", resp->ru.ru_oublock);
- break;
- case 'P': /* Percent of CPU this job got. */
- /* % cpu is (total cpu time)/(elapsed time). */
- if (r > 0)
- fprintf (fp, "%lu%%", (v * 100 / r));
- else
- fprintf (fp, "?%%");
- break;
- case 'R': /* Minor page faults (reclaims). */
- fprintf (fp, "%ld", resp->ru.ru_minflt);
- break;
- case 'S': /* System time. */
- fprintf (fp, "%ld.%02ld",
- resp->ru.ru_stime.tv_sec,
- resp->ru.ru_stime.TV_MSEC / 10);
- break;
- case 'T': /* System time. */
- if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
- fprintf (fp, "%ldh %ldm %02lds",
- resp->ru.ru_stime.tv_sec / 3600,
- (resp->ru.ru_stime.tv_sec % 3600) / 60,
- resp->ru.ru_stime.tv_sec % 60);
- else
- fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */
- resp->ru.ru_stime.tv_sec / 60,
- resp->ru.ru_stime.tv_sec % 60,
- resp->ru.ru_stime.tv_usec / 10000);
- break;
- case 'U': /* User time. */
- fprintf (fp, "%ld.%02ld",
- resp->ru.ru_utime.tv_sec,
- resp->ru.ru_utime.TV_MSEC / 10);
- break;
- case 'u': /* User time. */
- if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
- fprintf (fp, "%ldh %ldm %02lds",
- resp->ru.ru_utime.tv_sec / 3600,
- (resp->ru.ru_utime.tv_sec % 3600) / 60,
- resp->ru.ru_utime.tv_sec % 60);
- else
- fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */
- resp->ru.ru_utime.tv_sec / 60,
- resp->ru.ru_utime.tv_sec % 60,
- resp->ru.ru_utime.tv_usec / 10000);
- break;
- case 'W': /* Times swapped out. */
- fprintf (fp, "%ld", resp->ru.ru_nswap);
- break;
- case 'X': /* Average shared text size. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
- break;
- case 'Z': /* Page size. */
- fprintf (fp, "%d", getpagesize ());
- break;
- case 'c': /* Involuntary context switches. */
- fprintf (fp, "%ld", resp->ru.ru_nivcsw);
- break;
- case 'e': /* Elapsed real time in seconds. */
- fprintf (fp, "%ld.%02ld",
- resp->elapsed.tv_sec,
- resp->elapsed.tv_usec / 10000);
- break;
- case 'k': /* Signals delivered. */
- fprintf (fp, "%ld", resp->ru.ru_nsignals);
- break;
- case 'p': /* Average stack segment. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
- break;
- case 'r': /* Incoming socket messages received. */
- fprintf (fp, "%ld", resp->ru.ru_msgrcv);
- break;
- case 's': /* Outgoing socket messages sent. */
- fprintf (fp, "%ld", resp->ru.ru_msgsnd);
- break;
- case 't': /* Average resident set size. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v));
- break;
- case 'w': /* Voluntary context switches. */
- fprintf (fp, "%ld", resp->ru.ru_nvcsw);
- break;
- case 'x': /* Exit status. */
- fprintf (fp, "%d", WEXITSTATUS (resp->waitstatus));
- break;
- case '\0':
- putc ('?', fp);
- return;
- default:
- putc ('?', fp);
- putc (*fmt, fp);
+ unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
+ unsigned cpu_ticks; /* Same, in "CPU ticks" */
+ unsigned pagesize = getpagesize();
+
+ /* Impossible: we do not use WUNTRACED flag in wait()...
+ if (WIFSTOPPED(resp->waitstatus))
+ printf("Command stopped by signal %u\n",
+ WSTOPSIG(resp->waitstatus));
+ else */
+ if (WIFSIGNALED(resp->waitstatus))
+ printf("Command terminated by signal %u\n",
+ WTERMSIG(resp->waitstatus));
+ else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
+ printf("Command exited with non-zero status %u\n",
+ WEXITSTATUS(resp->waitstatus));
+
+ vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
+ + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
+
+#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
+ /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
+ cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
+#else
+ cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
+#endif
+ if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
+
+ while (*fmt) {
+ /* Handle leading literal part */
+ int n = strcspn(fmt, "%\\");
+ if (n) {
+ printf("%.*s", n, fmt);
+ fmt += n;
+ continue;
}
- ++fmt;
- break;
- case '\\': /* Format escape. */
- switch (*++fmt)
- {
- case 't':
- putc ('\t', fp);
+ switch (*fmt) {
+#ifdef NOT_NEEDED
+ /* Handle literal char */
+ /* Usually we optimize for size, but there is a limit
+ * for everything. With this we do a lot of 1-byte writes */
+ default:
+ bb_putchar(*fmt);
break;
- case 'n':
- putc ('\n', fp);
+#endif
+
+ case '%':
+ switch (*++fmt) {
+#ifdef NOT_NEEDED_YET
+ /* Our format strings do not have these */
+ /* and we do not take format str from user */
+ default:
+ bb_putchar('%');
+ /*FALLTHROUGH*/
+ case '%':
+ if (!*fmt) goto ret;
+ bb_putchar(*fmt);
+ break;
+#endif
+ case 'C': /* The command that got timed. */
+ printargv(command);
+ break;
+ case 'D': /* Average unshared data size. */
+ printf("%lu",
+ (ptok(pagesize, (UL) resp->ru.ru_idrss) +
+ ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
+ break;
+ case 'E': { /* Elapsed real (wall clock) time. */
+ unsigned seconds = resp->elapsed_ms / 1000;
+ if (seconds >= 3600) /* One hour -> h:m:s. */
+ printf("%uh %um %02us",
+ seconds / 3600,
+ (seconds % 3600) / 60,
+ seconds % 60);
+ else
+ printf("%um %u.%02us", /* -> m:s. */
+ seconds / 60,
+ seconds % 60,
+ (unsigned)(resp->elapsed_ms / 10) % 100);
+ break;
+ }
+ case 'F': /* Major page faults. */
+ printf("%lu", resp->ru.ru_majflt);
+ break;
+ case 'I': /* Inputs. */
+ printf("%lu", resp->ru.ru_inblock);
+ break;
+ case 'K': /* Average mem usage == data+stack+text. */
+ printf("%lu",
+ (ptok(pagesize, (UL) resp->ru.ru_idrss) +
+ ptok(pagesize, (UL) resp->ru.ru_isrss) +
+ ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
+ break;
+ case 'M': /* Maximum resident set size. */
+ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
+ break;
+ case 'O': /* Outputs. */
+ printf("%lu", resp->ru.ru_oublock);
+ break;
+ case 'P': /* Percent of CPU this job got. */
+ /* % cpu is (total cpu time)/(elapsed time). */
+ if (resp->elapsed_ms > 0)
+ printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
+ else
+ printf("?%%");
+ break;
+ case 'R': /* Minor page faults (reclaims). */
+ printf("%lu", resp->ru.ru_minflt);
+ break;
+ case 'S': /* System time. */
+ printf("%u.%02u",
+ (unsigned)resp->ru.ru_stime.tv_sec,
+ (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
+ break;
+ case 'T': /* System time. */
+ if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
+ printf("%uh %um %02us",
+ (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
+ (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
+ (unsigned)(resp->ru.ru_stime.tv_sec % 60));
+ else
+ printf("%um %u.%02us", /* -> m:s. */
+ (unsigned)(resp->ru.ru_stime.tv_sec / 60),
+ (unsigned)(resp->ru.ru_stime.tv_sec % 60),
+ (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
+ break;
+ case 'U': /* User time. */
+ printf("%u.%02u",
+ (unsigned)resp->ru.ru_utime.tv_sec,
+ (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
+ break;
+ case 'u': /* User time. */
+ if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
+ printf("%uh %um %02us",
+ (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
+ (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
+ (unsigned)(resp->ru.ru_utime.tv_sec % 60));
+ else
+ printf("%um %u.%02us", /* -> m:s. */
+ (unsigned)(resp->ru.ru_utime.tv_sec / 60),
+ (unsigned)(resp->ru.ru_utime.tv_sec % 60),
+ (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
+ break;
+ case 'W': /* Times swapped out. */
+ printf("%lu", resp->ru.ru_nswap);
+ break;
+ case 'X': /* Average shared text size. */
+ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
+ break;
+ case 'Z': /* Page size. */
+ printf("%u", pagesize);
+ break;
+ case 'c': /* Involuntary context switches. */
+ printf("%lu", resp->ru.ru_nivcsw);
+ break;
+ case 'e': /* Elapsed real time in seconds. */
+ printf("%u.%02u",
+ (unsigned)resp->elapsed_ms / 1000,
+ (unsigned)(resp->elapsed_ms / 10) % 100);
+ break;
+ case 'k': /* Signals delivered. */
+ printf("%lu", resp->ru.ru_nsignals);
+ break;
+ case 'p': /* Average stack segment. */
+ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
+ break;
+ case 'r': /* Incoming socket messages received. */
+ printf("%lu", resp->ru.ru_msgrcv);
+ break;
+ case 's': /* Outgoing socket messages sent. */
+ printf("%lu", resp->ru.ru_msgsnd);
+ break;
+ case 't': /* Average resident set size. */
+ printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
+ break;
+ case 'w': /* Voluntary context switches. */
+ printf("%lu", resp->ru.ru_nvcsw);
+ break;
+ case 'x': /* Exit status. */
+ printf("%u", WEXITSTATUS(resp->waitstatus));
+ break;
+ }
break;
- case '\\':
- putc ('\\', fp);
+
+#ifdef NOT_NEEDED_YET
+ case '\\': /* Format escape. */
+ switch (*++fmt) {
+ default:
+ bb_putchar('\\');
+ /*FALLTHROUGH*/
+ case '\\':
+ if (!*fmt) goto ret;
+ bb_putchar(*fmt);
+ break;
+ case 't':
+ bb_putchar('\t');
+ break;
+ case 'n':
+ bb_putchar('\n');
+ break;
+ }
break;
- default:
- putc ('?', fp);
- putc ('\\', fp);
- putc (*fmt, fp);
+#endif
}
++fmt;
- break;
-
- default:
- putc (*fmt++, fp);
}
-
- if (ferror (fp))
- bb_error_msg_and_die("write error");
- }
- putc ('\n', fp);
-
- if (ferror (fp))
- bb_error_msg_and_die("write error");
+ /* ret: */
+ bb_putchar('\n');
}
/* Run command CMD and return statistics on it.
Put the statistics in *RESP. */
-static void run_command (char *const *cmd, resource_t *resp)
+static void run_command(char *const *cmd, resource_t *resp)
{
- pid_t pid; /* Pid of child. */
- __sighandler_t interrupt_signal, quit_signal;
-
- gettimeofday (&resp->start, (struct timezone *) 0);
- pid = fork (); /* Run CMD as child process. */
- if (pid < 0)
- bb_error_msg_and_die("cannot fork");
- else if (pid == 0)
- { /* If child. */
- /* Don't cast execvp arguments; that causes errors on some systems,
- versus merely warnings if the cast is left off. */
- execvp (cmd[0], cmd);
- bb_error_msg("cannot run %s", cmd[0]);
- _exit (errno == ENOENT ? 127 : 126);
- }
-
- /* Have signals kill the child but not self (if possible). */
- interrupt_signal = signal (SIGINT, SIG_IGN);
- quit_signal = signal (SIGQUIT, SIG_IGN);
-
- if (resuse_end (pid, resp) == 0)
- bb_error_msg("error waiting for child process");
-
- /* Re-enable signals. */
- signal (SIGINT, interrupt_signal);
- signal (SIGQUIT, quit_signal);
+ pid_t pid; /* Pid of child. */
+ void (*interrupt_signal)(int);
+ void (*quit_signal)(int);
+
+ resp->elapsed_ms = monotonic_us() / 1000;
+ pid = vfork(); /* Run CMD as child process. */
+ if (pid < 0)
+ bb_perror_msg_and_die("fork");
+ if (pid == 0) { /* If child. */
+ /* Don't cast execvp arguments; that causes errors on some systems,
+ versus merely warnings if the cast is left off. */
+ BB_EXECVP(cmd[0], cmd);
+ xfunc_error_retval = (errno == ENOENT ? 127 : 126);
+ bb_error_msg_and_die("cannot run %s", cmd[0]);
+ }
+
+ /* Have signals kill the child but not self (if possible). */
+//TODO: just block all sigs? and reenable them in the very end in main?
+ interrupt_signal = signal(SIGINT, SIG_IGN);
+ quit_signal = signal(SIGQUIT, SIG_IGN);
+
+ resuse_end(pid, resp);
+
+ /* Re-enable signals. */
+ signal(SIGINT, interrupt_signal);
+ signal(SIGQUIT, quit_signal);
}
-extern int time_main (int argc, char **argv)
+int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int time_main(int argc UNUSED_PARAM, char **argv)
{
- int gotone;
- resource_t res;
- const char *output_format = default_format;
-
- argc--;
- argv++;
- /* Parse any options -- don't use getopt() here so we don't
- * consume the args of our client application... */
- while (argc > 0 && **argv == '-') {
- gotone = 0;
- while (gotone==0 && *++(*argv)) {
- switch (**argv) {
- case 'v':
- output_format = long_format;
- break;
- case 'p':
- output_format = posix_format;
- break;
- default:
- bb_show_usage();
- }
- argc--;
- argv++;
- gotone = 1;
- }
- }
-
- if (argv == NULL || *argv == NULL)
- bb_show_usage();
-
- run_command (argv, &res);
- summarize (stdout, output_format, argv, &res);
- fflush (stdout);
-
- if (WIFSTOPPED (res.waitstatus))
- exit (WSTOPSIG (res.waitstatus));
- else if (WIFSIGNALED (res.waitstatus))
- exit (WTERMSIG (res.waitstatus));
- else if (WIFEXITED (res.waitstatus))
- exit (WEXITSTATUS (res.waitstatus));
- return 0;
+ resource_t res;
+ const char *output_format = default_format;
+ int opt;
+
+ opt_complementary = "-1"; /* at least one arg */
+ /* "+": stop on first non-option */
+ opt = getopt32(argv, "+vp");
+ argv += optind;
+ if (opt & 1)
+ output_format = long_format;
+ if (opt & 2)
+ output_format = posix_format;
+
+ run_command(argv, &res);
+
+ /* Cheat. printf's are shorter :) */
+ /* (but see bb_putchar() body for additional wrinkle!) */
+ xdup2(2, 1); /* just in case libc does something silly :( */
+ stdout = stderr;
+ summarize(output_format, argv, &res);
+
+ if (WIFSTOPPED(res.waitstatus))
+ return WSTOPSIG(res.waitstatus);
+ if (WIFSIGNALED(res.waitstatus))
+ return WTERMSIG(res.waitstatus);
+ if (WIFEXITED(res.waitstatus))
+ return WEXITSTATUS(res.waitstatus);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
}
diff --git a/release/src/router/busybox/miscutils/timeout.c b/release/src/router/busybox/miscutils/timeout.c
new file mode 100644
index 00000000..83ae56e6
--- /dev/null
+++ b/release/src/router/busybox/miscutils/timeout.c
@@ -0,0 +1,115 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * COPYING NOTES
+ *
+ * timeout.c -- a timeout handler for shell commands
+ *
+ * Copyright (C) 2005-6, Roberto A. Foglietta <me@roberto.foglietta.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * REVISION NOTES:
+ * released 17-11-2005 by Roberto A. Foglietta
+ * talarm 04-12-2005 by Roberto A. Foglietta
+ * modified 05-12-2005 by Roberto A. Foglietta
+ * sizerdct 06-12-2005 by Roberto A. Foglietta
+ * splitszf 12-05-2006 by Roberto A. Foglietta
+ * rewrite 14-11-2008 vda
+ */
+
+#include "libbb.h"
+
+int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int timeout_main(int argc UNUSED_PARAM, char **argv)
+{
+ int signo;
+ int status;
+ int parent = 0;
+ int timeout = 10;
+ pid_t pid;
+#if !BB_MMU
+ char *sv1, *sv2;
+#endif
+ const char *opt_s = "TERM";
+
+ /* -p option is not documented, it is needed to support NOMMU. */
+
+ /* -t SECONDS; -p PARENT_PID */
+ opt_complementary = "t+" USE_FOR_NOMMU(":p+");
+ /* '+': stop at first non-option */
+ getopt32(argv, "+s:t:" USE_FOR_NOMMU("p:"), &opt_s, &timeout, &parent);
+ /*argv += optind; - no, wait for bb_daemonize_or_rexec! */
+ signo = get_signum(opt_s);
+ if (signo < 0)
+ bb_error_msg_and_die("unknown signal '%s'", opt_s);
+
+ /* We want to create a grandchild which will watch
+ * and kill the grandparent. Other methods:
+ * making parent watch child disrupts parent<->child link
+ * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" -
+ * it's better if service_prog is a child of tcpsvd!),
+ * making child watch parent results in programs having
+ * unexpected children. */
+
+ if (parent) /* we were re-execed, already grandchild */
+ goto grandchild;
+ if (!argv[optind]) /* no PROG? */
+ bb_show_usage();
+
+#if !BB_MMU
+ sv1 = argv[optind];
+ sv2 = argv[optind + 1];
+#endif
+ pid = vfork();
+ if (pid < 0)
+ bb_perror_msg_and_die("vfork");
+ if (pid == 0) {
+ /* Child: spawn grandchild and exit */
+ parent = getppid();
+#if !BB_MMU
+ argv[optind] = xasprintf("-p%u", parent);
+ argv[optind + 1] = NULL;
+#endif
+ /* NB: exits with nonzero on error: */
+ bb_daemonize_or_rexec(0, argv);
+ /* Here we are grandchild. Sleep, then kill grandparent */
+ grandchild:
+ /* Just sleep(NUGE_NUM); kill(parent) may kill wrong process! */
+ while (1) {
+ sleep(1);
+ if (--timeout <= 0)
+ break;
+ if (kill(parent, 0)) {
+ /* process is gone */
+ return EXIT_SUCCESS;
+ }
+ }
+ kill(parent, signo);
+ return EXIT_SUCCESS;
+ }
+
+ /* Parent */
+ wait(&status); /* wait for child to die */
+ /* Did intermediate [v]fork or exec fail? */
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ return EXIT_FAILURE;
+ /* Ok, exec a program as requested */
+ argv += optind;
+#if !BB_MMU
+ argv[0] = sv1;
+ argv[1] = sv2;
+#endif
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("exec '%s'", argv[0]);
+}
diff --git a/release/src/router/busybox/miscutils/ttysize.c b/release/src/router/busybox/miscutils/ttysize.c
new file mode 100644
index 00000000..05455543
--- /dev/null
+++ b/release/src/router/busybox/miscutils/ttysize.c
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Replacement for "stty size", which is awkward for shell script use.
+ * - Allows to request width, height, or both, in any order.
+ * - Does not complain on error, but returns width 80, height 24.
+ * - Size: less than 200 bytes
+ *
+ * Copyright (C) 2007 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under the GPL v2, see the file LICENSE in this tarball.
+ */
+#include "libbb.h"
+
+int ttysize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ttysize_main(int argc, char **argv)
+{
+ unsigned w, h;
+ struct winsize wsz;
+
+ w = 80;
+ h = 24;
+ if (!ioctl(0, TIOCGWINSZ, &wsz)) {
+ w = wsz.ws_col;
+ h = wsz.ws_row;
+ }
+
+ if (argc == 1) {
+ printf("%u %u", w, h);
+ } else {
+ const char *fmt, *arg;
+
+ fmt = "%u %u" + 3; /* "%u" */
+ while ((arg = *++argv) != NULL) {
+ char c = arg[0];
+ if (c == 'w')
+ printf(fmt, w);
+ if (c == 'h')
+ printf(fmt, h);
+ fmt = "%u %u" + 2; /* " %u" */
+ }
+ }
+ bb_putchar('\n');
+ return 0;
+}
diff --git a/release/src/router/busybox/miscutils/watchdog.c b/release/src/router/busybox/miscutils/watchdog.c
index b1167dc9..f85138e1 100644
--- a/release/src/router/busybox/miscutils/watchdog.c
+++ b/release/src/router/busybox/miscutils/watchdog.c
@@ -3,79 +3,83 @@
* Mini watchdog implementation for busybox
*
* Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org>
+ * Copyright (C) 2006 Bernhard Reutner-Fischer <busybox@busybox.net>
+ * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <signal.h>
-#include "busybox.h"
+#include "libbb.h"
+#include "linux/types.h" /* for __u32 */
+#include "linux/watchdog.h"
-/* Userspace timer duration, in seconds */
-static unsigned int timer_duration = 30;
+#define OPT_FOREGROUND (1 << 0)
+#define OPT_STIMER (1 << 1)
+#define OPT_HTIMER (1 << 2)
-/* Watchdog file descriptor */
-static int fd;
-
-static void watchdog_shutdown(int unused)
+static void watchdog_shutdown(int sig UNUSED_PARAM)
{
- write(fd, "V", 1); /* Magic */
- close(fd);
- exit(0);
+ static const char V = 'V';
+
+ write(3, &V, 1); /* Magic, see watchdog-api.txt in kernel */
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(3);
+ exit(EXIT_SUCCESS);
}
-extern int watchdog_main(int argc, char **argv)
+int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int watchdog_main(int argc, char **argv)
{
- int opt;
+ static const struct suffix_mult suffixes[] = {
+ { "ms", 1 },
+ { "", 1000 },
+ { }
+ };
- while ((opt = getopt(argc, argv, "t:")) > 0) {
- switch (opt) {
- case 't':
- timer_duration = bb_xgetlarg(optarg, 10, 0, INT_MAX);
- break;
- default:
- bb_show_usage();
- }
- }
+ unsigned opts;
+ unsigned stimer_duration; /* how often to restart */
+ unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */
+ char *st_arg;
+ char *ht_arg;
- /* We're only interested in the watchdog device .. */
- if (optind < argc - 1 || argc == 1)
- bb_show_usage();
+ opt_complementary = "=1"; /* must have exactly 1 argument */
+ opts = getopt32(argv, "Ft:T:", &st_arg, &ht_arg);
- if (daemon(0, 1) < 0)
- bb_perror_msg_and_die("Failed forking watchdog daemon");
+ if (opts & OPT_HTIMER)
+ htimer_duration = xatou_sfx(ht_arg, suffixes);
+ stimer_duration = htimer_duration / 2;
+ if (opts & OPT_STIMER)
+ stimer_duration = xatou_sfx(st_arg, suffixes);
- signal(SIGHUP, watchdog_shutdown);
- signal(SIGINT, watchdog_shutdown);
+ bb_signals(BB_FATAL_SIGS, watchdog_shutdown);
- fd = bb_xopen(argv[argc - 1], O_WRONLY);
+ /* Use known fd # - avoid needing global 'int fd' */
+ xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3);
+
+ /* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
+ htimer_duration = htimer_duration / 1000;
+#ifndef WDIOC_SETTIMEOUT
+#error WDIOC_SETTIMEOUT is not defined, cannot compile watchdog applet
+#else
+ ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
+#endif
+#if 0
+ ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
+ printf("watchdog: SW timer is %dms, HW timer is %dms\n",
+ stimer_duration, htimer_duration * 1000);
+#endif
+
+ if (!(opts & OPT_FOREGROUND)) {
+ bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
+ }
while (1) {
- /*
- * Make sure we clear the counter before sleeping, as the counter value
- * is undefined at this point -- PFM
+ /*
+ * Make sure we clear the counter before sleeping,
+ * as the counter value is undefined at this point -- PFM
*/
- write(fd, "\0", 1);
- sleep(timer_duration);
+ write(3, "", 1); /* write zero byte */
+ usleep(stimer_duration * 1000L);
}
-
- watchdog_shutdown(0);
-
- return EXIT_SUCCESS;
+ return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
}