toybox升级0.8.12

Signed-off-by: gong-guilin <gongguilin@h-partners.com>
This commit is contained in:
gong-guilin
2026-05-21 18:43:05 +08:00
parent 058f6d0466
commit 93bfb80ea8
58 changed files with 1870 additions and 5578 deletions
+9 -16
View File
@@ -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
+198
View File
@@ -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
+284
View File
@@ -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.
+1
View File
@@ -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)
{
+4
View File
@@ -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
View File
@@ -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
View File
@@ -1 +0,0 @@
sha1sum.test
+1
View File
@@ -0,0 +1 @@
sha1sum.test
-1
View File
@@ -1 +0,0 @@
sha1sum.test
+1
View File
@@ -0,0 +1 @@
sha1sum.test
-1
View File
@@ -1 +0,0 @@
sha1sum.test
+1
View File
@@ -0,0 +1 @@
sha1sum.test
-1
View File
@@ -1 +0,0 @@
sha1sum.test
+1
View File
@@ -0,0 +1 @@
sha1sum.test
-1
View File
@@ -1 +0,0 @@
sha1sum.test
+1
View File
@@ -0,0 +1 @@
sha1sum.test
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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.
-75
View File
@@ -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
View File
@@ -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
View File
@@ -255,4 +255,4 @@ cleanup:
close(in1);
close(sockfd);
}
}
}
+4 -6
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -2,8 +2,8 @@
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
// Flag order is signifcant: b-t are units in order, FLAG_h-1 is unit mask
USE_FREE(NEWTOY(free, "htgmkb[!htgmkb]", TOYFLAG_USR|TOYFLAG_BIN))
// 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"
@@ -13,7 +13,7 @@ config FREE
Display the total, free and used amount of physical memory and swap space.
-bkmgt Output units (default is bytes)
-bkmg Output units (default is bytes)
-h Human readable (K=1024)
*/
@@ -31,7 +31,7 @@ static char *convert(unsigned long d)
long long ll = d*TT.units;
char *s = TT.buf;
if (toys.optflags & FLAG_h) human_readable(s, ll, 0);
if (FLAG(h)) human_readable(s, ll, 0);
else sprintf(s, "%llu",ll>>TT.bits);
TT.buf += strlen(TT.buf)+1;
+172 -149
View File
@@ -7,33 +7,35 @@
*
* TODO cleanup
USE_LSATTR(NEWTOY(lsattr, "vldaR", TOYFLAG_BIN))
USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN))
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 [-Radlv] [Files...]
usage: lsattr [-Radlpv] [FILE...]
List file attributes on a Linux second extended file system.
(AacDdijsStu defined in chattr --help)
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] [-v version] [File...]
usage: chattr [-R] [-+=AacDdijsStTu] [-p PROJID] [-v VERSION] [FILE...]
Change file attributes on a Linux second extended file system.
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:
@@ -42,77 +44,121 @@ config CHATTR
'=' Set attributes
Attributes:
A Don't track atime
a Append mode only
c Enable compress
D Write dir contents synchronously
d Don't backup with dump
i Cannot be modified (immutable)
j Write all data to journal first
s Zero disk storage when deleted
S Write file contents synchronously
t Disable tail-merging of partial blocks with other files
u Allow file to be undeleted
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 long flag;
unsigned flag;
char opt;
} e2attrs[] = {
{"Secure_Deletion", FS_SECRM_FL, 's'}, // Secure deletion
{"Undelete", FS_UNRM_FL, 'u'}, // Undelete
{"Compression_Requested", FS_COMPR_FL, 'c'}, // Compress file
{"Synchronous_Updates", FS_SYNC_FL, 'S'}, // Synchronous updates
{"Immutable", FS_IMMUTABLE_FL, 'i'}, // Immutable file
{"Append_Only", FS_APPEND_FL, 'a'}, // writes to file may only append
{"No_Dump", FS_NODUMP_FL, 'd'}, // do not dump file
{"No_Atime", FS_NOATIME_FL, 'A'}, // do not update atime
{"Indexed_directory", FS_INDEX_FL, 'I'}, // hash-indexed directory
{"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'}, // file data should be journaled
{"No_Tailmerging", FS_NOTAIL_FL, 't'}, // file tail should not be merged
{"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'}, // dirsync behaviour (directories only)
{"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'}, // Top of directory hierarchies
{NULL, -1, 0},
// 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 long *flag)
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, (void*)flag));
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 long flag = 0, version = 0;
int fd;
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 LABEL1;
goto error;
}
if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto LABEL1;
if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto error;
if (toys.optflags & FLAG_v) {
if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto LABEL2;
xprintf("%5lu ", version);
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 (toys.optflags & FLAG_l) {
int name_found = 0;
if (FLAG(l)) {
xprintf("%-50s ", path);
for (; ptr->name; ptr++) {
if (flag & ptr->flag) {
@@ -123,127 +169,89 @@ static void print_file_attr(char *path)
}
if (!name_found) xprintf("---");
xputc('\n');
} else {
int index = 0;
for (; ptr->name; ptr++)
toybuf[index++] = (flag & ptr->flag) ? ptr->opt : '-';
toybuf[index] = '\0';
xprintf("%s %s\n", toybuf, path);
}
} else xprintf("%s %s\n", attrstr(flag, 1), path);
}
path = 0;
error:
xclose(fd);
return;
LABEL2: xclose(fd);
LABEL1: perror_msg("reading '%s'", path);
if (path) perror_msg("reading '%s'", path);
}
// Get directory information.
static int retell_dir(struct dirtree *root)
{
char *fpath = NULL;
char *fpath = 0;
if (root->again) {
xputc('\n');
return 0;
}
if (S_ISDIR(root->st.st_mode) && !root->parent)
return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN);
if (S_ISDIR(root->st.st_mode) && !root->parent)
return DIRTREE_RECURSE|DIRTREE_COMEAGAIN;
fpath = dirtree_path(root, NULL);
//Special case: with '-a' option and '.'/'..' also included in printing list.
if ((root->name[0] != '.') || (toys.optflags & FLAG_a)) {
if (*root->name != '.' || FLAG(a)) {
print_file_attr(fpath);
if (S_ISDIR(root->st.st_mode) && (toys.optflags & FLAG_R)
&& dirtree_notdotdot(root)) {
if (S_ISDIR(root->st.st_mode) && FLAG(R) && dirtree_notdotdot(root)) {
xprintf("\n%s:\n", fpath);
free(fpath);
return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN);
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;
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) && !(toys.optflags & FLAG_d))
dirtree_read(*toys.optargs, retell_dir);
else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir"
}
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 CLEANUP_lsattr
#define FOR_chattr
#include "generated/flags.h"
static struct _chattr {
unsigned long add, rm, set, version;
unsigned char vflag, recursive;
} chattr;
// Set file flags on a Linux second extended file system.
static inline int ext2_setflag(int fd, struct stat *sb, unsigned long flag)
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, (void*)&flag));
return ioctl(fd, FS_IOC_SETFLAGS, &flag);
}
static unsigned long get_flag_val(char ch)
static unsigned get_flag_val(char ch)
{
struct ext2_attr *ptr = e2attrs;
for (; ptr->name; ptr++)
if (ptr->opt == ch) return ptr->flag;
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 = NULL;
char *arg = **argv, *ptr;
while (arg) {
switch (arg[0]) {
case '-':
for (ptr = ++arg; *ptr; ptr++) {
if (*ptr == 'R') {
chattr.recursive = 1;
continue;
} else if (*ptr == 'v') {// get version from next argv.
char *endptr;
errno = 0;
arg = *(*argv += 1);
if (!arg) help_exit("bad -v");
if (*arg == '-') perror_exit("Invalid Number '%s'", arg);
chattr.version = strtoul(arg, &endptr, 0);
if (errno || *endptr) perror_exit("bad version '%s'", arg);
chattr.vflag = 1;
continue;
} else chattr.rm |= get_flag_val(*ptr);
}
break;
case '+':
for (ptr = ++arg; *ptr; ptr++)
chattr.add |= get_flag_val(*ptr);
break;
case '=':
for (ptr = ++arg; *ptr; ptr++)
chattr.set |= get_flag_val(*ptr);
break;
default: return;
}
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);
}
}
@@ -251,17 +259,14 @@ static void parse_cmdline_arg(char ***argv)
// Update attribute of given file.
static int update_attr(struct dirtree *root)
{
unsigned long fval = 0;
char *fpath = NULL;
int fd;
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) && chattr.recursive)
// 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;
@@ -271,44 +276,62 @@ static int update_attr(struct dirtree *root)
free(fpath);
return DIRTREE_ABORT;
}
// Get current attr of file.
if (ext2_getflag(fd, &(root->st), &fval) < 0) {
perror_msg("read flags of '%s'", fpath);
free(fpath);
xclose(fd);
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));
}
if (chattr.set) { // for '=' operator.
if (ext2_setflag(fd, &(root->st), chattr.set) < 0)
perror_msg("setting flags '%s'", fpath);
} else { // for '-' / '+' operator.
fval &= ~(chattr.rm);
fval |= chattr.add;
if (!S_ISDIR(root->st.st_mode)) fval &= ~FS_DIRSYNC_FL;
if (ext2_setflag(fd, &(root->st), fval) < 0)
perror_msg("setting flags '%s'", fpath);
// (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);
}
// set file version
if (chattr.vflag && (ioctl(fd, FS_IOC_SETVERSION, &chattr.version)<0))
perror_msg("while setting version on '%s'", fpath);
free(fpath);
xclose(fd);
return (S_ISDIR(root->st.st_mode) && chattr.recursive) ? DIRTREE_RECURSE : 0;
return (FLAG(R) && S_ISDIR(root->st.st_mode)) ? DIRTREE_RECURSE : 0;
}
void chattr_main(void)
{
char **argv = toys.optargs;
memset(&chattr, 0, sizeof(struct _chattr));
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 (chattr.set && (chattr.add || chattr.rm))
if (TT.have_set && (TT.add || TT.rm))
error_exit("no '=' with '-' or '+'");
if (chattr.rm & chattr.add) error_exit("set/unset same flag");
if (!(chattr.add || chattr.rm || chattr.set || chattr.vflag))
error_exit("need '-v', '=', '-' 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);
toys.exitval = 0; //always set success at this point.
}
-128
View File
@@ -1,128 +0,0 @@
/*
* lspci - written by Isaac Dunham
USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN))
config LSPCI
bool "lspci"
default y
help
usage: lspci [-ekm]
List PCI devices.
-e Print all 6 digits in class
-k Print kernel driver
-m Machine parseable format
config LSPCI_TEXT
bool "lspci readable output"
depends on LSPCI
default y
help
usage: lspci [-n] [-i FILE ]
-n Numeric output (repeat for readable and numeric)
-i PCI ID database (default /usr/share/misc/pci.ids)
*/
#define FOR_lspci
#include "toys.h"
GLOBALS(
char *i;
long n;
FILE *db;
)
static int do_lspci(struct dirtree *new)
{
char *p = toybuf, *vendor = toybuf+9, *device = toybuf+18,
driver[256], *vbig = 0, *dbig = 0, **fields;
int dirfd;
if (!new->parent) return DIRTREE_RECURSE;
// Parse data out of /proc
if (-1 == (dirfd = openat(dirtree_parentfd(new), new->name, O_RDONLY)))
return 0;
*driver = 0;
if (toys.optflags & FLAG_k)
readlinkat0(dirfd, "driver", driver, sizeof(driver));
for (fields = (char*[]){"class", "vendor", "device", 0}; *fields; fields++) {
int fd, size = 6 + 2*((toys.optflags & FLAG_e) && p == toybuf);
*p = 0;
if (-1 == (fd = openat(dirfd, *fields, O_RDONLY))) {
close(dirfd);
return 0;
}
xreadall(fd, p, size);
memmove(p, p+2, size -= 2);
p[size] = 0;
close(fd);
p += 9;
}
close(dirfd);
// Lookup/display data from pci.ids?
if (CFG_LSPCI_TEXT && TT.db) {
if (TT.n != 1) {
char *s;
fseek(TT.db, 0, SEEK_SET);
while (!vbig || !dbig) {
s = p;
if (!fgets(s, sizeof(toybuf)-(p-toybuf)-1, TT.db)) break;
while (isspace(*s)) s++;
if (*s == '#') continue;
if (vbig && s == p) break;
if (strstart(&s, vbig ? device : vendor)) {
if (vbig) dbig = s+2;
else vbig = s+2;
s += strlen(s);
s[-1] = 0; // trim ending newline
p = s + 1;
}
}
}
if (TT.n > 1) {
printf((toys.optflags & FLAG_m)
? "%s, \"%s\" \"%s [%s]\" \"%s [%s]\""
: "%s Class %s: %s [%s] %s [%s]",
new->name+5, toybuf, vbig ? vbig : "", vendor,
dbig ? dbig : "", device);
goto driver;
}
}
printf((toys.optflags & FLAG_m) ? "%s \"%s\" \"%s\" \"%s\""
: "%s Class %s: %s:%s", new->name+5, toybuf,
vbig ? vbig : vendor, dbig ? dbig : device);
driver:
if (*driver)
printf((toys.optflags & FLAG_m) ? " \"%s\"" : " %s", basename(driver));
xputc('\n');
return 0;
}
void lspci_main(void)
{
if (CFG_LSPCI_TEXT && TT.n != 1) {
if (!TT.i) TT.i = "/usr/share/misc/pci.ids";
if (!(TT.db = fopen(TT.i, "r"))) perror_msg("%s", TT.i);
}
dirtree_read("/sys/bus/pci/devices", do_lspci);
}
+234 -22
View File
@@ -1,49 +1,261 @@
/* 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, NULL, TOYFLAG_USR|TOYFLAG_BIN))
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
usage: lsusb [-i]
List USB hosts/devices.
-i ID database (default /etc/usb.ids[.gz])
*/
#define FOR_lsusb
#include "toys.h"
static int list_device(struct dirtree *new)
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)
{
FILE *file;
char *name;
int busnum = 0, devnum = 0, pid = 0, vid = 0;
char *n1, *n2;
if (!new->parent) return DIRTREE_RECURSE;
if (new->name[0] == '.') return 0;
name = dirtree_path(new, 0);
sprintf(toybuf, "%s/uevent", name);
file = fopen(toybuf, "r");
if (file) {
int count = 0;
while (fgets(toybuf, sizeof(toybuf), file))
if (sscanf(toybuf, "BUSNUM=%u\n", &busnum)
|| sscanf(toybuf, "DEVNUM=%u\n", &devnum)
|| sscanf(toybuf, "PRODUCT=%x/%x/", &pid, &vid)) count++;
if (count == 3)
printf("Bus %03d Device %03d: ID %04x:%04x\n", busnum, devnum, pid, vid);
fclose(file);
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);
}
free(name);
return 0;
}
void lsusb_main(void)
{
dirtree_read("/sys/bus/usb/devices/", list_device);
// 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);
}
+41 -52
View File
@@ -12,45 +12,52 @@
*
// Note: flags go in same order (right to left) for shared subset
USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNSHARE(NEWTOY(unshare, "<1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
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
depends on TOYBOX_CONTAINER
help
usage: unshare [-imnpuUr] COMMAND...
Create new container namespace(s) for this process and its children, so
some attribute is not shared with the parent process.
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)
-r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user)
-u Host and domain names (--uts)
-U UIDs, GIDs, capabilities (--user)
A namespace allows a set of processes to have a different view of the
system than other sets of processes.
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"
depends on TOYBOX_CONTAINER
default y
help
usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND...
Run COMMAND in an existing (set of) namespace(s).
-t PID to take namespaces from (--target)
-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)
@@ -64,62 +71,41 @@ config NSENTER
#define FOR_nsenter
#include "toys.h"
#include <sys/syscall.h>
#include <linux/sched.h>
#define unshare(flags) syscall(SYS_unshare, flags)
#define setns(fd, nstype) syscall(SYS_setns, fd, nstype)
GLOBALS(
char *Uupnmi[6];
char *UupnmiC[7];
long t;
)
// Code that must run in unshare's flag context
#define CLEANUP_nsenter
#define FOR_unshare
#include <generated/flags.h>
static void write_ugid_map(char *map, unsigned eugid)
{
int bytes = sprintf(toybuf, "0 %u 1", eugid), fd = xopen(map, O_WRONLY);
int fd = xopen(map, O_WRONLY);
xwrite(fd, toybuf, bytes);
dprintf(fd, "0 %u 1", eugid);
xclose(fd);
}
static void handle_r(int euid, int egid)
{
int fd;
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);
}
static int test_r()
{
return toys.optflags & FLAG_r;
}
static int test_f()
{
return toys.optflags & FLAG_f;
}
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 CLEANUP_unshare
#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}, f = 0;
CLONE_NEWNS, CLONE_NEWIPC, CLONE_NEWCGROUP}, f = 0;
int i, fd;
// Create new namespace(s)?
@@ -131,10 +117,17 @@ void unshare_main(void)
if (test_r()) toys.optflags |= FLAG_U;
for (i = 0; i<ARRAY_LEN(flags); i++)
if (toys.optflags & (1<<i)) f |= flags[i];
if (test_a() || (toys.optflags & (1<<i))) f |= flags[i];
if (unshare(f)) perror_exit(0);
if (test_r()) handle_r(euid, egid);
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);
@@ -143,25 +136,21 @@ void unshare_main(void)
}
// Bind to existing namespace(s)?
} else if (CFG_NSENTER) {
char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc";
for (i = 0; i<ARRAY_LEN(flags); i++, nsnames += strlen(nsnames)+1) {
if (FLAG(a) || (toys.optflags & (1<<i))) {
char *filename = TT.UupnmiC[i];
for (i = 0; i<ARRAY_LEN(flags); i++) {
char *filename = TT.Uupnmi[i];
if (toys.optflags & (1<<i)) {
if (!filename || !*filename) {
if (!(toys.optflags & FLAG_t)) error_exit("need -t or =filename");
sprintf(toybuf, "/proc/%ld/ns/%s", TT.t, nsnames);
filename = toybuf;
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);
}
nsnames += strlen(nsnames)+1;
}
if ((toys.optflags & FLAG_p) && !(toys.optflags & FLAG_F)) {
if (FLAG(p) && !FLAG(F)) {
toys.exitval = xrun(toys.optargs);
return;
+54 -64
View File
@@ -4,19 +4,22 @@
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
*
* No Standard.
*
* TODO: two passes so we can auto-size the columns?
USE_PMAP(NEWTOY(pmap, "<1xq", TOYFLAG_USR|TOYFLAG_BIN))
USE_PMAP(NEWTOY(pmap, "<1pqx", TOYFLAG_USR|TOYFLAG_BIN))
config PMAP
bool "pmap"
default y
help
usage: pmap [-xq] [pids...]
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
-q Do not display some header/footer lines
*/
#define FOR_pmap
@@ -24,90 +27,77 @@ config PMAP
void pmap_main(void)
{
char **optargs;
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;
char *line, *oldline = 0, *name = 0,
*k = (toys.optflags & FLAG_x) ? "" : "K";
size_t len;
long long start, end, pss, tpss = 0, dirty, tdirty = 0, swap, tswap = 0,
total = 0;
int xx = 0;
snprintf(toybuf, sizeof(toybuf), "/proc/%u/cmdline", pid);
line = readfile(toybuf, 0, 0);
if (!line) error_msg("No %lu", (long)pid);
xprintf("%u: %s\n", (int)pid, line);
free(line);
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);
// Header
// Only use the more verbose file in -x mode
sprintf(toybuf, "/proc/%u/%smaps", pid,
(toys.optflags & FLAG_x) ? "s" : "");
// 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 %ld\n", (long)pid);
return;
error_msg("no %s", toybuf);
continue;
}
if ((toys.optflags & (FLAG_q|FLAG_x)) == FLAG_x)
xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n",
(int)(sizeof(long)*2)-4, ' ');
// Loop through mappings
for (;;) {
int off, count;
line = 0;
if (0 >= getline(&line, &len, fp)) break;
count = sscanf(line, "%llx-%llx %s %*s %*s %*s %n",
&start, &end, toybuf, &off);
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 (toybuf[3] == 'p') toybuf[3] = '-';
if (mode[3] == 'p') mode[3] = '-';
total += end = (end-start)/1024;
printf("%0*llx % *lld%s ", (int)(2*sizeof(long)), start,
6+!!(toys.optflags & FLAG_x), end, k);
if (toys.optflags & FLAG_x) {
oldline = line;
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 (0<sscanf(line, "Pss: %lld", &pss)
|| 0<sscanf(line, "Private_Dirty: %lld", &dirty)
|| 0<sscanf(line, "Swap: %lld", &swap)) xx++;
free(line);
if (xx<3) continue;
line = oldline;
name = basename(name);
xx = 0;
printf("% 7lld %7lld %7lld ", pss, dirty, swap);
tpss += pss;
tdirty += dirty;
tswap += swap;
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", toybuf, line[off]=='[' ? " " : "", name);
free(line);
line = 0;
xprintf("%s- %s%s", mode, *name == '[' ? " " : "",
FLAG(p) ? name : basename(name));
}
// Trailer
if (!(toys.optflags & FLAG_q)) {
int x = !!(toys.optflags & FLAG_x);
if (x) {
memset(toybuf, '-', 16);
xprintf("%.*s ------ ------ ------ ------\n", (int)(sizeof(long)*2),
toybuf);
if (!FLAG(q)) {
if (FLAG(x)) {
xprintf("---------------- ------ ------ ------ ------\n" +
((sizeof(long)==4)?8:0));
}
printf("total% *lld%s", 2*(int)(sizeof(long)+1)+x, total, k);
if (x) printf("% 8lld% 8lld% 8lld", tpss, tdirty, tswap);
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 -1
View File
@@ -1,6 +1,6 @@
/* pwgen.c - A password generator.
*
* Copyright 2020 Moritz Rhrich <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))
+35 -5
View File
@@ -2,27 +2,57 @@
*
* Copyright 2006 Rob Landley <rob@landley.net>
USE_SETSID(NEWTOY(setsid, "^<1t", TOYFLAG_USR|TOYFLAG_BIN))
USE_SETSID(NEWTOY(setsid, "^<1wc@d[!dc]", TOYFLAG_USR|TOYFLAG_BIN))
config SETSID
bool "setsid"
default y
help
usage: setsid [-t] command [args...]
usage: setsid [-cdw] command [args...]
Run process in a new session.
-t Grab tty (become foreground process, receiving keyboard signals)
-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)
{
while (setsid()<0) if (XVFORK()) _exit(0);
if (toys.optflags) {
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);
}
+14 -37
View File
@@ -33,8 +33,8 @@ config STAT
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 Fragment size
%S Best transfer size |%t FS type (hex) |%T FS type (driver name)
%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
@@ -141,52 +141,29 @@ static void print_statfs(char type) {
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') {
#ifdef __APPLE__
// TODO: move this into portability.c somehow, or just use this everywhere?
// (glibc and bionic will just re-do the statfs and return f_namelen.)
out('d', pathconf(TT.file, _PC_NAME_MAX));
#else
out('d', statfs->f_namelen);
#endif
} else if (type == 't') out('x', statfs->f_type);
else if (type == 'T') {
char *s = "unknown";
struct {unsigned num; char *name;} nn[] = {
{0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"},
{0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"},
{0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"},
{0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"},
{0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"},
{0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"},
{0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"},
{0x73717368, "squashfs"}
};
int i;
for (i=0; i<ARRAY_LEN(nn); i++)
if (nn[i].num == statfs->f_type) s = nn[i].name;
strout(s);
} else if (type == 'i') {
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->f_frsize);
else if (type == 'S') out('d', statfs->f_bsize);
} 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 flagf = FLAG(f), i;
int i;
char *format, *f;
if (FLAG(t)) format = flagf
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 = flagf
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"
@@ -203,8 +180,8 @@ void stat_main(void)
// stat the file or filesystem
TT.file = toys.optargs[i];
if (flagf && !statfs(TT.file, (void *)&TT.stat));
else if (flagf || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) {
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;
}
@@ -218,7 +195,7 @@ void stat_main(void)
TT.patlen = f-TT.pattern;
if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern);
if (*f == 'n') strout(TT.file);
else if (flagf) print_statfs(*f);
else if (FLAG(f)) print_statfs(*f);
else print_stat(*f);
}
}
+13 -19
View File
@@ -4,18 +4,18 @@
* 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 [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...]
usage: sysctl [-aeNnqw] [-p [FILE] | KEY[=VALUE]...]
Read/write system control data (under /proc/sys).
-a,A Show all values
-a Show all values
-e Don't warn about unknown keys
-N Don't print key values
-n Don't print key names
@@ -43,9 +43,8 @@ static void replace_char(char *str, char old, char new)
static void key_error(char *key)
{
if (errno == ENOENT) {
if (!(toys.optflags & FLAG_e)) error_msg("unknown key '%s'", key);
} else perror_msg("key '%s'", 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)
@@ -77,11 +76,11 @@ static int do_show_keys(struct dirtree *dt)
if (!data) key_error(key);
else {
// Print the parts that aren't switched off by flags.
if (!(toys.optflags & FLAG_n)) xprintf("%s", key);
if (!(toys.optflags & (FLAG_N|FLAG_n))) xprintf(" = ");
if (!FLAG(n)) xprintf("%s", key);
if (!FLAG(N) && !FLAG(n)) xprintf(" = ");
for (key = data+strlen(data); key > data && isspace(*--key); *key = 0);
if (!(toys.optflags & FLAG_N)) xprintf("%s", data);
if ((toys.optflags & (FLAG_N|FLAG_n)) != (FLAG_N|FLAG_n)) xputc('\n');
if (!FLAG(N)) xprintf("%s", data);
if (!FLAG(N) || !FLAG(n)) xputc('\n');
}
free(data);
@@ -96,16 +95,11 @@ static void process_key(char *key, char *value)
char *path;
if (!value) value = split_key(key);
if ((toys.optflags & FLAG_w) && !value) {
error_msg("'%s' not key=value", key);
return;
}
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) || (toys.optflags & FLAG_q)))) {
if (!(value && (!write_key(path, key, value) || FLAG(q)))) {
if (!access(path, R_OK)) dirtree_read(path, do_show_keys);
else key_error(key);
}
@@ -117,10 +111,10 @@ void sysctl_main()
char **args = 0;
// Display all keys
if (toys.optflags & FLAG_a) dirtree_read("/proc/sys", do_show_keys);
if (FLAG(a)) dirtree_read("/proc/sys", do_show_keys);
// read file
else if (toys.optflags & FLAG_p) {
else if (FLAG(p)) {
FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r");
size_t len;
+58 -41
View File
@@ -4,13 +4,13 @@
*
* No standard
USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
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 [-k DURATION] [-s SIGNAL] DURATION COMMAND...
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.
@@ -18,8 +18,9 @@ config TIMEOUT
DURATION can be a decimal fraction. An optional suffix can be "m"
(minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
-s Send specified signal (default TERM)
-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
@@ -31,60 +32,76 @@ config TIMEOUT
GLOBALS(
char *s, *k;
int nextsig;
pid_t pid;
struct timeval ktv;
struct itimerval itv;
struct pollfd pfd;
sigjmp_buf sj;
int fds[2], pid, rc;
)
static void handler(int i)
static void handler(int sig, siginfo_t *si)
{
if (FLAG(v))
fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
toys.exitval = (TT.nextsig==9) ? 137 : 124;
kill(TT.pid, TT.nextsig);
if (TT.k) {
TT.k = 0;
TT.nextsig = SIGKILL;
xsignal(SIGALRM, handler);
TT.itv.it_value = TT.ktv;
setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
}
TT.rc = si->si_status + ((si->si_code!=CLD_EXITED)<<7);
siglongjmp(TT.sj, 1);
}
// timeval inexplicably makes up a new type for microseconds, despite timespec's
// nanoseconds field (needing to store 1000* the range) using "long". Bravo.
void xparsetimeval(char *s, struct timeval *tv)
static long nantomil(struct timespec *ts)
{
long ll;
return ts->tv_sec*1000+ts->tv_nsec/1000000;
}
tv->tv_sec = xparsetime(s, 6, &ll);
tv->tv_usec = ll;
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;
xparsetimeval(*toys.optargs, &TT.itv.it_value);
if (TT.k) xparsetimeval(TT.k, &TT.ktv);
TT.nextsig = SIGTERM;
if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s)))
error_exit("bad -s: '%s'", TT.s);
if (!FLAG(foreground)) setpgid(0, 0);
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;
if (!(TT.pid = XVFORK())) xexec(toys.optargs+1);
else {
int status;
TT.pfd.events = POLLIN;
TT.fds[1] = -1;
if (sigsetjmp(TT.sj, 1)) goto done;
xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP|SA_SIGINFO);
xsignal(SIGALRM, handler);
setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
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);
status = xwaitpid(TT.pid);
if (FLAG(preserve_status) || !toys.exitval) toys.exitval = status;
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;
}
+3 -3
View File
@@ -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
View File
@@ -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.
+9 -9
View File
@@ -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
View File
@@ -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;
-768
View File
@@ -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
View File
@@ -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));
+265 -336
View File
@@ -6,30 +6,25 @@
*
* No Standard
*
* TODO: autodetect -net -host target dev -A (but complain)
* 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_BIN))
USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_SBIN))
config ROUTE
bool "route"
default n
help
usage: route [-ne] [-A [46]] [add|del TARGET [OPTIONS]]
usage: route [-ne] [-A [inet|inet6]] [add|del TARGET [OPTIONS]]
Display, add or delete network routes in the "Forwarding Information Base".
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
Routing means sending packets out a network interface to an address.
The kernel can tell where to send packets one hop away by examining each
interface's address and netmask, so the most common use of this command
is to identify a "gateway" that forwards other traffic.
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
@@ -48,16 +43,13 @@ config ROUTE
#define FOR_route
#include "toys.h"
#include <net/route.h>
#define _LINUX_SYSINFO_H // workaround for musl bug
#include <linux/rtnetlink.h>
GLOBALS(
char *family;
char *A;
)
#define DEFAULT_PREFIXLEN 128
#define INVALID_ADDR 0xffffffffUL
#define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0')
struct _arglist {
char *arg;
int action;
@@ -73,103 +65,176 @@ static struct _arglist arglist2[] = {
{ NULL, 0 }
};
// to get the host name from the given ip.
static int get_hostname(char *ipstr, struct sockaddr_in *sockin)
void xsend(int sockfd, void *buf, size_t len)
{
struct hostent *host;
sockin->sin_family = AF_INET;
sockin->sin_port = 0;
if (!strcmp(ipstr, "default")) {
sockin->sin_addr.s_addr = INADDR_ANY;
return 1;
}
if (inet_aton(ipstr, &sockin->sin_addr)) return 0;
if (!(host = gethostbyname(ipstr))) perror_exit("resolving '%s'", ipstr);
memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr));
return 0;
if (send(sockfd, buf, len, 0) != len) perror_exit("xsend");
}
// used to extract the address info from the given ip.
static int get_addrinfo(char *ip, struct sockaddr_in6 *sock_in6)
int xrecv(int sockfd, void *buf, size_t len)
{
struct addrinfo hints, *result;
int status = 0;
int msg_len = recv(sockfd, buf, len, 0);
if (msg_len < 0) perror_exit("xrecv");
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET6;
if ((status = getaddrinfo(ip, NULL, &hints, &result))) {
perror_msg("getaddrinfo: %s", gai_strerror(status));
return -1;
}
if (result) {
memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6));
freeaddrinfo(result);
}
return 0;
return msg_len;
}
static void get_flag_value(char *str, int flags)
void addAttr(struct nlmsghdr *nl, int maxlen, void *attr, int type, int len)
{
// RTF_* bits in order:
// UP, GATEWAY, HOST, REINSTATE, DYNAMIC, MODIFIED, DEFAULT, ADDRCONF, CACHE
int i = 0, mask = 0x105003f;
for (; mask; mask>>=1) if (mask&1) {
if (flags&(1<<i)) *str++ = "UGHRDMDAC"[i];
i++;
}
*str = 0;
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;
}
// extract inet4 route info from /proc/net/route file and display it.
static void display_routes(void)
{
unsigned long dest, gate, mask;
int flags, ref, use, metric, mss, win, irtt, items;
char iface[64] = {0,}, flag_val[10]; //there are 9 flags "UGHRDMDAC" for route.
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);
FILE *fp = xfopen("/proc/net/route", "r");
xprintf("Kernel IP routing table\n"
"Destination Gateway Genmask Flags %s Iface\n",
(toys.optflags & FLAG_e)? " MSS Window irtt" : "Metric Ref Use");
if (fscanf(fp, "%*[^\n]\n") < 0) perror_exit("fscanf"); //skip 1st line
while ((items = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest,
&gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt)) == 11)
{
char *destip = toybuf, *gateip = toybuf+32, *maskip = toybuf+64; //ip string 16
if (!(flags & RTF_UP)) continue; //skip down interfaces.
if (!dest && !(toys.optflags & FLAG_n)) strcpy( destip, "default");
else if (!inet_ntop(AF_INET, &dest, destip, 32)) perror_exit("inet");
if (!gate && !(toys.optflags & FLAG_n)) strcpy( gateip, "*");
else if (!inet_ntop(AF_INET, &gate, gateip, 32)) perror_exit("inet");
if (!inet_ntop(AF_INET, &mask, maskip, 32)) perror_exit("inet");
//Get flag Values
get_flag_value(flag_val, flags);
if (flags & RTF_REJECT) flag_val[0] = '!';
xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
if (toys.optflags & FLAG_e) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
}
if (items > 0 && feof(fp)) perror_exit("fscanf %d", items);
fclose(fp);
struct hostent *host = gethostbyaddr(a, a_len, f);
if (host) xstrncpy(dst, host->h_name, len);
}
/*
* find the given parameter in list like add/del/net/host.
* and if match found return the appropriate action.
*/
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;
@@ -184,271 +249,135 @@ static int get_action(char ***argv, struct _arglist *list)
return 0;
}
/*
* used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values.
* additionally set the flag values for reject, mod, dyn and reinstate.
*/
static void get_next_params(char **argv, struct rtentry *rt, char **netmask)
{
for (;*argv;argv++) {
if (!strcmp(*argv, "reject")) rt->rt_flags |= RTF_REJECT;
else if (!strcmp(*argv, "mod")) rt->rt_flags |= RTF_MODIFIED;
else if (!strcmp(*argv, "dyn")) rt->rt_flags |= RTF_DYNAMIC;
else if (!strcmp(*argv, "reinstate")) rt->rt_flags |= RTF_REINSTATE;
else {
if (!argv[1]) help_exit(0);
//set the metric field in the routing table.
if (!strcmp(*argv, "metric"))
rt->rt_metric = atolx_range(argv[1], 0, ULONG_MAX) + 1;
else if (!strcmp(*argv, "netmask")) {
//when adding a network route, the netmask to be used.
struct sockaddr sock;
unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
if (addr_mask) help_exit("dup netmask");
*netmask = argv[1];
get_hostname(*netmask, (struct sockaddr_in *) &sock);
rt->rt_genmask = sock;
} else if (!strcmp(*argv, "gw")) {
//route packets via a gateway.
if (!(rt->rt_flags & RTF_GATEWAY)) {
if (!get_hostname(argv[1], (struct sockaddr_in *) &rt->rt_gateway))
rt->rt_flags |= RTF_GATEWAY;
else perror_exit("gateway '%s' is a NETWORK", argv[1]);
} else help_exit("dup gw");
} else if (!strcmp(*argv, "mss")) {
//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")) {
//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")) {
rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX);
rt->rt_flags |= RTF_IRTT;
} else if (!strcmp(*argv, "dev") && !rt->rt_dev) rt->rt_dev = argv[1];
else help_exit("no '%s'", *argv);
argv++;
}
}
if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo";
}
// verify the netmask and conflict in netmask and route address.
static void verify_netmask(struct rtentry *rt, char *netmask)
{
unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr);
if (addr_mask) {
addr_mask = ~ntohl(addr_mask);
if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR)
perror_exit("conflicting netmask and host route");
if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask);
addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
if (addr_mask & router_addr) perror_exit("conflicting netmask and route address");
}
}
// add/del a route.
static void setroute(char **argv)
static void setroute(sa_family_t f, char **argv)
{
struct rtentry rt;
char *netmask, *targetip;
int is_net_or_host = 0, sokfd, arg2_action;
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);
memset(&rt, 0, sizeof(struct rtentry));
targetip = *argv++;
nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
netmask = strchr(targetip, '/');
if (netmask) {
*netmask++ = 0;
//used to verify the netmask and route conflict.
(((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr)
= htonl((1<<(32-atolx_range(netmask, 0, 32)))-1);
rt.rt_genmask.sa_family = AF_INET;
netmask = 0;
} else netmask = "default";
//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;
}
is_net_or_host = get_hostname(targetip, (void *)&rt.rt_dst);
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) is_net_or_host = arg2_action & 1;
rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST));
if (arg2_action == 2) rtMsg->rtm_scope = RT_SCOPE_HOST;
get_next_params(argv, &rt, (char **)&netmask);
verify_netmask(&rt, (char *)netmask);
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,};
if ((action == 1) && (rt.rt_flags & RTF_HOST))
(((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR;
sokfd = xsocket(AF_INET, SOCK_DGRAM, 0);
if (action == 1) xioctl(sokfd, SIOCADDRT, &rt);
else xioctl(sokfd, SIOCDELRT, &rt);
xclose(sokfd);
}
/*
* get prefix len (if any) and remove the prefix from target ip.
* if no prefix then set default prefix len.
*/
static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt)
{
unsigned long plen;
char *prefix = strchr(*tip, '/');
if (prefix) {
*prefix = '\0';
plen = atolx_range(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN);
} else plen = DEFAULT_PREFIXLEN;
rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP;
rt->rtmsg_dst_len = plen;
}
/*
* used to get the params like: metric, gw, dev and their values.
* additionally set the flag values for mod and dyn.
*/
static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name)
{
for (;*argv;argv++) {
if (!strcmp(*argv, "mod")) rt->rtmsg_flags |= RTF_MODIFIED;
else if (!strcmp(*argv, "dyn")) rt->rtmsg_flags |= RTF_DYNAMIC;
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]) help_exit(0);
if (!argv[1]) show_help(stdout, 1);
if (!strcmp(*argv, "metric"))
rt->rtmsg_metric = atolx_range(argv[1], 0, ULONG_MAX);
else if (!strcmp(*argv, "gw")) {
//route packets via a gateway.
if (!(rt->rtmsg_flags & RTF_GATEWAY)) {
if (!get_addrinfo(argv[1], (struct sockaddr_in6 *) &sock_in6)) {
memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr));
rt->rtmsg_flags |= RTF_GATEWAY;
} else perror_exit("resolving '%s'", argv[1]);
} else help_exit(0);
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")) {
if (!*dev_name) *dev_name = argv[1];
} else help_exit(0);
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++;
}
}
}
// add/del a route.
static void setroute_inet6(char **argv)
{
struct sockaddr_in6 sock_in6;
struct in6_rtmsg rt;
char *targetip, *dev_name = 0;
int sockfd, action = get_action(&argv, arglist1);
if (strcmp(tgtip, "default") != 0) {
char *prefix = strtok(0, "/");
if (!action || !*argv) help_exit(0);
memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
memset(&rt, 0, sizeof(struct in6_rtmsg));
targetip = *argv++;
if (!*argv) help_exit(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;
if (!strcmp(targetip, "default")) {
rt.rtmsg_flags = RTF_UP;
rt.rtmsg_dst_len = 0;
} else {
is_prefix_inet6((char **)&targetip, &rt);
if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6))
perror_exit("resolving '%s'", targetip);
}
rt.rtmsg_metric = 1; //default metric.
memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr));
get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name);
sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
if (dev_name) {
char ifre_buf[sizeof(struct ifreq)] = {0,};
struct ifreq *ifre = (struct ifreq*)ifre_buf;
xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ);
xioctl(sockfd, SIOGIFINDEX, ifre);
rt.rtmsg_ifindex = ifre->ifr_ifindex;
}
if (action == 1) xioctl(sockfd, SIOCADDRT, &rt);
else xioctl(sockfd, SIOCDELRT, &rt);
xsend(sockfd, nlMsg, nlMsg->nlmsg_len);
xclose(sockfd);
}
/*
* format the dest and src address in ipv6 format.
* e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62
*/
static void ipv6_addr_formating(char *ptr, char *addr)
{
int i = 0;
while (i <= IPV6_ADDR_LEN) {
if (!*ptr) {
if (i == IPV6_ADDR_LEN) {
addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' separated address.
break;
}
error_exit("IPv6 ip format error");
}
addr[i++] = *ptr++;
if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit
}
}
static void display_routes6(void)
{
char iface[16] = {0,}, ipv6_dest_addr[41];
char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN];
int prefixlen, metric, use, refcount, flag, items = 0;
unsigned char buf[sizeof(struct in6_addr)];
FILE *fp = xfopen("/proc/net/ipv6_route", "r");
xprintf("Kernel IPv6 routing table\n"
"%-43s%-40s Flags Metric Ref Use Iface\n", "Destination", "Next Hop");
while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%15s\n", ipv6_dest_addr+8,
&prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag,
iface)) == 8)
{
if (!(flag & RTF_UP)) continue; //skip down interfaces.
//ipv6_dest_addr+8: as the values are filled from the 8th location of the array.
ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr);
ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr);
get_flag_value(flag_val, flag);
if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet");
if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
sprintf(toybuf, "%s/%d", buf2, prefixlen);
if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet");
if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n",
toybuf, buf2, flag_val, metric, refcount, use, iface);
}
if ((items > 0) && feof(fp)) perror_exit("fscanf");
fclose(fp);
}
void route_main(void)
{
if (!TT.family) TT.family = "inet";
if (!*toys.optargs) {
if (!strcmp(TT.family, "inet")) display_routes();
else if (!strcmp(TT.family, "inet6")) display_routes6();
else help_exit(0);
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 (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs);
else setroute(toys.optargs);
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);
}
}
+32 -32
View File
@@ -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);
}
+125 -87
View File
@@ -13,35 +13,40 @@
* 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: 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, "(no-preserve-owner)(trailer)mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN))
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} [-v] [--verbose] [-F FILE] [--no-preserve-owner]
[ignored: -mdu -H newc]
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
-p DEST Copy-pass mode, copy stdin file list to directory DEST
-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)
--trailer Add legacy trailer (prevents concatenation)
--no-preserve-owner Don't set ownership during extract
*/
#define FOR_cpio
#include "toys.h"
GLOBALS(
char *F, *p, *H;
char *F, *H, *R;
)
// Read strings, tail padded to 4 byte alignment. Argument "align" is amount
@@ -72,20 +77,37 @@ static unsigned x8u(char *hex)
// 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 header");
if (inpos != outpos) error_exit("bad hex");
return val;
}
void cpio_main(void)
{
// Subtle bit: FLAG_o is 1 so we can just use it to select stdin/stdout.
int pipe, afd = toys.optflags & FLAG_o;
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 (TT.p) {
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
@@ -94,31 +116,44 @@ void cpio_main(void)
} else {
// child
toys.optflags |= FLAG_i;
xchdir(TT.p);
xchdir(*toys.optargs);
}
}
if (TT.F) {
int perm = (toys.optflags & FLAG_o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY;
int perm = FLAG(o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY;
afd = xcreate(TT.F, perm, 0644);
}
// read cpio archive
if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) {
char *name, *tofree, *data;
unsigned size, mode, uid, gid, timestamp;
int test = toys.optflags & FLAG_t, err = 0;
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;
// Read header and name.
if (!(size =readall(afd, toybuf, 110))) break;
if (size != 110 || memcmp(toybuf, "070701", 6)) error_exit("bad header");
tofree = name = strpad(afd, x8u(toybuf+94), 110);
if (!strcmp("TRAILER!!!", name)) {
if (CFG_TOYBOX_FREE) free(tofree);
break;
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++;
@@ -126,13 +161,16 @@ void cpio_main(void)
size = x8u(toybuf+54);
mode = x8u(toybuf+14);
uid = x8u(toybuf+22);
gid = x8u(toybuf+30);
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
if (toys.optflags & (FLAG_t|FLAG_v)) puts(name);
// (This output is unaffected by --quiet.)
if (FLAG(t) || FLAG(v)) puts(name);
if (!test && strrchr(name, '/') && mkpath(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++;
}
@@ -141,16 +179,25 @@ void cpio_main(void)
// properly aligned with next file.
if (S_ISDIR(mode)) {
if (!test) err = mkdir(name, mode);
} else if (S_ISLNK(mode)) {
data = strpad(afd, size, 0);
if (!test) err = symlink(data, name);
free(data);
// Can't get a filehandle to a symlink, so do special chown
if (!err && !geteuid() && !(toys.optflags & FLAG_no_preserve_owner))
err = lchown(name, uid, gid);
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_TRUNC|O_NOFOLLOW, 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
@@ -173,73 +220,71 @@ void cpio_main(void)
if (!test) {
// set owner, restore dropped suid bit
if (!geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) {
err = fchown(fd, uid, gid);
if (!err) err = fchmod(fd, mode);
}
if (reown) err = fchown(fd, uid, gid) && fchmod(fd, mode);
close(fd);
}
} else if (!test)
err = mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86)));
} 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)));
// Set ownership and timestamp.
// 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) {
// 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_ISREG(mode) && !S_ISLNK(mode) && !geteuid()
&& !(toys.optflags & FLAG_no_preserve_owner))
{
int fd = open(name, O_RDONLY|O_NOFOLLOW);
struct stat st;
struct timespec times[2];
if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT))
err = fchown(fd, uid, gid);
else err = 1;
close(fd);
}
// set timestamp
if (!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);
}
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);
free(tofree);
// 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 (lstat(name, &st) || (S_ISREG(st.st_mode)
&& st.st_size && (fd = open(name, O_RDONLY))<0))
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,
@@ -254,14 +299,9 @@ void cpio_main(void)
if (llen) xwrite(afd, &zero, 4-llen);
// Write out body for symlink or regular file
llen = st.st_size;
if (S_ISLNK(st.st_mode)) {
if (readlink(name, toybuf, sizeof(toybuf)-1) == llen)
xwrite(afd, toybuf, llen);
else perror_msg("readlink '%s'", name);
} else while (llen) {
if (link) xwrite(afd, link, st.st_size);
else for (llen = st.st_size; llen; llen -= nlen) {
nlen = llen > sizeof(toybuf) ? sizeof(toybuf) : llen;
llen -= nlen;
// 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);
@@ -270,17 +310,15 @@ void cpio_main(void)
llen = st.st_size & 3;
if (llen) xwrite(afd, &zero, 4-llen);
}
close(fd);
free(link);
xclose(fd);
}
free(name);
if (CFG_TOYBOX_FREE) free(name);
if (FLAG(trailer)) {
memset(toybuf, 0, sizeof(toybuf));
xwrite(afd, toybuf,
sprintf(toybuf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0)+4);
}
// 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 (TT.p) toys.exitval |= xpclose(pid, pipe);
if (FLAG(p) && pid) toys.exitval |= xpclose(pid, pipe);
}
+202 -166
View File
@@ -8,40 +8,39 @@
* TODO:
* -b backup
* -N ignore already applied
* -d chdir first
* -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
* -F fuzz (number, default 2)
* [file] which file to patch
* git syntax (rename, etc)
USE_PATCH(NEWTOY(patch, "(no-backup-if-mismatch)(dry-run)"USE_TOYBOX_DEBUG("x")"g#fulp#d:i:Rs(quiet)", TOYFLAG_USR|TOYFLAG_BIN))
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 [-d DIR] [-i file] [-p depth] [-Rlsu] [--dry-run]
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
-i Input file (default=stdin)
-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
-u Ignored (only handles "unified" diffs)
-v Verbose (-vv to see decisions)
--dry-run Don't change files, just confirm patch applies
This version of patch only handles unified diffs, and only modifies
a file when all hunks to that file apply. Patch prints failed hunks
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.
A file compared against /dev/null (or with a date <= the epoch) is
created/deleted as appropriate.
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
@@ -49,15 +48,33 @@ config PATCH
GLOBALS(
char *i, *d;
long p, g;
long v, p, g, F;
struct double_list *current_hunk;
long oldline, oldlen, newline, newlen;
long linenum;
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
@@ -67,20 +84,14 @@ GLOBALS(
static void do_line(void *data)
{
struct double_list *dlist = (struct double_list *)data;
struct double_list *dlist = data;
if (TT.state>1 && *dlist->data != TT.state) {
char *s = dlist->data+(TT.state>3);
int i = TT.state == 2 ? 2 : TT.fileout;
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");
xwrite(i, s, strlen(s));
xwrite(i, "\n", 1);
}
if (FLAG(x)) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data);
free(dlist->data);
free(data);
llist_free_double(data);
}
static void finish_oldfile(void)
@@ -102,7 +113,7 @@ static void fail_hunk(void)
TT.state = 2;
llist_traverse(TT.current_hunk, do_line);
TT.current_hunk = NULL;
TT.current_hunk = 0;
if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
TT.state = 0;
}
@@ -123,156 +134,168 @@ static int loosecmp(char *aa, char *bb)
// 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
// 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
// (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 = NULL, *check;
int matcheof, trailing = 0, reverse = FLAG(R), backwarn = 0;
int (*lcmp)(char *aa, char *bb);
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;
lcmp = FLAG(l) ? (void *)loosecmp : (void *)strcmp;
dlist_terminate(TT.current_hunk);
if (TT.v>1) printf("START %d\n", TT.hunknum);
// Match EOF if there aren't as many ending context lines as beginning
for (plist = TT.current_hunk; plist; plist = plist->next) {
if (plist->data[0]==' ') trailing++;
else trailing = 0;
if (FLAG(x)) fprintf(stderr, "HUNK:%s\n", plist->data);
}
matcheof = !trailing || trailing < TT.context;
dlist_terminate(TT.current_hunk);
for (fuzz = 0, plist = TT.current_hunk; plist; plist = plist->next) {
char *s = plist->data, c = *s;
if (FLAG(x)) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
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 all lines to be removed until we've found the end of a
// complete hunk.
// lines and lines to be removed until we've found end of complete hunk.
plist = TT.current_hunk;
buf = NULL;
fuzz = 0;
for (;;) {
char *data = get_line(TT.filein);
TT.linenum++;
// Figure out which line of hunk to compare with next. (Skip lines
// of the hunk we'd be adding.)
while (plist && *plist->data == "+-"[reverse]) {
if (data && !lcmp(data, plist->data+1))
if (!backwarn) backwarn = TT.linenum;
plist = plist->next;
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)");
// Is this EOF?
if (!data) {
if (FLAG(x)) fprintf(stderr, "INEOF\n");
// Does this hunk need to match EOF?
if (!plist && matcheof) break;
if (backwarn && !FLAG(s))
fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
TT.hunknum, TT.linenum);
// File ended before we found a place for this hunk.
fail_hunk();
goto done;
} else if (FLAG(x)) fprintf(stderr, "IN: %s\n", data);
check = dlist_add(&buf, data);
// Compare this line with next expected line of hunk.
// A match can fail because the next line doesn't match, or because
// we hit the end of a hunk that needed EOF, and this isn't EOF.
// If match failed, flush first line of buffered data and
// recheck buffered data for a new match until we find one or run
// out of buffer.
// 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 (;;) {
if (!plist || lcmp(check->data, plist->data+1)) {
// Match failed. Write out first line of buffered data and
// recheck remaining buffered data for a new match.
if (FLAG(x)) {
int bug = 0;
if (!plist) fprintf(stderr, "NULL plist\n");
else {
while (plist->data[bug] == check->data[bug]) bug++;
fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug],
check->data[bug], plist->data);
}
// 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;
}
// If this hunk must match start of file, fail if it didn't.
if (!TT.context || trailing>TT.context) {
fail_hunk();
goto done;
}
TT.state = 3;
do_line(check = dlist_pop(&buf));
plist = TT.current_hunk;
// If we've reached the end of the buffer without confirming a
// match, read more lines.
if (!buf) break;
check = buf;
} else {
if (FLAG(x)) fprintf(stderr, "MAYBE: %s\n", plist->data);
// This line matches. Advance plist, detect successful match.
plist = plist->next;
if (!plist && !matcheof) goto out;
check = check->next;
if (check == buf) break;
}
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 = "-+"[reverse];
llist_traverse(TT.current_hunk, do_line);
TT.current_hunk = NULL;
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:
if (buf) {
dlist_terminate(buf);
llist_traverse(buf, do_line);
}
llist_traverse(buf, do_line);
return TT.state;
}
// read a filename that has been quoted or escaped
char *unquote_file(char *filename) {
char *s = filename, *result, *t, *u;
int quote = 0, ch;
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
result = xmalloc(strlen(filename) + 1);
t = result;
if (*s == '"') {
s++;
quote = 1;
}
for (; *s && !(quote && *s == '"' && !s[1]); s++) {
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 (quote && *s == '\\' && s[1]) {
if (s[1] >= '0' && s[1] < '8') {
*t++ = strtoul(s + 1, &u, 8);
s = u - 1;
} else {
ch = unescape(s[1]);
*t++ = ch ? ch : s[1];
s++;
}
} else *t++ = *s;
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 result;
return newfile;
}
// Read a patch file and find hunks, opening/creating/deleting files.
@@ -285,28 +308,28 @@ char *unquote_file(char *filename) {
void patch_main(void)
{
int reverse = FLAG(R), state = 0, patchlinenum = 0, strip = 0;
char *oldname = NULL, *newname = NULL;
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
// Loop through the lines in the patch file (-i or stdin) collecting hunks
for (;;) {
char *patchline;
patchline = get_line(TT.filepatch);
if (!patchline) break;
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 (patchline[len-1] == '\r') {
if (len && patchline[len-1] == '\r') {
if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n");
strip = 1;
patchline[len-1]=0;
patchline[len-1] = 0;
}
}
if (!*patchline) {
@@ -317,7 +340,7 @@ void patch_main(void)
// Are we assembling a hunk?
if (state >= 2) {
if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
dlist_add(&TT.current_hunk, patchline);
dlist_add((void *)&TT.current_hunk, patchline);
if (*patchline != '+') TT.oldlen--;
if (*patchline != '-') TT.newlen--;
@@ -328,11 +351,11 @@ void patch_main(void)
// If we've consumed all expected hunk lines, apply the hunk.
if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
continue;
} else {
dlist_terminate(TT.current_hunk);
fail_hunk();
state = 0;
}
dlist_terminate(TT.current_hunk);
fail_hunk();
state = 0;
continue;
}
@@ -349,7 +372,7 @@ void patch_main(void)
free(*name);
finish_oldfile();
// Trim date from end of filename (if any). We don't care.
// 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");
@@ -388,13 +411,24 @@ void patch_main(void)
oldsum = TT.oldline + TT.oldlen;
newsum = TT.newline + TT.newlen;
name = reverse ? oldname : newname;
// 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") || !(reverse ? oldsum : newsum))
{
name = reverse ? newname : oldname;
if (!strcmp(name, "/dev/null") || !(FLAG(R) ? oldsum : newsum)) {
name = FLAG(R) ? newname : oldname;
del++;
}
@@ -409,7 +443,7 @@ void patch_main(void)
if (del) {
if (!FLAG(s)) printf("removing %s\n", name);
xunlink(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) {
@@ -417,16 +451,18 @@ void patch_main(void)
if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK))
{
if (!FLAG(s)) printf("creating %s\n", name);
if (mkpath(name)) perror_exit("mkpath %s", name);
TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
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 = 0;
TT.hunknum = 0;
TT.linenum = TT.outnum = TT.hunknum = 0;
}
}
+6 -6
View File
@@ -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;
}
}
+77 -81
View File
@@ -7,40 +7,42 @@
* Deviations from POSIX: Lots.
* We invented -x
USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
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 [-runbcdfiMsz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE]
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 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
-M Month sort (jan, feb, etc)
-x Hexadecimal numerical sort
-s Skip fallback sort (only sort with keys)
-z Zero (null) terminated lines
-k Sort by "key" (see below)
-t Use a key separator other than whitespace
-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. 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.
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
@@ -61,9 +63,8 @@ GLOBALS(
char *o, *T, S;
void *key_list;
int linecount;
char **lines;
char *name;
unsigned linecount;
char **lines, *name;
)
// The sort types are n, g, and M.
@@ -73,50 +74,57 @@ GLOBALS(
#define FLAG_bb (1<<31) // Ignore trailing blanks
struct sort_key
{
struct sort_key {
struct sort_key *next_key; // linked list
unsigned range[4]; // start word, start char, end word, end char
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)
{
int start=0, end, len, i, j;
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 (!key->range[2*j]) end=len;
if (!(k = key->range[2*j])) end=len;
// Loop through fields
else {
end=0;
for (i=1; i < key->range[2*j]+j; i++) {
// 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;
}
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;
if (!j) start = end;
}
// Key with explicit separator starts after the separator
@@ -128,17 +136,17 @@ static char *get_key_data(char *str, struct sort_key *key, int flags)
if (flags&FLAG_bb) while (end>start && isspace(str[end-1])) end--;
// Handle offsets on start and end
if (key->range[3]) {
if (key->range[3]>0) {
end += key->range[3]-1;
if (end>len) end=len;
}
if (key->range[1]) {
if (key->range[1]>0) {
start += key->range[1]-1;
if (start>len) start=len;
}
// Make the copy
if (end<start) end=start;
if (end<start) end = start;
str = xstrndup(str+start, end-start);
// Handle -d
@@ -162,8 +170,7 @@ static char *get_key_data(char *str, struct sort_key *key, int flags)
static struct sort_key *add_key(void)
{
void **stupid_compiler = &TT.key_list;
struct sort_key **pkey = (struct sort_key **)stupid_compiler;
struct sort_key **pkey = (struct sort_key **)&TT.key_list;
while (*pkey) pkey = &((*pkey)->next_key);
return *pkey = xzalloc(sizeof(struct sort_key));
@@ -195,7 +202,7 @@ static int compare_values(int flags, char *x, char *y)
}
if (yinf) return dy<0 ? 1 : -1;
return dx>dy ? 1 : (dx<dy ? -1 : 0);
return dx<dy ? -1 : dx>dy;
} else if (flags & FLAG_M) {
struct tm thyme;
int dx;
@@ -228,14 +235,11 @@ static int compare_values(int flags, char *x, char *y)
}
}
return *x ? !!*y : -1;
// This is actually an integer sort with decimals sorted by string fallback.
} else if (flags & FLAG_n) {
// Full floating point version of -n
if (CFG_SORT_FLOAT) {
double dx = atof(x), dy = atof(y);
long long dx = atoll(x), dy = atoll(y);
return dx>dy ? 1 : (dx<dy ? -1 : 0);
// Integer version of -n for tiny systems
} else return atoi(x)-atoi(y);
return dx<dy ? -1 : dx>dy;
// Ascii sort
} else return ((flags&FLAG_f) ? strcasecmp : strcmp)(x, y);
@@ -248,8 +252,8 @@ static int compare_keys(const void *xarg, const void *yarg)
char *x, *y, *xx = *(char **)xarg, *yy = *(char **)yarg;
struct sort_key *key;
for (key=(struct sort_key *)TT.key_list; !retval && key; key = key->next_key){
flags = key->flags ? key->flags : toys.optflags;
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
@@ -279,21 +283,22 @@ static int compare_keys(const void *xarg, const void *yarg)
// Read each line from file, appending to a big array.
static void sort_lines(char **pline, long len)
{
char * line;
char *line;
if (!pline) return;
line = *pline;
if (!FLAG(z) && len && line[len-1]=='\n') line[--len] = 0;
*pline = NULL;
*pline = 0;
// handle -c here so we don't allocate more memory than necessary.
if (FLAG(c)) {
int j = FLAG(u) ? -1 : 0;
if (TT.lines && compare_keys((void *)&TT.lines, &line)>j)
error_exit("%s: Check line %d\n", TT.name, TT.linecount);
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 = (char **)line;
TT.lines = (void *)line;
} else {
if (!(TT.linecount&63))
TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64));
@@ -306,12 +311,14 @@ static void sort_lines(char **pline, long len)
static void sort_read(int fd, char *name)
{
TT.name = name;
do_lines(fd, FLAG(z) ? '\0' : '\n', sort_lines);
do_lines(fd, '\n'*!FLAG(z), sort_lines);
}
void sort_main(void)
{
int idx, fd = 1;
int idx, jdx, fd = 1;
if (FLAG(u)) toys.optflags |= FLAG_s;
// Parse -k sort keys.
if (TT.k) {
@@ -319,42 +326,34 @@ void sort_main(void)
for (arg = TT.k; arg; arg = arg->next) {
struct sort_key *key = add_key();
char *temp;
char *temp, *temp2, *optlist;
int flag;
idx = 0;
temp = arg->arg;
while (*temp) {
// Start of range
key->range[2*idx] = (unsigned)strtol(temp, &temp, 10);
if (*temp=='.')
key->range[(2*idx)+1] = (unsigned)strtol(temp+1, &temp, 10);
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++) {
char *temp2, *optlist;
// Note that a second comma becomes an "Unknown key" error.
// 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));
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)))
{
toys.exitval = 2;
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;
@@ -374,18 +373,15 @@ void sort_main(void)
// The compare (-c) logic was handled in sort_read(),
// so if we got here, we're done.
if (FLAG(c)) goto exit_now;
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)) {
int jdx;
for (jdx=0, idx=1; idx<TT.linecount; idx++) {
if (!compare_keys(&TT.lines[jdx], &TT.lines[idx]))
free(TT.lines[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;
-258
View File
@@ -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);
}
}
-58
View File
@@ -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));
}
-337
View File
@@ -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);
}
-261
View File
@@ -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);
}
-166
View File
@@ -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();
}
-103
View File
@@ -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);
}
-58
View File
@@ -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);
}
-204
View File
@@ -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');
}
}
-149
View File
@@ -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);
}
}
-107
View File
@@ -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;
}
-383
View File
@@ -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);
}
}
-324
View File
@@ -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);
}
-161
View File
@@ -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();
}
-485
View File
@@ -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);
}
}
-409
View File
@@ -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);
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB