mirror of
https://github.com/openharmony/third_party_toybox.git
synced 2026-06-30 21:37:54 -04:00
!286 merge master into master
update0.8.12 Created-by: gong-guilin Commit-by: gong-guilin Merged-by: openharmony_ci Description: ### 相关的Issue https://gitcode.com/openharmony/third_party_toybox/issues/252 ### 原因(目的、解决的问题等) ### 描述(做了什么,变更了什么) ### 测试用例(新增、改动、可能影响的功能) ### 是否需要同步至release(如:3.0LTS ... )分支? 必须选择一项(在MarkDown模式下用[x]替换[ ]即可勾选对应选项): - [ ] 是,需要同步的分支: - [ ] 否 理由: See merge request: openharmony/third_party_toybox!286
This commit is contained in:
+9
-16
@@ -1,17 +1,10 @@
|
||||
syntax: glob
|
||||
/.config.old
|
||||
.single*
|
||||
change/
|
||||
/generated/build.sh
|
||||
/generated/cflags
|
||||
/generated/config2help
|
||||
/generated/Config.in
|
||||
/generated/Config.probed
|
||||
/generated/flags.raw
|
||||
/generated/mkflags
|
||||
/generated/mktags
|
||||
/generated/obj
|
||||
/generated/optlibs.dat
|
||||
/kconfig
|
||||
toybox
|
||||
toybox_unstripped
|
||||
/.config*
|
||||
/.single*
|
||||
/change/
|
||||
/generated/
|
||||
/kconfig/mconf
|
||||
/kconfig/conf
|
||||
/kconfig/*.c
|
||||
/toybox
|
||||
/toybox_unstripped
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
CONFIG_BASENAME=y
|
||||
CONFIG_CAL=y
|
||||
CONFIG_CAT=y
|
||||
CONFIG_CHGRP=y
|
||||
CONFIG_CHOWN=y
|
||||
CONFIG_CHMOD=y
|
||||
CONFIG_CKSUM=y
|
||||
CONFIG_CMP=y
|
||||
CONFIG_COMM=y
|
||||
CONFIG_CP=y
|
||||
CONFIG_MV=y
|
||||
CONFIG_INSTALL=y
|
||||
CONFIG_CPIO=y
|
||||
CONFIG_CUT=y
|
||||
CONFIG_DATE=y
|
||||
CONFIG_DF=y
|
||||
CONFIG_DIRNAME=y
|
||||
CONFIG_DU=y
|
||||
CONFIG_ECHO=y
|
||||
CONFIG_ENV=y
|
||||
CONFIG_EXPAND=y
|
||||
CONFIG_FALSE=y
|
||||
CONFIG_FILE=y
|
||||
CONFIG_FIND=y
|
||||
CONFIG_GETCONF=y
|
||||
CONFIG_GREP=y
|
||||
CONFIG_HEAD=y
|
||||
CONFIG_ICONV=y
|
||||
CONFIG_ID=y
|
||||
CONFIG_GROUPS=y
|
||||
CONFIG_LOGNAME=y
|
||||
CONFIG_WHOAMI=y
|
||||
CONFIG_KILL=y
|
||||
CONFIG_LN=y
|
||||
CONFIG_LS=y
|
||||
CONFIG_MKDIR=y
|
||||
CONFIG_MKFIFO=y
|
||||
CONFIG_NICE=y
|
||||
CONFIG_NL=y
|
||||
CONFIG_NOHUP=y
|
||||
CONFIG_OD=y
|
||||
CONFIG_PASTE=y
|
||||
CONFIG_PATCH=y
|
||||
CONFIG_PRINTF=y
|
||||
CONFIG_PS=y
|
||||
CONFIG_TOP=y
|
||||
CONFIG_IOTOP=y
|
||||
CONFIG_PGREP=y
|
||||
CONFIG_PKILL=y
|
||||
CONFIG_PWD=y
|
||||
CONFIG_RENICE=y
|
||||
CONFIG_RM=y
|
||||
CONFIG_RMDIR=y
|
||||
CONFIG_SED=y
|
||||
CONFIG_SLEEP=y
|
||||
CONFIG_SORT=y
|
||||
CONFIG_SPLIT=y
|
||||
CONFIG_STRINGS=y
|
||||
CONFIG_TAIL=y
|
||||
CONFIG_TAR=y
|
||||
CONFIG_TEE=y
|
||||
CONFIG_TEST=y
|
||||
CONFIG_TIME=y
|
||||
CONFIG_TOUCH=y
|
||||
CONFIG_TRUE=y
|
||||
CONFIG_TTY=y
|
||||
CONFIG_ULIMIT=y
|
||||
CONFIG_UNAME=y
|
||||
CONFIG_UNIQ=y
|
||||
CONFIG_UNLINK=y
|
||||
CONFIG_UUDECODE=y
|
||||
CONFIG_UUENCODE=y
|
||||
CONFIG_WC=y
|
||||
CONFIG_XARGS=y
|
||||
CONFIG_DD=y
|
||||
CONFIG_DIFF=y
|
||||
CONFIG_EXPR=y
|
||||
CONFIG_GETFATTR=y
|
||||
CONFIG_GETOPT=y
|
||||
CONFIG_LSOF=y
|
||||
CONFIG_MODPROBE=y
|
||||
CONFIG_MORE=y
|
||||
CONFIG_STTY=y
|
||||
CONFIG_TR=y
|
||||
CONFIG_TRACEROUTE=y
|
||||
CONFIG_VI=y
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_BASE64=y
|
||||
CONFIG_BLKDISCARD=y
|
||||
CONFIG_BLKID=y
|
||||
CONFIG_BLOCKDEV=y
|
||||
CONFIG_CHCON=y
|
||||
CONFIG_CHROOT=y
|
||||
CONFIG_CHRT=y
|
||||
CONFIG_CLEAR=y
|
||||
CONFIG_DEVMEM=y
|
||||
CONFIG_DOS2UNIX=y
|
||||
CONFIG_UNIX2DOS=y
|
||||
CONFIG_FALLOCATE=y
|
||||
CONFIG_FLOCK=y
|
||||
CONFIG_FMT=y
|
||||
CONFIG_FREE=y
|
||||
CONFIG_FREERAMDISK=y
|
||||
CONFIG_FSFREEZE=y
|
||||
CONFIG_FSYNC=y
|
||||
CONFIG_HELP=y
|
||||
CONFIG_HWCLOCK=y
|
||||
CONFIG_I2CDETECT=y
|
||||
CONFIG_I2CDUMP=y
|
||||
CONFIG_I2CGET=y
|
||||
CONFIG_I2CSET=y
|
||||
CONFIG_INOTIFYD=y
|
||||
CONFIG_INSMOD=y
|
||||
CONFIG_IONICE=y
|
||||
CONFIG_IORENICE=y
|
||||
CONFIG_LOSETUP=y
|
||||
CONFIG_LSATTR=y
|
||||
CONFIG_CHATTR=y
|
||||
CONFIG_LSMOD=y
|
||||
CONFIG_LSPCI=y
|
||||
CONFIG_LSUSB=y
|
||||
CONFIG_MAKEDEVS=y
|
||||
CONFIG_MKSWAP=y
|
||||
CONFIG_MODINFO=y
|
||||
CONFIG_MOUNTPOINT=y
|
||||
CONFIG_NBD_CLIENT=y
|
||||
CONFIG_UNSHARE=y
|
||||
CONFIG_NSENTER=y
|
||||
CONFIG_PARTPROBE=y
|
||||
CONFIG_PIVOT_ROOT=y
|
||||
CONFIG_PMAP=y
|
||||
CONFIG_PRINTENV=y
|
||||
CONFIG_PWDX=y
|
||||
CONFIG_READELF=y
|
||||
CONFIG_READLINK=y
|
||||
CONFIG_REALPATH=y
|
||||
CONFIG_REV=y
|
||||
CONFIG_RMMOD=y
|
||||
CONFIG_RTCWAKE=y
|
||||
CONFIG_SETFATTR=y
|
||||
CONFIG_SETSID=y
|
||||
CONFIG_STAT=y
|
||||
CONFIG_SWAPOFF=y
|
||||
CONFIG_SWAPON=y
|
||||
CONFIG_SYSCTL=y
|
||||
CONFIG_TAC=y
|
||||
CONFIG_NPROC=y
|
||||
CONFIG_TASKSET=y
|
||||
CONFIG_TIMEOUT=y
|
||||
CONFIG_TRUNCATE=y
|
||||
CONFIG_UCLAMPSET=y
|
||||
CONFIG_UPTIME=y
|
||||
CONFIG_USLEEP=y
|
||||
CONFIG_UUIDGEN=y
|
||||
CONFIG_VCONFIG=y
|
||||
CONFIG_VMSTAT=y
|
||||
CONFIG_WATCH=y
|
||||
CONFIG_WHICH=y
|
||||
CONFIG_XXD=y
|
||||
CONFIG_YES=y
|
||||
CONFIG_IFCONFIG=y
|
||||
CONFIG_MICROCOM=y
|
||||
CONFIG_NETCAT=y
|
||||
CONFIG_NETSTAT=y
|
||||
CONFIG_PING=y
|
||||
CONFIG_RFKILL=y
|
||||
CONFIG_TUNCTL=y
|
||||
CONFIG_DMESG=y
|
||||
CONFIG_GZIP=y
|
||||
CONFIG_GUNZIP=y
|
||||
CONFIG_ZCAT=y
|
||||
CONFIG_HOSTNAME=y
|
||||
CONFIG_KILLALL=y
|
||||
CONFIG_MD5SUM=y
|
||||
CONFIG_SHA1SUM=y
|
||||
CONFIG_SHA224SUM=y
|
||||
CONFIG_SHA256SUM=y
|
||||
CONFIG_SHA384SUM=y
|
||||
CONFIG_SHA512SUM=y
|
||||
CONFIG_MKNOD=y
|
||||
CONFIG_MKTEMP=y
|
||||
CONFIG_MOUNT=y
|
||||
CONFIG_PIDOF=y
|
||||
CONFIG_SEQ=y
|
||||
CONFIG_SYNC=y
|
||||
CONFIG_UMOUNT=y
|
||||
CONFIG_GETENFORCE=y
|
||||
CONFIG_LOAD_POLICY=y
|
||||
CONFIG_RESTORECON=y
|
||||
CONFIG_RUNCON=y
|
||||
CONFIG_SETENFORCE=y
|
||||
CONFIG_TOYBOX_SELINUX=y
|
||||
CONFIG_TOYBOX_LIBCRYPTO=y
|
||||
CONFIG_TOYBOX_LIBZ=y
|
||||
CONFIG_TOYBOX_FLOAT=y
|
||||
CONFIG_TOYBOX_HELP=y
|
||||
CONFIG_TOYBOX_HELP_DASHDASH=y
|
||||
CONFIG_TOYBOX_NORECURSE=y
|
||||
@@ -0,0 +1,284 @@
|
||||
Linux 2.6.12 Documentation/kbuild/kconfig-language.txt
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The configuration database is collection of configuration options
|
||||
organized in a tree structure:
|
||||
|
||||
+- Code maturity level options
|
||||
| +- Prompt for development and/or incomplete code/drivers
|
||||
+- General setup
|
||||
| +- Networking support
|
||||
| +- System V IPC
|
||||
| +- BSD Process Accounting
|
||||
| +- Sysctl support
|
||||
+- Loadable module support
|
||||
| +- Enable loadable module support
|
||||
| +- Set version information on all module symbols
|
||||
| +- Kernel module loader
|
||||
+- ...
|
||||
|
||||
Every entry has its own dependencies. These dependencies are used
|
||||
to determine the visibility of an entry. Any child entry is only
|
||||
visible if its parent entry is also visible.
|
||||
|
||||
Menu entries
|
||||
------------
|
||||
|
||||
Most entries define a config option, all other entries help to organize
|
||||
them. A single configuration option is defined like this:
|
||||
|
||||
config MODVERSIONS
|
||||
bool "Set version information on all module symbols"
|
||||
depends MODULES
|
||||
help
|
||||
Usually, modules have to be recompiled whenever you switch to a new
|
||||
kernel. ...
|
||||
|
||||
Every line starts with a key word and can be followed by multiple
|
||||
arguments. "config" starts a new config entry. The following lines
|
||||
define attributes for this config option. Attributes can be the type of
|
||||
the config option, input prompt, dependencies, help text and default
|
||||
values. A config option can be defined multiple times with the same
|
||||
name, but every definition can have only a single input prompt and the
|
||||
type must not conflict.
|
||||
|
||||
Menu attributes
|
||||
---------------
|
||||
|
||||
A menu entry can have a number of attributes. Not all of them are
|
||||
applicable everywhere (see syntax).
|
||||
|
||||
- type definition: "bool"/"tristate"/"string"/"hex"/"int"
|
||||
Every config option must have a type. There are only two basic types:
|
||||
tristate and string, the other types are based on these two. The type
|
||||
definition optionally accepts an input prompt, so these two examples
|
||||
are equivalent:
|
||||
|
||||
bool "Networking support"
|
||||
and
|
||||
bool
|
||||
prompt "Networking support"
|
||||
|
||||
- input prompt: "prompt" <prompt> ["if" <expr>]
|
||||
Every menu entry can have at most one prompt, which is used to display
|
||||
to the user. Optionally dependencies only for this prompt can be added
|
||||
with "if".
|
||||
|
||||
- default value: "default" <expr> ["if" <expr>]
|
||||
A config option can have any number of default values. If multiple
|
||||
default values are visible, only the first defined one is active.
|
||||
Default values are not limited to the menu entry, where they are
|
||||
defined, this means the default can be defined somewhere else or be
|
||||
overridden by an earlier definition.
|
||||
The default value is only assigned to the config symbol if no other
|
||||
value was set by the user (via the input prompt above). If an input
|
||||
prompt is visible the default value is presented to the user and can
|
||||
be overridden by him.
|
||||
Optionally dependencies only for this default value can be added with
|
||||
"if".
|
||||
|
||||
- dependencies: "depends on"/"requires" <expr>
|
||||
This defines a dependency for this menu entry. If multiple
|
||||
dependencies are defined they are connected with '&&'. Dependencies
|
||||
are applied to all other options within this menu entry (which also
|
||||
accept an "if" expression), so these two examples are equivalent:
|
||||
|
||||
bool "foo" if BAR
|
||||
default y if BAR
|
||||
and
|
||||
depends on BAR
|
||||
bool "foo"
|
||||
default y
|
||||
|
||||
- reverse dependencies: "select" <symbol> ["if" <expr>]
|
||||
While normal dependencies reduce the upper limit of a symbol (see
|
||||
below), reverse dependencies can be used to force a lower limit of
|
||||
another symbol. The value of the current menu symbol is used as the
|
||||
minimal value <symbol> can be set to. If <symbol> is selected multiple
|
||||
times, the limit is set to the largest selection.
|
||||
Reverse dependencies can only be used with boolean or tristate
|
||||
symbols.
|
||||
|
||||
- numerical ranges: "range" <symbol> <symbol> ["if" <expr>]
|
||||
This allows to limit the range of possible input values for int
|
||||
and hex symbols. The user can only input a value which is larger than
|
||||
or equal to the first symbol and smaller than or equal to the second
|
||||
symbol.
|
||||
|
||||
- help text: "help" or "---help---"
|
||||
This defines a help text. The end of the help text is determined by
|
||||
the indentation level, this means it ends at the first line which has
|
||||
a smaller indentation than the first line of the help text.
|
||||
"---help---" and "help" do not differ in behaviour, "---help---" is
|
||||
used to help visually seperate configuration logic from help within
|
||||
the file as an aid to developers.
|
||||
|
||||
|
||||
Menu dependencies
|
||||
-----------------
|
||||
|
||||
Dependencies define the visibility of a menu entry and can also reduce
|
||||
the input range of tristate symbols. The tristate logic used in the
|
||||
expressions uses one more state than normal boolean logic to express the
|
||||
module state. Dependency expressions have the following syntax:
|
||||
|
||||
<expr> ::= <symbol> (1)
|
||||
<symbol> '=' <symbol> (2)
|
||||
<symbol> '!=' <symbol> (3)
|
||||
'(' <expr> ')' (4)
|
||||
'!' <expr> (5)
|
||||
<expr> '&&' <expr> (6)
|
||||
<expr> '||' <expr> (7)
|
||||
|
||||
Expressions are listed in decreasing order of precedence.
|
||||
|
||||
(1) Convert the symbol into an expression. Boolean and tristate symbols
|
||||
are simply converted into the respective expression values. All
|
||||
other symbol types result in 'n'.
|
||||
(2) If the values of both symbols are equal, it returns 'y',
|
||||
otherwise 'n'.
|
||||
(3) If the values of both symbols are equal, it returns 'n',
|
||||
otherwise 'y'.
|
||||
(4) Returns the value of the expression. Used to override precedence.
|
||||
(5) Returns the result of (2-/expr/).
|
||||
(6) Returns the result of min(/expr/, /expr/).
|
||||
(7) Returns the result of max(/expr/, /expr/).
|
||||
|
||||
An expression can have a value of 'n', 'm' or 'y' (or 0, 1, 2
|
||||
respectively for calculations). A menu entry becomes visible when it's
|
||||
expression evaluates to 'm' or 'y'.
|
||||
|
||||
There are two types of symbols: constant and nonconstant symbols.
|
||||
Nonconstant symbols are the most common ones and are defined with the
|
||||
'config' statement. Nonconstant symbols consist entirely of alphanumeric
|
||||
characters or underscores.
|
||||
Constant symbols are only part of expressions. Constant symbols are
|
||||
always surrounded by single or double quotes. Within the quote any
|
||||
other character is allowed and the quotes can be escaped using '\'.
|
||||
|
||||
Menu structure
|
||||
--------------
|
||||
|
||||
The position of a menu entry in the tree is determined in two ways. First
|
||||
it can be specified explicitly:
|
||||
|
||||
menu "Network device support"
|
||||
depends NET
|
||||
|
||||
config NETDEVICES
|
||||
...
|
||||
|
||||
endmenu
|
||||
|
||||
All entries within the "menu" ... "endmenu" block become a submenu of
|
||||
"Network device support". All subentries inherit the dependencies from
|
||||
the menu entry, e.g. this means the dependency "NET" is added to the
|
||||
dependency list of the config option NETDEVICES.
|
||||
|
||||
The other way to generate the menu structure is done by analyzing the
|
||||
dependencies. If a menu entry somehow depends on the previous entry, it
|
||||
can be made a submenu of it. First, the previous (parent) symbol must
|
||||
be part of the dependency list and then one of these two conditions
|
||||
must be true:
|
||||
- the child entry must become invisible, if the parent is set to 'n'
|
||||
- the child entry must only be visible, if the parent is visible
|
||||
|
||||
config MODULES
|
||||
bool "Enable loadable module support"
|
||||
|
||||
config MODVERSIONS
|
||||
bool "Set version information on all module symbols"
|
||||
depends MODULES
|
||||
|
||||
comment "module support disabled"
|
||||
depends !MODULES
|
||||
|
||||
MODVERSIONS directly depends on MODULES, this means it's only visible if
|
||||
MODULES is different from 'n'. The comment on the other hand is always
|
||||
visible when MODULES is visible (the (empty) dependency of MODULES is
|
||||
also part of the comment dependencies).
|
||||
|
||||
|
||||
Kconfig syntax
|
||||
--------------
|
||||
|
||||
The configuration file describes a series of menu entries, where every
|
||||
line starts with a keyword (except help texts). The following keywords
|
||||
end a menu entry:
|
||||
- config
|
||||
- menuconfig
|
||||
- choice/endchoice
|
||||
- comment
|
||||
- menu/endmenu
|
||||
- if/endif
|
||||
- source
|
||||
The first five also start the definition of a menu entry.
|
||||
|
||||
config:
|
||||
|
||||
"config" <symbol>
|
||||
<config options>
|
||||
|
||||
This defines a config symbol <symbol> and accepts any of above
|
||||
attributes as options.
|
||||
|
||||
menuconfig:
|
||||
"menuconfig" <symbol>
|
||||
<config options>
|
||||
|
||||
This is similiar to the simple config entry above, but it also gives a
|
||||
hint to front ends, that all suboptions should be displayed as a
|
||||
separate list of options.
|
||||
|
||||
choices:
|
||||
|
||||
"choice"
|
||||
<choice options>
|
||||
<choice block>
|
||||
"endchoice"
|
||||
|
||||
This defines a choice group and accepts any of above attributes as
|
||||
options. A choice can only be of type bool or tristate, while a boolean
|
||||
choice only allows a single config entry to be selected, a tristate
|
||||
choice also allows any number of config entries to be set to 'm'. This
|
||||
can be used if multiple drivers for a single hardware exists and only a
|
||||
single driver can be compiled/loaded into the kernel, but all drivers
|
||||
can be compiled as modules.
|
||||
A choice accepts another option "optional", which allows to set the
|
||||
choice to 'n' and no entry needs to be selected.
|
||||
|
||||
comment:
|
||||
|
||||
"comment" <prompt>
|
||||
<comment options>
|
||||
|
||||
This defines a comment which is displayed to the user during the
|
||||
configuration process and is also echoed to the output files. The only
|
||||
possible options are dependencies.
|
||||
|
||||
menu:
|
||||
|
||||
"menu" <prompt>
|
||||
<menu options>
|
||||
<menu block>
|
||||
"endmenu"
|
||||
|
||||
This defines a menu block, see "Menu structure" above for more
|
||||
information. The only possible options are dependencies.
|
||||
|
||||
if:
|
||||
|
||||
"if" <expr>
|
||||
<if block>
|
||||
"endif"
|
||||
|
||||
This defines an if block. The dependency expression <expr> is appended
|
||||
to all enclosed menu entries.
|
||||
|
||||
source:
|
||||
|
||||
"source" <prompt>
|
||||
|
||||
This reads the specified configuration file. This file is always parsed.
|
||||
@@ -1565,6 +1565,7 @@ int smemcmp(char *one, char *two, unsigned long len)
|
||||
return ii;
|
||||
}
|
||||
|
||||
// patch.c used
|
||||
// Slow, but small.
|
||||
char *get_line(int fd)
|
||||
{
|
||||
|
||||
@@ -121,7 +121,10 @@ void xputsl(char *s, int len);
|
||||
void xputsn(char *s);
|
||||
void xputs(char *s);
|
||||
void xputc(char c);
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
// watch.c used. fix exit with abnormal char ''
|
||||
void xflush(int flush);
|
||||
#endif
|
||||
void xvdaemon(void);
|
||||
void xexec(char **argv);
|
||||
pid_t xpopen_setup(char **argv, int *pipes, void (*callback)(char **argv));
|
||||
@@ -442,4 +445,5 @@ pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid);
|
||||
#define minof(a, b) ({typeof(a) aa = (a); typeof(b) bb = (b); aa<bb ? aa : bb;})
|
||||
#define maxof(a, b) ({typeof(a) aa = (a); typeof(b) bb = (b); aa>bb ? aa : bb;})
|
||||
|
||||
// patch.c used
|
||||
char *get_line(int fd);
|
||||
|
||||
+15
@@ -147,12 +147,15 @@ void xferror(FILE *fp)
|
||||
if (ferror(fp)) perror_exit(fp==stdout ? "stdout" : "write");
|
||||
}
|
||||
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
// watch.c used. fix exit with abnormal char ''
|
||||
// if !flush just check for error on stdout without flushing
|
||||
void xflush(int flush)
|
||||
{
|
||||
if ((flush && fflush(0)) || ferror(stdout))
|
||||
if (!toys.exitval) perror_msg("write");
|
||||
}
|
||||
#endif
|
||||
|
||||
void xprintf(char *format, ...)
|
||||
{
|
||||
@@ -729,7 +732,11 @@ struct group *xgetgrgid(gid_t gid)
|
||||
{
|
||||
struct group *group = getgrgid(gid);
|
||||
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
if (!group) perror_exit("bad gid %ld", (long)gid);
|
||||
#else
|
||||
if (!group) perror_exit("gid %ld", (long)gid);
|
||||
#endif
|
||||
return group;
|
||||
}
|
||||
|
||||
@@ -765,7 +772,11 @@ struct passwd *xgetpwnam(char *name)
|
||||
{
|
||||
struct passwd *up = getpwnam(name);
|
||||
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
if (!up) perror_exit("bad user '%s'", name);
|
||||
#else
|
||||
if (!up) perror_exit("user '%s'", name);
|
||||
#endif
|
||||
return up;
|
||||
}
|
||||
|
||||
@@ -773,7 +784,11 @@ struct group *xgetgrnam(char *name)
|
||||
{
|
||||
struct group *gr = getgrnam(name);
|
||||
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
if (!gr) perror_exit("bad group '%s'", name);
|
||||
#else
|
||||
if (!gr) perror_exit("group '%s'", name);
|
||||
#endif
|
||||
return gr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
sha1sum.test
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
sha1sum.test
|
||||
@@ -1 +0,0 @@
|
||||
sha1sum.test
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
sha1sum.test
|
||||
@@ -1 +0,0 @@
|
||||
sha1sum.test
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
sha1sum.test
|
||||
@@ -1 +0,0 @@
|
||||
sha1sum.test
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
sha1sum.test
|
||||
@@ -1 +0,0 @@
|
||||
sha1sum.test
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
sha1sum.test
|
||||
@@ -11,7 +11,9 @@
|
||||
// General posix-2008 headers
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ Android commands
|
||||
Commands primarily used by Android, not present in vanilla Linux.
|
||||
(Mostly SELinux stuff.)
|
||||
|
||||
Bug Elliott Hughes <enh@google.com> about this.
|
||||
Bug Elliott Hughes <enh@google.com> about this.
|
||||
|
||||
+1
-1
@@ -20,4 +20,4 @@ Some of the commands in here are test infrastructure:
|
||||
regression test library functions directly.
|
||||
|
||||
hostid.c is an obsolete command still in posix and present on some recent
|
||||
Linux systems, but not really useful on modern systems.
|
||||
Linux systems, but not really useful on modern systems.
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/* logwrapper.c - Record commands called out of $PATH to a log
|
||||
*
|
||||
* Copyright 2019 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* I made it up. Must be built standalone to work. (Is its own multiplexer.)
|
||||
|
||||
USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config LOGWRAPPER
|
||||
bool "logwrapper"
|
||||
default n
|
||||
help
|
||||
usage: logwrapper ...
|
||||
|
||||
Append command line to $WRAPLOG, then call second instance
|
||||
of command in $PATH.
|
||||
*/
|
||||
|
||||
#define FOR_logwrapper
|
||||
#include "toys.h"
|
||||
|
||||
void logwrapper_main(void)
|
||||
{
|
||||
char *log = getenv("WRAPLOG"), *omnom = basename(*toys.argv),
|
||||
*s, *ss, *sss;
|
||||
struct string_list *list;
|
||||
int i, len;
|
||||
|
||||
// Log the command line
|
||||
if (!log) error_exit("no $WRAPLOG");
|
||||
len = strlen(omnom)+2;
|
||||
for (i = 0; i<toys.optc; i++) len += 2*strlen(toys.optargs[i])+3;
|
||||
ss = stpcpy(s = xmalloc(len), omnom);
|
||||
|
||||
// Copy arguments surrounded by quotes with \ escapes for " \ or \n
|
||||
for (i = 0; i<toys.optc; i++) {
|
||||
*(ss++) = ' ';
|
||||
*(ss++) = '"';
|
||||
for (sss = toys.optargs[i]; *sss; sss++) {
|
||||
if (-1 == (len = stridx("\n\\\"", *sss))) *(ss++) = *sss;
|
||||
else {
|
||||
*(ss++) = '\\';
|
||||
*(ss++) = "n\\\""[len];
|
||||
}
|
||||
}
|
||||
*(ss++) = '"';
|
||||
}
|
||||
*(ss++) = '\n';
|
||||
|
||||
// Atomically append to log and free buffer
|
||||
i = xcreate(log, O_RDWR|O_CREAT|O_APPEND, 0644);
|
||||
xwrite(i, s, ss-s);
|
||||
close(i);
|
||||
free(s);
|
||||
|
||||
// Run next instance in $PATH after this one. If we were called via absolute
|
||||
// path search for this instance, otherwise assume we're first instance
|
||||
list = find_in_path(getenv("PATH"), omnom);
|
||||
if (**toys.argv == '/') {
|
||||
while (list) {
|
||||
if (!strcmp(list->str, *toys.argv)) break;
|
||||
free(llist_pop(&list));
|
||||
}
|
||||
}
|
||||
|
||||
// Skip first instance and try to run next one, until out of instances.
|
||||
for (;;) {
|
||||
if (list) free(llist_pop(&list));
|
||||
if (!list)
|
||||
error_exit("no %s after %s in $PATH=%s", omnom,
|
||||
**toys.argv == '/' ? *toys.argv : "logwrapper", getenv("PATH"));
|
||||
*toys.argv = list->str;
|
||||
execve(list->str, toys.argv, environ);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,4 +3,4 @@ Networking commands
|
||||
The Internet Engineering Task Force publishes standards drafts at
|
||||
https://www.ietf.org/rfc/rfc-index.txt (ala https://www.ietf.org/rfc/rfc3.txt)
|
||||
and the commands tend to be documented in manual page section 8
|
||||
https://man7.org/linux/man-pages/dir_section_8.html
|
||||
https://man7.org/linux/man-pages/dir_section_8.html
|
||||
|
||||
+1
-1
@@ -255,4 +255,4 @@ cleanup:
|
||||
close(in1);
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -13,7 +13,7 @@
|
||||
// -s > 4064 = sizeof(toybuf)-sizeof(struct icmphdr)-CMSG_SPACE(sizeof(uint8_t)), then kernel adds 20 bytes
|
||||
USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4064=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF))
|
||||
USE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF))
|
||||
|
||||
|
||||
config PING
|
||||
bool "ping"
|
||||
default y
|
||||
@@ -40,7 +40,7 @@ config PING
|
||||
-w SEC Exit after this many seconds
|
||||
*/
|
||||
|
||||
#define FOR_ping
|
||||
#define FOR_ping
|
||||
#include "toys.h"
|
||||
|
||||
#include <ifaddrs.h>
|
||||
@@ -215,7 +215,7 @@ void ping_main(void)
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
// left enought space to store ttl value
|
||||
|
||||
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
/* fix "ping -s 65500" fail problem*/
|
||||
char *mybuff = malloc(65536);
|
||||
@@ -338,9 +338,7 @@ void ping_main(void)
|
||||
toys.exitval = 0;
|
||||
}
|
||||
|
||||
sigatexit(0);
|
||||
summary(0);
|
||||
|
||||
// summary(0) gets called for us atexit.
|
||||
if (CFG_TOYBOX_FREE) {
|
||||
freeaddrinfo(ai2);
|
||||
if (ifa2) freeifaddrs(ifa2);
|
||||
|
||||
+1
-1
@@ -723,7 +723,7 @@ static void do_bunzip2(int fd, char *name)
|
||||
free(tmp);
|
||||
tmp = 0;
|
||||
#endif
|
||||
} else {
|
||||
} else if (!err) {
|
||||
if (dotbz) *dotbz = '.';
|
||||
// unlink success return 0, fail return -1
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* pwgen.c - A password generator.
|
||||
*
|
||||
* Copyright 2020 Moritz R�hrich <moritz@ildefons.de>
|
||||
* Copyright 2020 Moritz Röhrich <moritz@ildefons.de>
|
||||
|
||||
USE_PWGEN(NEWTOY(pwgen, ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
|
||||
+3
-3
@@ -58,7 +58,7 @@ static void watch_child(int sig)
|
||||
static int watch_escape(FILE *out, int cols, int wc)
|
||||
{
|
||||
if (wc==27 || (wc>=7 && wc<=13)) return -1;
|
||||
if (wc < 32) return 0;
|
||||
if (wc<32) return 0;
|
||||
|
||||
return crunch_escape(out, cols, wc);
|
||||
}
|
||||
@@ -106,8 +106,8 @@ void watch_main(void)
|
||||
// Get and measure time string, trimming gratuitous \n
|
||||
ctimelen = strlen(ss = ctime(&t));
|
||||
if (ss[ctimelen-1]=='\n') ss[--ctimelen] = 0;
|
||||
|
||||
// print cmdline, then * or ' ' (showing truncation), then ctime
|
||||
|
||||
// print cmdline, then * or ' ' (showing truncation), then ctime
|
||||
pad = width-++ctimelen;
|
||||
if (pad>0) draw_trim(cmd, -pad, pad);
|
||||
printf("%c", pad<cmdlen ? '*' : ' ');
|
||||
|
||||
+1
-1
@@ -12,4 +12,4 @@ TODO annotations.
|
||||
|
||||
This directory should go away before the 1.0 release. It's just a staging
|
||||
area so code submissions don't get lost while awaiting more thorough (and
|
||||
very time consuming) scrutiny as described in www/cleanup.html.
|
||||
very time consuming) scrutiny as described in www/cleanup.html.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* bootchartd.c - bootchartd is commonly used to profile the boot process.
|
||||
*
|
||||
* Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
|
||||
* Copyright 2014 Kyungwan Han <asura321@gmail.com>
|
||||
* Copyright 2014 Kyungwan Han <asura321@gmail.com>
|
||||
*
|
||||
* No Standard
|
||||
|
||||
|
||||
USE_BOOTCHARTD(NEWTOY(bootchartd, 0, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config BOOTCHARTD
|
||||
@@ -157,7 +157,7 @@ static void start_logging()
|
||||
(int) (ts.tv_nsec/10000000));
|
||||
dump_data_in_file("/proc/stat", sfd);
|
||||
dump_data_in_file("/proc/diskstats", dfd);
|
||||
// stop proc dumping in 2 secs if getty or gdm, kdm, xdm found
|
||||
// stop proc dumping in 2 secs if getty or gdm, kdm, xdm found
|
||||
if (dump_proc_data(proc_ps_fp))
|
||||
if (tcnt > 2 * 1000 / TT.msec) tcnt = 2 * 1000 / TT.msec;
|
||||
fflush(0);
|
||||
@@ -203,7 +203,7 @@ static void stop_logging(char *tmp_dir, char *prog)
|
||||
close(kcmd_line_fd);
|
||||
fclose(hdr_fp);
|
||||
memset(toybuf, 0, sizeof(toybuf));
|
||||
snprintf(toybuf, sizeof(toybuf), "tar -zcf /var/log/bootlog.tgz header %s *.log",
|
||||
snprintf(toybuf, sizeof(toybuf), "tar -zcf /var/log/bootlog.tgz header %s *.log",
|
||||
TT.proc_accounting ? "kernel_procs_acct" : "");
|
||||
system(toybuf);
|
||||
|
||||
@@ -246,7 +246,7 @@ void bootchartd_main()
|
||||
}
|
||||
} else if (TT.pid != 1) error_exit("not PID 1");
|
||||
|
||||
// Execute the code below for start or init or PID1
|
||||
// Execute the code below for start or init or PID1
|
||||
if (!parse_config_file("bootchartd.conf"))
|
||||
parse_config_file("/etc/bootchartd.conf");
|
||||
|
||||
@@ -256,23 +256,23 @@ void bootchartd_main()
|
||||
|
||||
sigatexit(generic_signal);
|
||||
raise(SIGSTOP);
|
||||
if (!bchartd_opt && !getenv("PATH"))
|
||||
if (!bchartd_opt && !getenv("PATH"))
|
||||
putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin");
|
||||
start_logging();
|
||||
stop_logging(tmp_dir, bchartd_opt == 1 ? toys.optargs[1] : NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
waitpid(lgr_pid, NULL, WUNTRACED);
|
||||
kill(lgr_pid, SIGCONT);
|
||||
|
||||
if (!bchartd_opt) {
|
||||
if (!bchartd_opt) {
|
||||
char *pbchart_init = getenv("bootchart_init");
|
||||
|
||||
if (pbchart_init) execl(pbchart_init, pbchart_init, NULL);
|
||||
execl("/init", "init", (void *)0);
|
||||
execl("/sbin/init", "init", (void *)0);
|
||||
}
|
||||
if (bchartd_opt == 1 && toys.optargs[1]) {
|
||||
if (bchartd_opt == 1 && toys.optargs[1]) {
|
||||
pid_t prog_pid;
|
||||
|
||||
if (!(prog_pid = xfork())) xexec(toys.optargs+1);
|
||||
|
||||
+1
-1
@@ -276,7 +276,7 @@ static void sem_array(void)
|
||||
"allocated semaphores = %d\n",
|
||||
info_buf.semusz, info_buf.semaem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (flag(l)) {
|
||||
printf("------ Semaphore Limits --------\n");
|
||||
u.array = (unsigned short *)&info_buf;
|
||||
|
||||
@@ -1,768 +0,0 @@
|
||||
/* mke2fs.c - Create an ext2 filesystem image.
|
||||
*
|
||||
* Copyright 2006, 2007 Rob Landley <rob@landley.net>
|
||||
|
||||
// Still to go: "E:jJ:L:m:O:"
|
||||
USE_MKE2FS(NEWTOY(mke2fs, "<1>2g:Fnqm#N#i#b#", TOYFLAG_SBIN))
|
||||
|
||||
config MKE2FS
|
||||
bool "mke2fs"
|
||||
default n
|
||||
help
|
||||
usage: mke2fs [-Fnq] [-b ###] [-N|i ###] [-m ###] device
|
||||
|
||||
Create an ext2 filesystem on a block device or filesystem image.
|
||||
|
||||
-F Force to run on a mounted device
|
||||
-n Don't write to device
|
||||
-q Quiet (no output)
|
||||
-b size Block size (1024, 2048, or 4096)
|
||||
-N inodes Allocate this many inodes
|
||||
-i bytes Allocate one inode for every XXX bytes of device
|
||||
-m percent Reserve this percent of filesystem space for root user
|
||||
|
||||
config MKE2FS_JOURNAL
|
||||
bool "Journaling support (ext3)"
|
||||
default n
|
||||
depends on MKE2FS
|
||||
help
|
||||
usage: mke2fs [-j] [-J size=###,device=XXX]
|
||||
|
||||
-j Create journal (ext3)
|
||||
-J Journal options
|
||||
size: Number of blocks (1024-102400)
|
||||
device: Specify an external journal
|
||||
|
||||
config MKE2FS_GEN
|
||||
bool "Generate (gene2fs)"
|
||||
default n
|
||||
depends on MKE2FS
|
||||
help
|
||||
usage: gene2fs [options] device filename
|
||||
|
||||
The [options] are the same as mke2fs.
|
||||
|
||||
config MKE2FS_LABEL
|
||||
bool "Label support"
|
||||
default n
|
||||
depends on MKE2FS
|
||||
help
|
||||
usage: mke2fs [-L label] [-M path] [-o string]
|
||||
|
||||
-L Volume label
|
||||
-M Path to mount point
|
||||
-o Created by
|
||||
|
||||
config MKE2FS_EXTENDED
|
||||
bool "Extended options"
|
||||
default n
|
||||
depends on MKE2FS
|
||||
help
|
||||
usage: mke2fs [-E stride=###] [-O option[,option]]
|
||||
|
||||
-E stride= Set RAID stripe size (in blocks)
|
||||
-O [opts] Specify fewer ext2 option flags (for old kernels)
|
||||
All of these are on by default (as appropriate)
|
||||
none Clear default options (all but journaling)
|
||||
dir_index Use htree indexes for large directories
|
||||
filetype Store file type info in directory entry
|
||||
has_journal Set by -j
|
||||
journal_dev Set by -J device=XXX
|
||||
sparse_super Don't allocate huge numbers of redundant superblocks
|
||||
*/
|
||||
|
||||
#define FOR_mke2fs
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
// Command line arguments.
|
||||
long blocksize;
|
||||
long bytes_per_inode;
|
||||
long inodes; // Total inodes in filesystem.
|
||||
long reserved_percent; // Integer precent of space to reserve for root.
|
||||
char *gendir; // Where to read dirtree from.
|
||||
|
||||
// Internal data.
|
||||
struct dirtree *dt; // Tree of files to copy into the new filesystem.
|
||||
unsigned treeblocks; // Blocks used by dt
|
||||
unsigned treeinodes; // Inodes used by dt
|
||||
|
||||
unsigned blocks; // Total blocks in the filesystem.
|
||||
unsigned freeblocks; // Free blocks in the filesystem.
|
||||
unsigned inodespg; // Inodes per group
|
||||
unsigned groups; // Total number of block groups.
|
||||
unsigned blockbits; // Bits per block. (Also blocks per group.)
|
||||
|
||||
// For gene2fs
|
||||
unsigned nextblock; // Next data block to allocate
|
||||
unsigned nextgroup; // Next group we'll be allocating from
|
||||
int fsfd; // File descriptor of filesystem (to output to).
|
||||
)
|
||||
|
||||
// Stuff defined in linux/ext2_fs.h
|
||||
|
||||
#define EXT2_SUPER_MAGIC 0xEF53
|
||||
|
||||
struct ext2_superblock {
|
||||
uint32_t inodes_count; // Inodes count
|
||||
uint32_t blocks_count; // Blocks count
|
||||
uint32_t r_blocks_count; // Reserved blocks count
|
||||
uint32_t free_blocks_count; // Free blocks count
|
||||
uint32_t free_inodes_count; // Free inodes count
|
||||
uint32_t first_data_block; // First Data Block
|
||||
uint32_t log_block_size; // Block size
|
||||
uint32_t log_frag_size; // Fragment size
|
||||
uint32_t blocks_per_group; // Blocks per group
|
||||
uint32_t frags_per_group; // Fragments per group
|
||||
uint32_t inodes_per_group; // Inodes per group
|
||||
uint32_t mtime; // Mount time
|
||||
uint32_t wtime; // Write time
|
||||
uint16_t mnt_count; // Mount count
|
||||
uint16_t max_mnt_count; // Maximal mount count
|
||||
uint16_t magic; // Magic signature
|
||||
uint16_t state; // File system state
|
||||
uint16_t errors; // Behaviour when detecting errors
|
||||
uint16_t minor_rev_level; // minor revision level
|
||||
uint32_t lastcheck; // time of last check
|
||||
uint32_t checkinterval; // max. time between checks
|
||||
uint32_t creator_os; // OS
|
||||
uint32_t rev_level; // Revision level
|
||||
uint16_t def_resuid; // Default uid for reserved blocks
|
||||
uint16_t def_resgid; // Default gid for reserved blocks
|
||||
uint32_t first_ino; // First non-reserved inode
|
||||
uint16_t inode_size; // size of inode structure
|
||||
uint16_t block_group_nr; // block group # of this superblock
|
||||
uint32_t feature_compat; // compatible feature set
|
||||
uint32_t feature_incompat; // incompatible feature set
|
||||
uint32_t feature_ro_compat; // readonly-compatible feature set
|
||||
char uuid[16]; // 128-bit uuid for volume
|
||||
char volume_name[16]; // volume name
|
||||
char last_mounted[64]; // directory where last mounted
|
||||
uint32_t alg_usage_bitmap; // For compression
|
||||
// For EXT2_COMPAT_PREALLOC
|
||||
uint8_t prealloc_blocks; // Nr of blocks to try to preallocate
|
||||
uint8_t prealloc_dir_blocks; //Nr to preallocate for dirs
|
||||
uint16_t padding1;
|
||||
// For EXT3_FEATURE_COMPAT_HAS_JOURNAL
|
||||
uint8_t journal_uuid[16]; // uuid of journal superblock
|
||||
uint32_t journal_inum; // inode number of journal file
|
||||
uint32_t journal_dev; // device number of journal file
|
||||
uint32_t last_orphan; // start of list of inodes to delete
|
||||
uint32_t hash_seed[4]; // HTREE hash seed
|
||||
uint8_t def_hash_version; // Default hash version to use
|
||||
uint8_t padding2[3];
|
||||
uint32_t default_mount_opts;
|
||||
uint32_t first_meta_bg; // First metablock block group
|
||||
uint32_t mkfs_time; // Creation timestamp
|
||||
uint32_t jnl_blocks[17]; // Backup of journal inode
|
||||
// uint32_t reserved[172]; // Padding to the end of the block
|
||||
};
|
||||
|
||||
struct ext2_group
|
||||
{
|
||||
uint32_t block_bitmap; // Block number of block bitmap
|
||||
uint32_t inode_bitmap; // Block number of inode bitmap
|
||||
uint32_t inode_table; // Block number of inode table
|
||||
uint16_t free_blocks_count; // How many free blocks in this group?
|
||||
uint16_t free_inodes_count; // How many free inodes in this group?
|
||||
uint16_t used_dirs_count; // How many directories?
|
||||
uint16_t reserved[7]; // pad to 32 bytes
|
||||
};
|
||||
|
||||
struct ext2_dentry {
|
||||
uint32_t inode; // Inode number
|
||||
uint16_t rec_len; // Directory entry length
|
||||
uint8_t name_len; // Name length
|
||||
uint8_t file_type;
|
||||
char name[0]; // File name
|
||||
};
|
||||
|
||||
struct ext2_inode {
|
||||
uint16_t mode; // File mode
|
||||
uint16_t uid; // Low 16 bits of Owner Uid
|
||||
uint32_t size; // Size in bytes
|
||||
uint32_t atime; // Access time
|
||||
uint32_t ctime; // Creation time
|
||||
uint32_t mtime; // Modification time
|
||||
uint32_t dtime; // Deletion Time
|
||||
uint16_t gid; // Low 16 bits of Group Id
|
||||
uint16_t links_count; // Links count
|
||||
uint32_t blocks; // Blocks count
|
||||
uint32_t flags; // File flags
|
||||
uint32_t reserved1;
|
||||
uint32_t block[15]; // Pointers to blocks
|
||||
uint32_t generation; // File version (for NFS)
|
||||
uint32_t file_acl; // File ACL
|
||||
uint32_t dir_acl; // Directory ACL (or top bits of file length)
|
||||
uint32_t faddr; // Last block in file
|
||||
uint8_t frag; // Fragment number
|
||||
uint8_t fsize; // Fragment size
|
||||
uint16_t pad1;
|
||||
uint16_t uid_high; // High bits of uid
|
||||
uint16_t gid_high; // High bits of gid
|
||||
uint32_t reserved2;
|
||||
};
|
||||
|
||||
#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
|
||||
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
|
||||
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
|
||||
#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
|
||||
#define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010
|
||||
#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
|
||||
|
||||
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
|
||||
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
|
||||
#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
|
||||
|
||||
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
|
||||
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
|
||||
#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
|
||||
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
|
||||
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
|
||||
|
||||
#define EXT2_NAME_LEN 255
|
||||
|
||||
// Ext2 directory file types. Only the low 3 bits are used. The
|
||||
// other bits are reserved for now.
|
||||
|
||||
enum {
|
||||
EXT2_FT_UNKNOWN,
|
||||
EXT2_FT_REG_FILE,
|
||||
EXT2_FT_DIR,
|
||||
EXT2_FT_CHRDEV,
|
||||
EXT2_FT_BLKDEV,
|
||||
EXT2_FT_FIFO,
|
||||
EXT2_FT_SOCK,
|
||||
EXT2_FT_SYMLINK,
|
||||
EXT2_FT_MAX
|
||||
};
|
||||
|
||||
#define INODES_RESERVED 10
|
||||
|
||||
static uint32_t div_round_up(uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t c = a/b;
|
||||
|
||||
if (a%b) c++;
|
||||
return c;
|
||||
}
|
||||
|
||||
// Calculate data blocks plus index blocks needed to hold a file.
|
||||
|
||||
static uint32_t file_blocks_used(uint64_t size, uint32_t *blocklist)
|
||||
{
|
||||
uint32_t dblocks = (uint32_t)((size+(TT.blocksize-1))/TT.blocksize);
|
||||
uint32_t idx=TT.blocksize/4, iblocks=0, diblocks=0, tiblocks=0;
|
||||
|
||||
// Fill out index blocks in inode.
|
||||
|
||||
if (blocklist) {
|
||||
int i;
|
||||
|
||||
// Direct index blocks
|
||||
for (i=0; i<13 && i<dblocks; i++) blocklist[i] = i;
|
||||
// Singly indirect index blocks
|
||||
if (dblocks > 13+idx) blocklist[13] = 13+idx;
|
||||
// Doubly indirect index blocks
|
||||
idx = 13 + idx + (idx*idx);
|
||||
if (dblocks > idx) blocklist[14] = idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Account for direct, singly, doubly, and triply indirect index blocks
|
||||
|
||||
if (dblocks > 12) {
|
||||
iblocks = ((dblocks-13)/idx)+1;
|
||||
if (iblocks > 1) {
|
||||
diblocks = ((iblocks-2)/idx)+1;
|
||||
if (diblocks > 1)
|
||||
tiblocks = ((diblocks-2)/idx)+1;
|
||||
}
|
||||
}
|
||||
|
||||
return dblocks + iblocks + diblocks + tiblocks;
|
||||
}
|
||||
|
||||
// Use the parent pointer to iterate through the tree non-recursively.
|
||||
static struct dirtree *treenext(struct dirtree *this)
|
||||
{
|
||||
while (this && !this->next) this = this->parent;
|
||||
if (this) this = this->next;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Recursively calculate the number of blocks used by each inode in the tree.
|
||||
// Returns blocks used by this directory, assigns bytes used to *size.
|
||||
// Writes total block count to TT.treeblocks and inode count to TT.treeinodes.
|
||||
|
||||
static long check_treesize(struct dirtree *that, off_t *size)
|
||||
{
|
||||
long blocks;
|
||||
|
||||
while (that) {
|
||||
*size += sizeof(struct ext2_dentry) + strlen(that->name);
|
||||
|
||||
if (that->child)
|
||||
that->st.st_blocks = check_treesize(that->child, &that->st.st_size);
|
||||
else if (S_ISREG(that->st.st_mode)) {
|
||||
that->st.st_blocks = file_blocks_used(that->st.st_size, 0);
|
||||
TT.treeblocks += that->st.st_blocks;
|
||||
}
|
||||
that = that->next;
|
||||
}
|
||||
TT.treeblocks += blocks = file_blocks_used(*size, 0);
|
||||
TT.treeinodes++;
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
// Calculate inode numbers and link counts.
|
||||
//
|
||||
// To do this right I need to copy the tree and sort it, but here's a really
|
||||
// ugly n^2 way of dealing with the problem that doesn't scale well to large
|
||||
// numbers of files (> 100,000) but can be done in very little code.
|
||||
// This rewrites inode numbers to their final values, allocating depth first.
|
||||
|
||||
static void check_treelinks(struct dirtree *tree)
|
||||
{
|
||||
struct dirtree *current=tree, *that;
|
||||
long inode = INODES_RESERVED;
|
||||
|
||||
while (current) {
|
||||
++inode;
|
||||
// Since we can't hardlink to directories, we know their link count.
|
||||
if (S_ISDIR(current->st.st_mode)) current->st.st_nlink = 2;
|
||||
else {
|
||||
dev_t new = current->st.st_dev;
|
||||
|
||||
if (!new) continue;
|
||||
|
||||
// Look for other copies of current node
|
||||
current->st.st_nlink = 0;
|
||||
for (that = tree; that; that = treenext(that)) {
|
||||
if (current->st.st_ino == that->st.st_ino &&
|
||||
current->st.st_dev == that->st.st_dev)
|
||||
{
|
||||
current->st.st_nlink++;
|
||||
current->st.st_ino = inode;
|
||||
}
|
||||
}
|
||||
}
|
||||
current->st.st_ino = inode;
|
||||
current = treenext(current);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate inodes per group from total inodes.
|
||||
static uint32_t get_inodespg(uint32_t inodes)
|
||||
{
|
||||
uint32_t temp;
|
||||
|
||||
// Round up to fill complete inode blocks.
|
||||
temp = (inodes + TT.groups - 1) / TT.groups;
|
||||
inodes = TT.blocksize/sizeof(struct ext2_inode);
|
||||
return ((temp + inodes - 1)/inodes)*inodes;
|
||||
}
|
||||
|
||||
// Fill out superblock and TT structures.
|
||||
|
||||
static void init_superblock(struct ext2_superblock *sb)
|
||||
{
|
||||
uint32_t temp;
|
||||
|
||||
// Set log_block_size and log_frag_size.
|
||||
|
||||
for (temp = 0; temp < 4; temp++) if (TT.blocksize == 1024<<temp) break;
|
||||
if (temp==4) error_exit("bad blocksize");
|
||||
sb->log_block_size = sb->log_frag_size = SWAP_LE32(temp);
|
||||
|
||||
// Fill out blocks_count, r_blocks_count, first_data_block
|
||||
|
||||
sb->blocks_count = SWAP_LE32(TT.blocks);
|
||||
sb->free_blocks_count = SWAP_LE32(TT.freeblocks);
|
||||
temp = (TT.blocks * (uint64_t)TT.reserved_percent) / 100;
|
||||
sb->r_blocks_count = SWAP_LE32(temp);
|
||||
|
||||
sb->first_data_block = SWAP_LE32(TT.blocksize == 1024 ? 1 : 0);
|
||||
|
||||
// Set blocks_per_group and frags_per_group, which is the size of an
|
||||
// allocation bitmap that fits in one block (I.E. how many bits per block)?
|
||||
|
||||
sb->blocks_per_group = sb->frags_per_group = SWAP_LE32(TT.blockbits);
|
||||
|
||||
// Set inodes_per_group and total inodes_count
|
||||
sb->inodes_per_group = SWAP_LE32(TT.inodespg);
|
||||
sb->inodes_count = SWAP_LE32(TT.inodespg * TT.groups);
|
||||
|
||||
// Determine free inodes.
|
||||
temp = TT.inodespg*TT.groups - INODES_RESERVED;
|
||||
if (temp < TT.treeinodes) error_exit("Not enough inodes.\n");
|
||||
sb->free_inodes_count = SWAP_LE32(temp - TT.treeinodes);
|
||||
|
||||
// Fill out the rest of the superblock.
|
||||
sb->max_mnt_count=0xFFFF;
|
||||
sb->wtime = sb->lastcheck = sb->mkfs_time = SWAP_LE32(time(NULL));
|
||||
sb->magic = SWAP_LE32(0xEF53);
|
||||
sb->state = sb->errors = SWAP_LE16(1);
|
||||
|
||||
sb->rev_level = SWAP_LE32(1);
|
||||
sb->first_ino = SWAP_LE32(INODES_RESERVED+1);
|
||||
sb->inode_size = SWAP_LE16(sizeof(struct ext2_inode));
|
||||
sb->feature_incompat = SWAP_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE);
|
||||
sb->feature_ro_compat = SWAP_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
|
||||
|
||||
create_uuid(sb->uuid);
|
||||
|
||||
// TODO If we're called as mke3fs or mkfs.ext3, do a journal.
|
||||
|
||||
//if (strchr(toys.which->name,'3'))
|
||||
// sb->feature_compat |= SWAP_LE32(EXT3_FEATURE_COMPAT_HAS_JOURNAL);
|
||||
}
|
||||
|
||||
// Does this group contain a superblock backup (and group descriptor table)?
|
||||
static int is_sb_group(uint32_t group)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Superblock backups are on groups 0, 1, and powers of 3, 5, and 7.
|
||||
if(!group || group==1) return 1;
|
||||
for (i=3; i<9; i+=2) {
|
||||
int j = i;
|
||||
while (j<group) j*=i;
|
||||
if (j==group) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Number of blocks used in group by optional superblock/group list backup.
|
||||
static int group_superblock_overhead(uint32_t group)
|
||||
{
|
||||
int used;
|
||||
|
||||
if (!is_sb_group(group)) return 0;
|
||||
|
||||
// How many blocks does the group descriptor table take up?
|
||||
used = TT.groups * sizeof(struct ext2_group);
|
||||
used += TT.blocksize - 1;
|
||||
used /= TT.blocksize;
|
||||
// Plus the superblock itself.
|
||||
used++;
|
||||
// And a corner case.
|
||||
if (!group && TT.blocksize == 1024) used++;
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
// Number of blocks used in group to store superblock/group/inode list
|
||||
static int group_overhead(uint32_t group)
|
||||
{
|
||||
// Return superblock backup overhead (if any), plus block/inode
|
||||
// allocation bitmaps, plus inode tables.
|
||||
return group_superblock_overhead(group) + 2 + get_inodespg(TT.inodespg)
|
||||
/ (TT.blocksize/sizeof(struct ext2_inode));
|
||||
}
|
||||
|
||||
// In bitmap "array" set "len" bits starting at position "start" (from 0).
|
||||
static void bits_set(char *array, int start, int len)
|
||||
{
|
||||
while(len) {
|
||||
if ((start&7) || len<8) {
|
||||
array[start/8]|=(1<<(start&7));
|
||||
start++;
|
||||
len--;
|
||||
} else {
|
||||
array[start/8]=255;
|
||||
start+=8;
|
||||
len-=8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Seek past len bytes (to maintain sparse file), or write zeroes if output
|
||||
// not seekable
|
||||
static void put_zeroes(int len)
|
||||
{
|
||||
if(-1 == lseek(TT.fsfd, len, SEEK_SET)) {
|
||||
memset(toybuf, 0, sizeof(toybuf));
|
||||
while (len) {
|
||||
int out = len > sizeof(toybuf) ? sizeof(toybuf) : len;
|
||||
xwrite(TT.fsfd, toybuf, out);
|
||||
len -= out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill out an inode structure from struct stat info in dirtree.
|
||||
static void fill_inode(struct ext2_inode *in, struct dirtree *that)
|
||||
{
|
||||
uint32_t fbu[15];
|
||||
int temp;
|
||||
|
||||
file_blocks_used(that->st.st_size, fbu);
|
||||
|
||||
// If that inode needs data blocks allocated to it.
|
||||
if (that->st.st_size) {
|
||||
int i, group = TT.nextblock/TT.blockbits;
|
||||
|
||||
// TODO: teach this about indirect blocks.
|
||||
for (i=0; i<15; i++) {
|
||||
// If we just jumped into a new group, skip group overhead blocks.
|
||||
while (group >= TT.nextgroup)
|
||||
TT.nextblock += group_overhead(TT.nextgroup++);
|
||||
}
|
||||
}
|
||||
// TODO : S_ISREG/DIR/CHR/BLK/FIFO/LNK/SOCK(m)
|
||||
in->mode = SWAP_LE32(that->st.st_mode);
|
||||
|
||||
in->uid = SWAP_LE16(that->st.st_uid & 0xFFFF);
|
||||
in->uid_high = SWAP_LE16(that->st.st_uid >> 16);
|
||||
in->gid = SWAP_LE16(that->st.st_gid & 0xFFFF);
|
||||
in->gid_high = SWAP_LE16(that->st.st_gid >> 16);
|
||||
in->size = SWAP_LE32(that->st.st_size & 0xFFFFFFFF);
|
||||
|
||||
// Contortions to make the compiler not generate a warning for x>>32
|
||||
// when x is 32 bits. The optimizer should clean this up.
|
||||
if (sizeof(that->st.st_size) > 4) temp = 32;
|
||||
else temp = 0;
|
||||
if (temp) in->dir_acl = SWAP_LE32(that->st.st_size >> temp);
|
||||
|
||||
in->atime = SWAP_LE32(that->st.st_atime);
|
||||
in->ctime = SWAP_LE32(that->st.st_ctime);
|
||||
in->mtime = SWAP_LE32(that->st.st_mtime);
|
||||
|
||||
in->links_count = SWAP_LE16(that->st.st_nlink);
|
||||
in->blocks = SWAP_LE32(that->st.st_blocks);
|
||||
// in->faddr
|
||||
}
|
||||
|
||||
// Works like an archiver.
|
||||
// The first argument is the name of the file to create. If it already
|
||||
// exists, that size will be used.
|
||||
|
||||
void mke2fs_main(void)
|
||||
{
|
||||
int i, temp;
|
||||
off_t length;
|
||||
uint32_t usedblocks, usedinodes, dtbblk;
|
||||
struct dirtree *dti, *dtb;
|
||||
struct ext2_superblock sb;
|
||||
|
||||
// Handle command line arguments.
|
||||
|
||||
if (toys.optargs[1]) {
|
||||
sscanf(toys.optargs[1], "%u", &TT.blocks);
|
||||
temp = O_RDWR|O_CREAT;
|
||||
} else temp = O_RDWR;
|
||||
if (!TT.reserved_percent) TT.reserved_percent = 5;
|
||||
|
||||
// TODO: Check if filesystem is mounted here
|
||||
|
||||
// For mke?fs, open file. For gene?fs, create file.
|
||||
TT.fsfd = xcreate(*toys.optargs, temp, 0777);
|
||||
|
||||
// Determine appropriate block size and block count from file length.
|
||||
// (If no length, default to 4k. They can override it on the cmdline.)
|
||||
|
||||
length = fdlength(TT.fsfd);
|
||||
if (!TT.blocksize) TT.blocksize = (length && length < 1<<29) ? 1024 : 4096;
|
||||
TT.blockbits = 8*TT.blocksize;
|
||||
if (!TT.blocks) TT.blocks = length/TT.blocksize;
|
||||
|
||||
// Collect gene2fs list or lost+found, calculate requirements.
|
||||
|
||||
if (TT.gendir) {
|
||||
strncpy(toybuf, TT.gendir, sizeof(toybuf));
|
||||
dti = dirtree_read(toybuf, dirtree_notdotdot);
|
||||
} else {
|
||||
dti = xzalloc(sizeof(struct dirtree)+11);
|
||||
strcpy(dti->name, "lost+found");
|
||||
dti->st.st_mode = S_IFDIR|0755;
|
||||
dti->st.st_ctime = dti->st.st_mtime = time(NULL);
|
||||
}
|
||||
|
||||
// Add root directory inode. This is iterated through for when finding
|
||||
// blocks, but not when finding inodes. The tree's parent pointers don't
|
||||
// point back into this.
|
||||
|
||||
dtb = xzalloc(sizeof(struct dirtree)+1);
|
||||
dtb->st.st_mode = S_IFDIR|0755;
|
||||
dtb->st.st_ctime = dtb->st.st_mtime = time(NULL);
|
||||
dtb->child = dti;
|
||||
|
||||
// Figure out how much space is used by preset files
|
||||
length = check_treesize(dtb, &(dtb->st.st_size));
|
||||
check_treelinks(dtb);
|
||||
|
||||
// Figure out how many total inodes we need.
|
||||
|
||||
if (!TT.inodes) {
|
||||
if (!TT.bytes_per_inode) TT.bytes_per_inode = 8192;
|
||||
TT.inodes = (TT.blocks * (uint64_t)TT.blocksize) / TT.bytes_per_inode;
|
||||
}
|
||||
|
||||
// If we're generating a filesystem and have no idea how many blocks it
|
||||
// needs, start with a minimal guess, find the overhead of that many
|
||||
// groups, and loop until this is enough groups to store this many blocks.
|
||||
if (!TT.blocks) TT.groups = (TT.treeblocks/TT.blockbits)+1;
|
||||
else TT.groups = div_round_up(TT.blocks, TT.blockbits);
|
||||
|
||||
for (;;) {
|
||||
temp = TT.treeblocks;
|
||||
|
||||
for (i = 0; i<TT.groups; i++) temp += group_overhead(i);
|
||||
|
||||
if (TT.blocks) {
|
||||
if (TT.blocks < temp) error_exit("Not enough space.\n");
|
||||
break;
|
||||
}
|
||||
if (temp <= TT.groups * TT.blockbits) {
|
||||
TT.blocks = temp;
|
||||
break;
|
||||
}
|
||||
TT.groups++;
|
||||
}
|
||||
TT.freeblocks = TT.blocks - temp;
|
||||
|
||||
// Now we know all the TT data, initialize superblock structure.
|
||||
|
||||
init_superblock(&sb);
|
||||
|
||||
// Start writing. Skip the first 1k to avoid the boot sector (if any).
|
||||
put_zeroes(1024);
|
||||
|
||||
// Loop through block groups, write out each one.
|
||||
dtbblk = usedblocks = usedinodes = 0;
|
||||
for (i=0; i<TT.groups; i++) {
|
||||
struct ext2_inode *in = (struct ext2_inode *)toybuf;
|
||||
uint32_t start, itable, used, end;
|
||||
int j, slot;
|
||||
|
||||
// Where does this group end?
|
||||
end = TT.blockbits;
|
||||
if ((i+1)*TT.blockbits > TT.blocks) end = TT.blocks & (TT.blockbits-1);
|
||||
|
||||
// Blocks used by inode table
|
||||
itable = (TT.inodespg*sizeof(struct ext2_inode))/TT.blocksize;
|
||||
|
||||
// If a superblock goes here, write it out.
|
||||
start = group_superblock_overhead(i);
|
||||
if (start) {
|
||||
struct ext2_group *bg = (struct ext2_group *)toybuf;
|
||||
int treeblocks = TT.treeblocks, treeinodes = TT.treeinodes;
|
||||
|
||||
sb.block_group_nr = SWAP_LE16(i);
|
||||
|
||||
// Write superblock and pad it up to block size
|
||||
xwrite(TT.fsfd, &sb, sizeof(struct ext2_superblock));
|
||||
temp = TT.blocksize - sizeof(struct ext2_superblock);
|
||||
if (!i && TT.blocksize > 1024) temp -= 1024;
|
||||
memset(toybuf, 0, TT.blocksize);
|
||||
xwrite(TT.fsfd, toybuf, temp);
|
||||
|
||||
// Loop through groups to write group descriptor table.
|
||||
for(j=0; j<TT.groups; j++) {
|
||||
|
||||
// Figure out what sector this group starts in.
|
||||
used = group_superblock_overhead(j);
|
||||
|
||||
// Find next array slot in this block (flush block if full).
|
||||
slot = j % (TT.blocksize/sizeof(struct ext2_group));
|
||||
if (!slot) {
|
||||
if (j) xwrite(TT.fsfd, bg, TT.blocksize);
|
||||
memset(bg, 0, TT.blocksize);
|
||||
}
|
||||
|
||||
// How many free inodes in this group?
|
||||
temp = TT.inodespg;
|
||||
if (!i) temp -= INODES_RESERVED;
|
||||
if (temp > treeinodes) {
|
||||
treeinodes -= temp;
|
||||
temp = 0;
|
||||
} else {
|
||||
temp -= treeinodes;
|
||||
treeinodes = 0;
|
||||
}
|
||||
bg[slot].free_inodes_count = SWAP_LE16(temp);
|
||||
|
||||
// How many free blocks in this group?
|
||||
temp = TT.inodespg/(TT.blocksize/sizeof(struct ext2_inode)) + 2;
|
||||
temp = end-used-temp;
|
||||
if (temp > treeblocks) {
|
||||
treeblocks -= temp;
|
||||
temp = 0;
|
||||
} else {
|
||||
temp -= treeblocks;
|
||||
treeblocks = 0;
|
||||
}
|
||||
bg[slot].free_blocks_count = SWAP_LE32(temp);
|
||||
|
||||
// Fill out rest of group structure
|
||||
used += j*TT.blockbits;
|
||||
bg[slot].block_bitmap = SWAP_LE32(used++);
|
||||
bg[slot].inode_bitmap = SWAP_LE32(used++);
|
||||
bg[slot].inode_table = SWAP_LE32(used);
|
||||
bg[slot].used_dirs_count = 0; // (TODO)
|
||||
}
|
||||
xwrite(TT.fsfd, bg, TT.blocksize);
|
||||
}
|
||||
|
||||
// Now write out stuff that every block group has.
|
||||
|
||||
// Write block usage bitmap
|
||||
|
||||
start += 2 + itable;
|
||||
memset(toybuf, 0, TT.blocksize);
|
||||
bits_set(toybuf, 0, start);
|
||||
bits_set(toybuf, end, TT.blockbits-end);
|
||||
temp = TT.treeblocks - usedblocks;
|
||||
if (temp) {
|
||||
if (end-start > temp) temp = end-start;
|
||||
bits_set(toybuf, start, temp);
|
||||
}
|
||||
xwrite(TT.fsfd, toybuf, TT.blocksize);
|
||||
|
||||
// Write inode bitmap
|
||||
memset(toybuf, 0, TT.blocksize);
|
||||
j = 0;
|
||||
if (!i) bits_set(toybuf, 0, j = INODES_RESERVED);
|
||||
bits_set(toybuf, TT.inodespg, slot = TT.blockbits-TT.inodespg);
|
||||
temp = TT.treeinodes - usedinodes;
|
||||
if (temp) {
|
||||
if (slot-j > temp) temp = slot-j;
|
||||
bits_set(toybuf, j, temp);
|
||||
}
|
||||
xwrite(TT.fsfd, toybuf, TT.blocksize);
|
||||
|
||||
// Write inode table for this group (TODO)
|
||||
for (j = 0; j<TT.inodespg; j++) {
|
||||
slot = j % (TT.blocksize/sizeof(struct ext2_inode));
|
||||
if (!slot) {
|
||||
if (j) xwrite(TT.fsfd, in, TT.blocksize);
|
||||
memset(in, 0, TT.blocksize);
|
||||
}
|
||||
if (!i && j<INODES_RESERVED) {
|
||||
// Write root inode
|
||||
if (j == 2) fill_inode(in+slot, dtb);
|
||||
} else if (dti) {
|
||||
fill_inode(in+slot, dti);
|
||||
dti = treenext(dti);
|
||||
}
|
||||
}
|
||||
xwrite(TT.fsfd, in, TT.blocksize);
|
||||
|
||||
while (dtb) {
|
||||
// TODO write index data block
|
||||
// TODO write root directory data block
|
||||
// TODO write directory data block
|
||||
// TODO write file data block
|
||||
put_zeroes(TT.blocksize);
|
||||
start++;
|
||||
if (start == end) break;
|
||||
}
|
||||
// Write data blocks (TODO)
|
||||
put_zeroes((end-start) * TT.blocksize);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -57,7 +57,7 @@ static int prompt(FILE *cin, const char* fmt, ...)
|
||||
// Clean up styles after Ctrl+C and other operations.
|
||||
printf("\33[0m");
|
||||
#endif
|
||||
|
||||
|
||||
while (1) {
|
||||
fflush(NULL);
|
||||
input_key = tolower(getc(cin));
|
||||
|
||||
+32
-32
@@ -100,7 +100,7 @@ static u_int16_t in_cksum(u_int16_t *p, u_int len)
|
||||
u.c[1] = 0;
|
||||
sum += u.w;
|
||||
}
|
||||
// end-around-carry
|
||||
// end-around-carry
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
return (~sum);
|
||||
@@ -133,7 +133,7 @@ static void send_probe4(int seq, int ttl)
|
||||
if (res < 0) perror_exit("setsockopt ttl %d", ttl);
|
||||
|
||||
len = TT.msg_len;
|
||||
res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest,
|
||||
res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest,
|
||||
sizeof(struct sockaddr_in));
|
||||
if (res != len) perror_exit(" sendto");
|
||||
}
|
||||
@@ -148,7 +148,7 @@ static void send_probe6(int seq, int ttl)
|
||||
send_data6->ident = TT.ident;
|
||||
((struct sockaddr_in6 *)&dest)->sin6_port = TT.port;
|
||||
|
||||
if (setsockopt(TT.snd_sock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl,
|
||||
if (setsockopt(TT.snd_sock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl,
|
||||
sizeof(ttl)) < 0) error_exit("setsockopt ttl %d", ttl);
|
||||
|
||||
out = send_data6;
|
||||
@@ -225,12 +225,12 @@ static void do_trace()
|
||||
gettimeofday(&t1, NULL);
|
||||
t2 = t1;
|
||||
|
||||
while ((tleft = (int)(tv - ((unsigned long long)(t2.tv_sec * 1000ULL
|
||||
while ((tleft = (int)(tv - ((unsigned long long)(t2.tv_sec * 1000ULL
|
||||
+ t2.tv_usec/1000) - (unsigned long long)(t1.tv_sec * 1000ULL
|
||||
+ t1.tv_usec/1000)))) >= 0) {
|
||||
unsigned delta = 0;
|
||||
if (!(res = poll(pfd, 1, tleft))) {
|
||||
xprintf(" *");
|
||||
if (!(res = poll(pfd, 1, tleft))) {
|
||||
xprintf(" *");
|
||||
break;
|
||||
}
|
||||
gettimeofday(&t2, NULL);
|
||||
@@ -276,28 +276,28 @@ static void do_trace()
|
||||
ricmp->icmp_code);
|
||||
} else {
|
||||
hicmp = (struct icmp *) ((char*)hip + (hip->ip_hl << 2));
|
||||
if (ricmp->icmp_type == ICMP_ECHOREPLY
|
||||
if (ricmp->icmp_type == ICMP_ECHOREPLY
|
||||
&& ricmp->icmp_id == ntohs(TT.ident)
|
||||
&& ricmp->icmp_seq == ntohs(seq))
|
||||
icmp_res = ICMP_UNREACH_PORT;
|
||||
else if ((hip->ip_hl << 2) + ICMP_HD_SIZE4
|
||||
else if ((hip->ip_hl << 2) + ICMP_HD_SIZE4
|
||||
<= (rcv_len - (rcv_pkt->ip_hl << 2))
|
||||
&& hip->ip_p == IPPROTO_ICMP
|
||||
&& hicmp->icmp_id == htons(TT.ident)
|
||||
&& hicmp->icmp_seq == htons(seq))
|
||||
icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 :
|
||||
icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 :
|
||||
ricmp->icmp_code);
|
||||
}
|
||||
}
|
||||
if (!icmp_res) continue;
|
||||
|
||||
if (addrlen > 0) {
|
||||
if (memcmp(&((struct sockaddr_in *)&last_addr)->sin_addr,
|
||||
&((struct sockaddr_in *)&from)->sin_addr,
|
||||
if (memcmp(&((struct sockaddr_in *)&last_addr)->sin_addr,
|
||||
&((struct sockaddr_in *)&from)->sin_addr,
|
||||
sizeof(struct in_addr))) {
|
||||
if (!(toys.optflags & FLAG_n)) {
|
||||
char host[NI_MAXHOST];
|
||||
if (!getnameinfo((struct sockaddr *) &from,
|
||||
if (!getnameinfo((struct sockaddr *) &from,
|
||||
sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, 0))
|
||||
xprintf(" %s (", host);
|
||||
else xprintf(" %s (", inet_ntoa(
|
||||
@@ -359,7 +359,7 @@ static void do_trace()
|
||||
xprintf(" !C");
|
||||
++fexit;
|
||||
break;
|
||||
case ICMP_UNREACH_NET_UNKNOWN:
|
||||
case ICMP_UNREACH_NET_UNKNOWN:
|
||||
case ICMP_UNREACH_HOST_UNKNOWN:
|
||||
xprintf(" !U");
|
||||
++fexit;
|
||||
@@ -407,8 +407,8 @@ static void do_trace()
|
||||
|
||||
if (!icmp_res) continue;
|
||||
if (addrlen > 0) {
|
||||
if (memcmp(&((struct sockaddr_in6 *)&last_addr)->sin6_addr,
|
||||
&((struct sockaddr_in6 *)&from)->sin6_addr,
|
||||
if (memcmp(&((struct sockaddr_in6 *)&last_addr)->sin6_addr,
|
||||
&((struct sockaddr_in6 *)&from)->sin6_addr,
|
||||
sizeof(struct in6_addr))) {
|
||||
if (!(toys.optflags & FLAG_n)) {
|
||||
char host[NI_MAXHOST];
|
||||
@@ -467,7 +467,7 @@ static void do_trace()
|
||||
}
|
||||
xputc('\n');
|
||||
if(!TT.istraceroute6) {
|
||||
if (!memcmp(&((struct sockaddr_in *)&from)->sin_addr,
|
||||
if (!memcmp(&((struct sockaddr_in *)&from)->sin_addr,
|
||||
&((struct sockaddr_in *)&dest)->sin_addr, sizeof(struct in_addr))
|
||||
|| dest_reach || (fexit && fexit >= TT.ttl_probes -1))
|
||||
break;
|
||||
@@ -479,8 +479,8 @@ void traceroute_main(void)
|
||||
{
|
||||
unsigned pack_size = 0, tyser = 0;
|
||||
int lsrr = 0, set = 1;
|
||||
|
||||
if(!(toys.optflags & FLAG_4) &&
|
||||
|
||||
if(!(toys.optflags & FLAG_4) &&
|
||||
(inet_pton(AF_INET6, toys.optargs[0], &dest)))
|
||||
toys.optflags |= FLAG_6;
|
||||
|
||||
@@ -511,29 +511,29 @@ void traceroute_main(void)
|
||||
if (TT.istraceroute6) {
|
||||
int two = 2;
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
setsockopt(TT.recv_sock, SOL_IPV6, IPV6_RECVPKTINFO, &set,
|
||||
setsockopt(TT.recv_sock, SOL_IPV6, IPV6_RECVPKTINFO, &set,
|
||||
sizeof(set));
|
||||
setsockopt(TT.recv_sock, SOL_IPV6, IPV6_2292PKTINFO, &set,
|
||||
setsockopt(TT.recv_sock, SOL_IPV6, IPV6_2292PKTINFO, &set,
|
||||
sizeof(set));
|
||||
#else
|
||||
setsockopt(TT.recv_sock, SOL_IPV6, IPV6_PKTINFO, &set, sizeof(set));
|
||||
#endif
|
||||
|
||||
if (setsockopt(TT.recv_sock, SOL_RAW, IPV6_CHECKSUM, &two,
|
||||
if (setsockopt(TT.recv_sock, SOL_RAW, IPV6_CHECKSUM, &two,
|
||||
sizeof(two)) < 0) perror_exit("setsockopt RAW_CHECKSUM");
|
||||
}
|
||||
|
||||
set_flag_dr(TT.recv_sock);
|
||||
|
||||
if (!TT.istraceroute6) {
|
||||
if (toys.optflags & FLAG_U)
|
||||
if (toys.optflags & FLAG_U)
|
||||
TT.snd_sock = xsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
else TT.snd_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
|
||||
if (toys.optflags & FLAG_i) bind_to_interface(TT.snd_sock);
|
||||
|
||||
resolve_addr(toys.optargs[0], AF_INET, ((toys.optflags & FLAG_U) ?
|
||||
SOCK_DGRAM : SOCK_RAW), ((toys.optflags & FLAG_U) ? IPPROTO_UDP :
|
||||
resolve_addr(toys.optargs[0], AF_INET, ((toys.optflags & FLAG_U) ?
|
||||
SOCK_DGRAM : SOCK_RAW), ((toys.optflags & FLAG_U) ? IPPROTO_UDP :
|
||||
IPPROTO_ICMP), &dest);
|
||||
if (lsrr > 0) {
|
||||
unsigned char optlist[MAX_IPOPTLEN];
|
||||
@@ -543,7 +543,7 @@ void traceroute_main(void)
|
||||
++lsrr;
|
||||
|
||||
optlist[0] = IPOPT_NOP;
|
||||
optlist[1] = IPOPT_LSRR;// loose source route option
|
||||
optlist[1] = IPOPT_LSRR;// loose source route option
|
||||
size = lsrr * sizeof(TT.gw_list[0]);
|
||||
optlist[2] = size + 3;
|
||||
optlist[3] = IPOPT_MINOFF;
|
||||
@@ -555,17 +555,17 @@ void traceroute_main(void)
|
||||
}
|
||||
} else TT.snd_sock = xsocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len,
|
||||
if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len,
|
||||
sizeof(TT.msg_len)) < 0) perror_exit("SO_SNDBUF failed ");
|
||||
|
||||
if (!TT.istraceroute6) {
|
||||
if ((toys.optflags & FLAG_t) &&
|
||||
if ((toys.optflags & FLAG_t) &&
|
||||
setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0)
|
||||
perror_exit("IP_TOS %ld failed ", TT.tos);
|
||||
|
||||
#ifdef IP_DONTFRAG
|
||||
if ((toys.optflags & FLAG_F) &&
|
||||
(setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set,
|
||||
(setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set,
|
||||
sizeof(set)) < 0)) perror_exit("IP_DONTFRAG failed ");
|
||||
#endif
|
||||
} else if (setsockopt(TT.snd_sock, IPPROTO_IPV6, IPV6_TCLASS, &TT.tos,
|
||||
@@ -593,10 +593,10 @@ void traceroute_main(void)
|
||||
xbind(TT.snd_sock,(struct sockaddr*)&source, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
if(TT.first_ttl > TT.max_ttl)
|
||||
if(TT.first_ttl > TT.max_ttl)
|
||||
error_exit("ERROR :Range for -f is 1 to %ld (max ttl)", TT.max_ttl);
|
||||
|
||||
xprintf("traceroute to %s(%s)", toys.optargs[0],
|
||||
xprintf("traceroute to %s(%s)", toys.optargs[0],
|
||||
inet_ntoa(((struct sockaddr_in *)&dest)->sin_addr));
|
||||
} else {
|
||||
if (toys.optflags & FLAG_i) bind_to_interface(TT.snd_sock);
|
||||
@@ -622,7 +622,7 @@ void traceroute_main(void)
|
||||
|
||||
((struct sockaddr_in6 *)&dest)->sin6_port = htons(1025);
|
||||
xconnect(p_fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_in6));
|
||||
if(getsockname(p_fd, (struct sockaddr *)&prb, &len))
|
||||
if(getsockname(p_fd, (struct sockaddr *)&prb, &len))
|
||||
error_exit("probe addr failed");
|
||||
close(p_fd);
|
||||
prb.sin6_port = 0;
|
||||
@@ -634,7 +634,7 @@ void traceroute_main(void)
|
||||
xbind(TT.recv_sock, (struct sockaddr*)&prb, sizeof(struct sockaddr_in6));
|
||||
}
|
||||
|
||||
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&dest)->sin6_addr,
|
||||
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&dest)->sin6_addr,
|
||||
addr_str, INET6_ADDRSTRLEN);
|
||||
xprintf("traceroute6 to %s(%s)", toys.optargs[0], addr_str);
|
||||
}
|
||||
|
||||
+6
-6
@@ -557,7 +557,7 @@ static char *string_field(struct procpid *tb, struct ofields *field)
|
||||
|
||||
// Clock displays
|
||||
} else if (which <= PS_TIME_) {
|
||||
int unit = 60, pad = 2, j = TT.ticks;
|
||||
int unit = 60, pad = 2, j = TT.ticks;
|
||||
time_t seconds;
|
||||
|
||||
if (which!=PS_TIME_) unit *= 60*24;
|
||||
@@ -620,7 +620,7 @@ static char *string_field(struct procpid *tb, struct ofields *field)
|
||||
if (slot[SLOT_sid]==*slot) *s++ = 's';
|
||||
if (slot[SLOT_vmlck]) *s++ = 'L';
|
||||
if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
|
||||
}
|
||||
}
|
||||
*s = 0;
|
||||
} else if (which==PS_STIME) {
|
||||
time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
|
||||
@@ -1284,7 +1284,7 @@ static int ksort(void *aa, void *bb)
|
||||
|
||||
// Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array
|
||||
// (recursion because tree from get_thread() isn't flat list of siblings)
|
||||
static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
|
||||
static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
|
||||
{
|
||||
while (dt) {
|
||||
struct dirtree *next = dt->next;
|
||||
@@ -1307,7 +1307,7 @@ static struct procpid **collate(int count, struct dirtree *dt)
|
||||
collate_leaves(tbsort, dt);
|
||||
|
||||
return tbsort;
|
||||
}
|
||||
}
|
||||
|
||||
// parse command line arguments (ala -k -o) with a comma separated FIELD list
|
||||
static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
|
||||
@@ -1660,10 +1660,10 @@ static void top_common(
|
||||
/* fix "iotop -m 10" show 13 lines problem*/
|
||||
if (TT.top.m) {
|
||||
if (toys.which->name[0] == 'i') {
|
||||
// iotop 鍛戒护
|
||||
// iotop 命令
|
||||
TT.height = TT.top.m + 2;
|
||||
} else {
|
||||
// top 鍛戒护
|
||||
// top 命令
|
||||
TT.height = TT.top.m + 5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
/* netcat.c - Forward stdin/stdout to a file or network connection.
|
||||
*
|
||||
* Copyright 2007 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* TODO: genericize for telnet/microcom/tail-f, fix -t with login_tty()
|
||||
|
||||
USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO]", TOYFLAG_BIN))
|
||||
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config NETCAT
|
||||
bool "netcat"
|
||||
default y
|
||||
help
|
||||
usage: netcat [-46ELlntUu] [-pqWw #] [-s addr] [-o FILE] {IPADDR PORTNUM|-f FILENAME|COMMAND...}
|
||||
|
||||
Forward stdin/stdout to a file or network connection.
|
||||
|
||||
-4 Force IPv4
|
||||
-6 Force IPv6
|
||||
-E Forward stderr
|
||||
-f Use FILENAME (ala /dev/ttyS0) instead of network
|
||||
-L Listen and background each incoming connection (server mode)
|
||||
-l Listen for one incoming connection, then exit
|
||||
-n No DNS lookup
|
||||
-o Hex dump to FILE (show packets, -o- writes hex only to stdout)
|
||||
-O Hex dump to FILE (streaming mode)
|
||||
-p Local port number
|
||||
-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet
|
||||
-s Local source address
|
||||
-t Allocate tty
|
||||
-u Use UDP
|
||||
-U Use a UNIX domain socket
|
||||
-W SECONDS timeout for more data on an idle connection
|
||||
-w SECONDS timeout to establish connection
|
||||
-z zero-I/O mode [used for scanning]
|
||||
|
||||
When listening the COMMAND line is executed as a child process to handle
|
||||
an incoming connection. With no COMMAND -l forwards the connection
|
||||
to stdin/stdout. If no -p specified, -l prints the port it bound to and
|
||||
backgrounds itself (returning immediately).
|
||||
|
||||
For a quick-and-dirty server, try something like:
|
||||
netcat -s 127.0.0.1 -p 1234 -tL sh -l
|
||||
|
||||
Or use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
|
||||
netcat -f to connect to a serial port.
|
||||
*/
|
||||
|
||||
#define FOR_netcat
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *f, *s, *o, *O;
|
||||
long q, p, W, w;
|
||||
|
||||
unsigned ofd, olast, opos, ocount[2];
|
||||
char obuf[16];
|
||||
)
|
||||
|
||||
static void timeout(int signum)
|
||||
{
|
||||
if (TT.w) error_exit("Timeout");
|
||||
xexit();
|
||||
}
|
||||
|
||||
// open AF_UNIX socket
|
||||
static int usock(char *name, int type, int out)
|
||||
{
|
||||
int sockfd;
|
||||
struct sockaddr_un sockaddr;
|
||||
|
||||
memset(&sockaddr, 0, sizeof(struct sockaddr_un));
|
||||
|
||||
if (strlen(name) + 1 > sizeof(sockaddr.sun_path))
|
||||
error_exit("socket path too long %s", name);
|
||||
strcpy(sockaddr.sun_path, name);
|
||||
sockaddr.sun_family = AF_UNIX;
|
||||
|
||||
sockfd = xsocket(AF_UNIX, type, 0);
|
||||
(out?xconnect:xbind)(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
// Hex dump accumulated buffer data
|
||||
void oflush(void)
|
||||
{
|
||||
char *s = toybuf;
|
||||
unsigned *oc = TT.ocount+(TT.olast==1), uu;
|
||||
|
||||
if (!TT.opos) return;
|
||||
s += sprintf(toybuf, "%c %08x", 60+2*(TT.olast==1), *oc);
|
||||
for (uu = 0; uu<16; uu++) {
|
||||
s += sprintf(s, uu<TT.opos ? " %02x" : " ", TT.obuf[uu]);
|
||||
if (TT.obuf[uu]-32u>95) TT.obuf[uu] = '.';
|
||||
}
|
||||
dprintf(TT.ofd, "%s # %.*s\n", toybuf, TT.opos, TT.obuf);
|
||||
*oc += TT.opos;
|
||||
TT.opos = 0;
|
||||
}
|
||||
|
||||
// Write data to output, and hex dump to -o if enabled.
|
||||
void ohexwrite(int fd, void *buf, size_t len)
|
||||
{
|
||||
// Hex dump if -o specified. Output is always to fd 1, input != 1.
|
||||
if (TT.ofd) {
|
||||
int i = 0, j;
|
||||
|
||||
if (TT.olast != fd) oflush();
|
||||
TT.olast = fd;
|
||||
|
||||
while (i<len) {
|
||||
j = minof(16-TT.opos, len-i);
|
||||
memcpy(TT.obuf+TT.opos, buf+i, j);
|
||||
TT.opos += j;
|
||||
i += j;
|
||||
if (TT.opos==16 || !TT.O) oflush();
|
||||
}
|
||||
|
||||
// Don't write data to stdout when -o goes to stdout.
|
||||
if (TT.ofd==1 && fd==1) return;
|
||||
}
|
||||
|
||||
// Pass along raw data
|
||||
xwrite(fd, buf, len);
|
||||
}
|
||||
|
||||
void netcat_main(void)
|
||||
{
|
||||
int sockfd = -1, in1 = 0, in2 = 0, out1 = 1, out2 = 1, family = AF_UNSPEC,
|
||||
type = FLAG(u) ? SOCK_DGRAM : SOCK_STREAM;
|
||||
socklen_t len;
|
||||
pid_t child;
|
||||
|
||||
// -o - disables normal writes to stdout, just gives hex dump.
|
||||
if (TT.O) TT.o = TT.O;
|
||||
if (TT.o) {
|
||||
if (!strcmp(TT.o, "-")) TT.ofd = 1;
|
||||
else TT.ofd = xcreate(TT.o, O_CREAT|O_TRUNC|O_WRONLY, 0666);
|
||||
sigatexit(oflush);
|
||||
}
|
||||
|
||||
// Adjust idle and quit_delay to ms or -1 for no timeout
|
||||
TT.W = TT.W ? TT.W*1000 : -1;
|
||||
TT.q = TT.q ? TT.q*1000 : -1;
|
||||
|
||||
xsignal(SIGCHLD, SIG_IGN);
|
||||
if (TT.w) {
|
||||
xsignal(SIGALRM, timeout);
|
||||
alarm(TT.w);
|
||||
}
|
||||
|
||||
// The argument parsing logic can't make "<2" conditional on other
|
||||
// arguments like -f and -l, so do it by hand here.
|
||||
if (FLAG(f) ? toys.optc : (!FLAG(l) && !FLAG(L) && toys.optc!=2-FLAG(U)))
|
||||
help_exit("bad argument count");
|
||||
|
||||
if (FLAG(4)) family = AF_INET;
|
||||
else if (FLAG(6)) family = AF_INET6;
|
||||
else if (FLAG(U)) family = AF_UNIX;
|
||||
|
||||
if (TT.f) {
|
||||
in1 = out2 = xopen(TT.f, O_RDWR);
|
||||
alarm(0);
|
||||
pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
|
||||
} else {
|
||||
// Setup socket
|
||||
if (!FLAG(l) && !FLAG(L)) {
|
||||
if (FLAG(U)) sockfd = usock(toys.optargs[0], type, 1);
|
||||
else sockfd = xconnectany(xgetaddrinfo(toys.optargs[0], toys.optargs[1],
|
||||
family, type, 0, AI_NUMERICHOST*FLAG(n)));
|
||||
|
||||
// Do not perform any I/O in zero mode
|
||||
if (FLAG(z)) goto cleanup;
|
||||
|
||||
// We have a connection. Disarm timeout and start poll/send loop.
|
||||
alarm(0);
|
||||
in1 = out2 = sockfd;
|
||||
pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
|
||||
} else {
|
||||
// Listen for incoming connections
|
||||
if (FLAG(U)) {
|
||||
if (!FLAG(s)) error_exit("-s must be provided if using -U with -L/-l");
|
||||
sockfd = usock(TT.s, type, 0);
|
||||
} else {
|
||||
sprintf(toybuf, "%ld", TT.p);
|
||||
sockfd = xbindany(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
|
||||
}
|
||||
|
||||
if (!FLAG(u) && listen(sockfd, 5)) perror_exit("listen");
|
||||
if (!TT.p && !FLAG(U)) {
|
||||
struct sockaddr* address = (void*)toybuf;
|
||||
short port_be;
|
||||
|
||||
len = sizeof(struct sockaddr_storage);
|
||||
getsockname(sockfd, address, &len);
|
||||
if (address->sa_family == AF_INET)
|
||||
port_be = ((struct sockaddr_in*)address)->sin_port;
|
||||
else if (address->sa_family == AF_INET6)
|
||||
port_be = ((struct sockaddr_in6*)address)->sin6_port;
|
||||
else perror_exit("getsockname: bad family");
|
||||
|
||||
dprintf(1, "%d\n", SWAP_BE16(port_be));
|
||||
// Return immediately if no -p and -Ll has arguments, so wrapper
|
||||
// script can use port number.
|
||||
if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup;
|
||||
}
|
||||
|
||||
do {
|
||||
len = sizeof(struct sockaddr_storage);
|
||||
if (FLAG(u)) {
|
||||
if (-1 == recvfrom(in1 = dup(sockfd), &child, 1, MSG_PEEK,
|
||||
(void *)toybuf, &len)) perror_exit("recvfrom");
|
||||
} else if ((in1 = accept(sockfd, 0, 0))<0) perror_exit("accept");
|
||||
out2 = in1;
|
||||
child = 0;
|
||||
|
||||
// We have a connection. Disarm timeout.
|
||||
alarm(0);
|
||||
|
||||
// Fork a child as necessary. Parent cleans up and continues here.
|
||||
if (toys.optc && FLAG(L)) NOEXIT(child = XVFORK());
|
||||
if (child) {
|
||||
close(in1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FLAG(u))
|
||||
xconnect(in1, (void *)toybuf, sizeof(struct sockaddr_storage));
|
||||
|
||||
// Cleanup and redirect for exec
|
||||
if (toys.optc) {
|
||||
// Do we need a tty?
|
||||
// TODO nommu and -t only affects server mode...
|
||||
// if (FLAG(t)) child = forkpty(&fdout, NULL, NULL, NULL);
|
||||
|
||||
close(sockfd);
|
||||
dup2(in1, 0);
|
||||
dup2(in1, 1);
|
||||
if (FLAG(E)) dup2(in1, 2);
|
||||
if (in1>2) close(in1);
|
||||
xexec(toys.optargs);
|
||||
|
||||
// Copy stdin/out
|
||||
} else {
|
||||
pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
|
||||
close(in1);
|
||||
}
|
||||
} while (FLAG(L));
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (CFG_TOYBOX_FREE) {
|
||||
close(in1);
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/* free.c - Display amount of free and used memory in the system.
|
||||
*
|
||||
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
|
||||
|
||||
// Flag order is signifcant: b-g are units in order, FLAG_h-1 is unit mask
|
||||
USE_FREE(NEWTOY(free, "hgmkb[!hgmkb]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config FREE
|
||||
bool "free"
|
||||
default y
|
||||
help
|
||||
usage: free [-bkmgt]
|
||||
|
||||
Display the total, free and used amount of physical memory and swap space.
|
||||
|
||||
-bkmg Output units (default is bytes)
|
||||
-h Human readable (K=1024)
|
||||
*/
|
||||
|
||||
#define FOR_free
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
unsigned bits;
|
||||
unsigned long long units;
|
||||
char *buf;
|
||||
)
|
||||
|
||||
static char *convert(unsigned long d)
|
||||
{
|
||||
long long ll = d*TT.units;
|
||||
char *s = TT.buf;
|
||||
|
||||
if (FLAG(h)) human_readable(s, ll, 0);
|
||||
else sprintf(s, "%llu",ll>>TT.bits);
|
||||
TT.buf += strlen(TT.buf)+1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void free_main(void)
|
||||
{
|
||||
struct sysinfo in;
|
||||
|
||||
sysinfo(&in);
|
||||
TT.units = in.mem_unit ? in.mem_unit : 1;
|
||||
while ((toys.optflags&(FLAG_h-1)) && !(toys.optflags&(1<<TT.bits))) TT.bits++;
|
||||
TT.bits *= 10;
|
||||
TT.buf = toybuf;
|
||||
|
||||
xprintf("\t\ttotal used free shared buffers\n"
|
||||
"Mem:%17s%12s%12s%12s%12s\n-/+ buffers/cache:%15s%12s\n"
|
||||
"Swap:%16s%12s%12s\n", convert(in.totalram),
|
||||
convert(in.totalram-in.freeram), convert(in.freeram), convert(in.sharedram),
|
||||
convert(in.bufferram), convert(in.totalram - in.freeram - in.bufferram),
|
||||
convert(in.freeram + in.bufferram), convert(in.totalswap),
|
||||
convert(in.totalswap - in.freeswap), convert(in.freeswap));
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
/* lsattr.c - List file attributes on a Linux second extended file system.
|
||||
*
|
||||
* Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
|
||||
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
|
||||
*
|
||||
* No Standard.
|
||||
*
|
||||
* TODO cleanup
|
||||
|
||||
USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN))
|
||||
USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN))
|
||||
|
||||
config LSATTR
|
||||
bool "lsattr"
|
||||
default y
|
||||
help
|
||||
usage: lsattr [-Radlpv] [FILE...]
|
||||
|
||||
List file attributes on a Linux file system.
|
||||
Flag letters are defined in chattr help.
|
||||
|
||||
-R Recursively list attributes of directories and their contents
|
||||
-a List all files in directories, including files that start with '.'
|
||||
-d List directories like other files, rather than listing their contents
|
||||
-l List long flag names
|
||||
-p List the file's project number
|
||||
-v List the file's version/generation number
|
||||
|
||||
config CHATTR
|
||||
bool "chattr"
|
||||
default y
|
||||
help
|
||||
usage: chattr [-R] [-+=AacDdijsStTu] [-p PROJID] [-v VERSION] [FILE...]
|
||||
|
||||
Change file attributes on a Linux file system.
|
||||
|
||||
-R Recurse
|
||||
-p Set the file's project number
|
||||
-v Set the file's version/generation number
|
||||
|
||||
Operators:
|
||||
'-' Remove attributes
|
||||
'+' Add attributes
|
||||
'=' Set attributes
|
||||
|
||||
Attributes:
|
||||
A No atime a Append only
|
||||
C No COW c Compression
|
||||
D Synchronous dir updates d No dump
|
||||
E Encrypted e Extents
|
||||
F Case-insensitive (casefold)
|
||||
I Indexed directory i Immutable
|
||||
j Journal data
|
||||
N Inline data in inode
|
||||
P Project hierarchy
|
||||
S Synchronous file updates s Secure delete
|
||||
T Top of dir hierarchy t No tail-merging
|
||||
u Allow undelete
|
||||
V Verity
|
||||
*/
|
||||
#define FOR_lsattr
|
||||
#include "toys.h"
|
||||
#include <linux/fs.h>
|
||||
|
||||
GLOBALS(
|
||||
long v, p;
|
||||
|
||||
unsigned add, rm, set;
|
||||
// !add and !rm tell us whether they were used, but `chattr =` is meaningful.
|
||||
int have_set;
|
||||
)
|
||||
|
||||
// Added more recently than the 7 year support horizon. TODO: remove
|
||||
#ifndef FS_CASEFOLD_FL
|
||||
#define FS_CASEFOLD_FL 0x40000000 // commit 71e90b4654a92 2019-07-23
|
||||
#endif
|
||||
#ifndef FS_VERITY_FL
|
||||
#define FS_VERITY_FL 0x00100000 // commit fe9918d3b228b 2019-07-22
|
||||
#endif
|
||||
|
||||
static struct ext2_attr {
|
||||
char *name;
|
||||
unsigned flag;
|
||||
char opt;
|
||||
} e2attrs[] = {
|
||||
// Do not sort! These are in the order that lsattr outputs them.
|
||||
{"Secure_Deletion", FS_SECRM_FL, 's'},
|
||||
{"Undelete", FS_UNRM_FL, 'u'},
|
||||
{"Synchronous_Updates", FS_SYNC_FL, 'S'},
|
||||
{"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'},
|
||||
{"Immutable", FS_IMMUTABLE_FL, 'i'},
|
||||
{"Append_Only", FS_APPEND_FL, 'a'},
|
||||
{"No_Dump", FS_NODUMP_FL, 'd'},
|
||||
{"No_Atime", FS_NOATIME_FL, 'A'},
|
||||
{"Compression_Requested", FS_COMPR_FL, 'c'},
|
||||
{"Encrypted", FS_ENCRYPT_FL, 'E'},
|
||||
{"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'},
|
||||
{"Indexed_directory", FS_INDEX_FL, 'I'},
|
||||
{"No_Tailmerging", FS_NOTAIL_FL, 't'},
|
||||
{"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'},
|
||||
{"Extents", FS_EXTENT_FL, 'e'},
|
||||
{"No_COW", FS_NOCOW_FL, 'C'},
|
||||
{"Casefold", FS_CASEFOLD_FL, 'F'},
|
||||
{"Inline_Data", FS_INLINE_DATA_FL, 'N'},
|
||||
{"Project_Hierarchy", FS_PROJINHERIT_FL, 'P'},
|
||||
{"Verity", FS_VERITY_FL, 'V'},
|
||||
{NULL, 0, 0},
|
||||
};
|
||||
|
||||
// Get file flags on a Linux second extended file system.
|
||||
static int ext2_getflag(int fd, struct stat *sb, unsigned *flag)
|
||||
{
|
||||
if(!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) {
|
||||
errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
}
|
||||
return ioctl(fd, FS_IOC_GETFLAGS, flag);
|
||||
}
|
||||
|
||||
static char *attrstr(unsigned attrs, int full)
|
||||
{
|
||||
struct ext2_attr *a = e2attrs;
|
||||
char *s = toybuf;
|
||||
|
||||
for (; a->name; a++)
|
||||
if (attrs & a->flag) *s++ = a->opt;
|
||||
else if (full) *s++ = '-';
|
||||
*s = 0;
|
||||
|
||||
return toybuf;
|
||||
}
|
||||
|
||||
static void print_file_attr(char *path)
|
||||
{
|
||||
unsigned flag = 0, version = 0;
|
||||
int fd = -1;
|
||||
struct stat sb;
|
||||
|
||||
if (!stat(path, &sb) && !S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) {
|
||||
errno = EOPNOTSUPP;
|
||||
goto error;
|
||||
}
|
||||
if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto error;
|
||||
|
||||
if (FLAG(p)) {
|
||||
struct fsxattr fsx;
|
||||
|
||||
if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) goto error;
|
||||
xprintf("%5u ", fsx.fsx_projid);
|
||||
}
|
||||
if (FLAG(v)) {
|
||||
if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto error;
|
||||
xprintf("%-10u ", version);
|
||||
}
|
||||
|
||||
if (ext2_getflag(fd, &sb, &flag) < 0) perror_msg("reading flags '%s'", path);
|
||||
else {
|
||||
struct ext2_attr *ptr = e2attrs;
|
||||
int name_found = 0;
|
||||
|
||||
if (FLAG(l)) {
|
||||
xprintf("%-50s ", path);
|
||||
for (; ptr->name; ptr++) {
|
||||
if (flag & ptr->flag) {
|
||||
if (name_found) xprintf(", "); //for formatting.
|
||||
xprintf("%s", ptr->name);
|
||||
name_found = 1;
|
||||
}
|
||||
}
|
||||
if (!name_found) xprintf("---");
|
||||
xputc('\n');
|
||||
} else xprintf("%s %s\n", attrstr(flag, 1), path);
|
||||
}
|
||||
path = 0;
|
||||
error:
|
||||
xclose(fd);
|
||||
if (path) perror_msg("reading '%s'", path);
|
||||
}
|
||||
|
||||
// Get directory information.
|
||||
static int retell_dir(struct dirtree *root)
|
||||
{
|
||||
char *fpath = 0;
|
||||
|
||||
if (root->again) {
|
||||
xputc('\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (S_ISDIR(root->st.st_mode) && !root->parent)
|
||||
return DIRTREE_RECURSE|DIRTREE_COMEAGAIN;
|
||||
|
||||
fpath = dirtree_path(root, NULL);
|
||||
if (*root->name != '.' || FLAG(a)) {
|
||||
print_file_attr(fpath);
|
||||
if (S_ISDIR(root->st.st_mode) && FLAG(R) && dirtree_notdotdot(root)) {
|
||||
xprintf("\n%s:\n", fpath);
|
||||
free(fpath);
|
||||
return DIRTREE_RECURSE|DIRTREE_COMEAGAIN;
|
||||
}
|
||||
}
|
||||
free(fpath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lsattr_main(void)
|
||||
{
|
||||
if (!*toys.optargs) dirtree_read(".", retell_dir);
|
||||
else for (; *toys.optargs; toys.optargs++) {
|
||||
struct stat sb;
|
||||
|
||||
if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs);
|
||||
else if (S_ISDIR(sb.st_mode) && !FLAG(d))
|
||||
dirtree_read(*toys.optargs, retell_dir);
|
||||
else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir"
|
||||
}
|
||||
}
|
||||
|
||||
// Switch gears from lsattr to chattr.
|
||||
#define FOR_chattr
|
||||
#include "generated/flags.h"
|
||||
|
||||
// Set file flags on a Linux second extended file system.
|
||||
static inline int ext2_setflag(int fd, struct stat *sb, unsigned flag)
|
||||
{
|
||||
if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) {
|
||||
errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
}
|
||||
return ioctl(fd, FS_IOC_SETFLAGS, &flag);
|
||||
}
|
||||
|
||||
static unsigned get_flag_val(char ch)
|
||||
{
|
||||
struct ext2_attr *ptr = e2attrs;
|
||||
|
||||
for (; ptr->name; ptr++) if (ptr->opt == ch) return ptr->flag;
|
||||
help_exit("bad '%c'", ch);
|
||||
}
|
||||
|
||||
// Parse command line argument and fill the chattr structure.
|
||||
static void parse_cmdline_arg(char ***argv)
|
||||
{
|
||||
char *arg = **argv, *ptr;
|
||||
|
||||
while (arg) {
|
||||
if (*arg=='-') for (ptr = ++arg; *ptr; ptr++) TT.rm |= get_flag_val(*ptr);
|
||||
else if (*arg=='+')
|
||||
for (ptr = ++arg; *ptr; ptr++) TT.add |= get_flag_val(*ptr);
|
||||
else if (*arg=='=') {
|
||||
TT.have_set = 1;
|
||||
for (ptr = ++arg; *ptr; ptr++) TT.set |= get_flag_val(*ptr);
|
||||
} else return;
|
||||
arg = *(*argv += 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Update attribute of given file.
|
||||
static int update_attr(struct dirtree *root)
|
||||
{
|
||||
char *fpath = 0;
|
||||
int vv = TT.v, fd;
|
||||
|
||||
if (!dirtree_notdotdot(root)) return 0;
|
||||
|
||||
// if file is a link and recursive is set or file is not regular+link+dir
|
||||
// (like fifo or dev file) then escape the file.
|
||||
if ((S_ISLNK(root->st.st_mode) && FLAG(R))
|
||||
|| (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode)
|
||||
&& !S_ISDIR(root->st.st_mode)))
|
||||
return 0;
|
||||
|
||||
fpath = dirtree_path(root, NULL);
|
||||
if (-1 == (fd=open(fpath, O_RDONLY | O_NONBLOCK))) {
|
||||
free(fpath);
|
||||
return DIRTREE_ABORT;
|
||||
}
|
||||
|
||||
// Any potential flag changes?
|
||||
if (TT.have_set | TT.add | TT.rm) {
|
||||
unsigned orig, new;
|
||||
|
||||
// Read current flags.
|
||||
if (ext2_getflag(fd, &(root->st), &orig) < 0) {
|
||||
perror_msg("read flags of '%s'", fpath);
|
||||
free(fpath);
|
||||
xclose(fd);
|
||||
return DIRTREE_ABORT;
|
||||
}
|
||||
// Apply the requested changes.
|
||||
if (TT.have_set) new = TT.set; // '='.
|
||||
else { // '-' and/or '+'.
|
||||
new = orig;
|
||||
new &= ~(TT.rm);
|
||||
new |= TT.add;
|
||||
if (!S_ISDIR(root->st.st_mode)) new &= ~FS_DIRSYNC_FL;
|
||||
}
|
||||
// Write them back if there was any change.
|
||||
if (orig != new && ext2_setflag(fd, &(root->st), new)<0)
|
||||
perror_msg("%s: setting flags to =%s failed", fpath, attrstr(new, 0));
|
||||
}
|
||||
|
||||
// (FS_IOC_SETVERSION works all the way back to 2.6, but FS_IOC_FSSETXATTR
|
||||
// isn't available until 4.5.)
|
||||
if (FLAG(v) && (ioctl(fd, FS_IOC_SETVERSION, &vv)<0))
|
||||
perror_msg("%s: setting version to %d failed", fpath, vv);
|
||||
|
||||
if (FLAG(p)) {
|
||||
struct fsxattr fsx;
|
||||
int fail = ioctl(fd, FS_IOC_FSGETXATTR, &fsx);
|
||||
|
||||
fsx.fsx_projid = TT.p;
|
||||
if (fail || ioctl(fd, FS_IOC_FSSETXATTR, &fsx))
|
||||
perror_msg("%s: setting projid to %u failed", fpath, fsx.fsx_projid);
|
||||
}
|
||||
|
||||
free(fpath);
|
||||
xclose(fd);
|
||||
return (FLAG(R) && S_ISDIR(root->st.st_mode)) ? DIRTREE_RECURSE : 0;
|
||||
}
|
||||
|
||||
void chattr_main(void)
|
||||
{
|
||||
char **argv = toys.optargs;
|
||||
|
||||
parse_cmdline_arg(&argv);
|
||||
if (TT.p < 0 || TT.p > UINT_MAX) error_exit("bad projid %lu", TT.p);
|
||||
if (TT.v < 0 || TT.v > UINT_MAX) error_exit("bad version %ld", TT.v);
|
||||
if (!*argv) help_exit("no file");
|
||||
if (TT.have_set && (TT.add || TT.rm))
|
||||
error_exit("no '=' with '-' or '+'");
|
||||
if (TT.rm & TT.add) error_exit("set/unset same flag");
|
||||
if (!(TT.add || TT.rm || TT.have_set || FLAG(p) || FLAG(v)))
|
||||
error_exit("need '-p', '-v', '=', '-', or '+'");
|
||||
for (; *argv; argv++) dirtree_read(*argv, update_attr);
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/* lsusb.c - list available USB devices
|
||||
*
|
||||
* Copyright 2013 Andre Renaud <andre@bluewatersys.com>
|
||||
* Copyright 2013 Isaac Dunham <ibid.ag@gmail.com>
|
||||
|
||||
USE_LSUSB(NEWTOY(lsusb, "i:", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_LSPCI(NEWTOY(lspci, "eDmkn@x@i:", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config LSPCI
|
||||
bool "lspci"
|
||||
default y
|
||||
help
|
||||
usage: lspci [-ekmn] [-i FILE]
|
||||
|
||||
List PCI devices.
|
||||
|
||||
-e Extended (6 digit) class
|
||||
-i ID database (default /etc/pci.ids[.gz])
|
||||
-k Show kernel driver
|
||||
-m Machine readable
|
||||
-n Numeric output (-nn for both)
|
||||
-D Print domain numbers
|
||||
-x Hex dump of config space (64 bytes; -xxx for 256, -xxxx for 4096)
|
||||
|
||||
config LSUSB
|
||||
bool "lsusb"
|
||||
default y
|
||||
help
|
||||
usage: lsusb [-i]
|
||||
|
||||
List USB hosts/devices.
|
||||
|
||||
-i ID database (default /etc/usb.ids[.gz])
|
||||
*/
|
||||
|
||||
#define FOR_lsusb
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *i;
|
||||
long x, n;
|
||||
|
||||
void *ids, *class;
|
||||
int count;
|
||||
)
|
||||
|
||||
struct dev_ids {
|
||||
struct dev_ids *next, *child;
|
||||
int id;
|
||||
char name[];
|
||||
};
|
||||
|
||||
struct scanloop {
|
||||
char *pattern;
|
||||
void *d1, *d2;
|
||||
};
|
||||
|
||||
// Common function to read uevent file under /proc for both pci and usb
|
||||
// note that %s is omitted (because pointer is into toybuf, avoiding copy).
|
||||
static int scan_uevent(struct dirtree *new, int len, struct scanloop *sl)
|
||||
{
|
||||
int ii, saw = 0;
|
||||
off_t flen = sizeof(toybuf);
|
||||
char *ss, *yy;
|
||||
|
||||
// Read data
|
||||
if (*new->name == '.') return 0;
|
||||
sprintf(toybuf, "%s/uevent", new->name);
|
||||
if (!readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &flen)) return 0;
|
||||
|
||||
// Loop over lines
|
||||
while ((flen = strcspn(ss, "\n"))) {
|
||||
if (ss[flen]) ss[flen++] = 0;
|
||||
yy = ss+flen;
|
||||
|
||||
// Try each pattern
|
||||
for (ii = 0; ii<len; ii++) {
|
||||
if (strchr(sl[ii].pattern, '%')) {
|
||||
if (2-!sl[ii].d2!=sscanf(ss, sl[ii].pattern, sl[ii].d1, sl[ii].d2))
|
||||
continue;
|
||||
} else if (strstart(&ss, sl[ii].pattern)) *(void **)sl[ii].d1 = ss;
|
||||
else continue;
|
||||
saw |= 1<<ii;
|
||||
|
||||
break;
|
||||
}
|
||||
ss = yy;
|
||||
}
|
||||
|
||||
return saw;
|
||||
}
|
||||
|
||||
static void get_names(struct dev_ids *ids, int id1, int id2,
|
||||
char **name1, char **name2)
|
||||
{
|
||||
// Look up matching dev_ids (if any)
|
||||
*name1 = *name2 = "";
|
||||
for (; ids; ids = ids->next) {
|
||||
if (id1 != ids->id) continue;
|
||||
*name1 = ids->name;
|
||||
for (ids = ids->child; ids; ids = ids->next) {
|
||||
if (id2 != ids->id) continue;
|
||||
*name2 = ids->name;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for pci.ids or usb.ids and return parsed structure or NULL
|
||||
struct dev_ids *parse_dev_ids(char *name, struct dev_ids **and)
|
||||
{
|
||||
char *path = "/etc:/vendor:/usr/share/hwdata:/usr/share/misc";
|
||||
struct string_list *sl = 0;
|
||||
FILE *fp;
|
||||
char *s, *ss, *sss;
|
||||
struct dev_ids *ids = 0, *new;
|
||||
int fd = -1;
|
||||
|
||||
// Open compressed or uncompressed file
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
s = TT.i;
|
||||
if (!s) {
|
||||
sprintf(toybuf, "%s.gz", name);
|
||||
if ((sl = find_in_path(path, toybuf)) || (sl = find_in_path(path, name)))
|
||||
s = sl->str;
|
||||
}
|
||||
if (s && strend(s, ".gz")) xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1);
|
||||
else if (s) fd = xopen(s, O_RDONLY);
|
||||
llist_traverse(sl, free);
|
||||
if (fd == -1) return 0;
|
||||
|
||||
for (fp = fdopen(fd, "r"); (s = ss = xgetline(fp)); free(s)) {
|
||||
// TODO parse and use third level instead of skipping it here
|
||||
if (s[strspn(s, " \t")]=='#' || strstart(&ss, "\t\t")) continue;
|
||||
|
||||
// Switch to device class list?
|
||||
if (strstart(&ss, "C ") && and) {
|
||||
*and = ids;
|
||||
and = 0;
|
||||
}
|
||||
fd = estrtol(sss = ss, &ss, 16);
|
||||
if (ss>sss && *ss++==' ') {
|
||||
while (isspace(*ss)) ss++;
|
||||
new = xmalloc(sizeof(*new)+strlen(ss)+1);
|
||||
new->child = 0;
|
||||
new->id = fd;
|
||||
strcpy(new->name, ss);
|
||||
if (!ids || *s!='\t') {
|
||||
new->next = ids;
|
||||
ids = new;
|
||||
} else {
|
||||
new->next = ids->child;
|
||||
ids->child = new;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
static int list_usb(struct dirtree *new)
|
||||
{
|
||||
int busnum = 0, devnum = 0, pid = 0, vid = 0;
|
||||
char *n1, *n2;
|
||||
|
||||
if (!new->parent) return DIRTREE_RECURSE;
|
||||
if (7 == scan_uevent(new, 3, (struct scanloop[]){{"BUSNUM=%u", &busnum, 0},
|
||||
{"DEVNUM=%u", &devnum, 0}, {"PRODUCT=%x/%x", &pid, &vid}}))
|
||||
{
|
||||
get_names(TT.ids, pid, vid, &n1, &n2);
|
||||
printf("Bus %03d Device %03d: ID %04x:%04x %s %s\n",
|
||||
busnum, devnum, pid, vid, n1, n2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lsusb_main(void)
|
||||
{
|
||||
// Parse http://www.linux-usb.org/usb.ids file (if available)
|
||||
TT.ids = parse_dev_ids("usb.ids", 0);
|
||||
dirtree_read("/sys/bus/usb/devices/", list_usb);
|
||||
}
|
||||
|
||||
#define FOR_lspci
|
||||
#include "generated/flags.h"
|
||||
|
||||
// TODO: -v
|
||||
static int list_pci(struct dirtree *new)
|
||||
{
|
||||
char *driver = 0, buf[16], *ss, *names[3];
|
||||
int cvd[3] = {0}, ii, revision = 0;
|
||||
off_t len = sizeof(toybuf);
|
||||
/* skip 0000: part by default */
|
||||
char *bus = strchr(new->name, ':') + 1;
|
||||
|
||||
// Output formats: -n, -nn, -m, -nm, -nnm, -k
|
||||
|
||||
if (!new->parent) return DIRTREE_RECURSE;
|
||||
if (!bus || strlen(new->name)<6) return 0;
|
||||
TT.count = 0;
|
||||
|
||||
// Load revision
|
||||
sprintf(toybuf, "%s/revision", new->name);
|
||||
if (readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &len)) {
|
||||
strstart(&ss, "0x");
|
||||
sscanf(ss, "%x", &revision);
|
||||
}
|
||||
|
||||
// Load uevent data, look up names in database
|
||||
if (6>scan_uevent(new, 3, (struct scanloop[]){{"DRIVER=", &driver, 0},
|
||||
{"PCI_CLASS=%x", cvd, 0}, {"PCI_ID=%x:%x", cvd+1, cvd+2}})) return 0;
|
||||
get_names(TT.class, 255&(cvd[0]>>16), 255&(cvd[0]>>8), names, names);
|
||||
get_names(TT.ids, cvd[1], cvd[2], names+1, names+2);
|
||||
if (!FLAG(e)) cvd[0] >>= 8;
|
||||
|
||||
// Output line according to flags
|
||||
if (FLAG(D) || strncmp(new->name, "0000:", bus-new->name)) bus = new->name;
|
||||
printf("%s", bus);
|
||||
for (ii = 0; ii<3; ii++) {
|
||||
sprintf(buf, "%0*x", 6-2*(ii||!FLAG(e)), cvd[ii]);
|
||||
if (!TT.n) printf(FLAG(m) ? " \"%s\"" : ": %s"+(ii!=1), names[ii] ? : buf);
|
||||
else if (TT.n==1) printf(FLAG(m) ? " \"%s\"" : (ii==2)?"%s ":" %s:", buf);
|
||||
else if (!FLAG(m)) {
|
||||
// This one permutes the order, so do it all first time and abort loop
|
||||
printf(" %s [%s]: %s %s [%04x:%04x]", names[0], buf, names[1], names[2],
|
||||
cvd[1], cvd[2]);
|
||||
break;
|
||||
} else printf(" \"%s [%s]\"", names[ii], buf);
|
||||
}
|
||||
if (revision) printf(FLAG(m) ? " -r%02x" : " (rev %02x)", revision);
|
||||
if (FLAG(k) && driver) printf(FLAG(m) ? " \"%s\"" : " %s", driver);
|
||||
xputc('\n');
|
||||
|
||||
if (TT.x) {
|
||||
FILE *fp;
|
||||
int b, col = 0, max = (TT.x >= 4) ? 4096 : ((TT.x >= 3) ? 256 : 64);
|
||||
|
||||
snprintf(toybuf, sizeof(toybuf), "/sys/bus/pci/devices/%s/config", new->name);
|
||||
fp = xfopen(toybuf, "r");
|
||||
while ((b = fgetc(fp)) != EOF) {
|
||||
if ((col % 16) == 0) printf("%02x: ", col & 0xf0);
|
||||
printf("%02x ", (b & 0xff));
|
||||
if ((++col % 16) == 0) xputc('\n');
|
||||
if (col == max) break;
|
||||
}
|
||||
xputc('\n');
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lspci_main(void)
|
||||
{
|
||||
// Parse https://pci-ids.ucw.cz/v2.2/pci.ids (if available)
|
||||
if (TT.n != 1) TT.class = parse_dev_ids("pci.ids", (void *)&TT.ids);
|
||||
dirtree_read("/sys/bus/pci/devices/", list_pci);
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/* nsenter.c - Enter existing namespaces
|
||||
*
|
||||
* Copyright 2014 Andy Lutomirski <luto@amacapital.net>
|
||||
*
|
||||
* See http://man7.org/linux/man-pages/man1/nsenter.1.html
|
||||
*
|
||||
* unshare.c - run command in new context
|
||||
*
|
||||
* Copyright 2011 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* See http://man7.org/linux/man-pages/man1/unshare.1.html
|
||||
*
|
||||
|
||||
// Note: flags go in same order (right to left) for shared subset
|
||||
USE_NSENTER(NEWTOY(nsenter, "<1a(all)F(no-fork)t#<1(target)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
|
||||
USE_UNSHARE(NEWTOY(unshare, "<1^a(all)f(fork)r(map-root-user)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
|
||||
|
||||
config UNSHARE
|
||||
bool "unshare"
|
||||
default y
|
||||
help
|
||||
usage: unshare [-imnpuUr] COMMAND...
|
||||
|
||||
Create new container namespace(s) for this process and its children, allowing
|
||||
the new set of processes to have a different view of the system than the
|
||||
parent process.
|
||||
|
||||
-a Unshare all supported namespaces
|
||||
-f Fork command in the background (--fork)
|
||||
-r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user)
|
||||
|
||||
Available namespaces:
|
||||
-C Control groups (--cgroup)
|
||||
-i SysV IPC (message queues, semaphores, shared memory) (--ipc)
|
||||
-m Mount/unmount tree (--mount)
|
||||
-n Network address, sockets, routing, iptables (--net)
|
||||
-p Process IDs and init (--pid)
|
||||
-u Host and domain names (--uts)
|
||||
-U UIDs, GIDs, capabilities (--user)
|
||||
|
||||
Each namespace can take an optional argument, a persistent mountpoint usable
|
||||
by the nsenter command to add new processes to that the namespace. (Specify
|
||||
multiple namespaces to unshare separately, ala -c -i -m because -cim is -c
|
||||
with persistent mount "im".)
|
||||
|
||||
config NSENTER
|
||||
bool "nsenter"
|
||||
default y
|
||||
help
|
||||
usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND...
|
||||
|
||||
Run COMMAND in an existing (set of) namespace(s).
|
||||
|
||||
-a Enter all supported namespaces (--all)
|
||||
-F don't fork, even if -p is used (--no-fork)
|
||||
-t PID to take namespaces from (--target)
|
||||
|
||||
The namespaces to switch are:
|
||||
|
||||
-C Control groups (--cgroup)
|
||||
-i SysV IPC: message queues, semaphores, shared memory (--ipc)
|
||||
-m Mount/unmount tree (--mount)
|
||||
-n Network address, sockets, routing, iptables (--net)
|
||||
-p Process IDs and init, will fork unless -F is used (--pid)
|
||||
-u Host and domain names (--uts)
|
||||
-U UIDs, GIDs, capabilities (--user)
|
||||
|
||||
If -t isn't specified, each namespace argument must provide a path
|
||||
to a namespace file, ala "-i=/proc/$PID/ns/ipc"
|
||||
*/
|
||||
|
||||
#define FOR_nsenter
|
||||
#include "toys.h"
|
||||
#include <linux/sched.h>
|
||||
|
||||
#define unshare(flags) syscall(SYS_unshare, flags)
|
||||
#define setns(fd, nstype) syscall(SYS_setns, fd, nstype)
|
||||
|
||||
GLOBALS(
|
||||
char *UupnmiC[7];
|
||||
long t;
|
||||
)
|
||||
|
||||
// Code that must run in unshare's flag context
|
||||
#define FOR_unshare
|
||||
#include <generated/flags.h>
|
||||
|
||||
static void write_ugid_map(char *map, unsigned eugid)
|
||||
{
|
||||
int fd = xopen(map, O_WRONLY);
|
||||
|
||||
dprintf(fd, "0 %u 1", eugid);
|
||||
xclose(fd);
|
||||
}
|
||||
|
||||
static int test_a() { return FLAG(a); }
|
||||
static int test_r() { return FLAG(r); }
|
||||
static int test_f() { return FLAG(f); }
|
||||
|
||||
// Shift back to the context GLOBALS lives in (I.E. matching the filename).
|
||||
#define FOR_nsenter
|
||||
#include <generated/flags.h>
|
||||
|
||||
void unshare_main(void)
|
||||
{
|
||||
char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc\0cgroup";
|
||||
unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET,
|
||||
CLONE_NEWNS, CLONE_NEWIPC, CLONE_NEWCGROUP}, f = 0;
|
||||
int i, fd;
|
||||
|
||||
// Create new namespace(s)?
|
||||
if (CFG_UNSHARE && *toys.which->name=='u') {
|
||||
// For -r, we have to save our original [ug]id before calling unshare()
|
||||
int euid = geteuid(), egid = getegid();
|
||||
|
||||
// unshare -U does not imply -r, so we cannot use [+rU]
|
||||
if (test_r()) toys.optflags |= FLAG_U;
|
||||
|
||||
for (i = 0; i<ARRAY_LEN(flags); i++)
|
||||
if (test_a() || (toys.optflags & (1<<i))) f |= flags[i];
|
||||
if (unshare(f)) perror_exit(0);
|
||||
if (test_r()) {
|
||||
if ((fd = open("/proc/self/setgroups", O_WRONLY)) >= 0) {
|
||||
xwrite(fd, "deny", 4);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
write_ugid_map("/proc/self/uid_map", euid);
|
||||
write_ugid_map("/proc/self/gid_map", egid);
|
||||
}
|
||||
|
||||
if (test_f()) {
|
||||
toys.exitval = xrun(toys.optargs);
|
||||
|
||||
return;
|
||||
}
|
||||
// Bind to existing namespace(s)?
|
||||
} else if (CFG_NSENTER) {
|
||||
for (i = 0; i<ARRAY_LEN(flags); i++, nsnames += strlen(nsnames)+1) {
|
||||
if (FLAG(a) || (toys.optflags & (1<<i))) {
|
||||
char *filename = TT.UupnmiC[i];
|
||||
|
||||
if (!filename || !*filename) {
|
||||
if (!FLAG(t)) error_exit("need -t or =filename");
|
||||
sprintf(filename = toybuf, "/proc/%ld/ns/%s", TT.t, nsnames);
|
||||
}
|
||||
|
||||
if (setns(fd = xopenro(filename), flags[i])) perror_exit("setns");
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAG(p) && !FLAG(F)) {
|
||||
toys.exitval = xrun(toys.optargs);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
xexec(toys.optargs);
|
||||
}
|
||||
|
||||
void nsenter_main(void)
|
||||
{
|
||||
unshare_main();
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/* pmap.c - Reports the memory map of a process or processes.
|
||||
*
|
||||
* Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
|
||||
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
|
||||
*
|
||||
* No Standard.
|
||||
*
|
||||
* TODO: two passes so we can auto-size the columns?
|
||||
|
||||
USE_PMAP(NEWTOY(pmap, "<1pqx", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config PMAP
|
||||
bool "pmap"
|
||||
default y
|
||||
help
|
||||
usage: pmap [-pqx] PID...
|
||||
|
||||
Report the memory map of a process or processes.
|
||||
|
||||
-p Show full paths
|
||||
-q Do not show header or footer
|
||||
-x Show the extended format
|
||||
*/
|
||||
|
||||
#define FOR_pmap
|
||||
#include "toys.h"
|
||||
|
||||
void pmap_main(void)
|
||||
{
|
||||
char **optargs, *line = 0;
|
||||
size_t len = 0;
|
||||
|
||||
for (optargs = toys.optargs; *optargs; optargs++) {
|
||||
long long start, end, pss, tpss=0, dirty, tdirty=0, swap, tswap=0, total=0;
|
||||
char *name = 0, *k = "K"+FLAG(x), mode[5];
|
||||
pid_t pid = atolx(*optargs);
|
||||
int extras = 0, off, count;
|
||||
FILE *fp;
|
||||
|
||||
sprintf(toybuf, "/proc/%u/cmdline", pid);
|
||||
if (!(name = readfile(toybuf, 0, 0))) {
|
||||
error_msg("no %s", toybuf);
|
||||
continue;
|
||||
}
|
||||
xprintf("%d: %s\n", pid, name);
|
||||
free(name);
|
||||
|
||||
// Only bother scanning the more verbose smaps file in -x mode.
|
||||
sprintf(toybuf, "/proc/%u/%smaps", pid, "s"+!FLAG(x));
|
||||
if (!(fp = fopen(toybuf, "r"))) {
|
||||
error_msg("no %s", toybuf);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FLAG(x) && !FLAG(q))
|
||||
xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n",
|
||||
(int)(sizeof(long)*2)-5, ' ');
|
||||
|
||||
while (getline(&line, &len, fp) > 0) {
|
||||
count = sscanf(line, "%llx-%llx %4s %*s %*s %*s %n", &start, &end, mode,
|
||||
&off);
|
||||
if (count == 3) {
|
||||
name = line[off] ? line+off : " [anon]\n";
|
||||
if (mode[3] == 'p') mode[3] = '-';
|
||||
total += end = (end-start)/1024;
|
||||
printf("%0*llx % *lld%s ", (int)(2*sizeof(long)), start, 6+FLAG(x),
|
||||
end, k);
|
||||
if (FLAG(x)) {
|
||||
strcpy(toybuf, name);
|
||||
name = toybuf;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (sscanf(line, "Pss: %lld", &pss) ||
|
||||
sscanf(line, "Private_Dirty: %lld", &dirty) ||
|
||||
sscanf(line, "Swap: %lld", &swap)) extras++;
|
||||
if (extras==3) {
|
||||
printf("% 7lld %7lld %7lld ", pss, dirty, swap);
|
||||
tpss += pss;
|
||||
tdirty += dirty;
|
||||
tswap += swap;
|
||||
extras = 0;
|
||||
} else continue;
|
||||
}
|
||||
|
||||
xprintf("%s- %s%s", mode, *name == '[' ? " " : "",
|
||||
FLAG(p) ? name : basename(name));
|
||||
}
|
||||
|
||||
if (!FLAG(q)) {
|
||||
if (FLAG(x)) {
|
||||
xprintf("---------------- ------ ------ ------ ------\n" +
|
||||
((sizeof(long)==4)?8:0));
|
||||
}
|
||||
printf("total% *lld%s", 2*(int)(sizeof(long)+1)+FLAG(x), total, k);
|
||||
if (FLAG(x)) printf("% 8lld% 8lld% 8lld", tpss, tdirty, tswap);
|
||||
xputc('\n');
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/* setsid.c - Run program in a new session ID.
|
||||
*
|
||||
* Copyright 2006 Rob Landley <rob@landley.net>
|
||||
|
||||
USE_SETSID(NEWTOY(setsid, "^<1wc@d[!dc]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config SETSID
|
||||
bool "setsid"
|
||||
default y
|
||||
help
|
||||
usage: setsid [-cdw] command [args...]
|
||||
|
||||
Run process in a new session.
|
||||
|
||||
-d Detach from tty
|
||||
-c Control tty (repeat to steal)
|
||||
-w Wait for child (and exit with its status)
|
||||
*/
|
||||
|
||||
#define FOR_setsid
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
long c;
|
||||
)
|
||||
|
||||
void setsid_main(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
// setsid() fails if we're already session leader, ala "exec setsid" from sh.
|
||||
// Second call can't fail, so loop won't continue endlessly.
|
||||
while (setsid()<0) {
|
||||
pid_t pid;
|
||||
|
||||
// This must be before vfork() or tcsetpgrp() will hang waiting for parent.
|
||||
setpgid(0, 0);
|
||||
|
||||
pid = XVFORK();
|
||||
if (pid) {
|
||||
i = 0;
|
||||
if (FLAG(w)) {
|
||||
i = 127;
|
||||
if (pid>0) i = xwaitpid(pid);
|
||||
}
|
||||
_exit(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAG(c)) {
|
||||
ioctl(0, TIOCSCTTY, TT.c>1);
|
||||
tcsetpgrp(0, getpid());
|
||||
} if (FLAG(d) && (i = open("/dev/tty", O_RDONLY)) != -1) {
|
||||
ioctl(i, TIOCNOTTY);
|
||||
close(i);
|
||||
}
|
||||
xexec(toys.optargs);
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/* stat.c : display file or file system status
|
||||
* Copyright 2012 <warior.linux@gmail.com>
|
||||
* Copyright 2013 <anand.sinha85@gmail.com>
|
||||
|
||||
USE_STAT(NEWTOY(stat, "<1c:(format)fLt", TOYFLAG_BIN))
|
||||
|
||||
config STAT
|
||||
bool stat
|
||||
default y
|
||||
help
|
||||
usage: stat [-tfL] [-c FORMAT] FILE...
|
||||
|
||||
Display status of files or filesystems.
|
||||
|
||||
-c Output specified FORMAT string instead of default
|
||||
-f Display filesystem status instead of file status
|
||||
-L Follow symlinks
|
||||
-t terse (-c "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o")
|
||||
(with -f = -c "%n %i %l %t %s %S %b %f %a %c %d")
|
||||
|
||||
The valid format escape sequences for files:
|
||||
%a Access bits (octal) |%A Access bits (flags)|%b Size/512
|
||||
%B Bytes per %b (512) |%C Security context |%d Device ID (dec)
|
||||
%D Device ID (hex) |%f All mode bits (hex)|%F File type
|
||||
%g Group ID |%G Group name |%h Hard links
|
||||
%i Inode |%m Mount point |%n Filename
|
||||
%N Long filename |%o I/O block size |%s Size (bytes)
|
||||
%t Devtype major (hex) |%T Devtype minor (hex)|%u User ID
|
||||
%U User name |%x Access time |%X Access unix time
|
||||
%y Modification time |%Y Mod unix time |%z Creation time
|
||||
%Z Creation unix time
|
||||
|
||||
The valid format escape sequences for filesystems:
|
||||
%a Available blocks |%b Total blocks |%c Total inodes
|
||||
%d Free inodes |%f Free blocks |%i File system ID
|
||||
%l Max filename length |%n File name |%s Best transfer size
|
||||
%S Actual block size |%t FS type (hex) |%T FS type (driver name)
|
||||
*/
|
||||
|
||||
#define FOR_stat
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *c;
|
||||
|
||||
union {
|
||||
struct stat st;
|
||||
struct statfs sf;
|
||||
} stat;
|
||||
char *file, *pattern;
|
||||
int patlen;
|
||||
)
|
||||
|
||||
// Force numeric output to long long instead of manually typecasting everything
|
||||
// and safely parse length prefix
|
||||
static void out(char c, long long val)
|
||||
{
|
||||
sprintf(toybuf, "%.*sll%c", TT.patlen, TT.pattern, c);
|
||||
printf(toybuf, val);
|
||||
}
|
||||
|
||||
// Output string with parsed length prefix
|
||||
static void strout(char *val)
|
||||
{
|
||||
sprintf(toybuf, "%.*ss", TT.patlen, TT.pattern);
|
||||
printf(toybuf, val);
|
||||
}
|
||||
|
||||
static void date_stat_format(struct timespec *ts)
|
||||
{
|
||||
strout(format_iso_time(toybuf+128, sizeof(toybuf)-128, ts));
|
||||
}
|
||||
|
||||
static void print_stat(char type)
|
||||
{
|
||||
struct stat *stat = (struct stat *)&TT.stat;
|
||||
|
||||
if (type == 'a') out('o', stat->st_mode&~S_IFMT);
|
||||
else if (type == 'A') {
|
||||
char str[11];
|
||||
|
||||
mode_to_string(stat->st_mode, str);
|
||||
strout(str);
|
||||
} else if (type == 'b') out('u', stat->st_blocks);
|
||||
else if (type == 'B') out('d', 512);
|
||||
else if (type == 'C') {
|
||||
char *context = NULL;
|
||||
|
||||
strout(lsm_get_context(TT.file, &context) != -1 ? context : "?");
|
||||
free(context);
|
||||
} else if (type == 'd') out('d', stat->st_dev);
|
||||
else if (type == 'D') out('x', stat->st_dev);
|
||||
else if (type == 'f') out('x', stat->st_mode);
|
||||
else if (type == 'F') {
|
||||
char *t = "character device\0directory\0block device\0" \
|
||||
"regular file\0symbolic link\0socket\0FIFO (named pipe)";
|
||||
int i, filetype = stat->st_mode & S_IFMT;
|
||||
|
||||
for (i = 1; filetype != (i*8192) && i < 7; i++) t += strlen(t)+1;
|
||||
if (!stat->st_size && filetype == S_IFREG) t = "regular empty file";
|
||||
strout(t);
|
||||
} else if (type == 'g') out('u', stat->st_gid);
|
||||
else if (type == 'G') strout(getgroupname(stat->st_gid));
|
||||
else if (type == 'h') out('u', stat->st_nlink);
|
||||
else if (type == 'i') out('u', stat->st_ino);
|
||||
else if (type == 'm') {
|
||||
struct mtab_list *mt = xgetmountlist(0);
|
||||
dev_t dev = stat->st_rdev ? stat->st_rdev : stat->st_dev;
|
||||
|
||||
// This mount point could exist multiple times, so show oldest.
|
||||
for (dlist_terminate(mt); mt; mt = mt->next) if (mt->stat.st_dev == dev) {
|
||||
strout(mt->dir);
|
||||
break;
|
||||
}
|
||||
llist_traverse(mt, free);
|
||||
} else if (type == 'N') {
|
||||
printf("%s", TT.file);
|
||||
if (S_ISLNK(stat->st_mode))
|
||||
if (readlink0(TT.file, toybuf, sizeof(toybuf)))
|
||||
printf(" -> '%s'", toybuf);
|
||||
} else if (type == 'o') out('u', stat->st_blksize);
|
||||
else if (type == 's') out('u', stat->st_size);
|
||||
else if (type == 't') out('x', dev_major(stat->st_rdev));
|
||||
else if (type == 'T') out('x', dev_minor(stat->st_rdev));
|
||||
else if (type == 'u') out('u', stat->st_uid);
|
||||
else if (type == 'U') strout(getusername(stat->st_uid));
|
||||
else if (type == 'x') date_stat_format(&stat->st_atim);
|
||||
else if (type == 'X') out('u', stat->st_atime);
|
||||
else if (type == 'y') date_stat_format(&stat->st_mtim);
|
||||
else if (type == 'Y') out('u', stat->st_mtime);
|
||||
else if (type == 'z') date_stat_format(&stat->st_ctim);
|
||||
else if (type == 'Z') out('u', stat->st_ctime);
|
||||
else putchar('?');
|
||||
}
|
||||
|
||||
static void print_statfs(char type) {
|
||||
struct statfs *statfs = (struct statfs *)&TT.stat;
|
||||
|
||||
if (type == 'a') out('u', statfs->f_bavail);
|
||||
else if (type == 'b') out('u', statfs->f_blocks);
|
||||
else if (type == 'c') out('u', statfs->f_files);
|
||||
else if (type == 'd') out('u', statfs->f_ffree);
|
||||
else if (type == 'f') out('u', statfs->f_bfree);
|
||||
else if (type == 'l') out('d', pathconf(TT.file, _PC_NAME_MAX));
|
||||
else if (type == 't') out('x', statfs->f_type);
|
||||
else if (type == 'T') strout(fs_type_name(statfs));
|
||||
else if (type == 'i') {
|
||||
int *val = (int *) &statfs->f_fsid;
|
||||
char buf[32];
|
||||
|
||||
sprintf(buf, "%08x%08x", val[0], val[1]);
|
||||
strout(buf);
|
||||
} else if (type == 's') out('d', statfs_bsize(statfs));
|
||||
else if (type == 'S') out('d', statfs_frsize(statfs));
|
||||
else strout("?");
|
||||
}
|
||||
|
||||
void stat_main(void)
|
||||
{
|
||||
int i;
|
||||
char *format, *f;
|
||||
|
||||
if (FLAG(t)) format = FLAG(f)
|
||||
? "%n %i %l %t %s %S %b %f %a %c %d"
|
||||
: "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
|
||||
else format = FLAG(f)
|
||||
? " File: \"%n\"\n ID: %i Namelen: %l Type: %T\n"
|
||||
"Block Size: %s Fundamental block size: %S\n"
|
||||
"Blocks: Total: %b\tFree: %f\tAvailable: %a\n"
|
||||
"Inodes: Total: %c\tFree: %d"
|
||||
: " File: %N\n Size: %s\t Blocks: %b\t IO Blocks: %B\t %F\n"
|
||||
"Device: %Dh/%dd\t Inode: %i\t Links: %h\t Device type: %t,%T\n"
|
||||
"Access: (%04a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n"
|
||||
"Access: %x\nModify: %y\nChange: %z";
|
||||
|
||||
if (FLAG(c)) format = TT.c;
|
||||
|
||||
// loop through files listed on command line
|
||||
for (i = 0; toys.optargs[i]; i++) {
|
||||
|
||||
// stat the file or filesystem
|
||||
TT.file = toys.optargs[i];
|
||||
if (FLAG(f) && !statfs(TT.file, (void *)&TT.stat));
|
||||
else if (FLAG(f) || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) {
|
||||
perror_msg("'%s'", TT.file);
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse format and print what it says
|
||||
for (f = format; *f; f++) {
|
||||
if (*f != '%' || !f[1]) putchar(*f);
|
||||
else if (f[1]=='%') putchar(*f++);
|
||||
else {
|
||||
f = next_printf(f, &TT.pattern);
|
||||
TT.patlen = f-TT.pattern;
|
||||
if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern);
|
||||
if (*f == 'n') strout(TT.file);
|
||||
else if (FLAG(f)) print_statfs(*f);
|
||||
else print_stat(*f);
|
||||
}
|
||||
}
|
||||
xputc('\n');
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/* sysctl.c - A utility to read and manipulate the sysctl parameters.
|
||||
*
|
||||
* Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
|
||||
* Copyright 2014 Kyungwan Han <asura321@gmail.com>
|
||||
*
|
||||
* No Standard
|
||||
|
||||
USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN))
|
||||
|
||||
config SYSCTL
|
||||
bool "sysctl"
|
||||
default y
|
||||
help
|
||||
usage: sysctl [-aeNnqw] [-p [FILE] | KEY[=VALUE]...]
|
||||
|
||||
Read/write system control data (under /proc/sys).
|
||||
|
||||
-a Show all values
|
||||
-e Don't warn about unknown keys
|
||||
-N Don't print key values
|
||||
-n Don't print key names
|
||||
-p Read values from FILE (default /etc/sysctl.conf)
|
||||
-q Don't show value after write
|
||||
-w Only write values (object to reading)
|
||||
*/
|
||||
#define FOR_sysctl
|
||||
#include "toys.h"
|
||||
|
||||
// Null terminate at =, return value
|
||||
static char *split_key(char *key)
|
||||
{
|
||||
char *value = strchr(key, '=');
|
||||
|
||||
if (value) *(value++)=0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void replace_char(char *str, char old, char new)
|
||||
{
|
||||
for (; *str; str++) if (*str == old) *str = new;
|
||||
}
|
||||
|
||||
static void key_error(char *key)
|
||||
{
|
||||
if (errno != ENOENT) perror_msg("key '%s'", key);
|
||||
else if (!FLAG(e)) error_msg("unknown key '%s'", key);
|
||||
}
|
||||
|
||||
static int write_key(char *path, char *key, char *value)
|
||||
{
|
||||
int fd = open(path, O_WRONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
key_error(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
xwrite(fd, value, strlen(value));
|
||||
xclose(fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Display all keys under a path
|
||||
static int do_show_keys(struct dirtree *dt)
|
||||
{
|
||||
char *path, *data, *key;
|
||||
|
||||
if (!dirtree_notdotdot(dt)) return 0; // Skip . and ..
|
||||
if (S_ISDIR(dt->st.st_mode)) return DIRTREE_RECURSE;
|
||||
|
||||
path = dirtree_path(dt, 0);
|
||||
data = readfile(path, 0, 0);
|
||||
replace_char(key = path + 10, '/', '.'); // skip "/proc/sys/"
|
||||
if (!data) key_error(key);
|
||||
else {
|
||||
// Print the parts that aren't switched off by flags.
|
||||
if (!FLAG(n)) xprintf("%s", key);
|
||||
if (!FLAG(N) && !FLAG(n)) xprintf(" = ");
|
||||
for (key = data+strlen(data); key > data && isspace(*--key); *key = 0);
|
||||
if (!FLAG(N)) xprintf("%s", data);
|
||||
if (!FLAG(N) || !FLAG(n)) xputc('\n');
|
||||
}
|
||||
|
||||
free(data);
|
||||
free(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read/write entries under a key. Accepts "key=value" in key if !value
|
||||
static void process_key(char *key, char *value)
|
||||
{
|
||||
char *path;
|
||||
|
||||
if (!value) value = split_key(key);
|
||||
if (FLAG(w) && !value) return error_msg("'%s' not key=value", key);
|
||||
path = xmprintf("/proc/sys/%s", key);
|
||||
replace_char(path, '.', '/');
|
||||
// Note: failure to assign to a non-leaf node suppresses the display.
|
||||
if (!(value && (!write_key(path, key, value) || FLAG(q)))) {
|
||||
if (!access(path, R_OK)) dirtree_read(path, do_show_keys);
|
||||
else key_error(key);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
void sysctl_main()
|
||||
{
|
||||
char **args = 0;
|
||||
|
||||
// Display all keys
|
||||
if (FLAG(a)) dirtree_read("/proc/sys", do_show_keys);
|
||||
|
||||
// read file
|
||||
else if (FLAG(p)) {
|
||||
FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r");
|
||||
size_t len;
|
||||
|
||||
for (;;) {
|
||||
char *line = 0, *key, *val;
|
||||
|
||||
if (-1 == (len = getline(&line, &len, fp))) break;
|
||||
key = line;
|
||||
while (isspace(*key)) key++;
|
||||
if (*key == '#' || *key == ';' || !*key) continue;
|
||||
while (len && isspace(line[len-1])) line[--len] = 0;
|
||||
if (!(val = split_key(line))) {
|
||||
error_msg("'%s' not key=value", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Trim whitespace around =
|
||||
len = (val-line)-1;
|
||||
while (len && isspace(line[len-1])) line[--len] = 0;
|
||||
while (isspace(*val)) val++;;
|
||||
|
||||
process_key(key, val);
|
||||
free(line);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
// Loop through arguments, displaying or assigning as appropriate
|
||||
} else {
|
||||
if (!*toys.optargs) help_exit("Needs 1 arg");
|
||||
for (args = toys.optargs; *args; args++) process_key(*args, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/* timeout.c - Run command line with a timeout
|
||||
*
|
||||
* Copyright 2013 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* No standard
|
||||
|
||||
USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):i", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
|
||||
|
||||
config TIMEOUT
|
||||
bool "timeout"
|
||||
default y
|
||||
help
|
||||
usage: timeout [-iv] [-k DURATION] [-s SIGNAL] DURATION COMMAND...
|
||||
|
||||
Run command line as a child process, sending child a signal if the
|
||||
command doesn't exit soon enough.
|
||||
|
||||
DURATION can be a decimal fraction. An optional suffix can be "m"
|
||||
(minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
|
||||
|
||||
-i Only kill for inactivity (restart timeout when command produces output)
|
||||
-k Send KILL signal if child still running this long after first signal
|
||||
-s Send specified signal (default TERM)
|
||||
-v Verbose
|
||||
--foreground Don't create new process group
|
||||
--preserve-status Exit with the child's exit status
|
||||
*/
|
||||
|
||||
#define FOR_timeout
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *s, *k;
|
||||
|
||||
struct pollfd pfd;
|
||||
sigjmp_buf sj;
|
||||
int fds[2], pid, rc;
|
||||
)
|
||||
|
||||
static void handler(int sig, siginfo_t *si)
|
||||
{
|
||||
TT.rc = si->si_status + ((si->si_code!=CLD_EXITED)<<7);
|
||||
siglongjmp(TT.sj, 1);
|
||||
}
|
||||
|
||||
static long nantomil(struct timespec *ts)
|
||||
{
|
||||
return ts->tv_sec*1000+ts->tv_nsec/1000000;
|
||||
}
|
||||
|
||||
static void callback(char *argv[])
|
||||
{
|
||||
if (!FLAG(foreground)) setpgid(0, 0);
|
||||
}
|
||||
|
||||
void timeout_main(void)
|
||||
{
|
||||
int ii, ms, nextsig = SIGTERM;
|
||||
struct timespec tts, kts;
|
||||
|
||||
// Use same ARGFAIL value for any remaining parsing errors
|
||||
toys.exitval = 125;
|
||||
xparsetimespec(*toys.optargs, &tts);
|
||||
if (TT.k) xparsetimespec(TT.k, &kts);
|
||||
if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s);
|
||||
|
||||
toys.exitval = 0;
|
||||
TT.pfd.events = POLLIN;
|
||||
TT.fds[1] = -1;
|
||||
if (sigsetjmp(TT.sj, 1)) goto done;
|
||||
xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP|SA_SIGINFO);
|
||||
|
||||
TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback);
|
||||
xsignal(SIGTTIN, SIG_IGN);
|
||||
xsignal(SIGTTOU, SIG_IGN);
|
||||
xsignal(SIGTSTP, SIG_IGN);
|
||||
if (!FLAG(i)) xpipe(TT.fds);
|
||||
TT.pfd.fd = TT.fds[1];
|
||||
ms = nantomil(&tts);
|
||||
for (;;) {
|
||||
if (1 != xpoll(&TT.pfd, 1, ms)) {
|
||||
if (FLAG(v))
|
||||
perror_msg("sending signal %s to command %s", num_to_sig(nextsig),
|
||||
toys.optargs[1]);
|
||||
toys.exitval = (nextsig==9) ? 137 : 124;
|
||||
kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig);
|
||||
if (!TT.k || nextsig==SIGKILL) break;
|
||||
nextsig = SIGKILL;
|
||||
ms = nantomil(&kts);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (TT.pfd.revents&POLLIN) {
|
||||
errno = 0;
|
||||
if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) {
|
||||
if (errno==EINTR) continue;
|
||||
break;
|
||||
}
|
||||
writeall(1, toybuf, ii);
|
||||
}
|
||||
if (TT.pfd.revents&POLLHUP) break;
|
||||
}
|
||||
done:
|
||||
xpclose_both(TT.pid, TT.fds);
|
||||
|
||||
if (FLAG(preserve_status) || !toys.exitval) toys.exitval = TT.rc;
|
||||
}
|
||||
@@ -1,383 +0,0 @@
|
||||
/* route.c - Display/edit network routing table.
|
||||
*
|
||||
* Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
|
||||
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
|
||||
* Copyright 2020 Eric Molitor <eric@molitor.org>
|
||||
*
|
||||
* No Standard
|
||||
*
|
||||
* route add -net target 10.0.0.0 netmask 255.0.0.0 dev eth0
|
||||
* route del delete
|
||||
* delete net route, must match netmask, informative error message
|
||||
*
|
||||
* mod dyn reinstate metric netmask gw mss window irtt dev
|
||||
|
||||
USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_SBIN))
|
||||
config ROUTE
|
||||
bool "route"
|
||||
default n
|
||||
help
|
||||
usage: route [-ne] [-A [inet|inet6]] [add|del TARGET [OPTIONS]]
|
||||
|
||||
Display, add or delete network routes in the "Forwarding Information Base",
|
||||
which send packets out a network interface to an address.
|
||||
|
||||
-n Show numerical addresses (no DNS lookups)
|
||||
-e display netstat fields
|
||||
|
||||
Assigning an address to an interface automatically creates an appropriate
|
||||
network route ("ifconfig eth0 10.0.2.15/8" does "route add 10.0.0.0/8 eth0"
|
||||
for you), although some devices (such as loopback) won't show it in the
|
||||
table. For machines more than one hop away, you need to specify a gateway
|
||||
(ala "route add default gw 10.0.2.2").
|
||||
|
||||
The address "default" is a wildcard address (0.0.0.0/0) matching all
|
||||
packets without a more specific route.
|
||||
|
||||
Available OPTIONS include:
|
||||
reject - blocking route (force match failure)
|
||||
dev NAME - force matching packets out this interface (ala "eth0")
|
||||
netmask - old way of saying things like ADDR/24
|
||||
gw ADDR - forward packets to gateway ADDR
|
||||
*/
|
||||
|
||||
#define FOR_route
|
||||
#include "toys.h"
|
||||
#define _LINUX_SYSINFO_H // workaround for musl bug
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
GLOBALS(
|
||||
char *A;
|
||||
)
|
||||
|
||||
struct _arglist {
|
||||
char *arg;
|
||||
int action;
|
||||
};
|
||||
|
||||
static struct _arglist arglist1[] = {
|
||||
{ "add", 1 }, { "del", 2 },
|
||||
{ "delete", 2 }, { NULL, 0 }
|
||||
};
|
||||
|
||||
static struct _arglist arglist2[] = {
|
||||
{ "-net", 1 }, { "-host", 2 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
void xsend(int sockfd, void *buf, size_t len)
|
||||
{
|
||||
if (send(sockfd, buf, len, 0) != len) perror_exit("xsend");
|
||||
}
|
||||
|
||||
int xrecv(int sockfd, void *buf, size_t len)
|
||||
{
|
||||
int msg_len = recv(sockfd, buf, len, 0);
|
||||
if (msg_len < 0) perror_exit("xrecv");
|
||||
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
void addAttr(struct nlmsghdr *nl, int maxlen, void *attr, int type, int len)
|
||||
{
|
||||
struct rtattr *rt;
|
||||
int rtlen = RTA_LENGTH(len);
|
||||
if (NLMSG_ALIGN(nl->nlmsg_len) + rtlen > maxlen) perror_exit("addAttr");
|
||||
rt = (struct rtattr*)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len));
|
||||
rt->rta_type = type;
|
||||
rt->rta_len = rtlen;
|
||||
memcpy(RTA_DATA(rt), attr, len);
|
||||
nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + rtlen;
|
||||
}
|
||||
|
||||
static void get_hostname(sa_family_t f, void *a, char *dst, size_t len) {
|
||||
size_t a_len = (AF_INET6 == f) ? sizeof(struct in6_addr) : sizeof(struct in_addr);
|
||||
|
||||
struct hostent *host = gethostbyaddr(a, a_len, f);
|
||||
if (host) xstrncpy(dst, host->h_name, len);
|
||||
}
|
||||
|
||||
static void display_routes(sa_family_t f)
|
||||
{
|
||||
int fd, msg_hdr_len, route_protocol;
|
||||
struct {
|
||||
struct nlmsghdr nl;
|
||||
struct rtmsg rt;
|
||||
} req;
|
||||
struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)];
|
||||
struct nlmsghdr *msg_hdr_ptr;
|
||||
struct rtmsg *route_entry;
|
||||
struct rtattr *rteattr;
|
||||
|
||||
fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
req.nl.nlmsg_type = RTM_GETROUTE;
|
||||
req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
req.nl.nlmsg_pid = getpid();
|
||||
req.nl.nlmsg_seq = 1;
|
||||
req.rt.rtm_family = f;
|
||||
req.rt.rtm_table = RT_TABLE_MAIN;
|
||||
xsend(fd, &req, sizeof(req));
|
||||
|
||||
if (f == AF_INET) {
|
||||
xprintf("Kernel IP routing table\n"
|
||||
"Destination Gateway Genmask Flags %s Iface\n",
|
||||
FLAG(e) ? " MSS Window irtt" : "Metric Ref Use");
|
||||
} else {
|
||||
xprintf("Kernel IPv6 routing table\n"
|
||||
"%-31s%-26s Flag Metric Ref Use If\n", "Destination", "Next Hop");
|
||||
}
|
||||
|
||||
msg_hdr_len = xrecv(fd, buf, sizeof(buf));
|
||||
msg_hdr_ptr = buf;
|
||||
while (msg_hdr_ptr->nlmsg_type != NLMSG_DONE) {
|
||||
while (NLMSG_OK(msg_hdr_ptr, msg_hdr_len)) {
|
||||
route_entry = NLMSG_DATA(msg_hdr_ptr);
|
||||
route_protocol = route_entry->rtm_protocol;
|
||||
|
||||
// Annoyingly NLM_F_MATCH is not yet implemented so even if we pass in
|
||||
// RT_TABLE_MAIN with RTM_GETROUTE it still returns everything so we
|
||||
// have to filter here.
|
||||
if (route_entry->rtm_table == RT_TABLE_MAIN) {
|
||||
int route_attribute_len;
|
||||
char dest[INET6_ADDRSTRLEN], gate[INET6_ADDRSTRLEN], netmask[32],
|
||||
flags[10] = "U", if_name[IF_NAMESIZE] = "-";
|
||||
unsigned priority = 0, mss = 0, win = 0, irtt = 0, ref = 0, use = 0,
|
||||
route_netmask, metric_len;
|
||||
struct in_addr netmask_addr;
|
||||
struct rtattr *metric;
|
||||
struct rta_cacheinfo *cache_info;
|
||||
|
||||
if (f == AF_INET) {
|
||||
strcpy(dest, FLAG(n) ? "0.0.0.0" : "default");
|
||||
strcpy(gate, FLAG(n) ? "*" : "0.0.0.0");
|
||||
strcpy(netmask, "0.0.0.0");
|
||||
} else {
|
||||
strcpy(dest, "::");
|
||||
strcpy(gate, "::");
|
||||
}
|
||||
|
||||
route_netmask = route_entry->rtm_dst_len;
|
||||
if (route_netmask == 0) netmask_addr.s_addr = ~((in_addr_t) -1);
|
||||
else netmask_addr.s_addr = htonl(~((1 << (32 - route_netmask)) - 1));
|
||||
inet_ntop(AF_INET, &netmask_addr, netmask, sizeof(netmask));
|
||||
|
||||
rteattr = RTM_RTA(route_entry);
|
||||
route_attribute_len = RTM_PAYLOAD(msg_hdr_ptr);
|
||||
while (RTA_OK(rteattr, route_attribute_len)) {
|
||||
switch (rteattr->rta_type) {
|
||||
case RTA_DST:
|
||||
if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), dest, sizeof(dest));
|
||||
else get_hostname(f, RTA_DATA(rteattr), dest, sizeof(dest));
|
||||
break;
|
||||
|
||||
case RTA_GATEWAY:
|
||||
if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), gate, sizeof(dest));
|
||||
else get_hostname(f, RTA_DATA(rteattr), gate, sizeof(dest));
|
||||
strcat(flags, "G");
|
||||
break;
|
||||
|
||||
case RTA_PRIORITY:
|
||||
priority = *(unsigned *)RTA_DATA(rteattr);
|
||||
break;
|
||||
|
||||
case RTA_OIF:
|
||||
if_indextoname(*(int *)RTA_DATA(rteattr), if_name);
|
||||
break;
|
||||
|
||||
case RTA_METRICS:
|
||||
metric_len = RTA_PAYLOAD(rteattr);
|
||||
for (metric = RTA_DATA(rteattr); RTA_OK(metric, metric_len);
|
||||
metric = RTA_NEXT(metric, metric_len))
|
||||
if (metric->rta_type == RTAX_ADVMSS)
|
||||
mss = *(unsigned *)RTA_DATA(metric);
|
||||
else if (metric->rta_type == RTAX_WINDOW)
|
||||
win = *(unsigned *)RTA_DATA(metric);
|
||||
else if (metric->rta_type == RTAX_RTT)
|
||||
irtt = (*(unsigned *)RTA_DATA(metric))/8;
|
||||
break;
|
||||
|
||||
case RTA_CACHEINFO:
|
||||
cache_info = RTA_DATA(rteattr);
|
||||
ref = cache_info->rta_clntref;
|
||||
use = cache_info->rta_used;
|
||||
break;
|
||||
}
|
||||
|
||||
rteattr = RTA_NEXT(rteattr, route_attribute_len);
|
||||
}
|
||||
|
||||
if (route_entry->rtm_type == RTN_UNREACHABLE) flags[0] = '!';
|
||||
if (route_netmask == 32) strcat(flags, "H");
|
||||
if (route_protocol == RTPROT_REDIRECT) strcat(flags, "D");
|
||||
|
||||
if (f == AF_INET) {
|
||||
xprintf("%-15.15s %-15.15s %-16s%-6s", dest, gate, netmask, flags);
|
||||
if (FLAG(e)) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, if_name);
|
||||
else xprintf("%-6d %-2d %7d %s\n", priority, ref, use, if_name);
|
||||
} else {
|
||||
char *dest_with_mask = xmprintf("%s/%u", dest, route_netmask);
|
||||
xprintf("%-30s %-26s %-4s %-6d %-4d %2d %-8s\n",
|
||||
dest_with_mask, gate, flags, priority, ref, use, if_name);
|
||||
free(dest_with_mask);
|
||||
}
|
||||
}
|
||||
msg_hdr_ptr = NLMSG_NEXT(msg_hdr_ptr, msg_hdr_len);
|
||||
}
|
||||
|
||||
msg_hdr_len = xrecv(fd, buf, sizeof(buf));
|
||||
msg_hdr_ptr = buf;
|
||||
}
|
||||
|
||||
xclose(fd);
|
||||
}
|
||||
|
||||
// find parameter (add/del/net/host) in list, return appropriate action or 0.
|
||||
static int get_action(char ***argv, struct _arglist *list)
|
||||
{
|
||||
struct _arglist *alist;
|
||||
|
||||
if (!**argv) return 0;
|
||||
for (alist = list; alist->arg; alist++) { //find the given parameter in list
|
||||
if (!strcmp(**argv, alist->arg)) {
|
||||
*argv += 1;
|
||||
return alist->action;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// add/del a route.
|
||||
static void setroute(sa_family_t f, char **argv)
|
||||
{
|
||||
char *tgtip;
|
||||
int sockfd, arg2_action;
|
||||
int action = get_action(&argv, arglist1); //verify the arg for add/del.
|
||||
struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)];
|
||||
struct nlmsghdr *nlMsg;
|
||||
struct rtmsg *rtMsg;
|
||||
|
||||
if (!action || !*argv) help_exit("setroute");
|
||||
arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host
|
||||
if (!*argv) help_exit("setroute");
|
||||
tgtip = *argv++;
|
||||
sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
nlMsg = (struct nlmsghdr *) buf;
|
||||
rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg);
|
||||
|
||||
nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
|
||||
//TODO(emolitor): Improve action and arg2_action handling
|
||||
if (action == 1) { // Add
|
||||
nlMsg->nlmsg_type = RTM_NEWROUTE;
|
||||
nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
|
||||
} else { // Delete
|
||||
nlMsg->nlmsg_type = RTM_DELROUTE;
|
||||
nlMsg->nlmsg_flags = NLM_F_REQUEST;
|
||||
}
|
||||
|
||||
nlMsg->nlmsg_pid = getpid();
|
||||
nlMsg->nlmsg_seq = 1;
|
||||
rtMsg->rtm_family = f;
|
||||
rtMsg->rtm_table = RT_TABLE_UNSPEC;
|
||||
rtMsg->rtm_type = RTN_UNICAST;
|
||||
rtMsg->rtm_protocol = RTPROT_UNSPEC;
|
||||
rtMsg->rtm_flags = RTM_F_NOTIFY;
|
||||
rtMsg->rtm_dst_len = rtMsg->rtm_src_len = (f == AF_INET) ? 32 : 128;
|
||||
|
||||
if (arg2_action == 2) rtMsg->rtm_scope = RT_SCOPE_HOST;
|
||||
|
||||
size_t addr_len = sizeof(struct in_addr);
|
||||
if (f == AF_INET6) addr_len = sizeof(struct in6_addr);
|
||||
unsigned char addr[sizeof(struct in6_addr)] = {0,};
|
||||
|
||||
for (; *argv; argv++) {
|
||||
if (!strcmp(*argv, "mod")) continue;
|
||||
else if (!strcmp(*argv, "dyn")) continue;
|
||||
else if (!strcmp(*argv, "reinstate")) continue;
|
||||
else if (!strcmp(*argv, "reject")) rtMsg->rtm_type = RTN_UNREACHABLE;
|
||||
else {
|
||||
if (!argv[1]) show_help(stdout, 1);
|
||||
|
||||
if (!strcmp(*argv, "metric")) {
|
||||
unsigned int priority = atolx_range(argv[1], 0, UINT_MAX);
|
||||
addAttr(nlMsg, sizeof(toybuf), &priority, RTA_PRIORITY, sizeof(unsigned int));
|
||||
} else if (!strcmp(*argv, "netmask")) {
|
||||
uint32_t netmask;
|
||||
char *ptr;
|
||||
uint32_t naddr[4] = {0,};
|
||||
uint64_t plen;
|
||||
|
||||
netmask = (f == AF_INET6) ? 128 : 32; // set default netmask
|
||||
plen = strtoul(argv[1], &ptr, 0);
|
||||
|
||||
if (!ptr || ptr == argv[1] || *ptr || !plen || plen > netmask) {
|
||||
if (!inet_pton(f, argv[1], &naddr)) error_exit("invalid netmask");
|
||||
if (f == AF_INET) {
|
||||
uint32_t mask = htonl(*naddr), host = ~mask;
|
||||
if (host & (host + 1)) error_exit("invalid netmask");
|
||||
for (plen = 0; mask; mask <<= 1) ++plen;
|
||||
if (plen > 32) error_exit("invalid netmask");
|
||||
}
|
||||
}
|
||||
netmask = plen;
|
||||
rtMsg->rtm_dst_len = netmask;
|
||||
} else if (!strcmp(*argv, "gw")) {
|
||||
if (!inet_pton(f, argv[1], &addr)) error_exit("invalid gw");
|
||||
addAttr(nlMsg, sizeof(toybuf), &addr, RTA_GATEWAY, addr_len);
|
||||
} else if (!strcmp(*argv, "mss")) {
|
||||
// TODO(emolitor): Add RTA_METRICS support
|
||||
//set the TCP Maximum Segment Size for connections over this route.
|
||||
//rt->rt_mtu = atolx_range(argv[1], 64, 65536);
|
||||
//rt->rt_flags |= RTF_MSS;
|
||||
} else if (!strcmp(*argv, "window")) {
|
||||
// TODO(emolitor): Add RTA_METRICS support
|
||||
//set the TCP window size for connections over this route to W bytes.
|
||||
//rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low
|
||||
//rt->rt_flags |= RTF_WINDOW;
|
||||
} else if (!strcmp(*argv, "irtt")) {
|
||||
// TODO(emolitor): Add RTA_METRICS support
|
||||
//rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX);
|
||||
//rt->rt_flags |= RTF_IRTT;
|
||||
} else if (!strcmp(*argv, "dev")) {
|
||||
unsigned int if_idx = if_nametoindex(argv[1]);
|
||||
if (!if_idx) perror_exit("dev");
|
||||
addAttr(nlMsg, sizeof(toybuf), &if_idx, RTA_OIF, sizeof(unsigned int));
|
||||
} else help_exit("no '%s'", *argv);
|
||||
argv++;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(tgtip, "default") != 0) {
|
||||
char *prefix = strtok(0, "/");
|
||||
|
||||
if (prefix) rtMsg->rtm_dst_len = strtoul(prefix, &prefix, 0);
|
||||
if (!inet_pton(f, strtok(tgtip, "/"), &addr)) error_exit("invalid target");
|
||||
addAttr(nlMsg, sizeof(toybuf), &addr, RTA_DST, addr_len);
|
||||
} else rtMsg->rtm_dst_len = 0;
|
||||
|
||||
xsend(sockfd, nlMsg, nlMsg->nlmsg_len);
|
||||
xclose(sockfd);
|
||||
}
|
||||
|
||||
void route_main(void)
|
||||
{
|
||||
if (!*toys.optargs) {
|
||||
if (!TT.A || !strcmp(TT.A, "inet")) display_routes(AF_INET);
|
||||
else if (!strcmp(TT.A, "inet6")) display_routes(AF_INET6);
|
||||
else show_help(stdout, 1);
|
||||
} else {
|
||||
if (!TT.A) {
|
||||
if (toys.optc>1 && strchr(toys.optargs[1], ':')) {
|
||||
xprintf("WARNING: Implicit IPV6 address using -Ainet6\n");
|
||||
TT.A = "inet6";
|
||||
} else TT.A = "inet";
|
||||
}
|
||||
|
||||
if (!strcmp(TT.A, "inet")) setroute(AF_INET, toys.optargs);
|
||||
else setroute(AF_INET6, toys.optargs);
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
/* cpio.c - a basic cpio
|
||||
*
|
||||
* Copyright 2013 Isaac Dunham <ibid.ag@gmail.com>
|
||||
* Copyright 2015 Frontier Silicon Ltd.
|
||||
*
|
||||
* see https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt
|
||||
* and http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cpio.html
|
||||
* and http://pubs.opengroup.org/onlinepubs/7908799/xcu/cpio.html
|
||||
*
|
||||
* Yes, that's SUSv2, newer versions removed it, but RPM and initramfs use
|
||||
* this archive format. We implement (only) the modern "-H newc" variant which
|
||||
* expanded headers to 110 bytes (first field 6 bytes, rest are 8).
|
||||
* In order: magic ino mode uid gid nlink mtime filesize devmajor devminor
|
||||
* rdevmajor rdevminor namesize check
|
||||
* This is the equivalent of mode -H newc in other implementations.
|
||||
* We always do --quiet, but accept it as a compatibility NOP.
|
||||
*
|
||||
* TODO: export/import linux file list text format ala gen_initramfs_list.sh
|
||||
* TODO: hardlink support, -A, -0, -a, -L, --sparse
|
||||
* TODO: --renumber-archives (probably always?) --ignore-devno --reproducible
|
||||
|
||||
USE_CPIO(NEWTOY(cpio, "(ignore-devno)(renumber-inodes)(quiet)(no-preserve-owner)R(owner):md(make-directories)uLH:p|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN))
|
||||
|
||||
config CPIO
|
||||
bool "cpio"
|
||||
default y
|
||||
help
|
||||
usage: cpio -{o|t|i|p DEST} [-dLtuv] [--verbose] [-F FILE] [-R [USER][:GROUP] [--no-preserve-owner]
|
||||
|
||||
Copy files into and out of a "newc" format cpio archive.
|
||||
|
||||
-d Create directories if needed
|
||||
-F FILE Use archive FILE instead of stdin/stdout
|
||||
-i Extract from archive into file system (stdin=archive)
|
||||
-L Follow symlinks
|
||||
-o Create archive (stdin=list of files, stdout=archive)
|
||||
-p DEST Copy-pass mode, copy stdin file list to directory DEST
|
||||
-R USER Replace owner with USER[:GROUP]
|
||||
-t Test files (list only, stdin=archive, stdout=list of files)
|
||||
-u Unlink existing files when extracting
|
||||
-v Verbose
|
||||
--no-preserve-owner Don't set ownership during extract
|
||||
*/
|
||||
|
||||
#define FOR_cpio
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *F, *H, *R;
|
||||
)
|
||||
|
||||
// Read strings, tail padded to 4 byte alignment. Argument "align" is amount
|
||||
// by which start of string isn't aligned (usually 0, but header is 110 bytes
|
||||
// which is 2 bytes off because the first field wasn't expanded from 6 to 8).
|
||||
static char *strpad(int fd, unsigned len, unsigned align)
|
||||
{
|
||||
char *str;
|
||||
|
||||
align = (align + len) & 3;
|
||||
if (align) len += (4-align);
|
||||
xreadall(fd, str = xmalloc(len+1), len);
|
||||
str[len]=0; // redundant, in case archive is bad
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
//convert hex to uint; mostly to allow using bits of non-terminated strings
|
||||
static unsigned x8u(char *hex)
|
||||
{
|
||||
unsigned val, inpos = 8, outpos;
|
||||
char pattern[6];
|
||||
|
||||
while (*hex == '0') {
|
||||
hex++;
|
||||
if (!--inpos) return 0;
|
||||
}
|
||||
// Because scanf gratuitously treats %*X differently than printf does.
|
||||
sprintf(pattern, "%%%dX%%n", inpos);
|
||||
sscanf(hex, pattern, &val, &outpos);
|
||||
if (inpos != outpos) error_exit("bad hex");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void cpio_main(void)
|
||||
{
|
||||
int pipe, afd = FLAG(o), reown = !geteuid() && !FLAG(no_preserve_owner),
|
||||
empty = 1;
|
||||
pid_t pid = 0;
|
||||
long Ruid = -1, Rgid = -1;
|
||||
char *tofree = 0;
|
||||
|
||||
if (TT.R) {
|
||||
char *group = TT.R+strcspn(TT.R, ":.");
|
||||
|
||||
if (*group) {
|
||||
Rgid = xgetgid(group+1);
|
||||
*group = 0;
|
||||
}
|
||||
if (group != TT.R) Ruid = xgetuid(TT.R);
|
||||
}
|
||||
|
||||
// In passthrough mode, parent stays in original dir and generates archive
|
||||
// to pipe, child does chdir to new dir and reads archive from stdin (pipe).
|
||||
if (FLAG(p)) {
|
||||
if (FLAG(d)) {
|
||||
if (!*toys.optargs) error_exit("need directory for -p");
|
||||
if (mkdir(*toys.optargs, 0700) == -1 && errno != EEXIST)
|
||||
perror_msg("mkdir %s", *toys.optargs);
|
||||
}
|
||||
if (toys.stacktop) {
|
||||
// xpopen() doesn't return from child due to vfork(), instead restarts
|
||||
// with !toys.stacktop
|
||||
pid = xpopen(0, &pipe, 0);
|
||||
afd = pipe;
|
||||
} else {
|
||||
// child
|
||||
toys.optflags |= FLAG_i;
|
||||
xchdir(*toys.optargs);
|
||||
}
|
||||
}
|
||||
|
||||
if (TT.F) {
|
||||
int perm = FLAG(o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY;
|
||||
|
||||
afd = xcreate(TT.F, perm, 0644);
|
||||
}
|
||||
|
||||
// read cpio archive
|
||||
|
||||
if (FLAG(i) || FLAG(t)) for (;; empty = 0) {
|
||||
char *name, *data;
|
||||
unsigned mode, uid, gid, timestamp;
|
||||
int test = FLAG(t), err = 0, size = 0, len;
|
||||
|
||||
free(tofree);
|
||||
tofree = 0;
|
||||
// read header, skipping arbitrary leading NUL bytes (concatenated archives)
|
||||
for (;;) {
|
||||
if (1>(len = readall(afd, toybuf+size, 110-size))) break;
|
||||
if (size || *toybuf) {
|
||||
size += len;
|
||||
break;
|
||||
}
|
||||
for (size = 0; size<len; size++) if (toybuf[size]) break;
|
||||
memmove(toybuf, toybuf+size, len-size);
|
||||
size = len-size;
|
||||
}
|
||||
if (!size) {
|
||||
if (empty) error_exit("empty archive");
|
||||
else break;
|
||||
}
|
||||
if (size != 110 || smemcmp(toybuf, "070701", 6)) error_exit("bad header");
|
||||
tofree = name = strpad(afd, x8u(toybuf+94), 110);
|
||||
// TODO: this flushes hardlink detection via major/minor/ino match
|
||||
if (!strcmp("TRAILER!!!", name)) continue;
|
||||
|
||||
// If you want to extract absolute paths, "cd /" and run cpio.
|
||||
while (*name == '/') name++;
|
||||
// TODO: remove .. entries
|
||||
|
||||
size = x8u(toybuf+54);
|
||||
mode = x8u(toybuf+14);
|
||||
uid = (Ruid>=0) ? Ruid : x8u(toybuf+22);
|
||||
gid = (Rgid>=0) ? Rgid : x8u(toybuf+30);
|
||||
timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem
|
||||
|
||||
// (This output is unaffected by --quiet.)
|
||||
if (FLAG(t) || FLAG(v)) puts(name);
|
||||
|
||||
if (FLAG(u) && !test) if (unlink(name) && errno == EISDIR) rmdir(name);
|
||||
|
||||
if (!test && FLAG(d) && strrchr(name, '/') && mkpath(name)) {
|
||||
perror_msg("mkpath '%s'", name);
|
||||
test++;
|
||||
}
|
||||
|
||||
// Consume entire record even if it couldn't create file, so we're
|
||||
// properly aligned with next file.
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
if (test) continue;
|
||||
err = mkdir(name, mode) && (errno != EEXIST && !FLAG(u));
|
||||
|
||||
// Creading dir/dev doesn't give us a filehandle, we have to refer to it
|
||||
// by name to chown/utime, but how do we know it's the same item?
|
||||
// Check that we at least have the right type of entity open, and do
|
||||
// NOT restore dropped suid bit in this case.
|
||||
if (S_ISDIR(mode) && reown) {
|
||||
int fd = open(name, O_RDONLY|O_NOFOLLOW);
|
||||
struct stat st;
|
||||
|
||||
if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT))
|
||||
err = fchown(fd, uid, gid);
|
||||
else err = 1;
|
||||
|
||||
close(fd);
|
||||
}
|
||||
} else if (S_ISREG(mode)) {
|
||||
int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, mode);
|
||||
|
||||
// If write fails, we still need to read/discard data to continue with
|
||||
// archive. Since doing so overwrites errno, report error now
|
||||
if (fd < 0) {
|
||||
perror_msg("create %s", name);
|
||||
test++;
|
||||
}
|
||||
|
||||
data = toybuf;
|
||||
while (size) {
|
||||
if (size < sizeof(toybuf)) data = strpad(afd, size, 0);
|
||||
else xreadall(afd, toybuf, sizeof(toybuf));
|
||||
if (!test) xwrite(fd, data, data == toybuf ? sizeof(toybuf) : size);
|
||||
if (data != toybuf) {
|
||||
free(data);
|
||||
break;
|
||||
}
|
||||
size -= sizeof(toybuf);
|
||||
}
|
||||
|
||||
if (!test) {
|
||||
// set owner, restore dropped suid bit
|
||||
if (reown) err = fchown(fd, uid, gid) && fchmod(fd, mode);
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
data = S_ISLNK(mode) ? strpad(afd, size, 0) : 0;
|
||||
if (!test) {
|
||||
err = data ? symlink(data, name)
|
||||
: mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86)));
|
||||
|
||||
// Can't get a filehandle to a symlink or a node on nodev mount,
|
||||
// so do special chown that at least doesn't follow symlinks.
|
||||
// We also don't chmod after, so dropped suid bit isn't restored
|
||||
if (!err && reown) err = lchown(name, uid, gid);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
// Set timestamp.
|
||||
if (!test && !err) {
|
||||
struct timespec times[2];
|
||||
|
||||
memset(times, 0, sizeof(struct timespec)*2);
|
||||
times[0].tv_sec = times[1].tv_sec = timestamp;
|
||||
err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW);
|
||||
}
|
||||
|
||||
if (err) perror_msg_raw(name);
|
||||
|
||||
// Output cpio archive
|
||||
|
||||
} else {
|
||||
char *name = 0;
|
||||
size_t size = 0;
|
||||
unsigned inode = 0;
|
||||
|
||||
for (;;) {
|
||||
struct stat st;
|
||||
unsigned nlen, error = 0, zero = 0;
|
||||
int len, fd = -1;
|
||||
char *link = 0;
|
||||
ssize_t llen;
|
||||
|
||||
len = getline(&name, &size, stdin);
|
||||
if (len<1) break;
|
||||
if (name[len-1] == '\n') name[--len] = 0;
|
||||
if (!len) continue;
|
||||
nlen = len+1;
|
||||
if ((FLAG(L)?stat:lstat)(name, &st) || (S_ISREG(st.st_mode)
|
||||
&& st.st_size && (fd = open(name, O_RDONLY))<0)
|
||||
|| (S_ISLNK(st.st_mode) && !(link = xreadlink(name))))
|
||||
{
|
||||
perror_msg_raw(name);
|
||||
continue;
|
||||
}
|
||||
// encrypted filesystems can stat the wrong link size
|
||||
if (link) st.st_size = strlen(link);
|
||||
|
||||
if (Ruid>=0) st.st_uid = Ruid;
|
||||
if (Rgid>=0) st.st_gid = Rgid;
|
||||
if (FLAG(no_preserve_owner)) st.st_uid = st.st_gid = 0;
|
||||
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) st.st_size = 0;
|
||||
if (st.st_size >> 32) perror_msg("skipping >2G file '%s'", name);
|
||||
else {
|
||||
if (FLAG(renumber_inodes)) st.st_ino = ++inode;
|
||||
if (FLAG(ignore_devno)) st.st_rdev = 0;
|
||||
llen = sprintf(toybuf,
|
||||
"070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
|
||||
(int)st.st_ino, st.st_mode, st.st_uid, st.st_gid, (int)st.st_nlink,
|
||||
(int)st.st_mtime, (int)st.st_size, dev_major(st.st_dev),
|
||||
dev_minor(st.st_dev), dev_major(st.st_rdev), dev_minor(st.st_rdev),
|
||||
nlen, 0);
|
||||
xwrite(afd, toybuf, llen);
|
||||
xwrite(afd, name, nlen);
|
||||
|
||||
// NUL Pad header up to 4 multiple bytes.
|
||||
llen = (llen + nlen) & 3;
|
||||
if (llen) xwrite(afd, &zero, 4-llen);
|
||||
|
||||
// Write out body for symlink or regular file
|
||||
if (link) xwrite(afd, link, st.st_size);
|
||||
else for (llen = st.st_size; llen; llen -= nlen) {
|
||||
nlen = llen > sizeof(toybuf) ? sizeof(toybuf) : llen;
|
||||
// If read fails, write anyway (already wrote size in header)
|
||||
if (nlen != readall(fd, toybuf, nlen))
|
||||
if (!error++) perror_msg("bad read from file '%s'", name);
|
||||
xwrite(afd, toybuf, nlen);
|
||||
}
|
||||
llen = st.st_size & 3;
|
||||
if (llen) xwrite(afd, &zero, 4-llen);
|
||||
}
|
||||
free(link);
|
||||
xclose(fd);
|
||||
}
|
||||
if (CFG_TOYBOX_FREE) free(name);
|
||||
|
||||
// nlink=1, namesize=11, with padding
|
||||
dprintf(afd, "070701%040X%056X%08XTRAILER!!!%c%c%c%c", 1, 11, 0, 0, 0, 0,0);
|
||||
}
|
||||
if (TT.F) xclose(afd);
|
||||
|
||||
if (FLAG(p) && pid) toys.exitval |= xpclose(pid, pipe);
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/* kill.c - a program to send signals to processes
|
||||
*
|
||||
* Copyright 2012 Daniel Walter <d.walter@0x90.at>
|
||||
*
|
||||
* See http://opengroup.org/onlinepubs/9699919799/utilities/kill.html
|
||||
*
|
||||
* killall5.c - Send signal to all processes outside current session.
|
||||
*
|
||||
* Copyright 2014 Ranjan Kumar <ranjankumar.bth@gmail.com>
|
||||
* Copyright 2014 Kyungwan Han <asura321@gamil.com>
|
||||
*
|
||||
* No Standard
|
||||
*
|
||||
* TODO: toysh jobspec support, -n -L
|
||||
|
||||
USE_KILL(NEWTOY(kill, "?ls: ", TOYFLAG_BIN|TOYFLAG_MAYFORK))
|
||||
USE_KILLALL5(NEWTOY(killall5, "?o*ls: [!lo][!ls]", TOYFLAG_SBIN))
|
||||
|
||||
config KILL
|
||||
bool "kill"
|
||||
default y
|
||||
help
|
||||
usage: kill [-l [SIGNAL] | -s SIGNAL | -SIGNAL] PID...
|
||||
|
||||
Send signal to process(es).
|
||||
|
||||
-l List signal name(s) and number(s)
|
||||
-s Send SIGNAL (default SIGTERM)
|
||||
|
||||
config KILLALL5
|
||||
bool "killall5"
|
||||
default y
|
||||
depends on KILL
|
||||
help
|
||||
usage: killall5 [-l [SIGNAL]] [-SIGNAL|-s SIGNAL] [-o PID]...
|
||||
|
||||
Send a signal to all processes outside current session.
|
||||
|
||||
-l List signal name(s) and number(s)
|
||||
-o PID Omit PID
|
||||
-s Send SIGNAL (default SIGTERM)
|
||||
*/
|
||||
|
||||
// This has to match the filename:
|
||||
#define FOR_kill
|
||||
#define FORCE_FLAGS
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *s;
|
||||
struct arg_list *o;
|
||||
)
|
||||
|
||||
// But kill's flags are a subset of killall5's
|
||||
|
||||
#define FOR_killall5
|
||||
#include "generated/flags.h"
|
||||
|
||||
void kill_main(void)
|
||||
{
|
||||
int signum;
|
||||
char *tmp, **args = toys.optargs;
|
||||
pid_t pid;
|
||||
|
||||
// list signal(s)
|
||||
if (FLAG(l)) {
|
||||
if (*args) {
|
||||
int signum = sig_to_num(*args);
|
||||
char *s = 0;
|
||||
|
||||
if (signum>=0) s = num_to_sig(signum&127);
|
||||
if (isdigit(**args)) puts(s ? s : "UNKNOWN");
|
||||
else printf("%d\n", signum);
|
||||
} else list_signals();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// signal must come before pids, so "kill -9 -1" isn't confusing.
|
||||
|
||||
if (!TT.s && *args && **args=='-') TT.s = *(args++)+1;
|
||||
if (TT.s) {
|
||||
char *arg;
|
||||
int i = strtol(TT.s, &arg, 10);
|
||||
|
||||
if (!*arg) arg = num_to_sig(i);
|
||||
else arg = TT.s;
|
||||
|
||||
if (!arg || -1 == (signum = sig_to_num(arg)))
|
||||
error_exit("Unknown signal '%s'", arg);
|
||||
} else signum = SIGTERM;
|
||||
|
||||
// is it killall5?
|
||||
if (CFG_KILLALL5 && toys.which->name[4]=='a') {
|
||||
DIR *dp;
|
||||
struct dirent *entry;
|
||||
int pid, sid;
|
||||
long *olist = 0, ocount = 0;
|
||||
|
||||
// parse omit list
|
||||
if (FLAG(o)) {
|
||||
struct arg_list *ptr;
|
||||
|
||||
for (ptr = TT.o; ptr; ptr = ptr->next) ocount++;
|
||||
olist = xmalloc(ocount*sizeof(long));
|
||||
ocount = 0;
|
||||
for (ptr = TT.o; ptr; ptr=ptr->next) olist[ocount++] = atolx(ptr->arg);
|
||||
}
|
||||
|
||||
sid = getsid(pid = getpid());
|
||||
|
||||
if (!(dp = opendir("/proc"))) {
|
||||
free(olist);
|
||||
perror_exit("/proc");
|
||||
}
|
||||
while ((entry = readdir(dp))) {
|
||||
int count, procpid, procsid;
|
||||
|
||||
if (!(procpid = atoi(entry->d_name))) continue;
|
||||
|
||||
snprintf(toybuf, sizeof(toybuf), "/proc/%d/stat", procpid);
|
||||
if (!readfile(toybuf, toybuf, sizeof(toybuf))) continue;
|
||||
// command name can have embedded space and/or )
|
||||
if (!(tmp = strrchr(toybuf, ')'))
|
||||
|| sscanf(tmp, " %*c %*d %*d %d", &procsid) != 1) continue;
|
||||
if (pid == procpid || sid == procsid || procpid == 1) continue;
|
||||
|
||||
// Check for kernel threads.
|
||||
snprintf(toybuf, sizeof(toybuf), "/proc/%d/cmdline", procpid);
|
||||
if (!readfile(toybuf, toybuf, sizeof(toybuf)) || !*toybuf) continue;
|
||||
|
||||
// Check with omit list.
|
||||
for (count = 0; count < ocount; count++)
|
||||
if (procpid == olist[count]) break;
|
||||
if (count != ocount) continue;
|
||||
|
||||
kill(procpid, signum);
|
||||
}
|
||||
closedir(dp);
|
||||
free(olist);
|
||||
|
||||
// is it kill?
|
||||
} else {
|
||||
|
||||
// "<1" in optstr wouldn't cover this because "-SIGNAL"
|
||||
if (!*args) help_exit("missing argument");
|
||||
|
||||
while (*args) {
|
||||
char *arg = *(args++);
|
||||
|
||||
pid = estrtol(arg, &tmp, 10);
|
||||
if (!errno && *tmp) errno = ESRCH;
|
||||
if (errno || kill(pid, signum)<0) perror_msg("bad pid '%s'", arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void killall5_main(void)
|
||||
{
|
||||
kill_main();
|
||||
}
|
||||
@@ -1,485 +0,0 @@
|
||||
/* patch.c - Apply a "universal" diff.
|
||||
*
|
||||
* Copyright 2007 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* see http://opengroup.org/onlinepubs/9699919799/utilities/patch.html
|
||||
* (But only does -u, because who still cares about "ed"?)
|
||||
*
|
||||
* TODO:
|
||||
* -b backup
|
||||
* -N ignore already applied
|
||||
* -D define wrap #ifdef and #ifndef around changes
|
||||
* -o outfile output here instead of in place
|
||||
* -r rejectfile write rejected hunks to this file
|
||||
* -E remove empty files --remove-empty-files
|
||||
* git syntax (rename, etc)
|
||||
|
||||
USE_PATCH(NEWTOY(patch, ">2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config PATCH
|
||||
bool "patch"
|
||||
default y
|
||||
help
|
||||
usage: patch [-Rlsuv] [-d DIR] [-i FILE] [-p DEPTH] [-F FUZZ] [--dry-run] [FILE [PATCH]]
|
||||
|
||||
Apply a unified diff to one or more files.
|
||||
|
||||
-d Modify files in DIR
|
||||
-F Fuzz factor (number of non-matching context lines allowed per hunk)
|
||||
-i Input patch from FILE (default=stdin)
|
||||
-l Loose match (ignore whitespace)
|
||||
-p Number of '/' to strip from start of file paths (default=all)
|
||||
-R Reverse patch
|
||||
-s Silent except for errors
|
||||
-v Verbose (-vv to see decisions)
|
||||
--dry-run Don't change files, just confirm patch applies
|
||||
|
||||
Only handles "unified" diff format (-u is assumed and ignored). Only
|
||||
modifies files when all hunks to that file apply. Prints failed hunks
|
||||
to stderr, and exits with nonzero status if any hunks fail.
|
||||
|
||||
Files compared against /dev/null (or with a date <= the unix epoch) are
|
||||
created/deleted as appropriate. Default -F value is the number of
|
||||
leading/trailing context lines minus one (usually 2).
|
||||
*/
|
||||
|
||||
#define FOR_patch
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *i, *d;
|
||||
long v, p, g, F;
|
||||
|
||||
void *current_hunk;
|
||||
long oldline, oldlen, newline, newlen, linenum, outnum;
|
||||
int context, state, filein, fileout, filepatch, hunknum;
|
||||
char *tempname;
|
||||
)
|
||||
|
||||
// TODO xgetline() instead, but replace_tempfile() wants fd...
|
||||
char *get_line(int fd)
|
||||
{
|
||||
char c, *buf = 0;
|
||||
long len = 0;
|
||||
|
||||
for (;;) {
|
||||
if (1>read(fd, &c, 1)) break;
|
||||
if (!(len & 63)) buf=xrealloc(buf, len+65);
|
||||
if ((buf[len++]=c) == '\n') break;
|
||||
}
|
||||
if (buf) {
|
||||
buf[len]=0;
|
||||
if (buf[--len]=='\n') buf[len]=0;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Dispose of a line of input, either by writing it out or discarding it.
|
||||
|
||||
// state < 2: just free
|
||||
// state = 2: write whole line to stderr
|
||||
// state = 3: write whole line to fileout
|
||||
// state > 3: write line+1 to fileout when *line != state
|
||||
|
||||
static void do_line(void *data)
|
||||
{
|
||||
struct double_list *dlist = data;
|
||||
|
||||
TT.outnum++;
|
||||
if (TT.state>1)
|
||||
if (0>dprintf(TT.state==2 ? 2 : TT.fileout,"%s\n",dlist->data+(TT.state>3)))
|
||||
perror_exit("write");
|
||||
|
||||
llist_free_double(data);
|
||||
}
|
||||
|
||||
static void finish_oldfile(void)
|
||||
{
|
||||
if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname);
|
||||
TT.fileout = TT.filein = -1;
|
||||
}
|
||||
|
||||
static void fail_hunk(void)
|
||||
{
|
||||
if (!TT.current_hunk) return;
|
||||
|
||||
fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n",
|
||||
TT.hunknum, TT.oldline, TT.newline);
|
||||
toys.exitval = 1;
|
||||
|
||||
// If we got to this point, we've seeked to the end. Discard changes to
|
||||
// this file and advance to next file.
|
||||
|
||||
TT.state = 2;
|
||||
llist_traverse(TT.current_hunk, do_line);
|
||||
TT.current_hunk = 0;
|
||||
if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
|
||||
TT.state = 0;
|
||||
}
|
||||
|
||||
// Compare ignoring whitespace. Just returns 0/1, no > or <
|
||||
static int loosecmp(char *aa, char *bb)
|
||||
{
|
||||
int a = 0, b = 0;
|
||||
|
||||
for (;;) {
|
||||
while (isspace(aa[a])) a++;
|
||||
while (isspace(bb[b])) b++;
|
||||
if (aa[a] != bb[b]) return 1;
|
||||
if (!aa[a]) return 0;
|
||||
a++, b++;
|
||||
}
|
||||
}
|
||||
|
||||
// Given a hunk of a unified diff, make the appropriate change to the file.
|
||||
// This does not use the location information, but instead treats a hunk
|
||||
// as a sort of regex. Copies data from input to output until it finds
|
||||
// the change to be made, then outputs the changed data and returns.
|
||||
// (Finding EOF first is an error.) This is a single pass operation, so
|
||||
// multiple hunks must occur in order in the file.
|
||||
|
||||
static int apply_one_hunk(void)
|
||||
{
|
||||
struct double_list *plist, *buf = 0, *check = 0;
|
||||
int matcheof, trail = 0, allfuzz = 0, fuzz, ii;
|
||||
int (*lcmp)(char *aa, char *bb) = FLAG(l) ? (void *)loosecmp : (void *)strcmp;
|
||||
long backwarn = 0;
|
||||
char *data = toybuf;
|
||||
|
||||
if (TT.v>1) printf("START %d\n", TT.hunknum);
|
||||
|
||||
// Match EOF if there aren't as many ending context lines as beginning
|
||||
dlist_terminate(TT.current_hunk);
|
||||
for (fuzz = 0, plist = TT.current_hunk; plist; plist = plist->next) {
|
||||
char *s = plist->data, c = *s;
|
||||
|
||||
if (c==' ') trail++;
|
||||
else trail = 0;
|
||||
|
||||
// Only allow fuzz if 2 context lines have multiple nonwhitespace chars.
|
||||
// avoids the "all context was blank or } lines" issue. Removed lines
|
||||
// count as context since they're matched.
|
||||
if (c==' ' || c=="-+"[FLAG(R)]) {
|
||||
while (isspace(*++s));
|
||||
if (*s && s[1] && !isspace(s[1])) fuzz++;
|
||||
}
|
||||
}
|
||||
matcheof = !trail || trail < TT.context;
|
||||
if (FLAG(F) && !TT.F) fuzz = 0;
|
||||
if (fuzz>1) allfuzz = TT.F ? : TT.context ? TT.context-1 : 0;
|
||||
|
||||
// Loop through input data searching for this hunk. Match all context
|
||||
// lines and lines to be removed until we've found end of complete hunk.
|
||||
plist = TT.current_hunk;
|
||||
fuzz = 0;
|
||||
for (;;) {
|
||||
if (data) {
|
||||
data = get_line(TT.filein);
|
||||
check = data ? dlist_add(&buf, data) : 0;
|
||||
TT.linenum++;
|
||||
}
|
||||
if (TT.v>1) printf("READ[%ld] %s\n", TT.linenum, data ? : "(NULL)");
|
||||
|
||||
// Compare buffered line(s) with expected lines of hunk. Match can fail
|
||||
// because next line doesn't match, or because we hit end of a hunk that
|
||||
// needed EOF and this isn't EOF.
|
||||
for (;;) {
|
||||
// Find hunk line to match (skip added lines) and detect reverse matches
|
||||
while (plist && *plist->data == "+-"[FLAG(R)]) {
|
||||
// TODO: proper backwarn = full hunk applies in reverse, not just 1 line
|
||||
if (data) {
|
||||
ii = strcspn(data, " \t");
|
||||
if (data[ii+!!data[ii]] && !lcmp(data, plist->data+1))
|
||||
backwarn = TT.linenum;
|
||||
}
|
||||
plist = plist->next;
|
||||
}
|
||||
if (TT.v>1 && plist)
|
||||
printf("HUNK %s\nLINE %s\n", plist->data+1, check ? check->data : "");
|
||||
|
||||
// End of hunk?
|
||||
if (!plist) {
|
||||
if (TT.v>1) printf("END OF HUNK\n");
|
||||
if (matcheof == !data) goto out;
|
||||
|
||||
// Compare line and handle match
|
||||
} else if (check && !lcmp(check->data, plist->data+1)) {
|
||||
if (TT.v>1) printf("MATCH\n");
|
||||
handle_match:
|
||||
plist = plist->next;
|
||||
if ((check = check->next) == buf) {
|
||||
if (plist || matcheof) break;
|
||||
goto out;
|
||||
} else continue;
|
||||
}
|
||||
|
||||
// Did we hit EOF?
|
||||
if (!data) {
|
||||
if (TT.v>1) printf("EOF\n");
|
||||
if (backwarn && !FLAG(s))
|
||||
fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
|
||||
TT.hunknum, backwarn);
|
||||
|
||||
// File ended before we found a place for this hunk.
|
||||
fail_hunk();
|
||||
goto done;
|
||||
}
|
||||
if (TT.v>1) printf("NOT MATCH\n");
|
||||
|
||||
// Match failed: can we fuzz it?
|
||||
if (plist && *plist->data == ' ' && fuzz<allfuzz) {
|
||||
fuzz++;
|
||||
if (TT.v>1) printf("FUZZ %d %s\n", fuzz, check->data);
|
||||
goto handle_match;
|
||||
}
|
||||
|
||||
// If this hunk must match start of file, fail if it didn't.
|
||||
if (!TT.context || trail>TT.context) {
|
||||
fail_hunk();
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Write out first line of buffer and recheck rest for new match.
|
||||
TT.state = 3;
|
||||
if (TT.v>1) printf("WRITE %s\n", buf->data);
|
||||
do_line(check = dlist_pop(&buf));
|
||||
plist = TT.current_hunk;
|
||||
fuzz = 0;
|
||||
|
||||
// If end of the buffer without finishing a match, read more lines.
|
||||
if (!buf) break;
|
||||
check = buf;
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (TT.v) xprintf("Hunk #%d succeeded at %ld.\n", TT.hunknum, TT.linenum);
|
||||
// We have a match. Emit changed data.
|
||||
TT.state = "-+"[FLAG(R)];
|
||||
while ((plist = dlist_pop(&TT.current_hunk))) {
|
||||
if (TT.state == *plist->data || *plist->data == ' ') {
|
||||
if (*plist->data == ' ') dprintf(TT.fileout, "%s\n", buf->data);
|
||||
llist_free_double(dlist_pop(&buf));
|
||||
} else dprintf(TT.fileout, "%s\n", plist->data+1);
|
||||
llist_free_double(plist);
|
||||
}
|
||||
TT.current_hunk = 0;
|
||||
TT.state = 1;
|
||||
done:
|
||||
llist_traverse(buf, do_line);
|
||||
|
||||
return TT.state;
|
||||
}
|
||||
|
||||
// read a filename that has been quoted or escaped
|
||||
static char *unquote_file(char *filename)
|
||||
{
|
||||
char *s = filename, *t, *newfile;
|
||||
|
||||
// Return copy of file that wasn't quoted
|
||||
if (*s++ != '"' || !*s) return xstrdup(filename);
|
||||
|
||||
// quoted and escaped filenames are larger than the original
|
||||
for (t = newfile = xmalloc(strlen(s) + 1); *s != '"'; s++) {
|
||||
if (!s[1]) error_exit("bad %s", filename);
|
||||
|
||||
// don't accept escape sequences unless the filename is quoted
|
||||
if (*s != '\\') *t++ = *s;
|
||||
else if (*++s >= '0' && *s < '8') {
|
||||
*t++ = strtoul(s, &s, 8);
|
||||
s--;
|
||||
} else {
|
||||
if (!(*t = unescape(*s))) *t = *s;;
|
||||
t++;
|
||||
}
|
||||
}
|
||||
*t = 0;
|
||||
|
||||
return newfile;
|
||||
}
|
||||
|
||||
// Read a patch file and find hunks, opening/creating/deleting files.
|
||||
// Call apply_one_hunk() on each hunk.
|
||||
|
||||
// state 0: Not in a hunk, look for +++.
|
||||
// state 1: Found +++ file indicator, look for @@
|
||||
// state 2: In hunk: counting initial context lines
|
||||
// state 3: In hunk: getting body
|
||||
|
||||
void patch_main(void)
|
||||
{
|
||||
int state = 0, patchlinenum = 0, strip = 0;
|
||||
char *oldname = 0, *newname = 0;
|
||||
|
||||
if (toys.optc == 2) TT.i = toys.optargs[1];
|
||||
if (TT.i) TT.filepatch = xopenro(TT.i);
|
||||
TT.filein = TT.fileout = -1;
|
||||
|
||||
if (TT.d) xchdir(TT.d);
|
||||
|
||||
// Loop through the lines in the patch file (-i or stdin) collecting hunks
|
||||
for (;;) {
|
||||
char *patchline;
|
||||
|
||||
if (!(patchline = get_line(TT.filepatch))) break;
|
||||
|
||||
// Other versions of patch accept damaged patches, so we need to also.
|
||||
if (strip || !patchlinenum++) {
|
||||
int len = strlen(patchline);
|
||||
if (len && patchline[len-1] == '\r') {
|
||||
if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n");
|
||||
strip = 1;
|
||||
patchline[len-1] = 0;
|
||||
}
|
||||
}
|
||||
if (!*patchline) {
|
||||
free(patchline);
|
||||
patchline = xstrdup(" ");
|
||||
}
|
||||
|
||||
// Are we assembling a hunk?
|
||||
if (state >= 2) {
|
||||
if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
|
||||
dlist_add((void *)&TT.current_hunk, patchline);
|
||||
|
||||
if (*patchline != '+') TT.oldlen--;
|
||||
if (*patchline != '-') TT.newlen--;
|
||||
|
||||
// Context line?
|
||||
if (*patchline==' ' && state==2) TT.context++;
|
||||
else state=3;
|
||||
|
||||
// If we've consumed all expected hunk lines, apply the hunk.
|
||||
if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
|
||||
} else {
|
||||
dlist_terminate(TT.current_hunk);
|
||||
fail_hunk();
|
||||
state = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Open a new file?
|
||||
if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
|
||||
char *s, **name = &oldname;
|
||||
int i;
|
||||
|
||||
if (*patchline == '+') {
|
||||
name = &newname;
|
||||
state = 1;
|
||||
}
|
||||
|
||||
free(*name);
|
||||
finish_oldfile();
|
||||
|
||||
// Trim date from end of filename (if any). Date<=epoch means delete.
|
||||
for (s = patchline+4; *s && *s!='\t'; s++);
|
||||
i = atoi(s);
|
||||
if (i>1900 && i<=1970) *name = xstrdup("/dev/null");
|
||||
else {
|
||||
*s = 0;
|
||||
*name = unquote_file(patchline+4);
|
||||
}
|
||||
|
||||
// We defer actually opening the file because svn produces broken
|
||||
// patches that don't signal they want to create a new file the
|
||||
// way the patch man page says, so you have to read the first hunk
|
||||
// and _guess_.
|
||||
|
||||
// Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@
|
||||
// but a missing ,value means the value is 1.
|
||||
} else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
|
||||
int i;
|
||||
char *s = patchline+4;
|
||||
|
||||
// Read oldline[,oldlen] +newline[,newlen]
|
||||
|
||||
TT.oldlen = TT.newlen = 1;
|
||||
TT.oldline = strtol(s, &s, 10);
|
||||
if (*s == ',') TT.oldlen=strtol(s+1, &s, 10);
|
||||
TT.newline = strtol(s+2, &s, 10);
|
||||
if (*s == ',') TT.newlen = strtol(s+1, &s, 10);
|
||||
|
||||
TT.context = 0;
|
||||
state = 2;
|
||||
|
||||
// If this is the first hunk, open the file.
|
||||
if (TT.filein == -1) {
|
||||
int oldsum, newsum, del = 0;
|
||||
char *name;
|
||||
|
||||
oldsum = TT.oldline + TT.oldlen;
|
||||
newsum = TT.newline + TT.newlen;
|
||||
|
||||
// If an original file was provided on the command line, it overrides
|
||||
// *all* files mentioned in the patch, not just the first.
|
||||
if (toys.optc) {
|
||||
char **which = FLAG(R) ? &oldname : &newname;
|
||||
|
||||
free(*which);
|
||||
*which = xstrdup(toys.optargs[0]);
|
||||
// The supplied path should be taken literally with or without -p.
|
||||
toys.optflags |= FLAG_p;
|
||||
TT.p = 0;
|
||||
}
|
||||
|
||||
name = FLAG(R) ? oldname : newname;
|
||||
|
||||
// We're deleting oldname if new file is /dev/null (before -p)
|
||||
// or if new hunk is empty (zero context) after patching
|
||||
if (!strcmp(name, "/dev/null") || !(FLAG(R) ? oldsum : newsum)) {
|
||||
name = FLAG(R) ? newname : oldname;
|
||||
del++;
|
||||
}
|
||||
|
||||
// handle -p path truncation.
|
||||
for (i = 0, s = name; *s;) {
|
||||
if (FLAG(p) && TT.p == i) break;
|
||||
if (*s++ != '/') continue;
|
||||
while (*s == '/') s++;
|
||||
name = s;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (del) {
|
||||
if (!FLAG(s)) printf("removing %s\n", name);
|
||||
if (!FLAG(dry_run)) xunlink(name);
|
||||
state = 0;
|
||||
// If we've got a file to open, do so.
|
||||
} else if (!FLAG(p) || i <= TT.p) {
|
||||
// If the old file was null, we're creating a new one.
|
||||
if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK))
|
||||
{
|
||||
if (!FLAG(s)) printf("creating %s\n", name);
|
||||
if (FLAG(dry_run)) TT.filein = xopen("/dev/null", O_RDWR);
|
||||
else {
|
||||
if (mkpath(name)) perror_exit("mkpath %s", name);
|
||||
TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
|
||||
}
|
||||
} else {
|
||||
if (!FLAG(s)) printf("patching %s\n", name);
|
||||
TT.filein = xopenro(name);
|
||||
}
|
||||
if (FLAG(dry_run)) TT.fileout = xopen("/dev/null", O_RDWR);
|
||||
else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
|
||||
TT.linenum = TT.outnum = TT.hunknum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
TT.hunknum++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we didn't continue above, discard this line.
|
||||
free(patchline);
|
||||
}
|
||||
|
||||
finish_oldfile();
|
||||
|
||||
if (CFG_TOYBOX_FREE) {
|
||||
close(TT.filepatch);
|
||||
free(oldname);
|
||||
free(newname);
|
||||
}
|
||||
}
|
||||
@@ -1,409 +0,0 @@
|
||||
/* sort.c - put input lines into order
|
||||
*
|
||||
* Copyright 2004, 2008 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* See http://opengroup.org/onlinepubs/007904975/utilities/sort.html
|
||||
*
|
||||
* Deviations from POSIX: Lots.
|
||||
* We invented -x
|
||||
|
||||
USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMCcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
|
||||
|
||||
config SORT
|
||||
bool "sort"
|
||||
default y
|
||||
help
|
||||
usage: sort [-runbCcdfiMsxVz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE]
|
||||
|
||||
Sort all lines of text from input files (or stdin) to stdout.
|
||||
|
||||
-r Reverse
|
||||
-u Unique lines only
|
||||
-n Numeric order (instead of alphabetical)
|
||||
|
||||
-b Ignore leading blanks (or trailing blanks in second part of key)
|
||||
-C Check whether input is sorted
|
||||
-c Warn if input is unsorted
|
||||
-d Dictionary order (use alphanumeric and whitespace chars only)
|
||||
-f Force uppercase (case insensitive sort)
|
||||
-i Ignore nonprinting characters
|
||||
-k Sort by "key" (see below)
|
||||
-M Month sort (jan, feb, etc)
|
||||
-o Output to FILE instead of stdout
|
||||
-s Skip fallback sort (only sort with keys)
|
||||
-t Use a key separator other than whitespace
|
||||
-x Hexadecimal numerical sort
|
||||
-V Version numbers (name-1.234-rc6.5b.tgz)
|
||||
-z Zero (null) terminated lines
|
||||
|
||||
Sorting by key looks at a subset of the words on each line. -k2 uses the
|
||||
second word to the end of the line, -k2,2 looks at only the second word,
|
||||
-k2,4 looks from the start of the second to the end of the fourth word.
|
||||
-k2.4,5 starts from the fourth character of the second word, to the end
|
||||
of the fifth word. Negative values count from the end. Specifying multiple
|
||||
keys uses the later keys as tie breakers, in order. A type specifier
|
||||
appended to a sort key (such as -2,2n) applies only to sorting that key.
|
||||
|
||||
config SORT_FLOAT
|
||||
bool
|
||||
default y
|
||||
depends on TOYBOX_FLOAT
|
||||
help
|
||||
usage: sort [-g]
|
||||
|
||||
-g General numeric sort (double precision with nan and inf)
|
||||
*/
|
||||
|
||||
#define FOR_sort
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *t;
|
||||
struct arg_list *k;
|
||||
char *o, *T, S;
|
||||
|
||||
void *key_list;
|
||||
unsigned linecount;
|
||||
char **lines, *name;
|
||||
)
|
||||
|
||||
// The sort types are n, g, and M.
|
||||
// u, c, s, and z apply to top level only, not to keys.
|
||||
// b at top level implies bb.
|
||||
// The remaining options can be applied to search keys.
|
||||
|
||||
#define FLAG_bb (1<<31) // Ignore trailing blanks
|
||||
|
||||
struct sort_key {
|
||||
struct sort_key *next_key; // linked list
|
||||
long range[4]; // start word, start char, end word, end char
|
||||
int flags;
|
||||
};
|
||||
|
||||
static int skip_key(char *str)
|
||||
{
|
||||
int end = 0;
|
||||
|
||||
// Skip leading blanks
|
||||
if (str[end] && !TT.t) while (isspace(str[end])) end++;
|
||||
|
||||
// Skip body of key
|
||||
for (; str[end]; end++) {
|
||||
if (TT.t) {
|
||||
if (str[end]==*TT.t) {
|
||||
end++;
|
||||
break;
|
||||
}
|
||||
} else if (isspace(str[end])) break;
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
// Copy of the part of this string corresponding to a key/flags.
|
||||
|
||||
static char *get_key_data(char *str, struct sort_key *key, int flags)
|
||||
{
|
||||
long start = 0, end, len, h, i, j, k;
|
||||
|
||||
// Special case whole string, so we don't have to make a copy
|
||||
if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3]
|
||||
&& !(flags&(FLAG_b|FLAG_d|FLAG_i|FLAG_bb))) return str;
|
||||
|
||||
// Find start of key on first pass, end on second pass
|
||||
len = strlen(str);
|
||||
for (j=0; j<2; j++) {
|
||||
if (!(k = key->range[2*j])) end=len;
|
||||
|
||||
// Loop through fields
|
||||
else {
|
||||
if (k<1) for (end = h = 0;; end += h) {
|
||||
++k;
|
||||
if (!(h = skip_key(str+end))) break;
|
||||
}
|
||||
if (k<1) end = len*!j;
|
||||
else for (end = 0, i = 1; i<k+j; i++) end += skip_key(str+end);
|
||||
}
|
||||
if (!j) start = end;
|
||||
}
|
||||
|
||||
// Key with explicit separator starts after the separator
|
||||
if (TT.t && str[start]==*TT.t) start++;
|
||||
|
||||
// Strip leading and trailing whitespace if necessary
|
||||
if ((flags&FLAG_b) || (!TT.t && !key->range[3]))
|
||||
while (isspace(str[start])) start++;
|
||||
if (flags&FLAG_bb) while (end>start && isspace(str[end-1])) end--;
|
||||
|
||||
// Handle offsets on start and end
|
||||
if (key->range[3]>0) {
|
||||
end += key->range[3]-1;
|
||||
if (end>len) end=len;
|
||||
}
|
||||
if (key->range[1]>0) {
|
||||
start += key->range[1]-1;
|
||||
if (start>len) start=len;
|
||||
}
|
||||
|
||||
// Make the copy
|
||||
if (end<start) end = start;
|
||||
str = xstrndup(str+start, end-start);
|
||||
|
||||
// Handle -d
|
||||
if (flags&FLAG_d) {
|
||||
for (start = end = 0; str[end]; end++)
|
||||
if (isspace(str[end]) || isalnum(str[end])) str[start++] = str[end];
|
||||
str[start] = 0;
|
||||
}
|
||||
|
||||
// Handle -i
|
||||
if (flags&FLAG_i) {
|
||||
for (start = end = 0; str[end]; end++)
|
||||
if (isprint(str[end])) str[start++] = str[end];
|
||||
str[start] = 0;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// append a sort_key to key_list.
|
||||
|
||||
static struct sort_key *add_key(void)
|
||||
{
|
||||
struct sort_key **pkey = (struct sort_key **)&TT.key_list;
|
||||
|
||||
while (*pkey) pkey = &((*pkey)->next_key);
|
||||
return *pkey = xzalloc(sizeof(struct sort_key));
|
||||
}
|
||||
|
||||
// Perform actual comparison
|
||||
static int compare_values(int flags, char *x, char *y)
|
||||
{
|
||||
if (CFG_SORT_FLOAT && (flags & FLAG_g)) {
|
||||
char *xx,*yy;
|
||||
double dx = strtod(x,&xx), dy = strtod(y,&yy);
|
||||
int xinf, yinf;
|
||||
|
||||
// not numbers < NaN < -infinity < numbers < +infinity
|
||||
|
||||
if (x==xx) return y==yy ? 0 : -1;
|
||||
if (y==yy) return 1;
|
||||
|
||||
// Check for isnan
|
||||
if (dx!=dx) return (dy!=dy) ? 0 : -1;
|
||||
if (dy!=dy) return 1;
|
||||
|
||||
// Check for infinity. (Could underflow, but avoids needing libm.)
|
||||
xinf = (1.0/dx == 0.0);
|
||||
yinf = (1.0/dy == 0.0);
|
||||
if (xinf) {
|
||||
if(dx<0) return (yinf && dy<0) ? 0 : -1;
|
||||
return (yinf && dy>0) ? 0 : 1;
|
||||
}
|
||||
if (yinf) return dy<0 ? 1 : -1;
|
||||
|
||||
return dx<dy ? -1 : dx>dy;
|
||||
} else if (flags & FLAG_M) {
|
||||
struct tm thyme;
|
||||
int dx;
|
||||
char *xx,*yy;
|
||||
|
||||
xx = strptime(x,"%b",&thyme);
|
||||
dx = thyme.tm_mon;
|
||||
yy = strptime(y,"%b",&thyme);
|
||||
if (!xx) return !yy ? 0 : -1;
|
||||
else if (!yy) return 1;
|
||||
else return dx==thyme.tm_mon ? 0 : dx-thyme.tm_mon;
|
||||
|
||||
} else if (flags & FLAG_x) return strtol(x, NULL, 16)-strtol(y, NULL, 16);
|
||||
else if (flags & FLAG_V) {
|
||||
while (*x && *y) {
|
||||
while (*x && *x == *y) x++, y++;
|
||||
if (isdigit(*x) && isdigit(*y)) {
|
||||
long long xx = strtoll(x, &x, 10), yy = strtoll(y, &y, 10);
|
||||
|
||||
if (xx<yy) return -1;
|
||||
if (xx>yy) return 1;
|
||||
} else {
|
||||
char xx = *x ? *x : x[-1], yy = *y ? *y : y[-1];
|
||||
|
||||
// -rc/-pre hack so abc-123 > abc-123-rc1 (other way already - < 0-9)
|
||||
if (xx != yy) {
|
||||
if (xx<yy && !strstart(&y, "-rc") && !strstart(&y, "-pre")) return -1;
|
||||
else return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return *x ? !!*y : -1;
|
||||
// This is actually an integer sort with decimals sorted by string fallback.
|
||||
} else if (flags & FLAG_n) {
|
||||
long long dx = atoll(x), dy = atoll(y);
|
||||
|
||||
return dx<dy ? -1 : dx>dy;
|
||||
|
||||
// Ascii sort
|
||||
} else return ((flags&FLAG_f) ? strcasecmp : strcmp)(x, y);
|
||||
}
|
||||
|
||||
// Callback from qsort(): Iterate through key_list and perform comparisons.
|
||||
static int compare_keys(const void *xarg, const void *yarg)
|
||||
{
|
||||
int flags = toys.optflags, retval = 0;
|
||||
char *x, *y, *xx = *(char **)xarg, *yy = *(char **)yarg;
|
||||
struct sort_key *key;
|
||||
|
||||
for (key=(void *)TT.key_list; !retval && key; key = key->next_key) {
|
||||
flags = key->flags ? : toys.optflags;
|
||||
|
||||
// Chop out and modify key chunks, handling -dfib
|
||||
|
||||
x = get_key_data(xx, key, flags);
|
||||
y = get_key_data(yy, key, flags);
|
||||
|
||||
retval = compare_values(flags, x, y);
|
||||
|
||||
// Free the copies get_key_data() made.
|
||||
|
||||
if (x != xx) free(x);
|
||||
if (y != yy) free(y);
|
||||
|
||||
if (retval) break;
|
||||
}
|
||||
|
||||
// Perform fallback sort if necessary (always case insensitive, no -f,
|
||||
// the point is to get a stable order even for -f sorts)
|
||||
if (!retval && !FLAG(s)) {
|
||||
flags = toys.optflags;
|
||||
retval = strcmp(xx, yy);
|
||||
}
|
||||
|
||||
return retval * ((flags&FLAG_r) ? -1 : 1);
|
||||
}
|
||||
|
||||
// Read each line from file, appending to a big array.
|
||||
static void sort_lines(char **pline, long len)
|
||||
{
|
||||
char *line;
|
||||
|
||||
if (!pline) return;
|
||||
line = *pline;
|
||||
if (!FLAG(z) && len && line[len-1]=='\n') line[--len] = 0;
|
||||
*pline = 0;
|
||||
|
||||
// handle -c here so we don't allocate more memory than necessary.
|
||||
if (FLAG(C)||FLAG(c)) {
|
||||
if (TT.lines && compare_keys((void *)&TT.lines, &line)>-FLAG(u)) {
|
||||
toys.exitval = 1;
|
||||
if (FLAG(C)) xexit();
|
||||
error_exit("%s: Check line %u", TT.name, TT.linecount+1);
|
||||
}
|
||||
free(TT.lines);
|
||||
TT.lines = (void *)line;
|
||||
} else {
|
||||
if (!(TT.linecount&63))
|
||||
TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64));
|
||||
TT.lines[TT.linecount] = line;
|
||||
}
|
||||
TT.linecount++;
|
||||
}
|
||||
|
||||
// Callback from loopfiles to handle input files.
|
||||
static void sort_read(int fd, char *name)
|
||||
{
|
||||
TT.name = name;
|
||||
do_lines(fd, '\n'*!FLAG(z), sort_lines);
|
||||
}
|
||||
|
||||
void sort_main(void)
|
||||
{
|
||||
int idx, jdx, fd = 1;
|
||||
|
||||
if (FLAG(u)) toys.optflags |= FLAG_s;
|
||||
|
||||
// Parse -k sort keys.
|
||||
if (TT.k) {
|
||||
struct arg_list *arg;
|
||||
|
||||
for (arg = TT.k; arg; arg = arg->next) {
|
||||
struct sort_key *key = add_key();
|
||||
char *temp, *temp2, *optlist;
|
||||
int flag;
|
||||
|
||||
idx = 0;
|
||||
temp = arg->arg;
|
||||
while (*temp) {
|
||||
// Start of range
|
||||
key->range[2*idx] = strtol(temp, &temp, 10);
|
||||
if (*temp=='.') key->range[(2*idx)+1] = strtol(temp+1, &temp, 10);
|
||||
|
||||
// Handle flags appended to a key type.
|
||||
for (;*temp;temp++) {
|
||||
|
||||
// Second comma becomes an "Unknown key" error.
|
||||
if (*temp==',' && !idx++) {
|
||||
temp++;
|
||||
break;
|
||||
}
|
||||
|
||||
// Which flag is this?
|
||||
optlist = toys.which->options;
|
||||
temp2 = strchr(optlist, *temp);
|
||||
flag = 1<<(optlist-temp2+strlen(optlist)-1);
|
||||
|
||||
// Was it a flag that can apply to a key?
|
||||
if (!temp2 || flag>FLAG_x || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z)))
|
||||
error_exit("Unknown key option.");
|
||||
|
||||
// b after , means strip _trailing_ space, not leading.
|
||||
if (idx && flag==FLAG_b) flag = FLAG_bb;
|
||||
key->flags |= flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// global b flag strips both leading and trailing spaces
|
||||
if (FLAG(b)) toys.optflags |= FLAG_bb;
|
||||
|
||||
// If no keys, perform alphabetic sort over the whole line.
|
||||
if (!TT.key_list) add_key()->range[0] = 1;
|
||||
|
||||
// Open input files and read data, populating TT.lines[TT.linecount]
|
||||
loopfiles(toys.optargs, sort_read);
|
||||
|
||||
// The compare (-c) logic was handled in sort_read(),
|
||||
// so if we got here, we're done.
|
||||
if (FLAG(C)||FLAG(c)) goto exit_now;
|
||||
|
||||
// Perform the actual sort
|
||||
qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys);
|
||||
|
||||
// handle unique (-u)
|
||||
if (FLAG(u)) {
|
||||
for (jdx=0, idx=1; idx<TT.linecount; idx++) {
|
||||
if (!compare_keys(&TT.lines[jdx], &TT.lines[idx])) free(TT.lines[idx]);
|
||||
else TT.lines[++jdx] = TT.lines[idx];
|
||||
}
|
||||
if (TT.linecount) TT.linecount = jdx+1;
|
||||
}
|
||||
|
||||
// Open output file if necessary. We can't do this until we've finished
|
||||
// reading in case the output file is one of the input files.
|
||||
if (TT.o) fd = xcreate(TT.o, O_CREAT|O_TRUNC|O_WRONLY, 0666);
|
||||
|
||||
// Output result
|
||||
for (idx = 0; idx<TT.linecount; idx++) {
|
||||
char *s = TT.lines[idx];
|
||||
unsigned i = strlen(s);
|
||||
|
||||
if (!FLAG(z)) s[i] = '\n';
|
||||
xwrite(fd, s, i+1);
|
||||
if (CFG_TOYBOX_FREE) free(s);
|
||||
}
|
||||
|
||||
exit_now:
|
||||
if (CFG_TOYBOX_FREE) {
|
||||
if (fd != 1) close(fd);
|
||||
free(TT.lines);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 378 KiB |
Reference in New Issue
Block a user