!286 merge master into master

update0.8.12

Created-by: gong-guilin
Commit-by: gong-guilin
Merged-by: openharmony_ci
Description: ### 相关的Issue
https://gitcode.com/openharmony/third_party_toybox/issues/252

### 原因(目的、解决的问题等)


### 描述(做了什么,变更了什么)


### 测试用例(新增、改动、可能影响的功能)


### 是否需要同步至release(如:3.0LTS ... )分支?

必须选择一项(在MarkDown模式下用[x]替换[ ]即可勾选对应选项):
- [ ] 是,需要同步的分支:
- [ ] 否

理由:




See merge request: openharmony/third_party_toybox!286
This commit is contained in:
openharmony_ci
2026-05-27 10:34:15 +08:00
44 changed files with 576 additions and 4387 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
+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))
+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));
+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);
}
+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;
}
}
-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