mirror of
https://github.com/openharmony/third_party_toybox.git
synced 2026-07-01 21:34:01 -04:00
update toybox patch2
Signed-off-by: gong-guilin <gongguilin@h-partners.com>
This commit is contained in:
@@ -63,7 +63,6 @@ if (defined(ohos_lite)) {
|
||||
"toys/other/bzcat.c",
|
||||
"toys/other/chroot.c",
|
||||
"toys/other/chrt.c",
|
||||
"toys/other/chvt.c",
|
||||
"toys/other/clear.c",
|
||||
"toys/other/count.c",
|
||||
"toys/other/devmem.c",
|
||||
@@ -100,6 +99,7 @@ if (defined(ohos_lite)) {
|
||||
"toys/other/nbd_client.c",
|
||||
"toys/other/nsenter.c",
|
||||
"toys/other/oneit.c",
|
||||
"toys/other/openvt.c",
|
||||
"toys/other/partprobe.c",
|
||||
"toys/other/pivot_root.c",
|
||||
"toys/other/pmap.c",
|
||||
@@ -558,7 +558,6 @@ if (defined(ohos_lite)) {
|
||||
"toys/other/bzcat.c",
|
||||
"toys/other/chroot.c",
|
||||
"toys/other/chrt.c",
|
||||
"toys/other/chvt.c",
|
||||
"toys/other/clear.c",
|
||||
"toys/other/count.c",
|
||||
"toys/other/devmem.c",
|
||||
@@ -595,6 +594,7 @@ if (defined(ohos_lite)) {
|
||||
"toys/other/nbd_client.c",
|
||||
"toys/other/nsenter.c",
|
||||
"toys/other/oneit.c",
|
||||
"toys/other/openvt.c",
|
||||
"toys/other/partprobe.c",
|
||||
"toys/other/pivot_root.c",
|
||||
"toys/other/pmap.c",
|
||||
|
||||
+16
-17
@@ -512,18 +512,16 @@
|
||||
#undef FLAG_b
|
||||
#endif
|
||||
|
||||
// date d:r:u[!dr] d:r:u[!dr]
|
||||
// date >1d:D:I(iso-8601):;r:s:u(utc)[!dr] >1d:D:I(iso-8601):;r:s:u(utc)[!dr]
|
||||
#undef OPTSTR_date
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
#define OPTSTR_date "d:r:u[!dr]"
|
||||
#else
|
||||
#define OPTSTR_date "d:D:r:u[!dr]"
|
||||
#endif
|
||||
#define OPTSTR_date ">1d:D:I(iso-8601):;r:s:u(utc)[!dr]"
|
||||
#ifdef CLEANUP_date
|
||||
#undef CLEANUP_date
|
||||
#undef FOR_date
|
||||
#undef FLAG_u
|
||||
#undef FLAG_s
|
||||
#undef FLAG_r
|
||||
#undef FLAG_I
|
||||
#undef FLAG_D
|
||||
#undef FLAG_d
|
||||
#endif
|
||||
@@ -3893,9 +3891,9 @@
|
||||
#undef FLAG_a
|
||||
#endif
|
||||
|
||||
// xargs ^E:P#optrn#<1(max-args)s#0[!0E] ^E:P#optrn#<1(max-args)s#0[!0E]
|
||||
// xargs ^E:P#<0(null)=1optr(no-run-if-empty)n#<1(max-args)s#0[!0E] ^E:P#<0(null)=1optr(no-run-if-empty)n#<1(max-args)s#0[!0E]
|
||||
#undef OPTSTR_xargs
|
||||
#define OPTSTR_xargs "^E:P#optrn#<1(max-args)s#0[!0E]"
|
||||
#define OPTSTR_xargs "^E:P#<0(null)=1optr(no-run-if-empty)n#<1(max-args)s#0[!0E]"
|
||||
#ifdef CLEANUP_xargs
|
||||
#undef CLEANUP_xargs
|
||||
#undef FOR_xargs
|
||||
@@ -7507,18 +7505,19 @@
|
||||
#endif
|
||||
|
||||
#ifdef FOR_xargs
|
||||
#define CLEANUP_xargs
|
||||
#ifndef TT
|
||||
#define TT this.xargs
|
||||
#endif
|
||||
#define FLAG_0 (1<<0)
|
||||
#define FLAG_s (1<<1)
|
||||
#define FLAG_n (1<<2)
|
||||
#define FLAG_r (1<<3)
|
||||
#define FLAG_t (1<<4)
|
||||
#define FLAG_p (1<<5)
|
||||
#define FLAG_o (1<<6)
|
||||
#define FLAG_P (1<<7)
|
||||
#define FLAG_E (1<<8)
|
||||
#define FLAG_0 (1LL<<0)
|
||||
#define FLAG_s (1LL<<1)
|
||||
#define FLAG_n (1LL<<2)
|
||||
#define FLAG_r (1LL<<3)
|
||||
#define FLAG_t (1LL<<4)
|
||||
#define FLAG_p (1LL<<5)
|
||||
#define FLAG_o (1LL<<6)
|
||||
#define FLAG_P (1LL<<7)
|
||||
#define FLAG_E (1LL<<8)
|
||||
#endif
|
||||
|
||||
#ifdef FOR_xxd
|
||||
|
||||
+3
-3
@@ -1081,9 +1081,9 @@ struct cut_data {
|
||||
|
||||
struct date_data {
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
char *r, *d;
|
||||
char *s, *r, *I, *d;
|
||||
#else
|
||||
char *r, *D, *d;
|
||||
char *s, *r, *I, *D, *d;
|
||||
#endif
|
||||
unsigned nano;
|
||||
};
|
||||
@@ -1400,7 +1400,7 @@ struct xargs_data {
|
||||
long s, n, P;
|
||||
char *E;
|
||||
|
||||
long entries, bytes;
|
||||
long entries, bytes, np;
|
||||
char delim;
|
||||
FILE *tty;
|
||||
};
|
||||
|
||||
+4
-4
@@ -268,7 +268,7 @@
|
||||
|
||||
#define HELP_mkswap "usage: mkswap [-L LABEL] DEVICE\n\nSet up a Linux swap area on a device or file."
|
||||
|
||||
#define HELP_mkpasswd "usage: mkpasswd [-P FD] [-m TYPE] [-S SALT] [PASSWORD] [SALT]\n\nEncrypt PASSWORD using crypt(3), with either random or provided SALT.\n\n-P FD Read password from file descriptor FD\n-m TYPE Encryption method (des, md5, sha256, or sha512; default is des)"
|
||||
#define HELP_mkpasswd "usage: mkpasswd [-P FD] [-m TYPE] [-S SALT] [PASSWORD] [SALT]\n\nEncrypt PASSWORD using crypt(3), with either random or provided SALT.\n\n-P FD Read password from file descriptor FD\n-m TYPE Encryption method (des, md5, sha256, or sha512; default is des)\n-S SALT"
|
||||
|
||||
#define HELP_mix "usage: mix [-d DEV] [-c CHANNEL] [-l VOL] [-r RIGHT]\n\nList OSS sound channels (module snd-mixer-oss), or set volume(s).\n\n-c CHANNEL Set/show volume of CHANNEL (default first channel found)\n-d DEV Device node (default /dev/mixer)\n-l VOL Volume level\n-r RIGHT Volume of right stereo channel (with -r, -l sets left volume)"
|
||||
|
||||
@@ -545,7 +545,7 @@
|
||||
|
||||
#define HELP_arp "usage: arp\n[-vn] [-H HWTYPE] [-i IF] -a [HOSTNAME]\n[-v] [-i IF] -d HOSTNAME [pub]\n[-v] [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [temp]\n[-v] [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [netmask MASK] pub\n[-v] [-H HWTYPE] [-i IF] -Ds HOSTNAME IFACE [netmask MASK] pub\n\nManipulate ARP cache.\n\n-a Display (all) hosts\n-s Set new ARP entry\n-d Delete a specified entry\n-v Verbose\n-n Don't resolve names\n-i IFACE Network interface\n-D Read <hwaddr> from given device\n-A,-p AF Protocol family\n-H HWTYPE Hardware address type"
|
||||
|
||||
#define HELP_xargs "usage: xargs [-0prt] [-s NUM] [-n NUM] [-E STR] COMMAND...\n\nRun command line one or more times, appending arguments from stdin.\n\nIf COMMAND exits with 255, don't launch another even if arguments remain.\n\n-0 Each argument is NULL terminated, no whitespace or quote processing\n-E Stop at line matching string\n-n Max number of arguments per command\n-o Open tty for COMMAND's stdin (default /dev/null)\n-p Prompt for y/n from tty before running each command\n-r Don't run command with empty input (otherwise always run command once)\n-s Size in bytes per command line\n-t Trace, print command line to stderr"
|
||||
#define HELP_xargs "usage: xargs [-0Pprt] [-snE STR] COMMAND...\n\nRun command line one or more times, appending arguments from stdin.\n\nIf COMMAND exits with 255, don't launch another even if arguments remain.\n\n-0 Each argument is NULL terminated, no whitespace or quote processing\n-E Stop at line matching string\n-n Max number of arguments per command\n-o Open tty for COMMAND's stdin (default /dev/null)\n-P Parallel processes (default 1)\n-p Prompt for y/n from tty before running each command\n-r Don't run with empty input (otherwise always run command once)\n-s Size in bytes per command line\n-t Trace, print command line to stderr"
|
||||
|
||||
#define HELP_who "usage: who\n\nPrint information about logged in users."
|
||||
|
||||
@@ -698,9 +698,9 @@
|
||||
#define HELP_dd "usage: dd [if|of=FILE] [ibs|obs|bs|count|seek|skip=N] [conv|status|iflag|oflag=FLAG[,FLAG...]]\n\nCopy/convert blocks of data from input to output, with the following\nkeyword=value modifiers (and their default values):\n\nif=FILE Read FILE (stdin) of=FILE Write to FILE (stdout)\n bs=N Block size in bytes (512) count=N Stop after copying N blocks (all)\n ibs=N Input block size (bs=) obs=N Output block size (bs=)\n skip=N Skip N input blocks (0) seek=N Skip N output blocks (0)\n\nEach =N value accepts the normal unit suffixes (see toybox --help).\n\nThese modifiers take a comma separated list of potential options:\n\niflag=count_bytes,skip_bytes count=N or skip=N is in bytes not blocks\noflag=seek_bytes,append seek=N is in bytes, append output to file\nstatus=noxfer,none don't show transfer rate, no summary info\nconv=\n notrunc Don't truncate output noerror Continue after read errors\n sync Zero pad short reads fsync Flush output to disk at end\n sparse Seek past zeroed output excl Fail if output file exists\n nocreat Fail if of=FILE missing"
|
||||
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
#define HELP_date "usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [SET]\n\nSet/get the current date/time. With no SET shows the current date.\n\n-d Show DATE instead of current time (convert date format)\n-r Use modification time of FILE instead of current date\n-u Use UTC instead of current timezone\n\nSupported input formats:\n\nMMDDhhmm[[CC]YY][.ss] POSIX\n@UNIXTIME[.FRACTION] seconds since midnight 1970-01-01\nYYYY-MM-DD [hh:mm[:ss]] ISO 8601\nhh:mm[:ss] 24-hour time today\n\nAll input formats can be preceded by TZ=\"id\" to set the input time zone\nseparately from the output time zone. Otherwise $TZ sets both.\n\n+FORMAT specifies display format string using strftime(3) syntax:\n\n%% literal % %n newline %t tab\n%S seconds (00-60) %M minute (00-59) %m month (01-12)\n%H hour (0-23) %I hour (01-12) %p AM/PM\n%y short year (00-99) %Y year %C century\n%a short weekday name %A weekday name %u day of week (1-7, 1=mon)\n%b short month name %B month name %Z timezone name\n%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)\n%N nanosec (output only)\n\n%U Week of year (0-53 start sunday) %W Week of year (0-53 start monday)\n%V Week of year (1-53 start monday, week < 4 days not part of this year)\n\n%D = \"%m/%d/%y\" %r = \"%I : %M : %S %p\" %T = \"%H:%M:%S\" %h = \"%b\"\n%x locale date %X locale time %c locale date/time"
|
||||
#define HELP_date "usage: date [-u] [-I RES] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]\n\nSet/get the current date/time. With no SET shows the current date.\n\n-d Show DATE instead of current time (convert date format)\n-D +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])\n-I RES ISO 8601 with RESolution d=date/h=hours/m=minutes/s=seconds/n=ns\n-r Use modification time of FILE instead of current date\n-s DATE Set the system clock to DATE.\n-u Use UTC instead of current timezone\n\nSupported input formats:\n\nMMDDhhmm[[CC]YY][.ss] POSIX\n@UNIXTIME[.FRACTION] seconds since midnight 1970-01-01\nYYYY-MM-DD [hh:mm[:ss]] ISO 8601\nhh:mm[:ss] 24-hour time today\n\nAll input formats can be followed by fractional seconds, and/or a UTC\noffset such as -0800.\n\nAll input formats can be preceded by TZ=\"id\" to set the input time zone\nseparately from the output time zone. Otherwise $TZ sets both.\n\n+FORMAT specifies display format string using strftime(3) syntax:\n\n%% literal % %n newline %t tab\n%S seconds (00-60) %M minute (00-59) %m month (01-12)\n%H hour (0-23) %I hour (01-12) %p AM/PM\n%y short year (00-99) %Y year %C century\n%a short weekday name %A weekday name %u day of week (1-7, 1=mon)\n%b short month name %B month name %Z timezone name\n%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)\n%N nanosec (output only)\n\n%U Week of year (0-53 start Sunday) %W Week of year (0-53 start Monday)\n%V Week of year (1-53 start Monday, week < 4 days not part of this year)\n\n%F \"%Y-%m-%d\" %R \"%H:%M\" %T \"%H:%M:%S\" %z timezone (-0800)\n%D \"%m/%d/%y\" %r \"%I:%M:%S %p\" %h \"%b\" %:z timezone (-08:00)\n%x locale date %X locale time %c locale date/time %s unix epoch time"
|
||||
#else
|
||||
#define HELP_date "usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]\n\nSet/get the current date/time. With no SET shows the current date.\n\n-d Show DATE instead of current time (convert date format)\n-D +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])\n-r Use modification time of FILE instead of current date\n-u Use UTC instead of current timezone\n\nSupported input formats:\n\nMMDDhhmm[[CC]YY][.ss] POSIX\n@UNIXTIME[.FRACTION] seconds since midnight 1970-01-01\nYYYY-MM-DD [hh:mm[:ss]] ISO 8601\nhh:mm[:ss] 24-hour time today\n\nAll input formats can be preceded by TZ=\"id\" to set the input time zone\nseparately from the output time zone. Otherwise $TZ sets both.\n\n+FORMAT specifies display format string using strftime(3) syntax:\n\n%% literal % %n newline %t tab\n%S seconds (00-60) %M minute (00-59) %m month (01-12)\n%H hour (0-23) %I hour (01-12) %p AM/PM\n%y short year (00-99) %Y year %C century\n%a short weekday name %A weekday name %u day of week (1-7, 1=mon)\n%b short month name %B month name %Z timezone name\n%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)\n%N nanosec (output only)\n\n%U Week of year (0-53 start sunday) %W Week of year (0-53 start monday)\n%V Week of year (1-53 start monday, week < 4 days not part of this year)\n\n%D = \"%m/%d/%y\" %r = \"%I : %M : %S %p\" %T = \"%H:%M:%S\" %h = \"%b\"\n%x locale date %X locale time %c locale date/time"
|
||||
#define HELP_date "usage: date [-u] [-I RES] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [SET]\n\nSet/get the current date/time. With no SET shows the current date.\n\n-d Show DATE instead of current time (convert date format)\n-I RES ISO 8601 with RESolution d=date/h=hours/m=minutes/s=seconds/n=ns\n-r Use modification time of FILE instead of current date\n-s DATE Set the system clock to DATE.\n-u Use UTC instead of current timezone\n\nSupported input formats:\n\nMMDDhhmm[[CC]YY][.ss] POSIX\n@UNIXTIME[.FRACTION] seconds since midnight 1970-01-01\nYYYY-MM-DD [hh:mm[:ss]] ISO 8601\nhh:mm[:ss] 24-hour time today\n\nAll input formats can be followed by fractional seconds, and/or a UTC\noffset such as -0800.\n\nAll input formats can be preceded by TZ=\"id\" to set the input time zone\nseparately from the output time zone. Otherwise $TZ sets both.\n\n+FORMAT specifies display format string using strftime(3) syntax:\n\n%% literal % %n newline %t tab\n%S seconds (00-60) %M minute (00-59) %m month (01-12)\n%H hour (0-23) %I hour (01-12) %p AM/PM\n%y short year (00-99) %Y year %C century\n%a short weekday name %A weekday name %u day of week (1-7, 1=mon)\n%b short month name %B month name %Z timezone name\n%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)\n%N nanosec (output only)\n\n%U Week of year (0-53 start Sunday) %W Week of year (0-53 start Monday)\n%V Week of year (1-53 start Monday, week < 4 days not part of this year)\n\n%F \"%Y-%m-%d\" %R \"%H:%M\" %T \"%H:%M:%S\" %z timezone (-0800)\n%D \"%m/%d/%y\" %r \"%I:%M:%S %p\" %h \"%b\" %:z timezone (-08:00)\n%x locale date %X locale time %c locale date/time %s unix epoch time"
|
||||
#endif
|
||||
|
||||
#define HELP_cut "usage: cut [-Ds] [-bcCfF LIST] [-dO DELIM] [FILE...]\n\nPrint selected parts of lines from each FILE to standard output.\n\nEach selection LIST is comma separated, either numbers (counting from 1)\nor dash separated ranges (inclusive, with X- meaning to end of line and -X\nfrom start). By default selection ranges are sorted and collated, use -D\nto prevent that.\n\n-b Select bytes (with -n round start/end down to start of utf8 char)\n-c Select UTF-8 characters\n-C Select unicode columns\n-d Input delimiter (default is TAB for -f, run of whitespace for -F)\n-D Don't sort/collate selections or match -fF lines without delimiter\n-f Select fields (words) separated by single DELIM character\n-F Select fields separated by DELIM regex\n-O Output separator (default one space for -F, input delim for -f)\n-s Skip lines without delimiters"
|
||||
|
||||
+1
-1
@@ -319,7 +319,7 @@ USE_WGET(NEWTOY(wget, "<1>1(max-redirect)#<0=20d(debug)O(output-document):p(post
|
||||
USE_WHICH(NEWTOY(which, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_WHO(NEWTOY(who, "a", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_WHOAMI(OLDTOY(whoami, logname, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_XARGS(NEWTOY(xargs, "^E:P#optrn#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_XARGS(NEWTOY(xargs, "^E:P#<0(null)=1optr(no-run-if-empty)n#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_XXD(NEWTOY(xxd, ">1c#<0>256l#o#g#<0=2eiprs#[!rs][!re]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_XZCAT(NEWTOY(xzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_YES(NEWTOY(yes, 0, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
+3
-3
@@ -729,7 +729,7 @@ struct group *xgetgrgid(gid_t gid)
|
||||
{
|
||||
struct group *group = getgrgid(gid);
|
||||
|
||||
if (!group) perror_exit("gid %ld", (long)gid);
|
||||
if (!group) perror_exit("bad gid %ld", (long)gid);
|
||||
return group;
|
||||
}
|
||||
|
||||
@@ -765,7 +765,7 @@ struct passwd *xgetpwnam(char *name)
|
||||
{
|
||||
struct passwd *up = getpwnam(name);
|
||||
|
||||
if (!up) perror_exit("user '%s'", name);
|
||||
if (!up) perror_exit("bad user '%s'", name);
|
||||
return up;
|
||||
}
|
||||
|
||||
@@ -773,7 +773,7 @@ struct group *xgetgrnam(char *name)
|
||||
{
|
||||
struct group *gr = getgrnam(name);
|
||||
|
||||
if (!gr) perror_exit("group '%s'", name);
|
||||
if (!gr) perror_exit("bad group '%s'", name);
|
||||
return gr;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/* log.c - Log to logcat.
|
||||
*
|
||||
* Copyright 2016 The Android Open Source Project
|
||||
|
||||
USE_LOG(NEWTOY(log, "<1p:t:", TOYFLAG_USR|TOYFLAG_SBIN))
|
||||
|
||||
config LOG
|
||||
bool "log"
|
||||
depends on TOYBOX_ON_ANDROID
|
||||
default y
|
||||
help
|
||||
usage: log [-p PRI] [-t TAG] MESSAGE...
|
||||
|
||||
Logs message to logcat.
|
||||
|
||||
-p Use the given priority instead of INFO:
|
||||
d: DEBUG e: ERROR f: FATAL i: INFO v: VERBOSE w: WARN s: SILENT
|
||||
-t Use the given tag instead of "log"
|
||||
*/
|
||||
|
||||
#define FOR_log
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *t, *p;
|
||||
)
|
||||
|
||||
void log_main(void)
|
||||
{
|
||||
android_LogPriority pri = ANDROID_LOG_INFO;
|
||||
char *s = toybuf;
|
||||
int i;
|
||||
|
||||
if (TT.p) {
|
||||
i = stridx("defisvw", tolower(*TT.p));
|
||||
if (i==-1 || strlen(TT.p)!=1) error_exit("bad -p '%s'", TT.p);
|
||||
pri = (android_LogPriority []){ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR,
|
||||
ANDROID_LOG_FATAL, ANDROID_LOG_INFO, ANDROID_LOG_SILENT,
|
||||
ANDROID_LOG_VERBOSE, ANDROID_LOG_WARN}[i];
|
||||
}
|
||||
if (!TT.t) TT.t = "log";
|
||||
|
||||
for (i = 0; toys.optargs[i]; i++) {
|
||||
if (i) *s++ = ' ';
|
||||
if ((s-toybuf)+strlen(toys.optargs[i])>=1024) {
|
||||
memcpy(s, toys.optargs[i], 1024-(s-toybuf));
|
||||
toybuf[1024] = 0;
|
||||
error_msg("log cut at 1024 bytes");
|
||||
|
||||
break;
|
||||
}
|
||||
s = stpcpy(s, toys.optargs[i]);
|
||||
}
|
||||
|
||||
__android_log_write(pri, TT.t, toybuf);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* runcon.c - Run command in specified security context
|
||||
*
|
||||
* Copyright 2015 The Android Open Source Project
|
||||
|
||||
USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
|
||||
|
||||
config RUNCON
|
||||
bool "runcon"
|
||||
depends on TOYBOX_SELINUX
|
||||
default y
|
||||
help
|
||||
usage: runcon CONTEXT COMMAND [ARGS...]
|
||||
|
||||
Run a command in a specified security context.
|
||||
*/
|
||||
|
||||
#define FOR_runcon
|
||||
#include "toys.h"
|
||||
|
||||
void runcon_main(void)
|
||||
{
|
||||
char *context = *toys.optargs;
|
||||
|
||||
if (setexeccon(context)) perror_exit("Could not set context to %s", context);
|
||||
|
||||
toys.stacktop = 0;
|
||||
xexec(++toys.optargs);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* su.c - switch user
|
||||
*
|
||||
* Copyright 2013 CE Strake <strake888@gmail.com>
|
||||
*
|
||||
* See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/su.html
|
||||
* TODO: log su attempts
|
||||
* TODO: suid support
|
||||
* Supports undocumented compatibility options: -m synonym for -p, - for -l
|
||||
|
||||
USE_SU(NEWTOY(su, "^lmpu:g:c:s:[!lmp]", TOYFLAG_BIN|TOYFLAG_ROOTONLY))
|
||||
|
||||
config SU
|
||||
bool "su"
|
||||
default y
|
||||
depends on TOYBOX_SHADOW
|
||||
help
|
||||
usage: su [-lp] [-u UID] [-g GID,...] [-s SHELL] [-c CMD] [USER [COMMAND...]]
|
||||
|
||||
Switch user, prompting for password of new user when not run as root.
|
||||
|
||||
With one argument, switch to USER and run user's shell from /etc/passwd.
|
||||
With no arguments, USER is root. If COMMAND line provided after USER,
|
||||
exec() it as new USER (bypassing shell). If -u or -g specified, first
|
||||
argument (if any) isn't USER (it's COMMAND).
|
||||
|
||||
first argument is USER name to switch to (which must exist).
|
||||
Non-root users are prompted for new user's password.
|
||||
|
||||
-s Shell to use (default is user's shell from /etc/passwd)
|
||||
-c Command line to pass to -s shell (ala sh -c "CMD")
|
||||
-l Reset environment as if new login.
|
||||
-u Switch to UID instead of USER
|
||||
-g Switch to GID (only root allowed, can be comma separated list)
|
||||
-p Preserve environment (except for $PATH and $IFS)
|
||||
*/
|
||||
|
||||
#define FOR_su
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *s;
|
||||
char *c;
|
||||
)
|
||||
|
||||
void su_main()
|
||||
{
|
||||
char *name, *passhash = 0, **argu, **argv;
|
||||
struct passwd *up;
|
||||
struct spwd *shp;
|
||||
|
||||
if (*toys.optargs && !strcmp("-", *toys.optargs)) {
|
||||
toys.optflags |= FLAG_l;
|
||||
toys.optargs++;
|
||||
}
|
||||
|
||||
if (*toys.optargs) name = *(toys.optargs++);
|
||||
else name = "root";
|
||||
|
||||
loggit(LOG_NOTICE, "%s->%s", getusername(geteuid()), name);
|
||||
|
||||
shp = getspnam(name);
|
||||
if (getuid() && shp) {
|
||||
if (*shp->sp_pwdp != '$') goto deny;
|
||||
if (read_password(toybuf, sizeof(toybuf), "Password: ")) goto deny;
|
||||
passhash = crypt(toybuf, shp->sp_pwdp);
|
||||
memset(toybuf, 0, sizeof(toybuf));
|
||||
if (!passhash || strcmp(passhash, shp->sp_pwdp)) goto deny;
|
||||
}
|
||||
closelog();
|
||||
|
||||
xsetuser(up = xgetpwnam(name));
|
||||
|
||||
if (FLAG(m)||FLAG(p)) {
|
||||
unsetenv("IFS");
|
||||
setenv("PATH", _PATH_DEFPATH, 1);
|
||||
} else reset_env(up, FLAG(l));
|
||||
|
||||
argv = argu = xmalloc(sizeof(char *)*(toys.optc + 4));
|
||||
*(argv++) = TT.s ? TT.s : up->pw_shell;
|
||||
loggit(LOG_NOTICE, "run %s", *argu);
|
||||
|
||||
if (FLAG(l)) *(argv++) = "-l";
|
||||
if (FLAG(c)) {
|
||||
*(argv++) = "-c";
|
||||
*(argv++) = TT.c;
|
||||
}
|
||||
while ((*(argv++) = *(toys.optargs++)));
|
||||
xexec(argu);
|
||||
|
||||
deny:
|
||||
syslog(LOG_NOTICE, "No.");
|
||||
puts("No.");
|
||||
toys.exitval = 1;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* mkswap.c - Format swap device.
|
||||
*
|
||||
* Copyright 2009 Rob Landley <rob@landley.net>
|
||||
|
||||
USE_MKSWAP(NEWTOY(mkswap, "<1>1L:", TOYFLAG_SBIN))
|
||||
|
||||
config MKSWAP
|
||||
bool "mkswap"
|
||||
default y
|
||||
help
|
||||
usage: mkswap [-L LABEL] DEVICE
|
||||
|
||||
Set up a Linux swap area on a device or file.
|
||||
*/
|
||||
|
||||
#define FOR_mkswap
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *L;
|
||||
)
|
||||
|
||||
void mkswap_main(void)
|
||||
{
|
||||
int fd = xopen(*toys.optargs, O_RDWR), pagesize = sysconf(_SC_PAGE_SIZE);
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
off_t len = fdlength(fd);
|
||||
unsigned int pages = (len/pagesize)-1, *swap = (unsigned int *)toybuf;
|
||||
char *label = (char *)(swap+7), *uuid = (char *)(swap+3);
|
||||
|
||||
// Write header. Note that older kernel versions checked signature
|
||||
// on disk (not in cache) during swapon, so sync after writing.
|
||||
|
||||
swap[0] = 1;
|
||||
swap[1] = pages;
|
||||
xlseek(fd, 1024, SEEK_SET);
|
||||
create_uuid(uuid);
|
||||
if (TT.L) strncpy(label, TT.L, 15);
|
||||
xwrite(fd, swap, 129*sizeof(unsigned int));
|
||||
xlseek(fd, pagesize-10, SEEK_SET);
|
||||
xwrite(fd, "SWAPSPACE2", 10);
|
||||
fsync(fd);
|
||||
|
||||
if (CFG_TOYBOX_FREE) close(fd);
|
||||
|
||||
if (TT.L) sprintf(toybuf, ", LABEL=%s", label);
|
||||
else *toybuf = 0;
|
||||
printf("Swapspace size: %luk%s, UUID=%s\n",
|
||||
pages*(unsigned long)(pagesize/1024),
|
||||
toybuf, show_uuid(uuid));
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* swapoff.c - Disable region for swapping
|
||||
*
|
||||
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
|
||||
|
||||
USE_SWAPOFF(NEWTOY(swapoff, "<1>1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
|
||||
|
||||
config SWAPOFF
|
||||
bool "swapoff"
|
||||
default y
|
||||
help
|
||||
usage: swapoff swapregion
|
||||
|
||||
Disable swapping on a given swapregion.
|
||||
*/
|
||||
|
||||
#include "toys.h"
|
||||
|
||||
void swapoff_main(void)
|
||||
{
|
||||
if (swapoff(toys.optargs[0])) perror_exit("failed to remove swaparea");
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/* switch_root.c - Switch from rootfs/initramfs to another filesystem
|
||||
*
|
||||
* Copyright 2005 Rob Landley <rob@landley.net>
|
||||
|
||||
USE_SWITCH_ROOT(NEWTOY(switch_root, "<2c:h", TOYFLAG_SBIN))
|
||||
|
||||
config SWITCH_ROOT
|
||||
bool "switch_root"
|
||||
default y
|
||||
help
|
||||
usage: switch_root [-c /dev/console] NEW_ROOT NEW_INIT...
|
||||
|
||||
Use from PID 1 under initramfs to free initramfs, chroot to NEW_ROOT,
|
||||
and exec NEW_INIT.
|
||||
|
||||
-c Redirect console to device in NEW_ROOT
|
||||
-h Hang instead of exiting on failure (avoids kernel panic)
|
||||
*/
|
||||
|
||||
#define FOR_switch_root
|
||||
#include "toys.h"
|
||||
#include <sys/vfs.h>
|
||||
|
||||
GLOBALS(
|
||||
char *c;
|
||||
|
||||
dev_t rootdev;
|
||||
)
|
||||
|
||||
static int del_node(struct dirtree *node)
|
||||
{
|
||||
if (node->st.st_dev == TT.rootdev && dirtree_notdotdot(node)) {
|
||||
int flag = 0;
|
||||
if (S_ISDIR(node->st.st_mode)) {
|
||||
if (!node->again) return DIRTREE_COMEAGAIN;
|
||||
flag = AT_REMOVEDIR;
|
||||
}
|
||||
unlinkat(dirtree_parentfd(node), node->name, flag);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void switch_root_main(void)
|
||||
{
|
||||
char *newroot = *toys.optargs, **cmdline = toys.optargs+1;
|
||||
struct stat st1, st2;
|
||||
struct statfs stfs;
|
||||
int console = console; // gcc's "may be used" warnings are broken.
|
||||
|
||||
if (getpid() != 1) error_exit("not pid 1");
|
||||
|
||||
// Root filesystem we're leaving must be ramfs or tmpfs
|
||||
if (statfs("/", &stfs) ||
|
||||
(stfs.f_type != 0x858458f6 && stfs.f_type != 0x01021994))
|
||||
{
|
||||
error_msg("not ramfs");
|
||||
goto panic;
|
||||
}
|
||||
|
||||
// New directory must be different filesystem instance
|
||||
if (chdir(newroot) || stat(".", &st1) || stat("/", &st2) ||
|
||||
st1.st_dev == st2.st_dev)
|
||||
{
|
||||
error_msg("bad newroot '%s'", newroot);
|
||||
goto panic;
|
||||
}
|
||||
TT.rootdev=st2.st_dev;
|
||||
|
||||
// trim any / characters from the init cmdline, as we want to test it with
|
||||
// stat(), relative to newroot. *cmdline is also used below, but by that
|
||||
// point we are in the chroot, so a relative path is still OK.
|
||||
while (**cmdline == '/') (*cmdline)++;
|
||||
|
||||
// init program must exist and be an executable file
|
||||
if (stat(*cmdline, &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) {
|
||||
error_msg("bad init");
|
||||
goto panic;
|
||||
}
|
||||
|
||||
if (TT.c && -1 == (console = open(TT.c, O_RDWR))) {
|
||||
perror_msg("bad console '%s'", TT.c);
|
||||
goto panic;
|
||||
}
|
||||
|
||||
// Ok, enough safety checks: wipe root partition.
|
||||
dirtree_read("/", del_node);
|
||||
|
||||
// Fix the appearance of the mount table in the newroot chroot
|
||||
if (mount(".", "/", NULL, MS_MOVE, NULL)) {
|
||||
perror_msg("mount");
|
||||
goto panic;
|
||||
}
|
||||
|
||||
// Enter the new root before starting init
|
||||
if (chroot(".")) {
|
||||
perror_msg("chroot");
|
||||
goto panic;
|
||||
}
|
||||
|
||||
// Make sure cwd does not point outside of the chroot
|
||||
if (chdir("/")) {
|
||||
perror_msg("chdir");
|
||||
goto panic;
|
||||
}
|
||||
|
||||
if (TT.c) {
|
||||
int i;
|
||||
for (i=0; i<3; i++) if (console != i) dup2(console, i);
|
||||
if (console>2) close(console);
|
||||
}
|
||||
execv(*cmdline, cmdline);
|
||||
perror_msg("Failed to exec '%s'", *cmdline);
|
||||
panic:
|
||||
if (toys.optflags & FLAG_h) for (;;) wait(NULL);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/* vmstat.c - Report virtual memory statistics.
|
||||
*
|
||||
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
|
||||
*
|
||||
* TODO: I have no idea how "system" category is calculated.
|
||||
* whatever we're doing isn't matching what other implementations are doing.
|
||||
|
||||
USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
|
||||
|
||||
config VMSTAT
|
||||
bool "vmstat"
|
||||
default y
|
||||
help
|
||||
usage: vmstat [-n] [DELAY [COUNT]]
|
||||
|
||||
Print virtual memory statistics, repeating each DELAY seconds, COUNT times.
|
||||
(With no DELAY, prints one line. With no COUNT, repeats until killed.)
|
||||
|
||||
Show processes running and blocked, kilobytes swapped, free, buffered, and
|
||||
cached, kilobytes swapped in and out per second, file disk blocks input and
|
||||
output per second, interrupts and context switches per second, percent
|
||||
of CPU time spent running user code, system code, idle, and awaiting I/O.
|
||||
First line is since system started, later lines are since last line.
|
||||
|
||||
-n Display the header only once
|
||||
*/
|
||||
|
||||
#define FOR_vmstat
|
||||
#include "toys.h"
|
||||
|
||||
struct vmstat_proc {
|
||||
// From /proc/stat (jiffies)
|
||||
uint64_t user, nice, sys, idle, wait, irq, sirq, intr, ctxt, running, blocked;
|
||||
// From /proc/meminfo (units are kb)
|
||||
uint64_t memfree, buffers, cached, swapfree, swaptotal;
|
||||
// From /proc/vmstat (units are kb)
|
||||
uint64_t io_in, io_out;
|
||||
// From /proc/vmstat (units are pages)
|
||||
uint64_t swap_in, swap_out;
|
||||
};
|
||||
|
||||
// All the elements of vmstat_proc are the same size, so we can populate it as
|
||||
// a big array, then read the elements back out by name
|
||||
static void get_vmstat_proc(struct vmstat_proc *vmstat_proc)
|
||||
{
|
||||
char *vmstuff[] = { "/proc/stat", "cpu ", 0, 0, 0, 0, 0, 0,
|
||||
"intr ", "ctxt ", "procs_running ", "procs_blocked ", "/proc/meminfo",
|
||||
"MemFree: ", "Buffers: ", "Cached: ", "SwapFree: ", "SwapTotal: ",
|
||||
"/proc/vmstat", "pgpgin ", "pgpgout ", "pswpin ", "pswpout " };
|
||||
uint64_t *new = (uint64_t *)vmstat_proc;
|
||||
char *p = p, *name = name, *file = NULL;
|
||||
int i, j;
|
||||
|
||||
// We use vmstuff to fill out vmstat_proc as an array of uint64_t:
|
||||
// Strings starting with / are the file to find next entries in
|
||||
// Any other string is a key to search for, with decimal value right after
|
||||
// 0 means parse another value on same line as last key
|
||||
|
||||
for (i = 0; i<ARRAY_LEN(vmstuff); i++) {
|
||||
if (!vmstuff[i]) p++;
|
||||
else if (*vmstuff[i] == '/') {
|
||||
// /proc/stat for a 48-core machine doesn't fit in toybuf.
|
||||
free(file);
|
||||
file = xreadfile(name = vmstuff[i], 0, 0);
|
||||
|
||||
continue;
|
||||
} else p = strafter(file, vmstuff[i]);
|
||||
if (!p || 1!=sscanf(p, "%"PRIu64"%n", new++, &j))
|
||||
error_exit("Bad %sin %s: %s", vmstuff[i], name, p ? p : "");
|
||||
p += j;
|
||||
}
|
||||
free(file);
|
||||
}
|
||||
|
||||
void vmstat_main(void)
|
||||
{
|
||||
struct vmstat_proc top[2];
|
||||
int i, loop_delay = 0, loop_max = 0;
|
||||
unsigned loop, rows = 25, page_kb = sysconf(_SC_PAGESIZE)/1024;
|
||||
char *headers="r\0b\0swpd\0free\0buff\0cache\0si\0so\0bi\0bo\0in\0cs\0us\0"
|
||||
"sy\0id\0wa", lengths[] = {2,2,7,7,6,7,5,5,5,5,5,5,2,2,2,2};
|
||||
|
||||
memset(top, 0, sizeof(top));
|
||||
if (toys.optc) loop_delay = atolx_range(toys.optargs[0], 0, INT_MAX);
|
||||
if (toys.optc > 1) loop_max = atolx_range(toys.optargs[1], 1, INT_MAX);
|
||||
|
||||
for (loop = 0; !loop_max || loop < loop_max; loop++) {
|
||||
unsigned idx = loop&1, offset = 0, expected = 0;
|
||||
uint64_t units, total_hz, *ptr = (uint64_t *)(top+idx),
|
||||
*oldptr = (uint64_t *)(top+!idx);
|
||||
|
||||
if (loop && loop_delay) sleep(loop_delay);
|
||||
|
||||
// Print headers
|
||||
if (rows>3 && !(loop % (rows-3))) {
|
||||
char *header = headers;
|
||||
|
||||
if (!(toys.optflags&FLAG_n) && isatty(1)) terminal_size(0, &rows);
|
||||
else rows = 0;
|
||||
|
||||
printf("procs ------------memory------------ ----swap--- -----io---- ---system-- ----cpu----\n");
|
||||
for (i=0; i<sizeof(lengths); i++) {
|
||||
printf(" %*s"+!i, lengths[i], header);
|
||||
header += strlen(header)+1;
|
||||
}
|
||||
xputc('\n');
|
||||
}
|
||||
|
||||
// Read data and combine some fields we display as aggregates
|
||||
get_vmstat_proc(top+idx);
|
||||
top[idx].running--; // Don't include ourselves
|
||||
top[idx].user += top[idx].nice;
|
||||
top[idx].sys += top[idx].irq + top[idx].sirq;
|
||||
top[idx].swaptotal -= top[idx].swapfree;
|
||||
|
||||
// Collect unit adjustments (outside the inner loop to save time)
|
||||
|
||||
if (!loop) {
|
||||
char *s = toybuf;
|
||||
|
||||
xreadfile("/proc/uptime", toybuf, sizeof(toybuf));
|
||||
while (*(s++) > ' ');
|
||||
sscanf(s, "%"PRIu64, &units);
|
||||
} else units = loop_delay;
|
||||
|
||||
// add up user, sys, idle, and wait time used since last time
|
||||
// (Already appended nice to user)
|
||||
total_hz = 0;
|
||||
for (i=0; i<4; i++) total_hz += ptr[i+!!i] - oldptr[i+!!i];
|
||||
|
||||
// Output values in order[]: running, blocked, swaptotal, memfree, buffers,
|
||||
// cache, swap_in, swap_out, io_in, io_out, sirq, ctxt, user, sys, idle,wait
|
||||
|
||||
for (i=0; i<sizeof(lengths); i++) {
|
||||
char order[] = {9, 10, 15, 11, 12, 13, 18, 19, 16, 17, 6, 8, 0, 2, 3, 4};
|
||||
uint64_t out = ptr[order[i]];
|
||||
int len;
|
||||
|
||||
// Adjust rate and units
|
||||
if (i>5) out -= oldptr[order[i]];
|
||||
if (order[i]<7) out = ((out*100) + (total_hz/2)) / total_hz;
|
||||
else if (order[i]>17) out = ((out * page_kb)+(units-1))/units;
|
||||
else if (order[i]>15) out = ((out)+(units-1))/units;
|
||||
else if (order[i]<9) out = (out+(units-1)) / units;
|
||||
|
||||
// If a field was too big to fit in its slot, try to compensate later
|
||||
expected += lengths[i] + !!i;
|
||||
len = expected - offset - !!i;
|
||||
if (len < 0) len = 0;
|
||||
offset += printf(" %*"PRIu64+!i, len, out);
|
||||
}
|
||||
xputc('\n');
|
||||
|
||||
if (!loop_delay) break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* yes.c - Repeatedly output a string.
|
||||
*
|
||||
* Copyright 2007 Rob Landley <rob@landley.net>
|
||||
|
||||
USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config YES
|
||||
bool "yes"
|
||||
default y
|
||||
help
|
||||
usage: yes [args...]
|
||||
|
||||
Repeatedly output line until killed. If no args, output 'y'.
|
||||
*/
|
||||
|
||||
#include "toys.h"
|
||||
|
||||
void yes_main(void)
|
||||
{
|
||||
for (;;) {
|
||||
int i;
|
||||
for (i=0; toys.optargs[i]; i++) {
|
||||
if (i) xputc(' ');
|
||||
xprintf("%s", toys.optargs[i]);
|
||||
}
|
||||
if (!i) xputc('y');
|
||||
xputc('\n');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/* mdev.c - Populate /dev directory and handle hotplug events
|
||||
*
|
||||
* Copyright 2005, 2008 Rob Landley <rob@landley.net>
|
||||
* Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
|
||||
|
||||
USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
|
||||
|
||||
config MDEV
|
||||
bool "mdev"
|
||||
default n
|
||||
help
|
||||
usage: mdev [-s]
|
||||
|
||||
Create devices in /dev using information from /sys.
|
||||
|
||||
-s Scan all entries in /sys to populate /dev
|
||||
|
||||
config MDEV_CONF
|
||||
bool "Configuration file for mdev"
|
||||
default y
|
||||
depends on MDEV
|
||||
help
|
||||
The mdev config file (/etc/mdev.conf) contains lines that look like:
|
||||
hd[a-z][0-9]* 0:3 660
|
||||
(sd[a-z]) root:disk 660 =usb_storage
|
||||
|
||||
Each line must contain three whitespace separated fields. The first
|
||||
field is a regular expression matching one or more device names,
|
||||
the second and third fields are uid:gid and file permissions for
|
||||
matching devices. Fourth field is optional. It could be used to change
|
||||
device name (prefix '='), path (prefix '=' and postfix '/') or create a
|
||||
symlink (prefix '>').
|
||||
*/
|
||||
|
||||
#include "toys.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// mknod in /dev based on a path like "/sys/block/hda/hda1"
|
||||
static void make_device(char *path)
|
||||
{
|
||||
char *device_name = 0, *custom_name = 0, *s, *temp;
|
||||
bool create_symlink = false;
|
||||
int major = 0, minor = 0, type, len, fd, mode = 0660;
|
||||
uid_t uid = 0;
|
||||
gid_t gid = 0;
|
||||
|
||||
if (path) {
|
||||
|
||||
// Try to read major/minor string, returning if we can't
|
||||
temp = strrchr(path, '/');
|
||||
fd = open(path, O_RDONLY);
|
||||
*temp = 0;
|
||||
len = read(fd, toybuf, 64);
|
||||
close(fd);
|
||||
if (len<1) return;
|
||||
toybuf[len] = 0;
|
||||
|
||||
// Determine device type, major and minor
|
||||
|
||||
type = path[5]=='c' ? S_IFCHR : S_IFBLK;
|
||||
sscanf(toybuf, "%u:%u", &major, &minor);
|
||||
} else {
|
||||
// if (!path), do hotplug
|
||||
|
||||
if (!(temp = getenv("MODALIAS"))) xrun((char *[]){"modprobe", temp, 0});
|
||||
if (!(temp = getenv("SUBSYSTEM"))) return;
|
||||
type = strcmp(temp, "block") ? S_IFCHR : S_IFBLK;
|
||||
if (!(temp = getenv("MAJOR"))) return;
|
||||
sscanf(temp, "%u", &major);
|
||||
if (!(temp = getenv("MINOR"))) return;
|
||||
sscanf(temp, "%u", &minor);
|
||||
if (!(path = getenv("DEVPATH"))) return;
|
||||
device_name = getenv("DEVNAME");
|
||||
}
|
||||
if (!device_name)
|
||||
device_name = strrchr(path, '/') + 1;
|
||||
|
||||
// as in linux/drivers/base/core.c, device_get_devnode()
|
||||
while ((temp = strchr(device_name, '!'))) {
|
||||
*temp = '/';
|
||||
}
|
||||
|
||||
// If we have a config file, look up permissions for this device
|
||||
|
||||
if (CFG_MDEV_CONF) {
|
||||
char *conf, *pos, *end;
|
||||
bool optional_field_valid = false;
|
||||
|
||||
// mmap the config file
|
||||
if (-1!=(fd = open("/etc/mdev.conf", O_RDONLY))) {
|
||||
int line = 0;
|
||||
|
||||
len = fdlength(fd);
|
||||
conf = xmmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
|
||||
// Loop through lines in mmaped file
|
||||
for (pos = conf; pos-conf<len;) {
|
||||
int field;
|
||||
char *end2;
|
||||
|
||||
line++;
|
||||
// find end of this line
|
||||
for(end = pos; end-conf<len && *end!='\n'; end++);
|
||||
|
||||
// Four fields (last is optional): regex, uid:gid, mode [, name|path ]
|
||||
// For example: (sd[a-z])1 root:disk 660 =usb_storage_p1
|
||||
for (field = 4; field; field--) {
|
||||
// Skip whitespace
|
||||
while (pos<end && isspace(*pos)) pos++;
|
||||
if (pos==end || *pos=='#') break;
|
||||
for (end2 = pos;
|
||||
end2<end && !isspace(*end2) && *end2!='#'; end2++);
|
||||
switch(field) {
|
||||
// Regex to match this device
|
||||
case 4:
|
||||
{
|
||||
char *regex = strndup(pos, end2-pos);
|
||||
regex_t match;
|
||||
regmatch_t off;
|
||||
int result;
|
||||
|
||||
// Is this it?
|
||||
xregcomp(&match, regex, REG_EXTENDED);
|
||||
result=regexec(&match, device_name, 1, &off, 0);
|
||||
regfree(&match);
|
||||
free(regex);
|
||||
|
||||
// If not this device, skip rest of line
|
||||
if (result || off.rm_so
|
||||
|| off.rm_eo!=strlen(device_name))
|
||||
goto end_line;
|
||||
|
||||
break;
|
||||
}
|
||||
// uid:gid
|
||||
case 3:
|
||||
{
|
||||
char *s2;
|
||||
|
||||
// Find :
|
||||
for(s = pos; s<end2 && *s!=':'; s++);
|
||||
if (s==end2) goto end_line;
|
||||
|
||||
// Parse UID
|
||||
uid = strtoul(pos,&s2,10);
|
||||
if (s!=s2) {
|
||||
struct passwd *pass;
|
||||
char *str = strndup(pos, s-pos);
|
||||
pass = getpwnam(str);
|
||||
free(str);
|
||||
if (!pass) goto end_line;
|
||||
uid = pass->pw_uid;
|
||||
}
|
||||
s++;
|
||||
// parse GID
|
||||
gid = strtoul(s,&s2,10);
|
||||
if (end2!=s2) {
|
||||
struct group *grp;
|
||||
char *str = strndup(s, end2-s);
|
||||
grp = getgrnam(str);
|
||||
free(str);
|
||||
if (!grp) goto end_line;
|
||||
gid = grp->gr_gid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// mode
|
||||
case 2:
|
||||
{
|
||||
char *beg_pos = pos;
|
||||
mode = strtoul(pos, &pos, 8);
|
||||
if (pos == beg_pos) {
|
||||
// The line is bad because mode setting could not be
|
||||
// converted to numeric value.
|
||||
goto end_line;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Try to look for name or path (optional field)
|
||||
case 1:
|
||||
{
|
||||
if(pos < end2){
|
||||
//char *name = strndup(pos, end2-pos);
|
||||
char *name = malloc(end2-pos+1);
|
||||
if(name){
|
||||
strncpy(name, pos, end2-pos+1);
|
||||
name[end2-pos] = '\0';
|
||||
switch(name[0]){
|
||||
case '>':
|
||||
create_symlink = true;
|
||||
case '=':
|
||||
custom_name = strdup(&name[1]);
|
||||
break;
|
||||
case '!':
|
||||
device_name = NULL;
|
||||
break;
|
||||
default:
|
||||
free(name);
|
||||
goto end_line;
|
||||
}
|
||||
free(name);
|
||||
optional_field_valid = true;
|
||||
}
|
||||
}
|
||||
goto found_device;
|
||||
}
|
||||
}
|
||||
pos=end2;
|
||||
}
|
||||
end_line:
|
||||
// Did everything parse happily?
|
||||
// Note: Last field is optional.
|
||||
if ((field>1 || (field==1 && !optional_field_valid)) && field!=4)
|
||||
error_exit("Bad line %d", line);
|
||||
// Next line
|
||||
pos = ++end;
|
||||
}
|
||||
found_device:
|
||||
munmap(conf, len);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if(device_name) {
|
||||
if(custom_name) {
|
||||
sprintf(toybuf, "/dev/%s", custom_name);
|
||||
if(custom_name[strlen(custom_name) - 1] == '/') {
|
||||
DIR *dir;
|
||||
dir = opendir(toybuf);
|
||||
if(dir) closedir(dir);
|
||||
else mkdir(toybuf, 0755);
|
||||
}
|
||||
}
|
||||
else
|
||||
sprintf(toybuf, "/dev/%s", device_name);
|
||||
|
||||
if ((temp=getenv("ACTION")) && !strcmp(temp, "remove")) {
|
||||
unlink(toybuf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strchr(device_name, '/')) mkpath(toybuf);
|
||||
if (mknod(toybuf, mode | type, dev_makedev(major, minor)) &&
|
||||
errno != EEXIST)
|
||||
perror_exit("mknod %s failed", toybuf);
|
||||
if(create_symlink){
|
||||
char *symlink_name = malloc(sizeof("/dev/") + strlen(device_name) + 1);
|
||||
if(symlink_name == NULL)
|
||||
perror_exit("malloc failed while creating symlink to %s", toybuf);
|
||||
sprintf(symlink_name, "/dev/%s", device_name);
|
||||
if(symlink(toybuf, symlink_name)){
|
||||
free(symlink_name);
|
||||
perror_exit("symlink creation failed for %s", toybuf);
|
||||
}
|
||||
free(symlink_name);
|
||||
}
|
||||
|
||||
if (type == S_IFBLK) close(open(toybuf, O_RDONLY)); // scan for partitions
|
||||
|
||||
if (CFG_MDEV_CONF) mode=chown(toybuf, uid, gid);
|
||||
}
|
||||
}
|
||||
|
||||
static int callback(struct dirtree *node)
|
||||
{
|
||||
// Entries in /sys/class/block aren't char devices, so skip 'em. (We'll
|
||||
// get block devices out of /sys/block.)
|
||||
if(!strcmp(node->name, "block")) return 0;
|
||||
|
||||
// Does this directory have a "dev" entry in it?
|
||||
// This is path based because the hotplug callbacks are
|
||||
if (S_ISDIR(node->st.st_mode) || S_ISLNK(node->st.st_mode)) {
|
||||
int len=4;
|
||||
char *dev = dirtree_path(node, &len);
|
||||
strcpy(dev+len, "/dev");
|
||||
if (!access(dev, R_OK)) make_device(dev);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
// Circa 2.6.25 the entries more than 2 deep are all either redundant
|
||||
// (mouse#, event#) or unnamed (every usb_* entry is called "device").
|
||||
|
||||
if (node->parent && node->parent->parent) return 0;
|
||||
return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
|
||||
}
|
||||
|
||||
void mdev_main(void)
|
||||
{
|
||||
// Handle -s
|
||||
|
||||
if (toys.optflags) {
|
||||
dirtree_read("/sys/class", callback);
|
||||
if (!access("/sys/block", R_OK)) dirtree_read("/sys/block", callback);
|
||||
} else { // hotplug support
|
||||
make_device(NULL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/* stty.c - Get/set terminal configuration.
|
||||
*
|
||||
* Copyright 2017 The Android Open Source Project.
|
||||
*
|
||||
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/stty.html
|
||||
|
||||
USE_STTY(NEWTOY(stty, "?aF:g[!ag]", TOYFLAG_BIN))
|
||||
|
||||
config STTY
|
||||
bool "stty"
|
||||
default n
|
||||
help
|
||||
usage: stty [-ag] [-F device] SETTING...
|
||||
|
||||
Get/set terminal configuration.
|
||||
|
||||
-F Open device instead of stdin
|
||||
-a Show all current settings (default differences from "sane")
|
||||
-g Show all current settings usable as input to stty
|
||||
|
||||
Special characters (syntax ^c or undef): intr quit erase kill eof eol eol2
|
||||
swtch start stop susp rprnt werase lnext discard
|
||||
|
||||
Control/input/output/local settings as shown by -a, '-' prefix to disable
|
||||
|
||||
Combo settings: cooked/raw, evenp/oddp/parity, nl, ek, sane
|
||||
|
||||
N set input and output speed (ispeed N or ospeed N for just one)
|
||||
cols N set number of columns
|
||||
rows N set number of rows
|
||||
line N set line discipline
|
||||
min N set minimum chars per read
|
||||
time N set read timeout
|
||||
speed show speed only
|
||||
size show size only
|
||||
*/
|
||||
|
||||
#define FOR_stty
|
||||
#include "toys.h"
|
||||
|
||||
#include <linux/tty.h>
|
||||
|
||||
GLOBALS(
|
||||
char *device;
|
||||
|
||||
int fd, col;
|
||||
unsigned output_cols;
|
||||
)
|
||||
|
||||
static const int bauds[] = {
|
||||
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600,
|
||||
19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600,
|
||||
1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000
|
||||
};
|
||||
|
||||
static int baud(speed_t speed)
|
||||
{
|
||||
if (speed&CBAUDEX) speed=(speed&~CBAUDEX)+15;
|
||||
return bauds[speed];
|
||||
}
|
||||
|
||||
static speed_t speed(int baud)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<ARRAY_LEN(bauds);i++) if (bauds[i] == baud) break;
|
||||
if (i == ARRAY_LEN(bauds)) error_exit("unknown speed: %d", baud);
|
||||
return i+4081*(i>16);
|
||||
}
|
||||
|
||||
struct flag {
|
||||
char *name;
|
||||
int value;
|
||||
int mask;
|
||||
};
|
||||
|
||||
static const struct flag chars[] = {
|
||||
{ "intr", VINTR }, { "quit", VQUIT }, { "erase", VERASE }, { "kill", VKILL },
|
||||
{ "eof", VEOF }, { "eol", VEOL }, { "eol2", VEOL2 }, { "swtch", VSWTC },
|
||||
{ "start", VSTART }, { "stop", VSTOP }, { "susp", VSUSP },
|
||||
{ "rprnt", VREPRINT }, { "werase", VWERASE }, { "lnext", VLNEXT },
|
||||
{ "discard", VDISCARD }, { "min", VMIN }, { "time", VTIME },
|
||||
};
|
||||
|
||||
static const struct flag cflags[] = {
|
||||
{ "parenb", PARENB }, { "parodd", PARODD }, { "cmspar", CMSPAR },
|
||||
{ "cs5", CS5, CSIZE }, { "cs6", CS6, CSIZE }, { "cs7", CS7, CSIZE },
|
||||
{ "cs8", CS8, CSIZE }, { "hupcl", HUPCL }, { "cstopb", CSTOPB },
|
||||
{ "cread", CREAD }, { "clocal", CLOCAL }, { "crtscts", CRTSCTS },
|
||||
};
|
||||
|
||||
static const struct flag iflags[] = {
|
||||
{ "ignbrk", IGNBRK }, { "brkint", BRKINT }, { "ignpar", IGNPAR },
|
||||
{ "parmrk", PARMRK }, { "inpck", INPCK }, { "istrip", ISTRIP },
|
||||
{ "inlcr", INLCR }, { "igncr", IGNCR }, { "icrnl", ICRNL }, { "ixon", IXON },
|
||||
{ "ixoff", IXOFF }, { "iuclc", IUCLC }, { "ixany", IXANY },
|
||||
{ "imaxbel", IMAXBEL }, { "iutf8", IUTF8 },
|
||||
};
|
||||
|
||||
static const struct flag oflags[] = {
|
||||
{ "opost", OPOST }, { "olcuc", OLCUC }, { "ocrnl", OCRNL },
|
||||
{ "onlcr", ONLCR }, { "onocr", ONOCR }, { "onlret", ONLRET },
|
||||
{ "ofill", OFILL }, { "ofdel", OFDEL }, { "nl0", NL0, NLDLY },
|
||||
{ "nl1", NL1, NLDLY }, { "cr0", CR0, CRDLY }, { "cr1", CR1, CRDLY },
|
||||
{ "cr2", CR2, CRDLY }, { "cr3", CR3, CRDLY }, { "tab0", TAB0, TABDLY },
|
||||
{ "tab1", TAB1, TABDLY }, { "tab2", TAB2, TABDLY }, { "tab3", TAB3, TABDLY },
|
||||
{ "bs0", BS0, BSDLY }, { "bs1", BS1, BSDLY }, { "vt0", VT0, VTDLY },
|
||||
{ "vt1", VT1, VTDLY }, { "ff0", FF0, FFDLY }, { "ff1", FF1, FFDLY },
|
||||
};
|
||||
|
||||
static const struct flag lflags[] = {
|
||||
{ "isig", ISIG }, { "icanon", ICANON }, { "iexten", IEXTEN },
|
||||
{ "echo", ECHO }, { "echoe", ECHOE }, { "echok", ECHOK },
|
||||
{ "echonl", ECHONL }, { "noflsh", NOFLSH }, { "xcase", XCASE },
|
||||
{ "tostop", TOSTOP }, { "echoprt", ECHOPRT }, { "echoctl", ECHOCTL },
|
||||
{ "echoke", ECHOKE }, { "flusho", FLUSHO }, { "extproc", EXTPROC },
|
||||
};
|
||||
|
||||
static const struct synonym {
|
||||
char *from;
|
||||
char *to;
|
||||
} synonyms[] = {
|
||||
{ "cbreak", "-icanon" }, { "-cbreak", "icanon" }, { "-cooked", "raw" },
|
||||
{ "crterase", "echoe" }, { "-crterase", "-echoe" }, { "crtkill", "echoke" },
|
||||
{ "-crtkill", "-echoke" }, { "ctlecho", "echoctl" }, { "-tandem", "-ixoff" },
|
||||
{ "-ctlecho", "-echoctl" }, { "hup", "hupcl" }, { "-hup", "-hupcl" },
|
||||
{ "prterase", "echoprt" }, { "-prterase", "-echoprt" }, { "-raw", "cooked" },
|
||||
{ "tabs", "tab0" }, { "-tabs", "tab3" }, { "tandem", "ixoff" },
|
||||
};
|
||||
|
||||
static void out(const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
int len;
|
||||
char *prefix = " ";
|
||||
|
||||
va_start(va, fmt);
|
||||
len = vsnprintf(toybuf, sizeof(toybuf), fmt, va);
|
||||
va_end(va);
|
||||
|
||||
if (TT.output_cols == 0) {
|
||||
TT.output_cols = 80;
|
||||
terminal_size(&TT.output_cols, NULL);
|
||||
}
|
||||
|
||||
if (TT.col == 0 || *fmt == '\n') prefix = "";
|
||||
else if (TT.col + 1 + len >= TT.output_cols) {
|
||||
prefix = "\n";
|
||||
TT.col = 0;
|
||||
}
|
||||
xprintf("%s%s", prefix, toybuf);
|
||||
|
||||
if (toybuf[len-1] == '\n') TT.col = 0;
|
||||
else TT.col += strlen(prefix) + len;
|
||||
}
|
||||
|
||||
static void show_flags(tcflag_t actual, tcflag_t sane,
|
||||
const struct flag *flags, int len)
|
||||
{
|
||||
int i, j, value, mask;
|
||||
|
||||
// Implement -a by ensuring that sane != actual so we'll show everything.
|
||||
if (toys.optflags&FLAG_a) sane = ~actual;
|
||||
|
||||
for (i=j=0;i<len;i++) {
|
||||
value = flags[i].value;
|
||||
if ((mask = flags[i].mask)) {
|
||||
if ((actual&mask)==value && (sane&mask)!=value) {
|
||||
out("%s", flags[i].name);
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
if ((actual&value) != (sane&value)) {
|
||||
out("%s%s", actual&value?"":"-", flags[i].name);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j) out("\n");
|
||||
}
|
||||
|
||||
static void show_size(int verbose)
|
||||
{
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.device);
|
||||
out(verbose ? "rows %d; columns %d;" : "%d %d\n", ws.ws_row, ws.ws_col);
|
||||
}
|
||||
|
||||
static void show_speed(struct termios *t, int verbose)
|
||||
{
|
||||
int ispeed = baud(cfgetispeed(t)), ospeed = baud(cfgetospeed(t));
|
||||
char *fmt = verbose ? "ispeed %d baud; ospeed %d baud;" : "%d %d\n";
|
||||
|
||||
if (ispeed == ospeed) fmt += (verbose ? 17 : 3);
|
||||
out(fmt, ispeed, ospeed);
|
||||
}
|
||||
|
||||
static int get_arg(int *i, long long low, long long high)
|
||||
{
|
||||
(*i)++;
|
||||
if (!toys.optargs[*i]) error_exit("missing arg");
|
||||
return atolx_range(toys.optargs[*i], low, high);
|
||||
}
|
||||
|
||||
static int set_flag(tcflag_t *f, const struct flag *flags, int len,
|
||||
char *name, int on)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<len;i++) {
|
||||
if (!strcmp(flags[i].name, name)) {
|
||||
if (on) {
|
||||
*f &= ~flags[i].mask;
|
||||
*f |= flags[i].value;
|
||||
} else {
|
||||
if (flags[i].mask) error_exit("%s isn't a boolean", name);
|
||||
*f &= ~flags[i].value;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_option(struct termios *new, char *option)
|
||||
{
|
||||
int on = (*option != '-');
|
||||
|
||||
if (!on) option++;
|
||||
if (!set_flag(&new->c_cflag, cflags, ARRAY_LEN(cflags), option, on) &&
|
||||
!set_flag(&new->c_iflag, iflags, ARRAY_LEN(iflags), option, on) &&
|
||||
!set_flag(&new->c_oflag, oflags, ARRAY_LEN(oflags), option, on) &&
|
||||
!set_flag(&new->c_lflag, lflags, ARRAY_LEN(lflags), option, on))
|
||||
error_exit("unknown option: %s", option);
|
||||
}
|
||||
|
||||
static void set_options(struct termios* new, ...)
|
||||
{
|
||||
va_list va;
|
||||
char *option;
|
||||
|
||||
va_start(va, new);
|
||||
while ((option = va_arg(va, char *))) set_option(new, option);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static void set_size(int is_rows, unsigned short value)
|
||||
{
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.device);
|
||||
if (is_rows) ws.ws_row = value;
|
||||
else ws.ws_col = value;
|
||||
if (ioctl(TT.fd, TIOCSWINSZ, &ws)) perror_exit("TIOCSWINSZ %s", TT.device);
|
||||
}
|
||||
|
||||
static int set_special_character(struct termios *new, int *i, char *char_name)
|
||||
{
|
||||
int j;
|
||||
|
||||
// The -2 is to ignore VMIN and VTIME, which are just unsigned integers.
|
||||
for (j=0;j<ARRAY_LEN(chars)-2;j++) {
|
||||
if (!strcmp(chars[j].name, char_name)) {
|
||||
char *arg = toys.optargs[++(*i)];
|
||||
cc_t ch;
|
||||
|
||||
if (!arg) error_exit("missing arg");
|
||||
if (!strcmp(arg, "^-") || !strcmp(arg, "undef")) ch = _POSIX_VDISABLE;
|
||||
else if (!strcmp(arg, "^?")) ch = 0x7f;
|
||||
else if (arg[0] == '^' && arg[2] == 0) ch = (toupper(arg[1])-'@');
|
||||
else if (!arg[1]) ch = arg[0];
|
||||
else error_exit("invalid arg: %s", arg);
|
||||
xprintf("setting %s to %s (%02x)\n", char_name, arg, ch);
|
||||
new->c_cc[chars[j].value] = ch;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void make_sane(struct termios *t)
|
||||
{
|
||||
// POSIX has no opinion about what "sane" means. From "man stty".
|
||||
// "cs8" is missing from the man page, but needed to get identical results.
|
||||
set_options(t, "cread", "-ignbrk", "brkint", "-inlcr", "-igncr", "icrnl",
|
||||
"icanon", "iexten", "echo", "echoe", "echok", "-echonl", "-noflsh",
|
||||
"-ixoff", "-iutf8", "-iuclc", "-ixany", "imaxbel", "-xcase", "-olcuc",
|
||||
"-ocrnl", "opost", "-ofill", "onlcr", "-onocr", "-onlret", "nl0", "cr0",
|
||||
"tab0", "bs0", "vt0", "ff0", "isig", "-tostop", "-ofdel", "-echoprt",
|
||||
"echoctl", "echoke", "-extproc", "-flusho", "cs8", NULL);
|
||||
memset(t->c_cc, 0, NCCS);
|
||||
t->c_cc[VINTR] = 0x3;
|
||||
t->c_cc[VQUIT] = 0x1c;
|
||||
t->c_cc[VERASE] = 0x7f;
|
||||
t->c_cc[VKILL] = 0x15;
|
||||
t->c_cc[VEOF] = 0x4;
|
||||
t->c_cc[VTIME] = 0;
|
||||
t->c_cc[VMIN] = 1;
|
||||
t->c_cc[VSWTC] = 0;
|
||||
t->c_cc[VSTART] = 0x11;
|
||||
t->c_cc[VSTOP] = 0x13;
|
||||
t->c_cc[VSUSP] = 0x1a;
|
||||
t->c_cc[VEOL] = 0;
|
||||
t->c_cc[VREPRINT] = 0x12;
|
||||
t->c_cc[VDISCARD] = 0xf;
|
||||
t->c_cc[VWERASE] = 0x17;
|
||||
t->c_cc[VLNEXT] = 0x16;
|
||||
t->c_cc[VEOL2] = 0;
|
||||
}
|
||||
|
||||
static void xtcgetattr(struct termios *t)
|
||||
{
|
||||
if (tcgetattr(TT.fd, t)) perror_exit("tcgetattr %s", TT.device);
|
||||
}
|
||||
|
||||
static void do_stty()
|
||||
{
|
||||
struct termios old, sane;
|
||||
int i, j, n;
|
||||
|
||||
xtcgetattr(&old);
|
||||
|
||||
if (*toys.optargs) {
|
||||
struct termios new = old;
|
||||
|
||||
for (i=0; toys.optargs[i]; i++) {
|
||||
char *arg = toys.optargs[i];
|
||||
|
||||
if (!strcmp(arg, "size")) show_size(0);
|
||||
else if (!strcmp(arg, "speed")) show_speed(&old, 0);
|
||||
else if (!strcmp(arg, "line")) new.c_line = get_arg(&i, N_TTY, NR_LDISCS);
|
||||
else if (!strcmp(arg, "min")) new.c_cc[VMIN] = get_arg(&i, 0, 255);
|
||||
else if (!strcmp(arg, "time")) new.c_cc[VTIME] = get_arg(&i, 0, 255);
|
||||
else if (atoi(arg) > 0) {
|
||||
int new_speed = speed(atolx_range(arg, 0, 4000000));
|
||||
|
||||
cfsetispeed(&new, new_speed);
|
||||
cfsetospeed(&new, new_speed);
|
||||
} else if (!strcmp(arg, "ispeed"))
|
||||
cfsetispeed(&new, speed(get_arg(&i, 0, 4000000)));
|
||||
else if (!strcmp(arg, "ospeed"))
|
||||
cfsetospeed(&new, speed(get_arg(&i, 0, 4000000)));
|
||||
else if (!strcmp(arg, "rows")) set_size(1, get_arg(&i, 0, USHRT_MAX));
|
||||
else if (!strcmp(arg, "cols") || !strcmp(arg, "columns"))
|
||||
set_size(0, get_arg(&i, 0, USHRT_MAX));
|
||||
else if (sscanf(arg, "%x:%x:%x:%x:%n", &new.c_iflag, &new.c_oflag,
|
||||
&new.c_cflag, &new.c_lflag, &n) == 4)
|
||||
{
|
||||
int value;
|
||||
|
||||
arg += n;
|
||||
for (j=0;j<NCCS;j++) {
|
||||
if (sscanf(arg, "%x%n", &value, &n) != 1) error_exit("bad -g string");
|
||||
new.c_cc[j] = value;
|
||||
arg += n+1;
|
||||
}
|
||||
} else if (set_special_character(&new, &i, arg));
|
||||
// Already done as a side effect.
|
||||
else if (!strcmp(arg, "cooked"))
|
||||
set_options(&new, "brkint", "ignpar", "istrip", "icrnl", "ixon",
|
||||
"opost", "isig", "icanon", NULL);
|
||||
else if (!strcmp(arg, "evenp") || !strcmp(arg, "parity"))
|
||||
set_options(&new, "parenb", "cs7", "-parodd", NULL);
|
||||
else if (!strcmp(arg, "oddp"))
|
||||
set_options(&new, "parenb", "cs7", "parodd", NULL);
|
||||
else if (!strcmp(arg, "-parity") || !strcmp(arg, "-evenp") ||
|
||||
!strcmp(arg, "-oddp")) {
|
||||
set_options(&new, "-parenb", "cs8", NULL);
|
||||
} else if (!strcmp(arg, "raw")) {
|
||||
// POSIX and "man stty" differ wildly. This is "man stty".
|
||||
set_options(&new, "-ignbrk", "-brkint", "-ignpar", "-parmrk", "-inpck",
|
||||
"-istrip", "-inlcr", "-igncr", "-icrnl", "-ixon", "-ixoff", "-iuclc",
|
||||
"-ixany", "-imaxbel", "-opost", "-isig", "-icanon", "-xcase", NULL);
|
||||
new.c_cc[VMIN] = 1;
|
||||
new.c_cc[VTIME] = 0;
|
||||
} else if (!strcmp(arg, "nl"))
|
||||
set_options(&new, "-icrnl", "-ocrnl", NULL);
|
||||
else if (!strcmp(arg, "-nl"))
|
||||
set_options(&new, "icrnl", "ocrnl", "-inlcr", "-igncr", NULL);
|
||||
else if (!strcmp(arg, "ek")) {
|
||||
new.c_cc[VERASE] = 0x7f;
|
||||
new.c_cc[VKILL] = 0x15;
|
||||
} else if (!strcmp(arg, "sane")) make_sane(&new);
|
||||
else {
|
||||
// Translate historical cruft into canonical forms.
|
||||
for (j=0;j<ARRAY_LEN(synonyms);j++) {
|
||||
if (!strcmp(synonyms[j].from, arg)) {
|
||||
arg = synonyms[j].to;
|
||||
break;
|
||||
}
|
||||
}
|
||||
set_option(&new, arg);
|
||||
}
|
||||
}
|
||||
tcsetattr(TT.fd, TCSAFLUSH, &new);
|
||||
xtcgetattr(&old);
|
||||
if (memcmp(&old, &new, sizeof(old)))
|
||||
error_exit("unable to perform all requested operations on %s", TT.device);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (toys.optflags&FLAG_g) {
|
||||
xprintf("%x:%x:%x:%x:", old.c_iflag, old.c_oflag, old.c_cflag, old.c_lflag);
|
||||
for (i=0;i<NCCS;i++) xprintf("%x%c", old.c_cc[i], i==NCCS-1?'\n':':');
|
||||
return;
|
||||
}
|
||||
|
||||
// Without arguments, "stty" only shows the speed, the line discipline,
|
||||
// special characters and any flags that differ from the "sane" settings.
|
||||
make_sane(&sane);
|
||||
show_speed(&old, 1);
|
||||
if (toys.optflags&FLAG_a) show_size(1);
|
||||
out("line = %d;\n", old.c_line);
|
||||
|
||||
for (i=j=0;i<ARRAY_LEN(chars);i++) {
|
||||
char vis[16] = {};
|
||||
cc_t ch = old.c_cc[chars[i].value];
|
||||
|
||||
if (ch == sane.c_cc[chars[i].value] && (toys.optflags&FLAG_a)==0)
|
||||
continue;
|
||||
|
||||
if (chars[i].value == VMIN || chars[i].value == VTIME) {
|
||||
snprintf(vis, sizeof(vis), "%u", ch);
|
||||
} else if (ch == _POSIX_VDISABLE) {
|
||||
strcat(vis, "<undef>");
|
||||
} else {
|
||||
if (ch > 0x7f) {
|
||||
strcat(vis, "M-");
|
||||
ch -= 128;
|
||||
}
|
||||
if (ch < ' ') sprintf(vis+strlen(vis), "^%c", (ch+'@'));
|
||||
else if (ch == 0x7f) strcat(vis, "^?");
|
||||
else sprintf(vis+strlen(vis), "%c", ch);
|
||||
}
|
||||
out("%s = %s;", chars[i].name, vis);
|
||||
j++;
|
||||
}
|
||||
if (j) out("\n");
|
||||
|
||||
show_flags(old.c_cflag, sane.c_cflag, cflags, ARRAY_LEN(cflags));
|
||||
show_flags(old.c_iflag, sane.c_iflag, iflags, ARRAY_LEN(iflags));
|
||||
show_flags(old.c_oflag, sane.c_oflag, oflags, ARRAY_LEN(oflags));
|
||||
show_flags(old.c_lflag, sane.c_lflag, lflags, ARRAY_LEN(lflags));
|
||||
}
|
||||
|
||||
void stty_main(void)
|
||||
{
|
||||
if (toys.optflags&(FLAG_a|FLAG_g) && *toys.optargs)
|
||||
error_exit("can't make settings with -a/-g");
|
||||
|
||||
if (!TT.device) TT.device = "standard input";
|
||||
else TT.fd=xopen(TT.device, (O_RDWR*!!*toys.optargs)|O_NOCTTY|O_NONBLOCK);
|
||||
|
||||
do_stty();
|
||||
|
||||
if (CFG_TOYBOX_FREE && TT.device) close(TT.fd);
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/* cut.c - print selected ranges from a file
|
||||
*
|
||||
* Copyright 2016 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html
|
||||
*
|
||||
* Deviations from posix: added -DF. We can only accept 512 selections, and
|
||||
* "-" counts as start to end. Using spaces to separate a comma-separated list
|
||||
* is silly and inconsistent with dd, ps, cp, and mount.
|
||||
*
|
||||
* TODO: -s with -c
|
||||
|
||||
USE_CUT(NEWTOY(cut, "b*|c*|f*|F(regex-fields)*|C*|O(output-delimiter):d:sD(allow-duplicates)n[!cbfF]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config CUT
|
||||
bool "cut"
|
||||
default y
|
||||
help
|
||||
usage: cut [-Ds] [-bcCfF LIST] [-dO DELIM] [FILE...]
|
||||
|
||||
Print selected parts of lines from each FILE to standard output.
|
||||
|
||||
Each selection LIST is comma separated, either numbers (counting from 1)
|
||||
or dash separated ranges (inclusive, with X- meaning to end of line and -X
|
||||
from start). By default selection ranges are sorted and collated, use -D
|
||||
to prevent that.
|
||||
|
||||
-b Select bytes (with -n round start/end down to start of utf8 char)
|
||||
-c Select UTF-8 characters
|
||||
-C Select unicode columns
|
||||
-d Use DELIM (default is TAB for -f, run of whitespace for -F)
|
||||
-D Don't sort/collate selections or match -fF lines without delimiter
|
||||
-f Select fields (words) separated by single DELIM character
|
||||
-F Select fields separated by DELIM regex
|
||||
-O Output delimiter (default one space for -F, input delim for -f)
|
||||
-s Skip lines without delimiters
|
||||
*/
|
||||
#define FOR_cut
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *d, *O;
|
||||
struct arg_list *select[5]; // we treat them the same, so loop through
|
||||
|
||||
unsigned line;
|
||||
int pairs;
|
||||
regex_t reg;
|
||||
)
|
||||
|
||||
// Apply selections to an input line, producing output
|
||||
static void cut_line(char **pline, long len)
|
||||
{
|
||||
unsigned *pairs = (void *)toybuf, wc;
|
||||
char *line;
|
||||
int i, j, k;
|
||||
|
||||
if (!pline) return;
|
||||
line = *pline;
|
||||
if (len && line[len-1]=='\n') line[--len] = 0;
|
||||
TT.line++;
|
||||
|
||||
// Loop through selections
|
||||
for (i=0; i<TT.pairs; i++) {
|
||||
unsigned start = pairs[2*i], end = pairs[(2*i)+1], count;
|
||||
char *s = line, *ss, *sss;
|
||||
|
||||
// when the delimiter is \n output lines.
|
||||
if (*TT.d == '\n') {
|
||||
if (TT.line<start || TT.line>end) {
|
||||
if (i+1 == TT.pairs) return;
|
||||
continue;
|
||||
}
|
||||
goto write_line;
|
||||
}
|
||||
|
||||
// input: start/end position, count=difference between them
|
||||
// output: s = start of string, len = bytes to output
|
||||
|
||||
if (start) start--;
|
||||
if (start>=len) continue;
|
||||
if (!end || end>len) end = len;
|
||||
count = end-start;
|
||||
|
||||
// Find start and end of output string for the relevant selection type
|
||||
if (FLAG(b)) {
|
||||
if (!FLAG(n)) s += start;
|
||||
else {
|
||||
if (end>len) end = len;
|
||||
for (sss = ss = s; (k = (ss-line))<end;) {
|
||||
if (0>(j = utf8towc(&wc, ss, len))) ss++;
|
||||
else {
|
||||
if (((ss += j)-line)<=end) sss = ss;
|
||||
if ((ss-line)<=start) s = ss;
|
||||
}
|
||||
}
|
||||
if (!(count = sss-s)) continue;
|
||||
}
|
||||
} else if (FLAG(C)) {
|
||||
// crunch_str() currently assumes that combining characters get
|
||||
// escaped, to provide an unambiguous visual representation.
|
||||
// This assumes the input string is null terminated.
|
||||
if (start) crunch_str(&s, start, 0, 0, 0);
|
||||
if (!*s) continue;
|
||||
start = s-line;
|
||||
ss = s;
|
||||
crunch_str(&ss, count, 0, 0, 0);
|
||||
count = ss-s;
|
||||
|
||||
} else if (FLAG(c)) {
|
||||
|
||||
// Find start
|
||||
ss = line+len;
|
||||
while (start && s<ss) {
|
||||
if (0<=(j = utf8towc(&wc, s, len))) start--;
|
||||
s += (j<1) ? 1 : j;
|
||||
}
|
||||
if (s == ss) continue;
|
||||
|
||||
// Find end
|
||||
end = count;
|
||||
sss = s;
|
||||
while (end && sss<ss) {
|
||||
if (0<=(j = utf8towc(&wc, sss, len))) end--;
|
||||
sss += (j<1) ? 1 : j;
|
||||
}
|
||||
count = sss-s;
|
||||
} else {
|
||||
regmatch_t match;
|
||||
|
||||
// Loop through skipping appropriate number of fields
|
||||
for (j = 0; j<2; j++) {
|
||||
ss = s;
|
||||
if (j) start = count;
|
||||
else end = start;
|
||||
while (*ss && start) {
|
||||
if (FLAG(f)) {
|
||||
if (!strchr(TT.d, *ss++)) continue;
|
||||
if (!--start && j) ss--;
|
||||
} else {
|
||||
if (regexec(&TT.reg, ss, 1, &match, REG_NOTBOL|REG_NOTEOL)) {
|
||||
ss = line+len;
|
||||
continue;
|
||||
}
|
||||
if (!match.rm_eo) break; // zero length match == no delimiter
|
||||
ss += (!--start && j) ? match.rm_so : match.rm_eo;
|
||||
}
|
||||
}
|
||||
if (!j && !*(s = ss)) break;
|
||||
}
|
||||
|
||||
// If we never encountered even one separator, print whole line (posix!)
|
||||
if (!j && end == start) {
|
||||
if (FLAG(D)) break;
|
||||
if (FLAG(s)) return;
|
||||
write_line:
|
||||
fwrite(line, len, 1, stdout);
|
||||
break;
|
||||
} else if (!*s) continue;
|
||||
count = ss-s;
|
||||
}
|
||||
if (i && TT.O) fputs(TT.O, stdout);
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
/* fix "cut -s -d, -f-3 A.txt" not filter problem*/
|
||||
if (!FLAG(s) || strchr(s, *TT.d)) {
|
||||
fwrite(s, count, 1, stdout);
|
||||
}
|
||||
#else
|
||||
fwrite(s, count, 1, stdout);
|
||||
#endif
|
||||
}
|
||||
xputc('\n');
|
||||
}
|
||||
|
||||
static int compar(unsigned *a, unsigned *b)
|
||||
{
|
||||
if (*a<*b) return -1;
|
||||
if (*a>*b) return 1;
|
||||
if (a[1]<b[1]) return -1;
|
||||
if (a[1]>b[1]) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// parse A or A-B or A- or -B
|
||||
static char *get_range(void *data, char *str, int len)
|
||||
{
|
||||
char *end = str;
|
||||
unsigned *pairs = (void *)toybuf, i;
|
||||
|
||||
// Using toybuf[] to store ranges means we can have 512 selections max.
|
||||
if (TT.pairs == sizeof(toybuf)/sizeof(int)) perror_exit("select limit");
|
||||
pairs += 2*TT.pairs++;
|
||||
|
||||
pairs[1] = UINT_MAX;
|
||||
for (i = 0; ;i++) {
|
||||
if (i==2) return end;
|
||||
if (isdigit(*end)) {
|
||||
long long ll = estrtol(end, &end, 10);
|
||||
|
||||
if (ll<1 || ll>UINT_MAX || errno) return end;
|
||||
pairs[i] = ll;
|
||||
}
|
||||
if (*end++ != '-') break;
|
||||
}
|
||||
if (!i) pairs[1] = pairs[0];
|
||||
if ((end-str)<len) return end;
|
||||
if (pairs[0]>pairs[1]) return str;
|
||||
|
||||
// No error
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cut_main(void)
|
||||
{
|
||||
int i;
|
||||
char buf[8];
|
||||
|
||||
// Parse command line arguments
|
||||
if ((toys.optflags&(FLAG_s|FLAG_f|FLAG_F))==FLAG_s)
|
||||
error_exit("-s needs -Ff");
|
||||
if ((toys.optflags&(FLAG_d|FLAG_f|FLAG_F))==FLAG_d)
|
||||
error_exit("-d needs -Ff");
|
||||
if (!TT.d) TT.d = (FLAG(F)) ? "[[:space:]][[:space:]]*" : "\t";
|
||||
if (FLAG(F)) xregcomp(&TT.reg, TT.d, REG_EXTENDED);
|
||||
if (!TT.O) {
|
||||
if (FLAG(F)) TT.O = " ";
|
||||
else if (FLAG(f)) TT.O = TT.d;
|
||||
}
|
||||
|
||||
// Parse ranges, which are attached to a selection type (only one can be set)
|
||||
for (i = 0; i<ARRAY_LEN(TT.select); i++) {
|
||||
sprintf(buf, "bad -%c", "CFfcb"[i]); // reverse order from newtoy optstr
|
||||
if (TT.select[i]) comma_args(TT.select[i], 0, buf, get_range);
|
||||
}
|
||||
if (!TT.pairs) error_exit("no selections");
|
||||
|
||||
// Sort and collate selections
|
||||
if (!FLAG(D)) {
|
||||
int from, to;
|
||||
unsigned *pairs = (void *)toybuf;
|
||||
|
||||
qsort(toybuf, TT.pairs, 8, (void *)compar);
|
||||
for (to = 0, from = 2; from/2 < TT.pairs; from += 2) {
|
||||
if (pairs[from] > pairs[to+1]) {
|
||||
to += 2;
|
||||
memcpy(pairs+to, pairs+from, 2*sizeof(unsigned));
|
||||
} else if (pairs[from+1] > pairs[to+1]) pairs[to+1] = pairs[from+1];
|
||||
}
|
||||
TT.pairs = (to/2)+1;
|
||||
}
|
||||
|
||||
// For each argument, loop through lines of file and call cut_line() on each
|
||||
loopfiles_lines(toys.optargs, cut_line);
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/* id.c - print real and effective user and group IDs
|
||||
*
|
||||
* Copyright 2012 Sony Network Entertainment, Inc.
|
||||
*
|
||||
* by Tim Bird <tim.bird@am.sony.com>
|
||||
*
|
||||
* See http://opengroup.org/onlinepubs/9699919799/utilities/id.html
|
||||
|
||||
USE_ID(NEWTOY(id, ">1"USE_ID_Z("Z")"nGgru[!"USE_ID_Z("Z")"Ggu]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_GROUPS(NEWTOY(groups, NULL, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_WHOAMI(OLDTOY(whoami, logname, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config ID
|
||||
bool "id"
|
||||
default y
|
||||
help
|
||||
usage: id [-nGgru] [USER...]
|
||||
|
||||
Print user and group ID.
|
||||
|
||||
-n Print names instead of numeric IDs (to be used with -Ggu)
|
||||
-G Show only the group IDs
|
||||
-g Show only the effective group ID
|
||||
-r Show real ID instead of effective ID
|
||||
-u Show only the effective user ID
|
||||
|
||||
config ID_Z
|
||||
bool
|
||||
default y
|
||||
depends on ID && !TOYBOX_LSM_NONE
|
||||
help
|
||||
usage: id [-Z]
|
||||
|
||||
-Z Show only security context
|
||||
|
||||
config GROUPS
|
||||
bool "groups"
|
||||
default y
|
||||
help
|
||||
usage: groups [user]
|
||||
|
||||
Print the groups a user is in.
|
||||
|
||||
config LOGNAME
|
||||
bool "logname"
|
||||
default y
|
||||
help
|
||||
usage: logname
|
||||
|
||||
Print the current user name.
|
||||
|
||||
config WHOAMI
|
||||
bool "whoami"
|
||||
default y
|
||||
help
|
||||
usage: whoami
|
||||
|
||||
Print the current user name.
|
||||
*/
|
||||
|
||||
#define FOR_id
|
||||
#define FORCE_FLAGS
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
int is_groups;
|
||||
)
|
||||
|
||||
static void s_or_u(char *s, unsigned u, int done)
|
||||
{
|
||||
if (toys.optflags&FLAG_n) printf("%s", s);
|
||||
else printf("%u", u);
|
||||
if (done) {
|
||||
xputc('\n');
|
||||
xexit();
|
||||
}
|
||||
}
|
||||
|
||||
static void showid(char *header, unsigned u, char *s)
|
||||
{
|
||||
printf("%s%u(%s)", header, u, s);
|
||||
}
|
||||
|
||||
static void do_id(char *username)
|
||||
{
|
||||
int flags, i, ngroups;
|
||||
struct passwd *pw;
|
||||
struct group *grp;
|
||||
uid_t uid = getuid(), euid = geteuid();
|
||||
gid_t gid = getgid(), egid = getegid(), *groups;
|
||||
|
||||
flags = toys.optflags;
|
||||
|
||||
// check if a username is given
|
||||
if (username) {
|
||||
pw = xgetpwnam(username);
|
||||
uid = euid = pw->pw_uid;
|
||||
gid = egid = pw->pw_gid;
|
||||
if (TT.is_groups) printf("%s : ", pw->pw_name);
|
||||
}
|
||||
|
||||
i = flags & FLAG_r;
|
||||
pw = xgetpwuid(i ? uid : euid);
|
||||
if (toys.optflags&FLAG_u) s_or_u(pw->pw_name, pw->pw_uid, 1);
|
||||
|
||||
grp = xgetgrgid(i ? gid : egid);
|
||||
if (flags & FLAG_g) s_or_u(grp->gr_name, grp->gr_gid, 1);
|
||||
|
||||
if (!(toys.optflags&(FLAG_G|FLAG_g|FLAG_Z))) {
|
||||
showid("uid=", pw->pw_uid, pw->pw_name);
|
||||
showid(" gid=", grp->gr_gid, grp->gr_name);
|
||||
|
||||
if (!i) {
|
||||
if (uid != euid) {
|
||||
pw = xgetpwuid(euid);
|
||||
showid(" euid=", pw->pw_uid, pw->pw_name);
|
||||
}
|
||||
if (gid != egid) {
|
||||
grp = xgetgrgid(egid);
|
||||
showid(" egid=", grp->gr_gid, grp->gr_name);
|
||||
}
|
||||
}
|
||||
|
||||
showid(" groups=", grp->gr_gid, grp->gr_name);
|
||||
}
|
||||
|
||||
if (!(toys.optflags&FLAG_Z)) {
|
||||
groups = (gid_t *)toybuf;
|
||||
i = sizeof(toybuf)/sizeof(gid_t);
|
||||
ngroups = username ? getgrouplist(username, gid, groups, &i)
|
||||
: getgroups(i, groups);
|
||||
if (ngroups<0) perror_exit(0);
|
||||
|
||||
int show_separator = !(toys.optflags&FLAG_G);
|
||||
for (i = 0; i<ngroups; i++) {
|
||||
if (show_separator) xputc((toys.optflags&FLAG_G) ? ' ' : ',');
|
||||
show_separator = 1;
|
||||
if (!(grp = getgrgid(groups[i]))) perror_msg(0);
|
||||
else if (toys.optflags&FLAG_G) s_or_u(grp->gr_name, grp->gr_gid, 0);
|
||||
else if (grp->gr_gid != egid) showid("", grp->gr_gid, grp->gr_name);
|
||||
else show_separator = 0; // Because we didn't show anything this time.
|
||||
}
|
||||
if (toys.optflags&FLAG_G) {
|
||||
xputc('\n');
|
||||
xexit();
|
||||
}
|
||||
}
|
||||
|
||||
if (!CFG_TOYBOX_LSM_NONE) {
|
||||
if (lsm_enabled()) {
|
||||
char *context = lsm_context();
|
||||
|
||||
printf(" context=%s"+!!(toys.optflags&FLAG_Z), context);
|
||||
if (CFG_TOYBOX_FREE) free(context);
|
||||
} else if (toys.optflags&FLAG_Z) error_exit("%s disabled", lsm_name());
|
||||
}
|
||||
|
||||
xputc('\n');
|
||||
}
|
||||
|
||||
void id_main(void)
|
||||
{
|
||||
if (toys.optc) while(*toys.optargs) do_id(*toys.optargs++);
|
||||
else do_id(NULL);
|
||||
}
|
||||
|
||||
void groups_main(void)
|
||||
{
|
||||
TT.is_groups = 1;
|
||||
toys.optflags = FLAG_G|FLAG_n;
|
||||
id_main();
|
||||
}
|
||||
|
||||
void logname_main(void)
|
||||
{
|
||||
toys.optflags = FLAG_u|FLAG_n;
|
||||
id_main();
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/* logger.c - Log messages.
|
||||
*
|
||||
* Copyright 2013 Ilya Kuzmich <ilya.kuzmich@gmail.com>
|
||||
*
|
||||
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/logger.html
|
||||
*
|
||||
* Deviations from posix: specified manner and format, defined implementation.
|
||||
|
||||
USE_LOGGER(NEWTOY(logger, "st:p:", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config LOGGER
|
||||
bool "logger"
|
||||
default y
|
||||
help
|
||||
usage: logger [-s] [-t TAG] [-p [FACILITY.]PRIORITY] [message...]
|
||||
|
||||
Log message (or stdin) to syslog.
|
||||
|
||||
-s Also write message to stderr
|
||||
-t Use TAG instead of username to identify message source
|
||||
-p Specify PRIORITY with optional FACILITY. Default is "user.notice"
|
||||
*/
|
||||
|
||||
#define FOR_logger
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *p, *t;
|
||||
)
|
||||
|
||||
// find str in names[], accepting unambiguous short matches
|
||||
// returns offset into array of match, or -1 if no match
|
||||
int arrayfind(char *str, char *names[], int len)
|
||||
{
|
||||
int try, i, matchlen = 0, found = -1, ambiguous = 1;
|
||||
|
||||
for (try = 0; try<len; try++) {
|
||||
for (i=0; ; i++) {
|
||||
if (!str[i]) {
|
||||
if (matchlen<i) found = try, ambiguous = 0;
|
||||
if (matchlen==i) ambiguous++;
|
||||
if (!names[try][i]) return try;
|
||||
break;
|
||||
}
|
||||
if (!names[try][i]) break;
|
||||
if (toupper(str[i]) != toupper(names[try][i])) break;
|
||||
}
|
||||
}
|
||||
return ambiguous ? -1 : found;
|
||||
}
|
||||
|
||||
void logger_main(void)
|
||||
{
|
||||
int facility = LOG_USER, priority = LOG_NOTICE, len;
|
||||
char *s1, *s2, **arg,
|
||||
*priorities[] = {"emerg", "alert", "crit", "error", "warning", "notice",
|
||||
"info", "debug"},
|
||||
*facilities[] = {"kern", "user", "mail", "daemon", "auth", "syslog",
|
||||
"lpr", "news", "uucp", "cron", "authpriv", "ftp"};
|
||||
|
||||
if (!TT.t) TT.t = xstrdup(xgetpwuid(geteuid())->pw_name);
|
||||
if (toys.optflags & FLAG_p) {
|
||||
if (!(s1 = strchr(TT.p, '.'))) s1 = TT.p;
|
||||
else {
|
||||
*s1++ = len = 0;
|
||||
facility = arrayfind(TT.p, facilities, ARRAY_LEN(facilities));
|
||||
if (facility == -1 && strncasecmp(TT.p, "local", 5)) {
|
||||
facility = s1[5]-'0';
|
||||
if (facility>7 || s1[6]) facility = -1;
|
||||
if (facility>=0) facility += 16;
|
||||
}
|
||||
if (facility<0) error_exit("bad facility: %s", TT.p);
|
||||
facility *= 8;
|
||||
}
|
||||
|
||||
priority = arrayfind(s1, priorities, ARRAY_LEN(priorities));
|
||||
if (priority<0) error_exit("bad priority: %s", s1);
|
||||
}
|
||||
|
||||
if (toys.optc) {
|
||||
for (len = 0, arg = toys.optargs; *arg; arg++) len += strlen(*arg)+1;
|
||||
s1 = s2 = xmalloc(len);
|
||||
for (arg = toys.optargs; *arg; arg++) {
|
||||
if (arg != toys.optargs) *s2++ = ' ';
|
||||
s2 = stpcpy(s2, *arg);
|
||||
}
|
||||
} else {
|
||||
toybuf[readall(0, toybuf, sizeof(toybuf)-1)] = 0;
|
||||
s1 = toybuf;
|
||||
}
|
||||
|
||||
openlog(TT.t, LOG_PERROR*!!(toys.optflags&FLAG_s), facility);
|
||||
syslog(priority, "%s", s1);
|
||||
closelog();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/* tee.c - cat to multiple outputs.
|
||||
*
|
||||
* Copyright 2008 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* See http://opengroup.org/onlinepubs/9699919799/utilities/tee.html
|
||||
|
||||
USE_TEE(NEWTOY(tee, "ia", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config TEE
|
||||
bool "tee"
|
||||
default y
|
||||
help
|
||||
usage: tee [-ai] [file...]
|
||||
|
||||
Copy stdin to each listed file, and also to stdout.
|
||||
Filename "-" is a synonym for stdout.
|
||||
|
||||
-a Append to files
|
||||
-i Ignore SIGINT
|
||||
*/
|
||||
|
||||
#define FOR_tee
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
void *outputs;
|
||||
)
|
||||
|
||||
struct fd_list {
|
||||
struct fd_list *next;
|
||||
int fd;
|
||||
};
|
||||
|
||||
// Open each output file, saving filehandles to a linked list.
|
||||
|
||||
static void do_tee_open(int fd, char *name)
|
||||
{
|
||||
struct fd_list *temp;
|
||||
|
||||
temp = xmalloc(sizeof(struct fd_list));
|
||||
temp->next = TT.outputs;
|
||||
temp->fd = fd;
|
||||
TT.outputs = temp;
|
||||
}
|
||||
|
||||
void tee_main(void)
|
||||
{
|
||||
if (FLAG(i)) xsignal(SIGINT, SIG_IGN);
|
||||
|
||||
// Open output files
|
||||
loopfiles_rw(toys.optargs,
|
||||
O_RDWR|O_CREAT|WARN_ONLY|(FLAG(a)?O_APPEND:O_TRUNC),
|
||||
0666, do_tee_open);
|
||||
|
||||
for (;;) {
|
||||
struct fd_list *fdl;
|
||||
int len;
|
||||
|
||||
// Read data from stdin
|
||||
len = xread(0, toybuf, sizeof(toybuf));
|
||||
if (len<1) break;
|
||||
|
||||
// Write data to each output file, plus stdout.
|
||||
fdl = TT.outputs;
|
||||
for (;;) {
|
||||
if(len != writeall(fdl ? fdl->fd : 1, toybuf, len)) toys.exitval=1;
|
||||
if (!fdl) break;
|
||||
fdl = fdl->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* time.c - time a simple command
|
||||
*
|
||||
* Copyright 2013 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/time.html
|
||||
|
||||
USE_TIME(NEWTOY(time, "<1^pv", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config TIME
|
||||
bool "time"
|
||||
default y
|
||||
depends on TOYBOX_FLOAT
|
||||
help
|
||||
usage: time [-pv] COMMAND [ARGS...]
|
||||
|
||||
Run command line and report real, user, and system time elapsed in seconds.
|
||||
(real = clock on the wall, user = cpu used by command's code,
|
||||
system = cpu used by OS on behalf of command.)
|
||||
|
||||
-p POSIX format output (default)
|
||||
-v Verbose
|
||||
*/
|
||||
|
||||
#define FOR_time
|
||||
#include "toys.h"
|
||||
|
||||
void time_main(void)
|
||||
{
|
||||
pid_t pid;
|
||||
struct timeval tv, tv2;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
if (!(pid = XVFORK())) xexec(toys.optargs);
|
||||
else {
|
||||
int stat;
|
||||
struct rusage ru;
|
||||
float r, u, s;
|
||||
|
||||
wait4(pid, &stat, 0, &ru);
|
||||
gettimeofday(&tv2, NULL);
|
||||
if (tv.tv_usec > tv2.tv_usec) {
|
||||
tv2.tv_usec += 1000000;
|
||||
tv2.tv_sec--;
|
||||
}
|
||||
r = (tv2.tv_sec-tv.tv_sec)+((tv2.tv_usec-tv.tv_usec)/1000000.0);
|
||||
u = ru.ru_utime.tv_sec+(ru.ru_utime.tv_usec/1000000.0);
|
||||
s = ru.ru_stime.tv_sec+(ru.ru_stime.tv_usec/1000000.0);
|
||||
if (FLAG(v)) {
|
||||
fprintf(stderr, "Real time (s): %f\n"
|
||||
"System time (s): %f\n"
|
||||
"User time (s): %f\n"
|
||||
"Max RSS (KiB): %ld\n"
|
||||
"Major faults: %ld\n"
|
||||
"Minor faults: %ld\n"
|
||||
"File system inputs: %ld\n"
|
||||
"File system outputs: %ld\n"
|
||||
"Voluntary context switches: %ld\n"
|
||||
"Involuntary context switches: %ld\n", r, s, u,
|
||||
ru.ru_maxrss, ru.ru_majflt, ru.ru_minflt, ru.ru_inblock,
|
||||
ru.ru_oublock, ru.ru_nvcsw, ru.ru_nivcsw);
|
||||
} else fprintf(stderr, "real %f\nuser %f\nsys %f\n", r, u, s);
|
||||
toys.exitval = WIFEXITED(stat) ? WEXITSTATUS(stat) : WTERMSIG(stat);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* tty.c - Show stdin's terminal name
|
||||
*
|
||||
* Copyright 2011 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* See http://opengroup.org/onlinepubs/9699919799/utilities/tty.html
|
||||
|
||||
USE_TTY(NEWTOY(tty, "s", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config TTY
|
||||
bool "tty"
|
||||
default y
|
||||
help
|
||||
usage: tty [-s]
|
||||
|
||||
Show filename of terminal connected to stdin.
|
||||
|
||||
Prints "not a tty" and exits with nonzero status if no terminal
|
||||
is connected to stdin.
|
||||
|
||||
-s Silent, exit code only
|
||||
*/
|
||||
|
||||
#include "toys.h"
|
||||
|
||||
void tty_main(void)
|
||||
{
|
||||
char *tty = ttyname(0);
|
||||
|
||||
if (!toys.optflags) puts(tty ? tty : "not a tty");
|
||||
|
||||
toys.exitval = !tty;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/* ulimit.c - Modify resource limits
|
||||
*
|
||||
* Copyright 2015 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ulimit.html
|
||||
* And man prlimit(2).
|
||||
*
|
||||
* Deviations from posix: The units on -f are supposed to be 512 byte
|
||||
* "blocks" (no other options are specified, and even hard drives don't
|
||||
* do that anymore). Bash uses 1024 byte blocks, so they don't care either.
|
||||
* We consistently use bytes everywhere we can.
|
||||
*
|
||||
* Deviations from bash: Sizes are in bytes (instead of -p 512 and -f 1024).
|
||||
* Bash's -p value has been wrong since 2010 (git 35f3d14dbbc5).
|
||||
* The kernel implementation of RLIMIT_LOCKS (-x) was removed from Linux in
|
||||
* 2003. Bash never implemented -b (it's in the help but unrecognized at
|
||||
* runtime). We support -P to affect processes other than us.
|
||||
|
||||
USE_ULIMIT(NEWTOY(ulimit, ">1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_ULIMIT(OLDTOY(prlimit, ulimit, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config ULIMIT
|
||||
bool "ulimit"
|
||||
default y
|
||||
depends on TOYBOX_PRLIMIT
|
||||
help
|
||||
usage: ulimit [-P PID] [-SHRacdefilmnpqrstuv] [LIMIT]
|
||||
|
||||
Print or set resource limits for process number PID. If no LIMIT specified
|
||||
(or read-only -ap selected) display current value (sizes in bytes).
|
||||
Default is ulimit -P $PPID -Sf" (show soft filesize of your shell).
|
||||
|
||||
-S Set/show soft limit -H Set/show hard (maximum) limit
|
||||
-a Show all limits -c Core file size
|
||||
-d Process data segment -e Max scheduling priority
|
||||
-f Output file size -i Pending signal count
|
||||
-l Locked memory -m Resident Set Size
|
||||
-n Number of open files -p Pipe buffer
|
||||
-q Posix message queue -r Max Real-time priority
|
||||
-R Realtime latency (usec) -s Stack size
|
||||
-t Total CPU time (in seconds) -u Maximum processes (under this UID)
|
||||
-v Virtual memory size -P PID to affect (default $PPID)
|
||||
*/
|
||||
|
||||
#define FOR_ulimit
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
long P;
|
||||
)
|
||||
|
||||
// This is a linux kernel syscall added in 2.6.36 (git c022a0acad53) which
|
||||
// glibc only exports a wrapper prototype for if you #define _FSF_HURD_RULZE.
|
||||
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit,
|
||||
struct rlimit *old_limit);
|
||||
|
||||
// I'd like to sort the RLIMIT values 0-15, but mips, alpha and sparc
|
||||
// override the asm-generic values for 5-9. Also, the kernel implementation
|
||||
// of RLIMIT_LOCKS (-x) was removed from Linux in 2003.
|
||||
void ulimit_main(void)
|
||||
{
|
||||
struct rlimit rr;
|
||||
int i;
|
||||
// Order is cdefilmnqRrstuv
|
||||
char map[] = {RLIMIT_CORE, RLIMIT_DATA, RLIMIT_NICE, RLIMIT_FSIZE,
|
||||
RLIMIT_SIGPENDING, RLIMIT_MEMLOCK, RLIMIT_RSS, RLIMIT_NOFILE, 0,
|
||||
RLIMIT_MSGQUEUE, RLIMIT_RTTIME, RLIMIT_RTPRIO, RLIMIT_STACK,
|
||||
RLIMIT_CPU, RLIMIT_NPROC, RLIMIT_AS};
|
||||
|
||||
if (!(toys.optflags&(FLAG_H-1))) toys.optflags |= FLAG_f;
|
||||
if ((FLAG(a)||FLAG(p)) && toys.optc) error_exit("can't set -ap");
|
||||
|
||||
// Fetch data
|
||||
if (!FLAG(P)) TT.P = getppid();
|
||||
|
||||
for (i=0; i<sizeof(map); i++) {
|
||||
char *flags="cdefilmnpqRrstuv";
|
||||
|
||||
int get = toys.optflags&(FLAG_a|(1<<i));
|
||||
|
||||
if (get && prlimit(TT.P, map[i], 0, &rr)) perror_exit("-%c", flags[i]);
|
||||
if (!toys.optc) {
|
||||
if (FLAG(a)) printf("-%c: ", flags[i]);
|
||||
if (get) {
|
||||
if ((1<<i)&FLAG_p) {
|
||||
if (FLAG(H))
|
||||
xreadfile("/proc/sys/fs/pipe-max-size", toybuf, sizeof(toybuf));
|
||||
else {
|
||||
int pp[2];
|
||||
|
||||
xpipe(pp);
|
||||
sprintf(toybuf, "%d\n", fcntl(*pp, F_GETPIPE_SZ));
|
||||
}
|
||||
printf("%s", toybuf);
|
||||
} else {
|
||||
rlim_t rl = FLAG(H) ? rr.rlim_max : rr.rlim_cur;
|
||||
|
||||
if (rl == RLIM_INFINITY) printf("unlimited\n");
|
||||
else printf("%ld\n", (long)rl);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toys.optflags&(1<<i)) break;
|
||||
}
|
||||
|
||||
if (FLAG(a)||FLAG(p)) return;
|
||||
|
||||
if (toys.optc) {
|
||||
rlim_t val;
|
||||
|
||||
if (tolower(**toys.optargs) == 'u') val = RLIM_INFINITY;
|
||||
else val = atolx_range(*toys.optargs, 0, LONG_MAX);
|
||||
|
||||
if (FLAG(H)) rr.rlim_max = val;
|
||||
else rr.rlim_cur = val;
|
||||
if (prlimit(TT.P, map[i], &rr, 0)) perror_exit(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/* xargs.c - Run command with arguments taken from stdin.
|
||||
*
|
||||
* Copyright 2011 Rob Landley <rob@landley.net>
|
||||
*
|
||||
* See http://opengroup.org/onlinepubs/9699919799/utilities/xargs.html
|
||||
*
|
||||
* TODO: Rich's whitespace objection, env size isn't fixed anymore.
|
||||
* TODO: -I Insert mode
|
||||
* TODO: -L Max number of lines of input per command
|
||||
* TODO: -x Exit if can't fit everything in one command
|
||||
* TODO: -P NUM Run up to NUM processes at once
|
||||
|
||||
USE_XARGS(NEWTOY(xargs, "^E:P#optrn#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config XARGS
|
||||
bool "xargs"
|
||||
default y
|
||||
help
|
||||
usage: xargs [-0prt] [-s NUM] [-n NUM] [-E STR] COMMAND...
|
||||
|
||||
Run command line one or more times, appending arguments from stdin.
|
||||
|
||||
If COMMAND exits with 255, don't launch another even if arguments remain.
|
||||
|
||||
-0 Each argument is NULL terminated, no whitespace or quote processing
|
||||
-E Stop at line matching string
|
||||
-n Max number of arguments per command
|
||||
-o Open tty for COMMAND's stdin (default /dev/null)
|
||||
-p Prompt for y/n from tty before running each command
|
||||
-r Don't run command with empty input (otherwise always run command once)
|
||||
-s Size in bytes per command line
|
||||
-t Trace, print command line to stderr
|
||||
*/
|
||||
|
||||
#define FOR_xargs
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
long s, n, P;
|
||||
char *E;
|
||||
|
||||
long entries, bytes;
|
||||
char delim;
|
||||
FILE *tty;
|
||||
)
|
||||
|
||||
// If !entry count TT.bytes and TT.entries, stopping at max.
|
||||
// Otherwise, fill out entry[].
|
||||
|
||||
// Returning NULL means need more data.
|
||||
// Returning char * means hit data limits, start of data left over
|
||||
// Returning 1 means hit data limits, but consumed all data
|
||||
// Returning 2 means hit -E STR
|
||||
|
||||
static char *handle_entries(char *data, char **entry)
|
||||
{
|
||||
if (TT.delim) {
|
||||
char *save, *s = data;
|
||||
|
||||
// Chop up whitespace delimited string into args
|
||||
while (*s) {
|
||||
while (isspace(*s)) {
|
||||
if (entry) *s = 0;
|
||||
s++;
|
||||
}
|
||||
|
||||
if (TT.n && TT.entries >= TT.n)
|
||||
return *s ? s : (char *)1;
|
||||
|
||||
if (!*s) break;
|
||||
save = s;
|
||||
|
||||
// We ought to add sizeof(char *) to TT.bytes to be correct, but we don't
|
||||
// for bug compatibility with busybox 1.30.1 and findutils 4.7.0.
|
||||
|
||||
for (;;) {
|
||||
if (++TT.bytes >= TT.s && TT.s) return save;
|
||||
if (!*s || isspace(*s)) break;
|
||||
s++;
|
||||
}
|
||||
if (TT.E && strstart(&save, TT.E)) return (char *)2;
|
||||
if (entry) entry[TT.entries] = save;
|
||||
++TT.entries;
|
||||
}
|
||||
|
||||
// -0 support
|
||||
} else {
|
||||
TT.bytes += sizeof(char *)+strlen(data)+1;
|
||||
if ((TT.s && TT.bytes >= TT.s) || (TT.n && TT.entries >= TT.n)) return data;
|
||||
if (entry) entry[TT.entries] = data;
|
||||
TT.entries++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xargs_main(void)
|
||||
{
|
||||
struct double_list *dlist = 0, *dtemp;
|
||||
int entries, bytes, done = 0, ran_once = 0, status;
|
||||
char *data = 0, **out;
|
||||
pid_t pid;
|
||||
|
||||
// POSIX requires that we never hit the ARG_MAX limit, even if we try to
|
||||
// with -s. POSIX also says we have to reserve 2048 bytes "to guarantee
|
||||
// that the invoked utility has room to modify its environment variables
|
||||
// and command line arguments and still be able to invoke another utility",
|
||||
// though obviously that's not really something you can guarantee.
|
||||
bytes = sysconf(_SC_ARG_MAX) - environ_bytes() - 2048;
|
||||
if (!TT.s || TT.s > bytes) TT.s = bytes;
|
||||
|
||||
TT.delim = '\n'*!FLAG(0);
|
||||
|
||||
// If no optargs, call echo.
|
||||
if (!toys.optc) {
|
||||
free(toys.optargs);
|
||||
*(toys.optargs = xzalloc(2*sizeof(char *)))="echo";
|
||||
toys.optc = 1;
|
||||
}
|
||||
|
||||
// count entries
|
||||
for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++)
|
||||
bytes += strlen(toys.optargs[entries]);
|
||||
if (bytes >= TT.s) error_exit("argument too long");
|
||||
|
||||
// Loop through exec chunks.
|
||||
while (data || !done) {
|
||||
TT.entries = 0;
|
||||
TT.bytes = bytes;
|
||||
|
||||
// Loop reading input
|
||||
for (;;) {
|
||||
|
||||
// Read line
|
||||
if (!data) {
|
||||
ssize_t l = 0;
|
||||
if (getdelim(&data, (size_t *)&l, TT.delim, stdin)<0) {
|
||||
data = 0;
|
||||
done++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
dlist_add(&dlist, data);
|
||||
|
||||
// Count data used
|
||||
if (!(data = handle_entries(data, 0))) continue;
|
||||
if (data == (char *)2) done++;
|
||||
if ((unsigned long)data <= 2) data = 0;
|
||||
else data = xstrdup(data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!TT.entries) {
|
||||
if (data) error_exit("argument too long");
|
||||
else if (ran_once) return;
|
||||
else if (FLAG(r)) continue;
|
||||
}
|
||||
|
||||
// Fill out command line to exec
|
||||
out = xzalloc((entries+TT.entries+1)*sizeof(char *));
|
||||
memcpy(out, toys.optargs, entries*sizeof(char *));
|
||||
TT.entries = 0;
|
||||
TT.bytes = bytes;
|
||||
if (dlist) dlist->prev->next = 0;
|
||||
for (dtemp = dlist; dtemp; dtemp = dtemp->next)
|
||||
handle_entries(dtemp->data, out+entries);
|
||||
|
||||
if (FLAG(p) || FLAG(t)) {
|
||||
int i;
|
||||
|
||||
for (i = 0; out[i]; ++i) fprintf(stderr, "%s ", out[i]);
|
||||
if (FLAG(p)) {
|
||||
fprintf(stderr, "?");
|
||||
if (!TT.tty) TT.tty = xfopen("/dev/tty", "re");
|
||||
if (!fyesno(TT.tty, 0)) goto skip;
|
||||
} else fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if (!(pid = XVFORK())) {
|
||||
close(0);
|
||||
xopen_stdio(FLAG(o) ? "/dev/tty" : "/dev/null", O_RDONLY);
|
||||
xexec(out);
|
||||
}
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
// xargs is yet another weird collection of exit value special cases,
|
||||
// different from all the others.
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) == 126 || WEXITSTATUS(status) == 127) {
|
||||
toys.exitval = WEXITSTATUS(status);
|
||||
return;
|
||||
} else if (WEXITSTATUS(status) >= 1 && WEXITSTATUS(status) <= 125) {
|
||||
toys.exitval = 123;
|
||||
} else if (WEXITSTATUS(status) == 255) {
|
||||
error_msg("%s: exited with status 255; aborting", out[0]);
|
||||
toys.exitval = 124;
|
||||
return;
|
||||
}
|
||||
} else toys.exitval = 127;
|
||||
|
||||
// Abritrary number of execs, can't just leak memory each time...
|
||||
skip:
|
||||
ran_once = 1;
|
||||
while (dlist) {
|
||||
struct double_list *dtemp = dlist->next;
|
||||
|
||||
free(dlist->data);
|
||||
free(dlist);
|
||||
dlist = dtemp;
|
||||
}
|
||||
free(out);
|
||||
}
|
||||
if (TT.tty) fclose(TT.tty);
|
||||
}
|
||||
+25
-16
@@ -2,16 +2,16 @@
|
||||
*
|
||||
* Copyright 2016 The Android Open Source Project
|
||||
|
||||
USE_LOG(NEWTOY(log, "<1p:t:", TOYFLAG_USR|TOYFLAG_SBIN))
|
||||
USE_LOG(NEWTOY(log, "p:t:", TOYFLAG_USR|TOYFLAG_SBIN))
|
||||
|
||||
config LOG
|
||||
bool "log"
|
||||
depends on TOYBOX_ON_ANDROID
|
||||
default y
|
||||
help
|
||||
usage: log [-p PRI] [-t TAG] MESSAGE...
|
||||
usage: log [-p PRI] [-t TAG] [MESSAGE...]
|
||||
|
||||
Logs message to logcat.
|
||||
Logs message (or stdin) to logcat.
|
||||
|
||||
-p Use the given priority instead of INFO:
|
||||
d: DEBUG e: ERROR f: FATAL i: INFO v: VERBOSE w: WARN s: SILENT
|
||||
@@ -23,34 +23,43 @@ config LOG
|
||||
|
||||
GLOBALS(
|
||||
char *t, *p;
|
||||
|
||||
int pri;
|
||||
)
|
||||
|
||||
static void log_line(char **pline, long len)
|
||||
{
|
||||
if (!pline) return;
|
||||
__android_log_write(TT.pri, TT.t, *pline);
|
||||
}
|
||||
|
||||
void log_main(void)
|
||||
{
|
||||
android_LogPriority pri = ANDROID_LOG_INFO;
|
||||
char *s = toybuf;
|
||||
int i;
|
||||
|
||||
TT.pri = ANDROID_LOG_INFO;
|
||||
if (TT.p) {
|
||||
i = stridx("defisvw", tolower(*TT.p));
|
||||
if (i==-1 || strlen(TT.p)!=1) error_exit("bad -p '%s'", TT.p);
|
||||
pri = (android_LogPriority []){ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR,
|
||||
TT.pri = (int[]) {ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR,
|
||||
ANDROID_LOG_FATAL, ANDROID_LOG_INFO, ANDROID_LOG_SILENT,
|
||||
ANDROID_LOG_VERBOSE, ANDROID_LOG_WARN}[i];
|
||||
}
|
||||
if (!TT.t) TT.t = "log";
|
||||
|
||||
for (i = 0; toys.optargs[i]; i++) {
|
||||
if (i) *s++ = ' ';
|
||||
if ((s-toybuf)+strlen(toys.optargs[i])>=1024) {
|
||||
memcpy(s, toys.optargs[i], 1024-(s-toybuf));
|
||||
toybuf[1024] = 0;
|
||||
error_msg("log cut at 1024 bytes");
|
||||
|
||||
break;
|
||||
if (toys.optc) {
|
||||
for (i = 0; toys.optargs[i]; i++) {
|
||||
if (i) *s++ = ' ';
|
||||
if ((s-toybuf)+strlen(toys.optargs[i])>=1024) {
|
||||
memcpy(s, toys.optargs[i], 1024-(s-toybuf));
|
||||
toybuf[1024] = 0;
|
||||
error_msg("log cut at 1024 bytes");
|
||||
break;
|
||||
}
|
||||
s = stpcpy(s, toys.optargs[i]);
|
||||
}
|
||||
s = stpcpy(s, toys.optargs[i]);
|
||||
}
|
||||
} else do_lines(0, '\n', log_line);
|
||||
|
||||
__android_log_write(pri, TT.t, toybuf);
|
||||
__android_log_write(TT.pri, TT.t, toybuf);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* Copyright 2015 The Android Open Source Project
|
||||
|
||||
USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
|
||||
USE_RUNCON(NEWTOY(runcon, "^<2", TOYFLAG_USR|TOYFLAG_SBIN))
|
||||
|
||||
config RUNCON
|
||||
bool "runcon"
|
||||
|
||||
+15
-16
@@ -12,7 +12,6 @@ USE_SU(NEWTOY(su, "^lmpu:g:c:s:[!lmp]", TOYFLAG_BIN|TOYFLAG_ROOTONLY))
|
||||
config SU
|
||||
bool "su"
|
||||
default y
|
||||
depends on TOYBOX_SHADOW
|
||||
help
|
||||
usage: su [-lp] [-u UID] [-g GID,...] [-s SHELL] [-c CMD] [USER [COMMAND...]]
|
||||
|
||||
@@ -38,15 +37,13 @@ config SU
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
char *s;
|
||||
char *c;
|
||||
char *s, *c;
|
||||
)
|
||||
|
||||
void su_main()
|
||||
{
|
||||
char *name, *passhash = 0, **argu, **argv;
|
||||
char *name, **shadow, *passhash = 0, **argu, **argv;
|
||||
struct passwd *up;
|
||||
struct spwd *shp;
|
||||
|
||||
if (*toys.optargs && !strcmp("-", *toys.optargs)) {
|
||||
toys.optflags |= FLAG_l;
|
||||
@@ -58,16 +55,18 @@ void su_main()
|
||||
|
||||
loggit(LOG_NOTICE, "%s->%s", getusername(geteuid()), name);
|
||||
|
||||
shp = getspnam(name);
|
||||
if (getuid() && shp) {
|
||||
if (*shp->sp_pwdp != '$') goto deny;
|
||||
if (getuid()) {
|
||||
if (!(shadow = get_userline("/etc/shadow", name)))
|
||||
perror_exit("no '%s'", name);
|
||||
if (*shadow[1] != '$') goto deny;
|
||||
if (read_password(toybuf, sizeof(toybuf), "Password: ")) goto deny;
|
||||
passhash = crypt(toybuf, shp->sp_pwdp);
|
||||
passhash = crypt(toybuf, shadow[1]);
|
||||
if (!passhash || strcmp(passhash, shadow[1])) name = 0;
|
||||
memset(toybuf, 0, sizeof(toybuf));
|
||||
if (!passhash || strcmp(passhash, shp->sp_pwdp)) goto deny;
|
||||
memset(shadow[1], 0, strlen(shadow[1]));
|
||||
if (passhash) memset(passhash, 0, strlen(passhash));
|
||||
if (!name) goto deny;
|
||||
}
|
||||
closelog();
|
||||
|
||||
xsetuser(up = xgetpwnam(name));
|
||||
|
||||
if (FLAG(m)||FLAG(p)) {
|
||||
@@ -76,15 +75,15 @@ void su_main()
|
||||
} else reset_env(up, FLAG(l));
|
||||
|
||||
argv = argu = xmalloc(sizeof(char *)*(toys.optc + 4));
|
||||
*(argv++) = TT.s ? TT.s : up->pw_shell;
|
||||
*argv++ = TT.s ? : up->pw_shell;
|
||||
loggit(LOG_NOTICE, "run %s", *argu);
|
||||
|
||||
if (FLAG(l)) *(argv++) = "-l";
|
||||
if (FLAG(c)) {
|
||||
*(argv++) = "-c";
|
||||
*(argv++) = TT.c;
|
||||
*argv++ = "-c";
|
||||
*argv++ = TT.c;
|
||||
}
|
||||
while ((*(argv++) = *(toys.optargs++)));
|
||||
while ((*argv++ = *toys.optargs++));
|
||||
xexec(argu);
|
||||
|
||||
deny:
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ void mkswap_main(void)
|
||||
|
||||
if (CFG_TOYBOX_FREE) close(fd);
|
||||
|
||||
if (TT.L) sprintf(toybuf, ", LABEL=%s", label);
|
||||
if (TT.L) sprintf(toybuf, ", LABEL=%.15s", TT.L);
|
||||
else *toybuf = 0;
|
||||
printf("Swapspace size: %luk%s, UUID=%s\n",
|
||||
pages*(unsigned long)(pagesize/1024),
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/* openvt.c - Run a program on a new VT
|
||||
*
|
||||
* Copyright 2008 David Anders <danders@amltd.com>
|
||||
* Copyright 2014 Vivek Kumar Bhagat <vivek.bhagat89@gmail.com>
|
||||
*
|
||||
* No Standard
|
||||
|
||||
USE_OPENVT(NEWTOY(openvt, "^<1c#<1>63sw", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
|
||||
USE_CHVT(NEWTOY(chvt, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_DEALLOCVT(NEWTOY(deallocvt, ">1", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NEEDROOT))
|
||||
|
||||
config OPENVT
|
||||
bool "openvt"
|
||||
default y
|
||||
help
|
||||
usage: openvt [-c NUM] [-sw] COMMAND...
|
||||
|
||||
Run COMMAND on a new virtual terminal.
|
||||
|
||||
-c NUM Use VT NUM
|
||||
-s Switch to the new VT
|
||||
-w Wait for command to exit (with -s, deallocates VT on exit)
|
||||
|
||||
config CHVT
|
||||
bool "chvt"
|
||||
default y
|
||||
help
|
||||
usage: chvt NUM
|
||||
|
||||
Change to virtual terminal number NUM. (This only works in text mode.)
|
||||
|
||||
Virtual terminals are the Linux VGA text mode (or framebuffer) displays,
|
||||
switched between via alt-F1, alt-F2, etc. Use ctrl-alt-F1 to switch
|
||||
from X11 to a virtual terminal, and alt-F6 (or F7, or F8) to get back.
|
||||
|
||||
config DEALLOCVT
|
||||
bool "deallocvt"
|
||||
default y
|
||||
help
|
||||
usage: deallocvt [NUM]
|
||||
|
||||
Deallocate unused virtual terminals, either a specific /dev/ttyNUM, or all.
|
||||
*/
|
||||
|
||||
#define FOR_openvt
|
||||
#include "toys.h"
|
||||
#include <linux/vt.h>
|
||||
#include <linux/kd.h>
|
||||
|
||||
GLOBALS(
|
||||
long c;
|
||||
)
|
||||
|
||||
static int open_console(void)
|
||||
{
|
||||
char arg = 0, *console_name[] = {"/dev/tty", "/dev/tty0", "/dev/console"};
|
||||
int i, fd;
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(console_name); i++) {
|
||||
if (0>(fd = open(console_name[i], O_RDWR))) continue;
|
||||
if (!ioctl(fd, KDGKBTYPE, &arg)) return fd;
|
||||
close(fd);
|
||||
}
|
||||
for (fd = 0; fd < 3; fd++) if (!ioctl(fd, KDGKBTYPE, &arg)) return fd;
|
||||
error_exit("can't open console");
|
||||
}
|
||||
|
||||
static int activate(int fd, int cc)
|
||||
{
|
||||
return ioctl(fd, VT_ACTIVATE, cc) || ioctl(fd, VT_WAITACTIVE, cc);
|
||||
}
|
||||
|
||||
void openvt_main(void)
|
||||
{
|
||||
struct vt_stat vstate;
|
||||
int fd, cc = (int)TT.c;
|
||||
pid_t pid;
|
||||
|
||||
// find current console
|
||||
if (-1 == (ioctl(fd = open_console(), VT_GETSTATE, &vstate)) ||
|
||||
(!cc && 0>=(cc = xioctl(fd, VT_OPENQRY, &fd))))
|
||||
perror_exit("can't find open VT");
|
||||
|
||||
sprintf(toybuf, "/dev/tty%d", cc);
|
||||
if (!(pid = XVFORK())) {
|
||||
close(0); //new vt becomes stdin
|
||||
dup2(dup2(xopen_stdio(toybuf, O_RDWR), 1), 2);
|
||||
if (FLAG(s)) activate(0, cc);
|
||||
setsid();
|
||||
ioctl(0, TIOCSCTTY, 0);
|
||||
if (fd>2) close(fd);
|
||||
xexec(toys.optargs);
|
||||
}
|
||||
if (FLAG(w)) {
|
||||
while (-1 == waitpid(pid, NULL, 0) && errno == EINTR) errno = 0;
|
||||
if (FLAG(s)) {
|
||||
activate(fd, vstate.v_active);
|
||||
dprintf(2, "%d\n", ioctl(fd, VT_DISALLOCATE, cc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void chvt_main(void)
|
||||
{
|
||||
if (activate(open_console(), atoi(*toys.optargs)))
|
||||
perror_exit_raw(*toys.optargs);
|
||||
}
|
||||
|
||||
void deallocvt_main(void)
|
||||
{
|
||||
int fd = open_console(), vt_num = 0; // 0 = all
|
||||
|
||||
if (*toys.optargs) vt_num = atolx_range(*toys.optargs, 1, 63);
|
||||
if (-1 == ioctl(fd, VT_DISALLOCATE, vt_num)) perror_exit("%d", vt_num);
|
||||
}
|
||||
+24
-4
@@ -2,20 +2,40 @@
|
||||
*
|
||||
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
|
||||
|
||||
USE_SWAPOFF(NEWTOY(swapoff, "<1>1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
|
||||
USE_SWAPOFF(NEWTOY(swapoff, "<1>1av", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
|
||||
|
||||
config SWAPOFF
|
||||
bool "swapoff"
|
||||
default y
|
||||
help
|
||||
usage: swapoff swapregion
|
||||
usage: swapoff FILE
|
||||
|
||||
Disable swapping on a given swapregion.
|
||||
Disable swapping on a device or file.
|
||||
*/
|
||||
|
||||
#define FOR_swapoff
|
||||
#include "toys.h"
|
||||
|
||||
static void xswapoff(char *str)
|
||||
{
|
||||
if (FLAG(v)) printf("swapoff %s", str);
|
||||
if (swapoff(str)) perror_msg("failed to remove swaparea");
|
||||
}
|
||||
|
||||
void swapoff_main(void)
|
||||
{
|
||||
if (swapoff(toys.optargs[0])) perror_exit("failed to remove swaparea");
|
||||
char *ss, *line, **args;
|
||||
FILE *fp;
|
||||
|
||||
if (FLAG(a) && (fp = fopen("/proc/swaps", "r"))) {
|
||||
while ((line = xgetline(fp))) {
|
||||
if (*line != '/' || !(ss = strchr(line, ' '))) continue;
|
||||
*ss = 0;
|
||||
octal_deslash(line);
|
||||
xswapoff(line);
|
||||
free(line);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
for (args = toys.optargs; *args; args++) xswapoff(*args);
|
||||
}
|
||||
|
||||
+30
-26
@@ -19,38 +19,49 @@ config SWITCH_ROOT
|
||||
|
||||
#define FOR_switch_root
|
||||
#include "toys.h"
|
||||
#include <sys/vfs.h>
|
||||
|
||||
GLOBALS(
|
||||
char *c;
|
||||
|
||||
struct stat new;
|
||||
dev_t rootdev;
|
||||
)
|
||||
|
||||
static int del_node(struct dirtree *node)
|
||||
{
|
||||
if (node->st.st_dev == TT.rootdev && dirtree_notdotdot(node)) {
|
||||
int flag = 0;
|
||||
if (S_ISDIR(node->st.st_mode)) {
|
||||
if (!node->again) return DIRTREE_COMEAGAIN;
|
||||
flag = AT_REMOVEDIR;
|
||||
}
|
||||
unlinkat(dirtree_parentfd(node), node->name, flag);
|
||||
int flag = 0;
|
||||
|
||||
if (same_file(&TT.new, &node->st) || !dirtree_notdotdot(node)) return 0;
|
||||
|
||||
if (node->st.st_dev != TT.rootdev) {
|
||||
char *s = dirtree_path(node, 0);
|
||||
|
||||
if (mount(s, s+1, "", MS_MOVE, "")) perror_msg("Failed to move %s", s);
|
||||
// TODO: handle undermounts
|
||||
rmdir(s);
|
||||
free(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (S_ISDIR(node->st.st_mode)) {
|
||||
if (!node->again) return DIRTREE_COMEAGAIN;
|
||||
flag = AT_REMOVEDIR;
|
||||
}
|
||||
unlinkat(dirtree_parentfd(node), node->name, flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void switch_root_main(void)
|
||||
{
|
||||
char *newroot = *toys.optargs, **cmdline = toys.optargs+1;
|
||||
struct stat st1, st2;
|
||||
struct stat st;
|
||||
struct statfs stfs;
|
||||
int console = console; // gcc's "may be used" warnings are broken.
|
||||
int ii, console QUIET;
|
||||
|
||||
// Must be root on a ramfs or tmpfs instance
|
||||
if (getpid() != 1) error_exit("not pid 1");
|
||||
|
||||
// Root filesystem we're leaving must be ramfs or tmpfs
|
||||
if (statfs("/", &stfs) ||
|
||||
(stfs.f_type != 0x858458f6 && stfs.f_type != 0x01021994))
|
||||
{
|
||||
@@ -59,13 +70,13 @@ void switch_root_main(void)
|
||||
}
|
||||
|
||||
// New directory must be different filesystem instance
|
||||
if (chdir(newroot) || stat(".", &st1) || stat("/", &st2) ||
|
||||
st1.st_dev == st2.st_dev)
|
||||
if (chdir(newroot) || stat(".", &TT.new) || stat("/", &st) ||
|
||||
same_file(&TT.new, &st))
|
||||
{
|
||||
error_msg("bad newroot '%s'", newroot);
|
||||
goto panic;
|
||||
}
|
||||
TT.rootdev=st2.st_dev;
|
||||
TT.rootdev = st.st_dev;
|
||||
|
||||
// trim any / characters from the init cmdline, as we want to test it with
|
||||
// stat(), relative to newroot. *cmdline is also used below, but by that
|
||||
@@ -73,7 +84,7 @@ void switch_root_main(void)
|
||||
while (**cmdline == '/') (*cmdline)++;
|
||||
|
||||
// init program must exist and be an executable file
|
||||
if (stat(*cmdline, &st1) || !S_ISREG(st1.st_mode) || !(st1.st_mode&0100)) {
|
||||
if (stat(*cmdline, &st) || !S_ISREG(st.st_mode) || !(st.st_mode&0100)) {
|
||||
error_msg("bad init");
|
||||
goto panic;
|
||||
}
|
||||
@@ -82,16 +93,10 @@ void switch_root_main(void)
|
||||
perror_msg("bad console '%s'", TT.c);
|
||||
goto panic;
|
||||
}
|
||||
|
||||
|
||||
// Ok, enough safety checks: wipe root partition.
|
||||
dirtree_read("/", del_node);
|
||||
|
||||
// Fix the appearance of the mount table in the newroot chroot
|
||||
if (mount(".", "/", NULL, MS_MOVE, NULL)) {
|
||||
perror_msg("mount");
|
||||
goto panic;
|
||||
}
|
||||
|
||||
// Enter the new root before starting init
|
||||
if (chroot(".")) {
|
||||
perror_msg("chroot");
|
||||
@@ -105,12 +110,11 @@ void switch_root_main(void)
|
||||
}
|
||||
|
||||
if (TT.c) {
|
||||
int i;
|
||||
for (i=0; i<3; i++) if (console != i) dup2(console, i);
|
||||
for (ii = 0; ii<3; ii++) dup2(console, ii);
|
||||
if (console>2) close(console);
|
||||
}
|
||||
execv(*cmdline, cmdline);
|
||||
perror_msg("Failed to exec '%s'", *cmdline);
|
||||
panic:
|
||||
if (toys.optflags & FLAG_h) for (;;) wait(NULL);
|
||||
if (FLAG(h)) for (;;) wait(NULL);
|
||||
}
|
||||
|
||||
+59
-60
@@ -5,7 +5,7 @@
|
||||
* TODO: I have no idea how "system" category is calculated.
|
||||
* whatever we're doing isn't matching what other implementations are doing.
|
||||
|
||||
USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
|
||||
USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN|TOYFLAG_LINEBUF))
|
||||
|
||||
config VMSTAT
|
||||
bool "vmstat"
|
||||
@@ -29,125 +29,124 @@ config VMSTAT
|
||||
#include "toys.h"
|
||||
|
||||
struct vmstat_proc {
|
||||
// From /proc/stat (jiffies)
|
||||
uint64_t user, nice, sys, idle, wait, irq, sirq, intr, ctxt, running, blocked;
|
||||
// From /proc/meminfo (units are kb)
|
||||
uint64_t memfree, buffers, cached, swapfree, swaptotal;
|
||||
// From /proc/vmstat (units are kb)
|
||||
uint64_t io_in, io_out;
|
||||
// From /proc/vmstat (units are pages)
|
||||
uint64_t swap_in, swap_out;
|
||||
unsigned long long
|
||||
// From /proc/stat (jiffies) 0-10
|
||||
user, nice, sys, idle, wait, irq, sirq, intr, ctxt, running, blocked,
|
||||
// From /proc/meminfo (units are kb) 11-16
|
||||
memfree, buffers, cached, swapfree, swaptotal, reclaimable,
|
||||
// From /proc/vmstat (units are kb) 17-18
|
||||
io_in, io_out,
|
||||
// From /proc/vmstat (units are pages) 19-20
|
||||
swap_in, swap_out;
|
||||
};
|
||||
|
||||
// All the elements of vmstat_proc are the same size, so we can populate it as
|
||||
// a big array, then read the elements back out by name
|
||||
static void get_vmstat_proc(struct vmstat_proc *vmstat_proc)
|
||||
static void get_vmstat_proc(struct vmstat_proc *vmsp)
|
||||
{
|
||||
char *vmstuff[] = { "/proc/stat", "cpu ", 0, 0, 0, 0, 0, 0,
|
||||
"intr ", "ctxt ", "procs_running ", "procs_blocked ", "/proc/meminfo",
|
||||
"MemFree: ", "Buffers: ", "Cached: ", "SwapFree: ", "SwapTotal: ",
|
||||
char *vmstuff[] = { "/proc/stat", "cpu ", 0, 0, 0, 0, 0, 0, "intr ", "ctxt ",
|
||||
"procs_running ", "procs_blocked ", "/proc/meminfo", "MemFree:",
|
||||
"Buffers:", "Cached:", "SwapFree:", "SwapTotal:", "SReclaimable:",
|
||||
"/proc/vmstat", "pgpgin ", "pgpgout ", "pswpin ", "pswpout " };
|
||||
uint64_t *new = (uint64_t *)vmstat_proc;
|
||||
char *p = p, *name = name, *file = NULL;
|
||||
unsigned long long *new = (void *)vmsp;
|
||||
char *p = 0, *name = name, *file = 0;
|
||||
int i, j;
|
||||
|
||||
// We use vmstuff to fill out vmstat_proc as an array of uint64_t:
|
||||
// We use vmstuff to fill out vmstat_proc as an array of long long:
|
||||
// Strings starting with / are the file to find next entries in
|
||||
// Any other string is a key to search for, with decimal value right after
|
||||
// 0 means parse another value on same line as last key
|
||||
|
||||
for (i = 0; i<ARRAY_LEN(vmstuff); i++) {
|
||||
if (!vmstuff[i]) p++;
|
||||
memset(new, 0, sizeof(struct vmstat_proc));
|
||||
for (i = j = 0; i<ARRAY_LEN(vmstuff); i++) {
|
||||
if (!vmstuff[i]) p++; // Read next entry on same line
|
||||
else if (*vmstuff[i] == '/') {
|
||||
// /proc/stat for a 48-core machine doesn't fit in toybuf.
|
||||
free(file);
|
||||
file = xreadfile(name = vmstuff[i], 0, 0);
|
||||
|
||||
continue;
|
||||
} else p = strafter(file, vmstuff[i]);
|
||||
if (!p || 1!=sscanf(p, "%"PRIu64"%n", new++, &j))
|
||||
error_exit("Bad %sin %s: %s", vmstuff[i], name, p ? p : "");
|
||||
p += j;
|
||||
} else if (file && !(p = strafter(file, vmstuff[i]))) {
|
||||
free(file);
|
||||
file = 0;
|
||||
}
|
||||
if (!file) new++;
|
||||
else if (1==sscanf(p, "%llu%n", new++, &j)) p += j;
|
||||
}
|
||||
free(file);
|
||||
|
||||
// combine some fields we display as aggregates
|
||||
vmsp->running--; // Don't include ourselves
|
||||
vmsp->user += vmsp->nice;
|
||||
vmsp->sys += vmsp->irq + vmsp->sirq;
|
||||
vmsp->swaptotal -= vmsp->swapfree;
|
||||
vmsp->cached += vmsp->reclaimable;
|
||||
}
|
||||
|
||||
void vmstat_main(void)
|
||||
{
|
||||
struct vmstat_proc top[2];
|
||||
int i, loop_delay = 0, loop_max = 0;
|
||||
unsigned loop, rows = 25, page_kb = sysconf(_SC_PAGESIZE)/1024;
|
||||
char *headers="r\0b\0swpd\0free\0buff\0cache\0si\0so\0bi\0bo\0in\0cs\0us\0"
|
||||
"sy\0id\0wa", lengths[] = {2,2,7,7,6,7,5,5,5,5,5,5,2,2,2,2};
|
||||
unsigned long long units, total_hz, *ptr, *oldptr;
|
||||
char *headers = "r\0b\0swpd\0free\0buff\0cache\0si\0so\0bi\0bo\0in\0cs\0us\0"
|
||||
"sy\0id\0wa", lengths[] = {2,2,7,7,6,7,4,4,5,5,4,4,2,2,2,2};
|
||||
|
||||
memset(top, 0, sizeof(top));
|
||||
if (toys.optc) loop_delay = atolx_range(toys.optargs[0], 0, INT_MAX);
|
||||
if (toys.optc > 1) loop_max = atolx_range(toys.optargs[1], 1, INT_MAX);
|
||||
if (toys.optc>1) loop_max = atolx_range(toys.optargs[1], 1, INT_MAX);
|
||||
|
||||
for (loop = 0; !loop_max || loop < loop_max; loop++) {
|
||||
unsigned idx = loop&1, offset = 0, expected = 0;
|
||||
uint64_t units, total_hz, *ptr = (uint64_t *)(top+idx),
|
||||
*oldptr = (uint64_t *)(top+!idx);
|
||||
xreadfile("/proc/uptime", toybuf, sizeof(toybuf));
|
||||
sscanf(toybuf, "%*s %llu", &units);
|
||||
|
||||
for (loop = 0; !loop_max || loop<loop_max; loop++) {
|
||||
unsigned offset = 0, expected = 0;
|
||||
|
||||
if (loop && loop_delay) sleep(loop_delay);
|
||||
|
||||
ptr = oldptr = (void *)toybuf;
|
||||
*((loop&1) ? &ptr : &oldptr) += sizeof(struct vmstat_proc);
|
||||
get_vmstat_proc((void *)ptr);
|
||||
|
||||
// Print headers
|
||||
if (rows>3 && !(loop % (rows-3))) {
|
||||
char *header = headers;
|
||||
|
||||
if (!(toys.optflags&FLAG_n) && isatty(1)) terminal_size(0, &rows);
|
||||
if (!FLAG(n) && isatty(1)) terminal_size(0, &rows);
|
||||
else rows = 0;
|
||||
|
||||
printf("procs ------------memory------------ ----swap--- -----io---- ---system-- ----cpu----\n");
|
||||
for (i=0; i<sizeof(lengths); i++) {
|
||||
printf("procs ------------memory------------ ---swap-- -----io---- --system- ----cpu----\n");
|
||||
for (i = 0; i<sizeof(lengths); i++) {
|
||||
printf(" %*s"+!i, lengths[i], header);
|
||||
header += strlen(header)+1;
|
||||
}
|
||||
xputc('\n');
|
||||
}
|
||||
|
||||
// Read data and combine some fields we display as aggregates
|
||||
get_vmstat_proc(top+idx);
|
||||
top[idx].running--; // Don't include ourselves
|
||||
top[idx].user += top[idx].nice;
|
||||
top[idx].sys += top[idx].irq + top[idx].sirq;
|
||||
top[idx].swaptotal -= top[idx].swapfree;
|
||||
|
||||
// Collect unit adjustments (outside the inner loop to save time)
|
||||
|
||||
if (!loop) {
|
||||
char *s = toybuf;
|
||||
|
||||
xreadfile("/proc/uptime", toybuf, sizeof(toybuf));
|
||||
while (*(s++) > ' ');
|
||||
sscanf(s, "%"PRIu64, &units);
|
||||
} else units = loop_delay;
|
||||
if (loop) units = loop_delay;
|
||||
|
||||
// add up user, sys, idle, and wait time used since last time
|
||||
// (Already appended nice to user)
|
||||
total_hz = 0;
|
||||
for (i=0; i<4; i++) total_hz += ptr[i+!!i] - oldptr[i+!!i];
|
||||
for (i = total_hz = 0; i<4; i++) total_hz += ptr[i+!!i] - oldptr[i+!!i];
|
||||
|
||||
// Output values in order[]: running, blocked, swaptotal, memfree, buffers,
|
||||
// cache, swap_in, swap_out, io_in, io_out, sirq, ctxt, user, sys, idle,wait
|
||||
// cache, swap_in, swap_out, io_in, io_out, intr, ctxt, user, sys, idle,wait
|
||||
|
||||
for (i=0; i<sizeof(lengths); i++) {
|
||||
char order[] = {9, 10, 15, 11, 12, 13, 18, 19, 16, 17, 6, 8, 0, 2, 3, 4};
|
||||
uint64_t out = ptr[order[i]];
|
||||
char order[] = {9, 10, 15, 11, 12, 13, 19, 20, 17, 18, 7, 8, 0, 2, 3, 4};
|
||||
unsigned long long out = ptr[order[i]];
|
||||
int len;
|
||||
|
||||
// Adjust rate and units
|
||||
if (i>5) out -= oldptr[order[i]];
|
||||
if (order[i]<7) out = ((out*100) + (total_hz/2)) / total_hz;
|
||||
else if (order[i]>17) out = ((out * page_kb)+(units-1))/units;
|
||||
else if (order[i]>15) out = ((out)+(units-1))/units;
|
||||
else if (order[i]<9) out = (out+(units-1)) / units;
|
||||
else if (order[i]>16) {
|
||||
if (order[i]>18) out *= page_kb;
|
||||
out = (out*page_kb+(units-1))/units;
|
||||
} else if (order[i]<9) out = (out+(units-1)) / units;
|
||||
|
||||
// If a field was too big to fit in its slot, try to compensate later
|
||||
expected += lengths[i] + !!i;
|
||||
len = expected - offset - !!i;
|
||||
if (len < 0) len = 0;
|
||||
offset += printf(" %*"PRIu64+!i, len, out);
|
||||
offset += printf(" %*llu"+!i, len, out);
|
||||
}
|
||||
xputc('\n');
|
||||
|
||||
|
||||
+25
-9
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* Copyright 2007 Rob Landley <rob@landley.net>
|
||||
|
||||
USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_YES(NEWTOY(yes, 0, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config YES
|
||||
bool "yes"
|
||||
@@ -17,13 +17,29 @@ config YES
|
||||
|
||||
void yes_main(void)
|
||||
{
|
||||
for (;;) {
|
||||
int i;
|
||||
for (i=0; toys.optargs[i]; i++) {
|
||||
if (i) xputc(' ');
|
||||
xprintf("%s", toys.optargs[i]);
|
||||
}
|
||||
if (!i) xputc('y');
|
||||
xputc('\n');
|
||||
struct iovec *iov = (void *)toybuf;
|
||||
char *out, *ss;
|
||||
long len, ll, i, j;
|
||||
|
||||
// Collate command line arguments into one string, or repeated "y\n".
|
||||
for (len = i = 0; toys.optargs[i]; i++) len += strlen(toys.optargs[i]) + 1;
|
||||
ss = out = xmalloc(len ? : 128);
|
||||
if (!i) for (i = 0; i<64; i++) {
|
||||
*ss++ = 'y';
|
||||
*ss++ = '\n';
|
||||
} else {
|
||||
for (i = 0; toys.optargs[i]; i++)
|
||||
ss += sprintf(ss, " %s"+!i, toys.optargs[i]);
|
||||
*ss++ = '\n';
|
||||
}
|
||||
|
||||
// Populate a redundant iovec[] outputting the same string many times
|
||||
for (i = ll = 0; i<sizeof(toybuf)/sizeof(*iov); i++) {
|
||||
iov[i].iov_base = out;
|
||||
ll += (iov[i].iov_len = ss-out);
|
||||
}
|
||||
|
||||
// Writev the output until stdout stops accepting it
|
||||
for (;;) for (len = 0; len<ll; len += j)
|
||||
if (0>(j = writev(1, iov, i))) perror_exit(0);
|
||||
}
|
||||
|
||||
+2
-2
@@ -234,10 +234,10 @@ found_device:
|
||||
else
|
||||
sprintf(toybuf, "/dev/%s", device_name);
|
||||
|
||||
if ((temp=getenv("ACTION")) && !strcmp(temp, "remove")) {
|
||||
if ((temp=getenv("ACTION")) && !strcmp(temp, "remove")) {
|
||||
unlink(toybuf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (strchr(device_name, '/')) mkpath(toybuf);
|
||||
if (mknod(toybuf, mode | type, dev_makedev(major, minor)) &&
|
||||
|
||||
+62
-61
@@ -41,7 +41,7 @@ config STTY
|
||||
#include <linux/tty.h>
|
||||
|
||||
GLOBALS(
|
||||
char *device;
|
||||
char *F;
|
||||
|
||||
int fd, col;
|
||||
unsigned output_cols;
|
||||
@@ -65,7 +65,7 @@ static speed_t speed(int baud)
|
||||
|
||||
for (i=0;i<ARRAY_LEN(bauds);i++) if (bauds[i] == baud) break;
|
||||
if (i == ARRAY_LEN(bauds)) error_exit("unknown speed: %d", baud);
|
||||
return i+4081*(i>16);
|
||||
return i+4081*(i>=16);
|
||||
}
|
||||
|
||||
struct flag {
|
||||
@@ -120,12 +120,15 @@ static const struct synonym {
|
||||
char *from;
|
||||
char *to;
|
||||
} synonyms[] = {
|
||||
{ "cbreak", "-icanon" }, { "-cbreak", "icanon" }, { "-cooked", "raw" },
|
||||
{ "crterase", "echoe" }, { "-crterase", "-echoe" }, { "crtkill", "echoke" },
|
||||
{ "-crtkill", "-echoke" }, { "ctlecho", "echoctl" }, { "-tandem", "-ixoff" },
|
||||
{ "-ctlecho", "-echoctl" }, { "hup", "hupcl" }, { "-hup", "-hupcl" },
|
||||
{ "prterase", "echoprt" }, { "-prterase", "-echoprt" }, { "-raw", "cooked" },
|
||||
{ "tabs", "tab0" }, { "-tabs", "tab3" }, { "tandem", "ixoff" },
|
||||
{ "cbreak", "-icanon" }, { "-cbreak", "icanon" },
|
||||
{ "-cooked", "raw" }, { "-raw", "cooked" },
|
||||
{ "crterase", "echoe" }, { "-crterase", "-echoe" },
|
||||
{ "crtkill", "echoke" }, { "-crtkill", "-echoke" },
|
||||
{ "ctlecho", "echoctl" }, { "-ctlecho", "-echoctl" },
|
||||
{ "-tandem", "-ixoff" }, { "tandem", "ixoff" },
|
||||
{ "hup", "hupcl" }, { "-hup", "-hupcl" },
|
||||
{ "prterase", "echoprt" }, { "-prterase", "-echoprt" },
|
||||
{ "tabs", "tab0" }, { "-tabs", "tab3" },
|
||||
};
|
||||
|
||||
static void out(const char *fmt, ...)
|
||||
@@ -160,9 +163,9 @@ static void show_flags(tcflag_t actual, tcflag_t sane,
|
||||
int i, j, value, mask;
|
||||
|
||||
// Implement -a by ensuring that sane != actual so we'll show everything.
|
||||
if (toys.optflags&FLAG_a) sane = ~actual;
|
||||
if (FLAG(a)) sane = ~actual;
|
||||
|
||||
for (i=j=0;i<len;i++) {
|
||||
for (i = j = 0; i<len; i++) {
|
||||
value = flags[i].value;
|
||||
if ((mask = flags[i].mask)) {
|
||||
if ((actual&mask)==value && (sane&mask)!=value) {
|
||||
@@ -183,7 +186,7 @@ static void show_size(int verbose)
|
||||
{
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.device);
|
||||
if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.F);
|
||||
out(verbose ? "rows %d; columns %d;" : "%d %d\n", ws.ws_row, ws.ws_col);
|
||||
}
|
||||
|
||||
@@ -196,11 +199,11 @@ static void show_speed(struct termios *t, int verbose)
|
||||
out(fmt, ispeed, ospeed);
|
||||
}
|
||||
|
||||
static int get_arg(int *i, long long low, long long high)
|
||||
static int get_arg(int *i, long long high)
|
||||
{
|
||||
(*i)++;
|
||||
if (!toys.optargs[*i]) error_exit("missing arg");
|
||||
return atolx_range(toys.optargs[*i], low, high);
|
||||
return atolx_range(toys.optargs[*i], 0, high);
|
||||
}
|
||||
|
||||
static int set_flag(tcflag_t *f, const struct flag *flags, int len,
|
||||
@@ -249,10 +252,10 @@ static void set_size(int is_rows, unsigned short value)
|
||||
{
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.device);
|
||||
if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.F);
|
||||
if (is_rows) ws.ws_row = value;
|
||||
else ws.ws_col = value;
|
||||
if (ioctl(TT.fd, TIOCSWINSZ, &ws)) perror_exit("TIOCSWINSZ %s", TT.device);
|
||||
if (ioctl(TT.fd, TIOCSWINSZ, &ws)) perror_exit("TIOCSWINSZ %s", TT.F);
|
||||
}
|
||||
|
||||
static int set_special_character(struct termios *new, int *i, char *char_name)
|
||||
@@ -271,7 +274,6 @@ static int set_special_character(struct termios *new, int *i, char *char_name)
|
||||
else if (arg[0] == '^' && arg[2] == 0) ch = (toupper(arg[1])-'@');
|
||||
else if (!arg[1]) ch = arg[0];
|
||||
else error_exit("invalid arg: %s", arg);
|
||||
xprintf("setting %s to %s (%02x)\n", char_name, arg, ch);
|
||||
new->c_cc[chars[j].value] = ch;
|
||||
return 1;
|
||||
}
|
||||
@@ -311,51 +313,61 @@ static void make_sane(struct termios *t)
|
||||
|
||||
static void xtcgetattr(struct termios *t)
|
||||
{
|
||||
if (tcgetattr(TT.fd, t)) perror_exit("tcgetattr %s", TT.device);
|
||||
if (tcgetattr(TT.fd, t)) perror_exit("tcgetattr %s", TT.F);
|
||||
}
|
||||
|
||||
static void do_stty()
|
||||
void stty_main(void)
|
||||
{
|
||||
struct termios old, sane;
|
||||
int i, j, n;
|
||||
|
||||
if (toys.optflags&(FLAG_a|FLAG_g) && *toys.optargs)
|
||||
error_exit("no settings with -a/-g");
|
||||
|
||||
if (!TT.F) TT.F = "standard input";
|
||||
else TT.fd = xopen(TT.F, (O_RDWR*!!*toys.optargs)|O_NOCTTY|O_NONBLOCK);
|
||||
|
||||
xtcgetattr(&old);
|
||||
|
||||
if (*toys.optargs) {
|
||||
struct termios new = old;
|
||||
struct termios new = old, tmp;
|
||||
|
||||
for (i=0; toys.optargs[i]; i++) {
|
||||
char *arg = toys.optargs[i];
|
||||
|
||||
if (!strcmp(arg, "size")) show_size(0);
|
||||
else if (!strcmp(arg, "speed")) show_speed(&old, 0);
|
||||
else if (!strcmp(arg, "line")) new.c_line = get_arg(&i, N_TTY, NR_LDISCS);
|
||||
else if (!strcmp(arg, "min")) new.c_cc[VMIN] = get_arg(&i, 0, 255);
|
||||
else if (!strcmp(arg, "time")) new.c_cc[VTIME] = get_arg(&i, 0, 255);
|
||||
else if (atoi(arg) > 0) {
|
||||
int new_speed = speed(atolx_range(arg, 0, 4000000));
|
||||
|
||||
cfsetispeed(&new, new_speed);
|
||||
cfsetospeed(&new, new_speed);
|
||||
} else if (!strcmp(arg, "ispeed"))
|
||||
cfsetispeed(&new, speed(get_arg(&i, 0, 4000000)));
|
||||
else if (!strcmp(arg, "ospeed"))
|
||||
cfsetospeed(&new, speed(get_arg(&i, 0, 4000000)));
|
||||
else if (!strcmp(arg, "rows")) set_size(1, get_arg(&i, 0, USHRT_MAX));
|
||||
else if (!strcmp(arg, "cols") || !strcmp(arg, "columns"))
|
||||
set_size(0, get_arg(&i, 0, USHRT_MAX));
|
||||
else if (sscanf(arg, "%x:%x:%x:%x:%n", &new.c_iflag, &new.c_oflag,
|
||||
&new.c_cflag, &new.c_lflag, &n) == 4)
|
||||
else if (!strcmp(arg, "line")) new.c_line = get_arg(&i, NR_LDISCS);
|
||||
else if (!strcmp(arg, "min")) new.c_cc[VMIN] = get_arg(&i, 255);
|
||||
else if (!strcmp(arg, "time")) new.c_cc[VTIME] = get_arg(&i, 255);
|
||||
else if (sscanf(arg, "%x:%x:%x:%x:%n", &tmp.c_iflag, &tmp.c_oflag,
|
||||
&tmp.c_cflag, &tmp.c_lflag, &n) == 4)
|
||||
{
|
||||
int value;
|
||||
|
||||
new.c_iflag = tmp.c_iflag;
|
||||
new.c_oflag = tmp.c_oflag;
|
||||
new.c_cflag = tmp.c_cflag;
|
||||
new.c_lflag = tmp.c_lflag;
|
||||
arg += n;
|
||||
for (j=0;j<NCCS;j++) {
|
||||
if (sscanf(arg, "%x%n", &value, &n) != 1) error_exit("bad -g string");
|
||||
new.c_cc[j] = value;
|
||||
arg += n+1;
|
||||
}
|
||||
} else if (set_special_character(&new, &i, arg));
|
||||
} else if (atoi(arg) > 0) {
|
||||
int new_speed = speed(atolx_range(arg, 0, 4000000));
|
||||
|
||||
cfsetispeed(&new, new_speed);
|
||||
cfsetospeed(&new, new_speed);
|
||||
} else if (!strcmp(arg, "ispeed"))
|
||||
cfsetispeed(&new, speed(get_arg(&i, 4000000)));
|
||||
else if (!strcmp(arg, "ospeed"))
|
||||
cfsetospeed(&new, speed(get_arg(&i, 4000000)));
|
||||
else if (!strcmp(arg, "rows")) set_size(1, get_arg(&i, USHRT_MAX));
|
||||
else if (!strcmp(arg, "cols") || !strcmp(arg, "columns"))
|
||||
set_size(0, get_arg(&i, USHRT_MAX));
|
||||
else if (set_special_character(&new, &i, arg));
|
||||
// Already done as a side effect.
|
||||
else if (!strcmp(arg, "cooked"))
|
||||
set_options(&new, "brkint", "ignpar", "istrip", "icrnl", "ixon",
|
||||
@@ -384,7 +396,7 @@ static void do_stty()
|
||||
} else if (!strcmp(arg, "sane")) make_sane(&new);
|
||||
else {
|
||||
// Translate historical cruft into canonical forms.
|
||||
for (j=0;j<ARRAY_LEN(synonyms);j++) {
|
||||
for (j=0; j<ARRAY_LEN(synonyms); j++) {
|
||||
if (!strcmp(synonyms[j].from, arg)) {
|
||||
arg = synonyms[j].to;
|
||||
break;
|
||||
@@ -393,15 +405,16 @@ static void do_stty()
|
||||
set_option(&new, arg);
|
||||
}
|
||||
}
|
||||
tcsetattr(TT.fd, TCSAFLUSH, &new);
|
||||
|
||||
tcsetattr(TT.fd, TCSADRAIN, &new);
|
||||
xtcgetattr(&old);
|
||||
if (memcmp(&old, &new, sizeof(old)))
|
||||
error_exit("unable to perform all requested operations on %s", TT.device);
|
||||
error_exit("unable to perform all requested operations on %s", TT.F);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (toys.optflags&FLAG_g) {
|
||||
if (FLAG(g)) {
|
||||
xprintf("%x:%x:%x:%x:", old.c_iflag, old.c_oflag, old.c_cflag, old.c_lflag);
|
||||
for (i=0;i<NCCS;i++) xprintf("%x%c", old.c_cc[i], i==NCCS-1?'\n':':');
|
||||
return;
|
||||
@@ -411,21 +424,20 @@ static void do_stty()
|
||||
// special characters and any flags that differ from the "sane" settings.
|
||||
make_sane(&sane);
|
||||
show_speed(&old, 1);
|
||||
if (toys.optflags&FLAG_a) show_size(1);
|
||||
if (FLAG(a)) show_size(1);
|
||||
out("line = %d;\n", old.c_line);
|
||||
|
||||
for (i=j=0;i<ARRAY_LEN(chars);i++) {
|
||||
for (i=j=0; i<ARRAY_LEN(chars); i++) {
|
||||
char vis[16] = {};
|
||||
cc_t ch = old.c_cc[chars[i].value];
|
||||
|
||||
if (ch == sane.c_cc[chars[i].value] && (toys.optflags&FLAG_a)==0)
|
||||
if (ch == sane.c_cc[chars[i].value] && !FLAG(a))
|
||||
continue;
|
||||
|
||||
if (chars[i].value == VMIN || chars[i].value == VTIME) {
|
||||
if (chars[i].value == VMIN || chars[i].value == VTIME)
|
||||
snprintf(vis, sizeof(vis), "%u", ch);
|
||||
} else if (ch == _POSIX_VDISABLE) {
|
||||
strcat(vis, "<undef>");
|
||||
} else {
|
||||
else if (ch == _POSIX_VDISABLE) strcat(vis, "<undef>");
|
||||
else {
|
||||
if (ch > 0x7f) {
|
||||
strcat(vis, "M-");
|
||||
ch -= 128;
|
||||
@@ -443,17 +455,6 @@ static void do_stty()
|
||||
show_flags(old.c_iflag, sane.c_iflag, iflags, ARRAY_LEN(iflags));
|
||||
show_flags(old.c_oflag, sane.c_oflag, oflags, ARRAY_LEN(oflags));
|
||||
show_flags(old.c_lflag, sane.c_lflag, lflags, ARRAY_LEN(lflags));
|
||||
}
|
||||
|
||||
void stty_main(void)
|
||||
{
|
||||
if (toys.optflags&(FLAG_a|FLAG_g) && *toys.optargs)
|
||||
error_exit("can't make settings with -a/-g");
|
||||
|
||||
if (!TT.device) TT.device = "standard input";
|
||||
else TT.fd=xopen(TT.device, (O_RDWR*!!*toys.optargs)|O_NOCTTY|O_NONBLOCK);
|
||||
|
||||
do_stty();
|
||||
|
||||
if (CFG_TOYBOX_FREE && TT.device) close(TT.fd);
|
||||
|
||||
if (TT.fd) close(TT.fd);
|
||||
}
|
||||
|
||||
+3
-3
@@ -28,11 +28,11 @@ config CUT
|
||||
-b Select bytes (with -n round start/end down to start of utf8 char)
|
||||
-c Select UTF-8 characters
|
||||
-C Select unicode columns
|
||||
-d Use DELIM (default is TAB for -f, run of whitespace for -F)
|
||||
-d Input delimiter (default is TAB for -f, run of whitespace for -F)
|
||||
-D Don't sort/collate selections or match -fF lines without delimiter
|
||||
-f Select fields (words) separated by single DELIM character
|
||||
-F Select fields separated by DELIM regex
|
||||
-O Output delimiter (default one space for -F, input delim for -f)
|
||||
-O Output separator (default one space for -F, input delim for -f)
|
||||
-s Skip lines without delimiters
|
||||
*/
|
||||
#define FOR_cut
|
||||
@@ -220,7 +220,7 @@ void cut_main(void)
|
||||
error_exit("-s needs -Ff");
|
||||
if ((toys.optflags&(FLAG_d|FLAG_f|FLAG_F))==FLAG_d)
|
||||
error_exit("-d needs -Ff");
|
||||
if (!TT.d) TT.d = (FLAG(F)) ? "[[:space:]][[:space:]]*" : "\t";
|
||||
if (!TT.d) TT.d = FLAG(F) ? "[[:space:]][[:space:]]*" : "\t";
|
||||
if (FLAG(F)) xregcomp(&TT.reg, TT.d, REG_EXTENDED);
|
||||
if (!TT.O) {
|
||||
if (FLAG(F)) TT.O = " ";
|
||||
|
||||
+64
-35
@@ -7,19 +7,21 @@
|
||||
* Note: setting a 2 year date is 50 years back/forward from today,
|
||||
* not posix's hardwired magic dates.
|
||||
|
||||
USE_DATE(NEWTOY(date, "d:D:r:u[!dr]", TOYFLAG_BIN))
|
||||
USE_DATE(NEWTOY(date, ">1d:D:I(iso-8601):;r:s:u(utc)[!dr]", TOYFLAG_BIN))
|
||||
|
||||
config DATE
|
||||
bool "date"
|
||||
default y
|
||||
help
|
||||
usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]
|
||||
usage: date [-u] [-I RES] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]
|
||||
|
||||
Set/get the current date/time. With no SET shows the current date.
|
||||
|
||||
-d Show DATE instead of current time (convert date format)
|
||||
-D +FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])
|
||||
-I RES ISO 8601 with RESolution d=date/h=hours/m=minutes/s=seconds/n=ns
|
||||
-r Use modification time of FILE instead of current date
|
||||
-s DATE Set the system clock to DATE.
|
||||
-u Use UTC instead of current timezone
|
||||
|
||||
Supported input formats:
|
||||
@@ -29,6 +31,9 @@ config DATE
|
||||
YYYY-MM-DD [hh:mm[:ss]] ISO 8601
|
||||
hh:mm[:ss] 24-hour time today
|
||||
|
||||
All input formats can be followed by fractional seconds, and/or a UTC
|
||||
offset such as -0800.
|
||||
|
||||
All input formats can be preceded by TZ="id" to set the input time zone
|
||||
separately from the output time zone. Otherwise $TZ sets both.
|
||||
|
||||
@@ -43,11 +48,12 @@ config DATE
|
||||
%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)
|
||||
%N nanosec (output only)
|
||||
|
||||
%U Week of year (0-53 start sunday) %W Week of year (0-53 start monday)
|
||||
%V Week of year (1-53 start monday, week < 4 days not part of this year)
|
||||
%U Week of year (0-53 start Sunday) %W Week of year (0-53 start Monday)
|
||||
%V Week of year (1-53 start Monday, week < 4 days not part of this year)
|
||||
|
||||
%D = "%m/%d/%y" %r = "%I : %M : %S %p" %T = "%H:%M:%S" %h = "%b"
|
||||
%x locale date %X locale time %c locale date/time
|
||||
%F "%Y-%m-%d" %R "%H:%M" %T "%H:%M:%S" %z timezone (-0800)
|
||||
%D "%m/%d/%y" %r "%I:%M:%S %p" %h "%b" %:z timezone (-08:00)
|
||||
%x locale date %X locale time %c locale date/time %s unix epoch time
|
||||
*/
|
||||
|
||||
#define FOR_date
|
||||
@@ -55,9 +61,9 @@ config DATE
|
||||
|
||||
GLOBALS(
|
||||
#ifdef TOYBOX_OH_ADAPT
|
||||
char *r, *d;
|
||||
char *s, *r, *I, *d;
|
||||
#else
|
||||
char *r, *D, *d;
|
||||
char *s, *r, *I, *D, *d;
|
||||
#endif
|
||||
|
||||
unsigned nano;
|
||||
@@ -66,7 +72,7 @@ GLOBALS(
|
||||
// Handles any leading `TZ="blah" ` in the input string.
|
||||
static void parse_date(char *str, time_t *t)
|
||||
{
|
||||
char *new_tz = NULL, *old_tz, *s = str;
|
||||
char *new_tz = NULL, *old_tz QUIET, *s = str;
|
||||
|
||||
if (!strncmp(str, "TZ=\"", 4)) {
|
||||
// Extract the time zone and skip any whitespace.
|
||||
@@ -88,30 +94,42 @@ static void parse_date(char *str, time_t *t)
|
||||
}
|
||||
}
|
||||
|
||||
// Print strftime plus %N escape(s). note: modifies fmt for %N
|
||||
// Print strftime plus %N and %:z escape(s). Note: modifies fmt in those cases.
|
||||
static void puts_time(char *fmt, struct tm *tm)
|
||||
{
|
||||
char *s, *snap;
|
||||
long width = width;
|
||||
char *s, *snap, *out;
|
||||
|
||||
for (s = fmt;;s++) {
|
||||
long n = 0;
|
||||
|
||||
// Find next %N or end
|
||||
if (*(snap = s) == '%') {
|
||||
width = isdigit(*++s) ? *(s++)-'0' : 9;
|
||||
if (*s && *s != 'N') continue;
|
||||
} else if (*s) continue;
|
||||
|
||||
// Don't modify input string if no %N (default format is constant string).
|
||||
if (*s) *snap = 0;
|
||||
if (!strftime(toybuf, sizeof(toybuf)-10, fmt, tm))
|
||||
perror_exit("bad format '%s'", fmt);
|
||||
if (*s) {
|
||||
snap = toybuf+strlen(toybuf);
|
||||
sprintf(snap, "%09u", TT.nano);
|
||||
snap[width] = 0;
|
||||
// Find next %N/%:z or end of format string.
|
||||
if (*(snap = s)) {
|
||||
if (*s != '%') continue;
|
||||
if (*++s == 'N') n = 9;
|
||||
else if (isdigit(*s) && s[1] == 'N') n = *s++-'0';
|
||||
else if (*s == ':' && s[1] == 'z') s++, n++;
|
||||
else continue;
|
||||
}
|
||||
fputs(toybuf, stdout);
|
||||
|
||||
// Only modify input string if needed (default format is constant string).
|
||||
if (*s) *snap = 0;
|
||||
// Do we have any regular work for strftime to do?
|
||||
out = toybuf;
|
||||
if (*fmt) {
|
||||
if (!strftime(out, sizeof(toybuf)-12, fmt, tm))
|
||||
perror_exit("bad format '%s'", fmt);
|
||||
out += strlen(out);
|
||||
}
|
||||
// Do we have any custom formatting to append to that?
|
||||
if (*s == 'N') {
|
||||
sprintf(out, "%09u", TT.nano);
|
||||
out[n] = 0;
|
||||
} else if (*s == 'z') {
|
||||
strftime(out, 10, "%z", tm);
|
||||
memmove(out+4, out+3, strlen(out+3)+1);
|
||||
out[3] = ':';
|
||||
}
|
||||
xputsn(toybuf);
|
||||
if (!*s || !*(fmt = s+1)) break;
|
||||
}
|
||||
xputc('\n');
|
||||
@@ -123,6 +141,14 @@ void date_main(void)
|
||||
*tz = NULL;
|
||||
time_t t;
|
||||
|
||||
if (FLAG(I)) {
|
||||
char *iso_formats[] = {"%F","%FT%H%:z","%FT%R%:z","%FT%T%:z","%FT%T,%N%:z"};
|
||||
int i = stridx("dhmsn", (TT.I && *TT.I) ? *TT.I : 'd');
|
||||
|
||||
if (i<0) help_exit("bad -I: %s", TT.I);
|
||||
format_string = xstrdup(iso_formats[i]);
|
||||
}
|
||||
|
||||
if (FLAG(u)) {
|
||||
tz = getenv("TZ");
|
||||
setenv("TZ", "UTC", 1);
|
||||
@@ -137,7 +163,7 @@ void date_main(void)
|
||||
struct tm tm = {};
|
||||
char *s = strptime(TT.d, TT.D+(*TT.D=='+'), &tm);
|
||||
|
||||
t = (s && *s) ? xvali_date(&tm, s) : xvali_date(0, TT.d);
|
||||
t = (s && !*s) ? xvali_date(&tm, s) : xvali_date(0, TT.d);
|
||||
} else parse_date(TT.d, &t);
|
||||
#endif
|
||||
} else {
|
||||
@@ -153,15 +179,19 @@ void date_main(void)
|
||||
TT.nano = ts.tv_nsec;
|
||||
}
|
||||
|
||||
// Fall through if no arguments
|
||||
if (FLAG(s)) {
|
||||
if (setdate) help_exit("can't set two dates at once");
|
||||
setdate = TT.s;
|
||||
}
|
||||
|
||||
// Fall through to display if no arguments
|
||||
if (!setdate);
|
||||
// Display the date?
|
||||
else if (*setdate == '+') {
|
||||
// Supplying a format argument means display
|
||||
else if (toys.optc && *toys.optargs[0] == '+')
|
||||
format_string = toys.optargs[0]+1;
|
||||
setdate = toys.optargs[1];
|
||||
|
||||
// Set the date
|
||||
} else if (setdate) {
|
||||
else {
|
||||
struct timeval tv;
|
||||
|
||||
parse_date(setdate, &t);
|
||||
@@ -177,6 +207,5 @@ void date_main(void)
|
||||
else unsetenv("TZ");
|
||||
tzset();
|
||||
}
|
||||
|
||||
return;
|
||||
if (CFG_TOYBOX_FREE && FLAG(I)) free(format_string);
|
||||
}
|
||||
|
||||
+16
-54
@@ -9,31 +9,23 @@
|
||||
* We also honor -- to _stop_ option parsing (bash doesn't, we go with
|
||||
* consistency over compatibility here).
|
||||
|
||||
USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK))
|
||||
USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK|TOYFLAG_LINEBUF))
|
||||
|
||||
config ECHO
|
||||
bool "echo"
|
||||
default y
|
||||
help
|
||||
usage: echo [-neE] [args...]
|
||||
usage: echo [-Een] [ARG...]
|
||||
|
||||
Write each argument to stdout, with one space between each, followed
|
||||
by a newline.
|
||||
Write each argument to stdout, one space between each, followed by a newline.
|
||||
|
||||
-n No trailing newline
|
||||
-E Print escape sequences literally (default)
|
||||
-e Process the following escape sequences:
|
||||
\\ Backslash
|
||||
\0NNN Octal values (1 to 3 digits)
|
||||
\a Alert (beep/flash)
|
||||
\b Backspace
|
||||
\c Stop output here (avoids trailing newline)
|
||||
\f Form feed
|
||||
\n Newline
|
||||
\r Carriage return
|
||||
\t Horizontal tab
|
||||
\v Vertical tab
|
||||
\xHH Hexadecimal values (1 to 2 digits)
|
||||
\\ Backslash \0NNN Octal (1-3 digit) \xHH Hex (1-2 digit)
|
||||
\a Alert (beep/flash) \b Backspace \c Stop here (no \n)
|
||||
\f Form feed \n Newline \r Carriage return
|
||||
\t Horizontal tab \v Vertical tab
|
||||
-n No trailing newline
|
||||
*/
|
||||
|
||||
#define FOR_echo
|
||||
@@ -41,12 +33,10 @@ config ECHO
|
||||
|
||||
void echo_main(void)
|
||||
{
|
||||
int i = 0, out;
|
||||
char *arg, *c;
|
||||
int i = 0;
|
||||
char *arg, *c, out[8];
|
||||
|
||||
for (;;) {
|
||||
arg = toys.optargs[i];
|
||||
if (!arg) break;
|
||||
while ((arg = toys.optargs[i])) {
|
||||
if (i++) putchar(' ');
|
||||
|
||||
// Should we output arg verbatim?
|
||||
@@ -58,40 +48,12 @@ void echo_main(void)
|
||||
|
||||
// Handle -e
|
||||
|
||||
for (c = arg;;) {
|
||||
if (!(out = *(c++))) break;
|
||||
for (c = arg; *c; ) {
|
||||
unsigned u;
|
||||
|
||||
// handle \escapes
|
||||
if (out == '\\' && *c) {
|
||||
int slash = *(c++), n = unescape(slash);
|
||||
|
||||
if (n) out = n;
|
||||
else if (slash=='c') return;
|
||||
else if (slash=='0') {
|
||||
out = 0;
|
||||
while (*c>='0' && *c<='7' && n++<3) out = (out*8)+*(c++)-'0';
|
||||
} else if (slash=='x') {
|
||||
out = 0;
|
||||
while (n++<2) {
|
||||
if (*c>='0' && *c<='9') out = (out*16)+*(c++)-'0';
|
||||
else {
|
||||
int temp = tolower(*c);
|
||||
if (temp>='a' && temp<='f') {
|
||||
out = (out*16)+temp-'a'+10;
|
||||
c++;
|
||||
} else {
|
||||
if (n==1) {
|
||||
--c;
|
||||
out = '\\';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Slash in front of unknown character, print literal.
|
||||
} else c--;
|
||||
}
|
||||
putchar(out);
|
||||
if (*c == '\\' && c[1] == 'c') return;
|
||||
if ((u = unescape2(&c, 1))<128) putchar(u);
|
||||
else printf("%.*s", (int)wcrtomb(out, u, 0), out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-43
@@ -15,13 +15,13 @@ config ID
|
||||
bool "id"
|
||||
default y
|
||||
help
|
||||
usage: id [-nGgru] [USER...]
|
||||
usage: id [-Ggnru] [USER...]
|
||||
|
||||
Print user and group ID.
|
||||
|
||||
-n Print names instead of numeric IDs (to be used with -Ggu)
|
||||
-G Show only the group IDs
|
||||
-G Show all group IDs
|
||||
-g Show only the effective group ID
|
||||
-n Print names instead of numeric IDs (to be used with -Ggu)
|
||||
-r Show real ID instead of effective ID
|
||||
-u Show only the effective user ID
|
||||
|
||||
@@ -67,51 +67,70 @@ GLOBALS(
|
||||
int is_groups;
|
||||
)
|
||||
|
||||
static void s_or_u(char *s, unsigned u, int done)
|
||||
static void showone(char *prefix, char *s, unsigned u, int done)
|
||||
{
|
||||
if (toys.optflags&FLAG_n) printf("%s", s);
|
||||
else printf("%u", u);
|
||||
if (FLAG(n)) printf("%s%s", prefix, s);
|
||||
else printf("%s%u", prefix, u);
|
||||
if (done) {
|
||||
xputc('\n');
|
||||
xexit();
|
||||
}
|
||||
}
|
||||
|
||||
static void showid(char *header, unsigned u, char *s)
|
||||
static void showid(char *prefix, unsigned u, char *s)
|
||||
{
|
||||
printf("%s%u(%s)", header, u, s);
|
||||
printf("%s%u(%s)", prefix, u, s);
|
||||
}
|
||||
|
||||
static void do_id(char *username)
|
||||
{
|
||||
int flags, i, ngroups;
|
||||
struct passwd *pw;
|
||||
struct group *grp;
|
||||
uid_t uid = getuid(), euid = geteuid();
|
||||
gid_t gid = getgid(), egid = getegid(), *groups;
|
||||
|
||||
flags = toys.optflags;
|
||||
gid_t gid = getgid(), egid = getegid();
|
||||
gid_t *groups = (gid_t *)toybuf;
|
||||
int i = sizeof(toybuf)/sizeof(gid_t), ngroups;
|
||||
|
||||
// check if a username is given
|
||||
if (username) {
|
||||
pw = xgetpwnam(username);
|
||||
pw = getpwnam(username);
|
||||
if (!pw) {
|
||||
uid = atolx_range(username, 0, INT_MAX);
|
||||
if ((pw = bufgetpwuid(uid))) username = pw->pw_name;
|
||||
}
|
||||
if (!pw) error_exit("no such user '%s'", username);
|
||||
uid = euid = pw->pw_uid;
|
||||
gid = egid = pw->pw_gid;
|
||||
if (TT.is_groups) printf("%s : ", pw->pw_name);
|
||||
}
|
||||
|
||||
i = flags & FLAG_r;
|
||||
pw = xgetpwuid(i ? uid : euid);
|
||||
if (toys.optflags&FLAG_u) s_or_u(pw->pw_name, pw->pw_uid, 1);
|
||||
pw = xgetpwuid(FLAG(r) ? uid : euid);
|
||||
if (FLAG(u)) showone("", pw->pw_name, pw->pw_uid, 1);
|
||||
|
||||
grp = xgetgrgid(i ? gid : egid);
|
||||
if (flags & FLAG_g) s_or_u(grp->gr_name, grp->gr_gid, 1);
|
||||
grp = xgetgrgid(FLAG(r) ? gid : egid);
|
||||
if (FLAG(g)) showone("", grp->gr_name, grp->gr_gid, 1);
|
||||
|
||||
if (!(toys.optflags&(FLAG_G|FLAG_g|FLAG_Z))) {
|
||||
ngroups = username ? getgrouplist(username, gid, groups, &i)
|
||||
: getgroups(i, groups);
|
||||
if (ngroups<0) perror_exit("getgroups");
|
||||
|
||||
if (FLAG(G)) {
|
||||
showone("", grp->gr_name, grp->gr_gid, 0);
|
||||
for (i = 0; i<ngroups; i++) {
|
||||
if (groups[i] != egid) {
|
||||
if ((grp=getgrgid(groups[i]))) showone(" ",grp->gr_name,grp->gr_gid,0);
|
||||
else printf(" %u", groups[i]);
|
||||
}
|
||||
}
|
||||
xputc('\n');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FLAG(Z)) {
|
||||
showid("uid=", pw->pw_uid, pw->pw_name);
|
||||
showid(" gid=", grp->gr_gid, grp->gr_name);
|
||||
|
||||
if (!i) {
|
||||
if (!FLAG(r)) {
|
||||
if (uid != euid) {
|
||||
pw = xgetpwuid(euid);
|
||||
showid(" euid=", pw->pw_uid, pw->pw_name);
|
||||
@@ -122,28 +141,12 @@ static void do_id(char *username)
|
||||
}
|
||||
}
|
||||
|
||||
showid(" groups=", grp->gr_gid, grp->gr_name);
|
||||
}
|
||||
|
||||
if (!(toys.optflags&FLAG_Z)) {
|
||||
groups = (gid_t *)toybuf;
|
||||
i = sizeof(toybuf)/sizeof(gid_t);
|
||||
ngroups = username ? getgrouplist(username, gid, groups, &i)
|
||||
: getgroups(i, groups);
|
||||
if (ngroups<0) perror_exit(0);
|
||||
|
||||
int show_separator = !(toys.optflags&FLAG_G);
|
||||
showid(" groups=", gid, grp->gr_name);
|
||||
for (i = 0; i<ngroups; i++) {
|
||||
if (show_separator) xputc((toys.optflags&FLAG_G) ? ' ' : ',');
|
||||
show_separator = 1;
|
||||
if (!(grp = getgrgid(groups[i]))) perror_msg(0);
|
||||
else if (toys.optflags&FLAG_G) s_or_u(grp->gr_name, grp->gr_gid, 0);
|
||||
else if (grp->gr_gid != egid) showid("", grp->gr_gid, grp->gr_name);
|
||||
else show_separator = 0; // Because we didn't show anything this time.
|
||||
}
|
||||
if (toys.optflags&FLAG_G) {
|
||||
xputc('\n');
|
||||
xexit();
|
||||
if (groups[i] != egid) {
|
||||
if ((grp=getgrgid(groups[i]))) showid(",", grp->gr_gid, grp->gr_name);
|
||||
else printf(",%u", groups[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,9 +154,9 @@ static void do_id(char *username)
|
||||
if (lsm_enabled()) {
|
||||
char *context = lsm_context();
|
||||
|
||||
printf(" context=%s"+!!(toys.optflags&FLAG_Z), context);
|
||||
printf("%s%s", FLAG(Z) ? "" : " context=", context);
|
||||
if (CFG_TOYBOX_FREE) free(context);
|
||||
} else if (toys.optflags&FLAG_Z) error_exit("%s disabled", lsm_name());
|
||||
} else if (FLAG(Z)) error_exit("%s disabled", lsm_name());
|
||||
}
|
||||
|
||||
xputc('\n');
|
||||
|
||||
+37
-34
@@ -6,13 +6,13 @@
|
||||
*
|
||||
* Deviations from posix: specified manner and format, defined implementation.
|
||||
|
||||
USE_LOGGER(NEWTOY(logger, "st:p:", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_LOGGER(NEWTOY(logger, "t:p:s", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config LOGGER
|
||||
bool "logger"
|
||||
default y
|
||||
help
|
||||
usage: logger [-s] [-t TAG] [-p [FACILITY.]PRIORITY] [message...]
|
||||
usage: logger [-s] [-t TAG] [-p [FACILITY.]PRIORITY] [MESSAGE...]
|
||||
|
||||
Log message (or stdin) to syslog.
|
||||
|
||||
@@ -26,70 +26,73 @@ config LOGGER
|
||||
|
||||
GLOBALS(
|
||||
char *p, *t;
|
||||
|
||||
int priority;
|
||||
)
|
||||
|
||||
// find str in names[], accepting unambiguous short matches
|
||||
// returns offset into array of match, or -1 if no match
|
||||
int arrayfind(char *str, char *names[], int len)
|
||||
// TODO: move to lib?
|
||||
static int arrayfind(char *str, char *names[], int len)
|
||||
{
|
||||
int try, i, matchlen = 0, found = -1, ambiguous = 1;
|
||||
int j, i, ll = 0, maybe = -1;
|
||||
|
||||
for (try = 0; try<len; try++) {
|
||||
for (i=0; ; i++) {
|
||||
if (!str[i]) {
|
||||
if (matchlen<i) found = try, ambiguous = 0;
|
||||
if (matchlen==i) ambiguous++;
|
||||
if (!names[try][i]) return try;
|
||||
break;
|
||||
}
|
||||
if (!names[try][i]) break;
|
||||
if (toupper(str[i]) != toupper(names[try][i])) break;
|
||||
for (j = 0; j<len; j++) for (i=0; ; i++) {
|
||||
if (!str[i]) {
|
||||
if (!names[j][i]) return j;
|
||||
if (i>ll) maybe = j;
|
||||
else if (i==ll) maybe = -1;
|
||||
break;
|
||||
}
|
||||
if (!names[j][i] || toupper(str[i])!=toupper(names[j][i])) break;
|
||||
}
|
||||
return ambiguous ? -1 : found;
|
||||
|
||||
return maybe;
|
||||
}
|
||||
|
||||
static void syslog_line(char **pline, long len)
|
||||
{
|
||||
if (!pline) return;
|
||||
syslog(TT.priority, "%s", *pline);
|
||||
}
|
||||
|
||||
void logger_main(void)
|
||||
{
|
||||
int facility = LOG_USER, priority = LOG_NOTICE, len;
|
||||
int facility = LOG_USER, len = 0;
|
||||
char *s1, *s2, **arg,
|
||||
*priorities[] = {"emerg", "alert", "crit", "error", "warning", "notice",
|
||||
"info", "debug"},
|
||||
*facilities[] = {"kern", "user", "mail", "daemon", "auth", "syslog",
|
||||
"lpr", "news", "uucp", "cron", "authpriv", "ftp"};
|
||||
|
||||
if (!TT.t) TT.t = xstrdup(xgetpwuid(geteuid())->pw_name);
|
||||
if (toys.optflags & FLAG_p) {
|
||||
if (!TT.t) TT.t = xgetpwuid(geteuid())->pw_name;
|
||||
TT.priority = LOG_NOTICE;
|
||||
if (TT.p) {
|
||||
if (!(s1 = strchr(TT.p, '.'))) s1 = TT.p;
|
||||
else {
|
||||
*s1++ = len = 0;
|
||||
*s1++ = 0;
|
||||
facility = arrayfind(TT.p, facilities, ARRAY_LEN(facilities));
|
||||
if (facility == -1 && strncasecmp(TT.p, "local", 5)) {
|
||||
facility = s1[5]-'0';
|
||||
if (facility>7 || s1[6]) facility = -1;
|
||||
if (facility>=0) facility += 16;
|
||||
if (facility<0) {
|
||||
if (sscanf(TT.p, "local%d", &facility)>0 && !(facility&~7))
|
||||
facility += 16;
|
||||
else error_exit("bad facility: %s", TT.p);
|
||||
}
|
||||
if (facility<0) error_exit("bad facility: %s", TT.p);
|
||||
facility *= 8;
|
||||
}
|
||||
|
||||
priority = arrayfind(s1, priorities, ARRAY_LEN(priorities));
|
||||
if (priority<0) error_exit("bad priority: %s", s1);
|
||||
TT.priority = arrayfind(s1, priorities, ARRAY_LEN(priorities));
|
||||
if (TT.priority<0) error_exit("bad priority: %s", s1);
|
||||
}
|
||||
|
||||
openlog(TT.t, LOG_PERROR*FLAG(s), facility);
|
||||
if (toys.optc) {
|
||||
for (len = 0, arg = toys.optargs; *arg; arg++) len += strlen(*arg)+1;
|
||||
for (arg = toys.optargs; *arg; arg++) len += strlen(*arg)+1;
|
||||
s1 = s2 = xmalloc(len);
|
||||
for (arg = toys.optargs; *arg; arg++) {
|
||||
if (arg != toys.optargs) *s2++ = ' ';
|
||||
s2 = stpcpy(s2, *arg);
|
||||
}
|
||||
} else {
|
||||
toybuf[readall(0, toybuf, sizeof(toybuf)-1)] = 0;
|
||||
s1 = toybuf;
|
||||
}
|
||||
|
||||
openlog(TT.t, LOG_PERROR*!!(toys.optflags&FLAG_s), facility);
|
||||
syslog(priority, "%s", s1);
|
||||
syslog(TT.priority, "%s", s1);
|
||||
} else do_lines(0, '\n', syslog_line);
|
||||
closelog();
|
||||
}
|
||||
|
||||
+12
-17
@@ -10,7 +10,7 @@ config TEE
|
||||
bool "tee"
|
||||
default y
|
||||
help
|
||||
usage: tee [-ai] [file...]
|
||||
usage: tee [-ai] [FILE...]
|
||||
|
||||
Copy stdin to each listed file, and also to stdout.
|
||||
Filename "-" is a synonym for stdout.
|
||||
@@ -24,6 +24,7 @@ config TEE
|
||||
|
||||
GLOBALS(
|
||||
void *outputs;
|
||||
int out;
|
||||
)
|
||||
|
||||
struct fd_list {
|
||||
@@ -39,33 +40,27 @@ static void do_tee_open(int fd, char *name)
|
||||
|
||||
temp = xmalloc(sizeof(struct fd_list));
|
||||
temp->next = TT.outputs;
|
||||
temp->fd = fd;
|
||||
if (1 == (temp->fd = fd)) TT.out++;
|
||||
TT.outputs = temp;
|
||||
}
|
||||
|
||||
void tee_main(void)
|
||||
{
|
||||
struct fd_list *fdl;
|
||||
int len;
|
||||
|
||||
if (FLAG(i)) xsignal(SIGINT, SIG_IGN);
|
||||
|
||||
// Open output files
|
||||
// Open output files (plus stdout if not already in output list)
|
||||
loopfiles_rw(toys.optargs,
|
||||
O_RDWR|O_CREAT|WARN_ONLY|(FLAG(a)?O_APPEND:O_TRUNC),
|
||||
0666, do_tee_open);
|
||||
if (!TT.out) do_tee_open(1, 0);
|
||||
|
||||
// Read data from stdin, write to each output file.
|
||||
for (;;) {
|
||||
struct fd_list *fdl;
|
||||
int len;
|
||||
|
||||
// Read data from stdin
|
||||
len = xread(0, toybuf, sizeof(toybuf));
|
||||
if (len<1) break;
|
||||
|
||||
// Write data to each output file, plus stdout.
|
||||
fdl = TT.outputs;
|
||||
for (;;) {
|
||||
if(len != writeall(fdl ? fdl->fd : 1, toybuf, len)) toys.exitval=1;
|
||||
if (!fdl) break;
|
||||
fdl = fdl->next;
|
||||
}
|
||||
if (1>(len = xread(0, toybuf, sizeof(toybuf)))) break;
|
||||
for (fdl = TT.outputs; fdl;fdl = fdl->next)
|
||||
if (len != writeall(fdl->fd, toybuf, len)) toys.exitval = 1;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-35
@@ -4,61 +4,57 @@
|
||||
*
|
||||
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/time.html
|
||||
|
||||
USE_TIME(NEWTOY(time, "<1^pv", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_TIME(NEWTOY(time, "<1^pv[-pv]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_MAYFORK))
|
||||
|
||||
config TIME
|
||||
bool "time"
|
||||
default y
|
||||
depends on TOYBOX_FLOAT
|
||||
help
|
||||
usage: time [-pv] COMMAND [ARGS...]
|
||||
usage: time [-pv] COMMAND...
|
||||
|
||||
Run command line and report real, user, and system time elapsed in seconds.
|
||||
(real = clock on the wall, user = cpu used by command's code,
|
||||
system = cpu used by OS on behalf of command.)
|
||||
|
||||
-p POSIX format output (default)
|
||||
-p POSIX format output
|
||||
-v Verbose
|
||||
*/
|
||||
|
||||
#define FOR_time
|
||||
#include "toys.h"
|
||||
|
||||
|
||||
void time_main(void)
|
||||
{
|
||||
struct timespec ts, ts2;
|
||||
struct rusage ru;
|
||||
long long sec[3];
|
||||
int stat, ii, idx, nano[3];
|
||||
pid_t pid;
|
||||
struct timeval tv, tv2;
|
||||
char *labels[] = {"\nreal"+FLAG(p), "user", "sys"}, **label = labels,
|
||||
*vlabels[] ={"Real", "User", "System"}, tab = toys.optflags ? ' ' : '\t';
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
if (FLAG(v)) label = vlabels;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (!(pid = XVFORK())) xexec(toys.optargs);
|
||||
else {
|
||||
int stat;
|
||||
struct rusage ru;
|
||||
float r, u, s;
|
||||
wait4(pid, &stat, 0, &ru);
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts2);
|
||||
sec[0] = nanodiff(&ts, &ts2);
|
||||
nano[0] = (sec[0] % 1000000000)/(toys.optflags ? 1000 : 1000000);
|
||||
sec[0] /= 1000000000;
|
||||
sec[1] = ru.ru_utime.tv_sec, nano[1] = ru.ru_utime.tv_usec;
|
||||
sec[2] = ru.ru_stime.tv_sec, nano[2] = ru.ru_stime.tv_usec;
|
||||
for (ii = idx = 0; ii<3; ii++)
|
||||
idx += sprintf(toybuf+idx, "%s%s%c%lld.%0*d\n", label[ii],
|
||||
FLAG(v) ? " time (s):" : "", tab, sec[ii],
|
||||
6>>!toys.optflags, nano[ii]);
|
||||
if (FLAG(v)) idx += sprintf(toybuf+idx,
|
||||
"Max RSS (KiB): %ld\nMajor faults: %ld\n"
|
||||
"Minor faults: %ld\nFile system inputs: %ld\nFile system outputs: %ld\n"
|
||||
"Voluntary context switches: %ld\nInvoluntary context switches: %ld\n",
|
||||
ru.ru_maxrss, ru.ru_majflt, ru.ru_minflt, ru.ru_inblock,
|
||||
ru.ru_oublock, ru.ru_nvcsw, ru.ru_nivcsw);
|
||||
writeall(2, toybuf, idx);
|
||||
|
||||
wait4(pid, &stat, 0, &ru);
|
||||
gettimeofday(&tv2, NULL);
|
||||
if (tv.tv_usec > tv2.tv_usec) {
|
||||
tv2.tv_usec += 1000000;
|
||||
tv2.tv_sec--;
|
||||
}
|
||||
r = (tv2.tv_sec-tv.tv_sec)+((tv2.tv_usec-tv.tv_usec)/1000000.0);
|
||||
u = ru.ru_utime.tv_sec+(ru.ru_utime.tv_usec/1000000.0);
|
||||
s = ru.ru_stime.tv_sec+(ru.ru_stime.tv_usec/1000000.0);
|
||||
if (FLAG(v)) {
|
||||
fprintf(stderr, "Real time (s): %f\n"
|
||||
"System time (s): %f\n"
|
||||
"User time (s): %f\n"
|
||||
"Max RSS (KiB): %ld\n"
|
||||
"Major faults: %ld\n"
|
||||
"Minor faults: %ld\n"
|
||||
"File system inputs: %ld\n"
|
||||
"File system outputs: %ld\n"
|
||||
"Voluntary context switches: %ld\n"
|
||||
"Involuntary context switches: %ld\n", r, s, u,
|
||||
ru.ru_maxrss, ru.ru_majflt, ru.ru_minflt, ru.ru_inblock,
|
||||
ru.ru_oublock, ru.ru_nvcsw, ru.ru_nivcsw);
|
||||
} else fprintf(stderr, "real %f\nuser %f\nsys %f\n", r, u, s);
|
||||
toys.exitval = WIFEXITED(stat) ? WEXITSTATUS(stat) : WTERMSIG(stat);
|
||||
}
|
||||
toys.exitval = WIFEXITED(stat) ? WEXITSTATUS(stat) : WTERMSIG(stat);
|
||||
}
|
||||
|
||||
+3
-6
@@ -12,10 +12,8 @@ config TTY
|
||||
help
|
||||
usage: tty [-s]
|
||||
|
||||
Show filename of terminal connected to stdin.
|
||||
|
||||
Prints "not a tty" and exits with nonzero status if no terminal
|
||||
is connected to stdin.
|
||||
Show filename of terminal connected to stdin. If none print "not a tty"
|
||||
and exit with nonzero status.
|
||||
|
||||
-s Silent, exit code only
|
||||
*/
|
||||
@@ -26,7 +24,6 @@ void tty_main(void)
|
||||
{
|
||||
char *tty = ttyname(0);
|
||||
|
||||
if (!toys.optflags) puts(tty ? tty : "not a tty");
|
||||
|
||||
toys.exitval = !tty;
|
||||
if (!toys.optflags) puts(tty ? : "not a tty");
|
||||
}
|
||||
|
||||
+28
-22
@@ -22,24 +22,24 @@ USE_ULIMIT(OLDTOY(prlimit, ulimit, TOYFLAG_USR|TOYFLAG_BIN))
|
||||
config ULIMIT
|
||||
bool "ulimit"
|
||||
default y
|
||||
depends on TOYBOX_PRLIMIT
|
||||
help
|
||||
usage: ulimit [-P PID] [-SHRacdefilmnpqrstuv] [LIMIT]
|
||||
|
||||
Print or set resource limits for process number PID. If no LIMIT specified
|
||||
(or read-only -ap selected) display current value (sizes in bytes).
|
||||
Default is ulimit -P $PPID -Sf" (show soft filesize of your shell).
|
||||
|
||||
-S Set/show soft limit -H Set/show hard (maximum) limit
|
||||
-a Show all limits -c Core file size
|
||||
-d Process data segment -e Max scheduling priority
|
||||
-f Output file size -i Pending signal count
|
||||
-l Locked memory -m Resident Set Size
|
||||
-n Number of open files -p Pipe buffer
|
||||
-q Posix message queue -r Max Real-time priority
|
||||
-R Realtime latency (usec) -s Stack size
|
||||
-t Total CPU time (in seconds) -u Maximum processes (under this UID)
|
||||
-v Virtual memory size -P PID to affect (default $PPID)
|
||||
|
||||
-P PID to affect (default $PPID) -a Show all limits
|
||||
-S Set/show soft limit -H Set/show hard (maximum) limit
|
||||
|
||||
-c Core file size (blocks) -d Process data segment (KiB)
|
||||
-e Max scheduling priority -f File size (KiB)
|
||||
-i Pending signal count -l Locked memory (KiB)
|
||||
-m Resident Set Size (KiB) -n Number of open files
|
||||
-p Pipe buffer (512 bytes) -q POSIX message queues
|
||||
-r Max realtime priority -R Realtime latency (us)
|
||||
-s Stack size (KiB) -t Total CPU time (s)
|
||||
-u Maximum processes (this UID) -v Virtual memory size (KiB)
|
||||
*/
|
||||
|
||||
#define FOR_ulimit
|
||||
@@ -55,17 +55,25 @@ int prlimit(pid_t pid, int resource, const struct rlimit *new_limit,
|
||||
struct rlimit *old_limit);
|
||||
|
||||
// I'd like to sort the RLIMIT values 0-15, but mips, alpha and sparc
|
||||
// override the asm-generic values for 5-9. Also, the kernel implementation
|
||||
// of RLIMIT_LOCKS (-x) was removed from Linux in 2003.
|
||||
// override the asm-generic values for 5-9, and alphabetical order is nice for
|
||||
// humans anyway.
|
||||
void ulimit_main(void)
|
||||
{
|
||||
struct rlimit rr;
|
||||
int i;
|
||||
// Order is cdefilmnqRrstuv
|
||||
char map[] = {RLIMIT_CORE, RLIMIT_DATA, RLIMIT_NICE, RLIMIT_FSIZE,
|
||||
RLIMIT_SIGPENDING, RLIMIT_MEMLOCK, RLIMIT_RSS, RLIMIT_NOFILE, 0,
|
||||
RLIMIT_MSGQUEUE, RLIMIT_RTTIME, RLIMIT_RTPRIO, RLIMIT_STACK,
|
||||
RLIMIT_CPU, RLIMIT_NPROC, RLIMIT_AS};
|
||||
// map and desc are in the same order as flags.
|
||||
// The Linux kernel implementation of RLIMIT_LOCKS (-x) was removed in 2003.
|
||||
char *flags="cdefilmnpqRrstuv";
|
||||
char map[] = {RLIMIT_CORE, RLIMIT_DATA, RLIMIT_NICE,
|
||||
RLIMIT_FSIZE, RLIMIT_SIGPENDING, RLIMIT_MEMLOCK,
|
||||
RLIMIT_RSS, RLIMIT_NOFILE, 0, RLIMIT_MSGQUEUE,
|
||||
RLIMIT_RTTIME, RLIMIT_RTPRIO, RLIMIT_STACK, RLIMIT_CPU, RLIMIT_NPROC,
|
||||
RLIMIT_AS};
|
||||
char *desc[]={"core dump size (blocks)", "data size (KiB)", "max nice",
|
||||
"file size (blocks)", "pending signals", "locked memory (KiB)",
|
||||
"RSS (KiB)", "open files", "pipe size (512 bytes)", "message queues",
|
||||
"RT time (us)", "RT priority", "stack (KiB)", "cpu time (s)", "processes",
|
||||
"address space (KiB)"};
|
||||
|
||||
if (!(toys.optflags&(FLAG_H-1))) toys.optflags |= FLAG_f;
|
||||
if ((FLAG(a)||FLAG(p)) && toys.optc) error_exit("can't set -ap");
|
||||
@@ -74,13 +82,11 @@ void ulimit_main(void)
|
||||
if (!FLAG(P)) TT.P = getppid();
|
||||
|
||||
for (i=0; i<sizeof(map); i++) {
|
||||
char *flags="cdefilmnpqRrstuv";
|
||||
|
||||
int get = toys.optflags&(FLAG_a|(1<<i));
|
||||
|
||||
if (get && prlimit(TT.P, map[i], 0, &rr)) perror_exit("-%c", flags[i]);
|
||||
if (!toys.optc) {
|
||||
if (FLAG(a)) printf("-%c: ", flags[i]);
|
||||
if (FLAG(a)) printf("-%c: %-25s ", flags[i], desc[i]);
|
||||
if (get) {
|
||||
if ((1<<i)&FLAG_p) {
|
||||
if (FLAG(H))
|
||||
|
||||
+73
-73
@@ -8,15 +8,14 @@
|
||||
* TODO: -I Insert mode
|
||||
* TODO: -L Max number of lines of input per command
|
||||
* TODO: -x Exit if can't fit everything in one command
|
||||
* TODO: -P NUM Run up to NUM processes at once
|
||||
|
||||
USE_XARGS(NEWTOY(xargs, "^E:P#optrn#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
USE_XARGS(NEWTOY(xargs, "^E:P#<0(null)=1optr(no-run-if-empty)n#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config XARGS
|
||||
bool "xargs"
|
||||
default y
|
||||
help
|
||||
usage: xargs [-0prt] [-s NUM] [-n NUM] [-E STR] COMMAND...
|
||||
usage: xargs [-0Pprt] [-snE STR] COMMAND...
|
||||
|
||||
Run command line one or more times, appending arguments from stdin.
|
||||
|
||||
@@ -26,8 +25,9 @@ config XARGS
|
||||
-E Stop at line matching string
|
||||
-n Max number of arguments per command
|
||||
-o Open tty for COMMAND's stdin (default /dev/null)
|
||||
-P Parallel processes (default 1)
|
||||
-p Prompt for y/n from tty before running each command
|
||||
-r Don't run command with empty input (otherwise always run command once)
|
||||
-r Don't run with empty input (otherwise always run command once)
|
||||
-s Size in bytes per command line
|
||||
-t Trace, print command line to stderr
|
||||
*/
|
||||
@@ -39,7 +39,7 @@ GLOBALS(
|
||||
long s, n, P;
|
||||
char *E;
|
||||
|
||||
long entries, bytes;
|
||||
long entries, bytes, np;
|
||||
char delim;
|
||||
FILE *tty;
|
||||
)
|
||||
@@ -55,38 +55,35 @@ GLOBALS(
|
||||
static char *handle_entries(char *data, char **entry)
|
||||
{
|
||||
if (TT.delim) {
|
||||
char *save, *s = data;
|
||||
char *save, *ss, *s;
|
||||
|
||||
// Chop up whitespace delimited string into args
|
||||
while (*s) {
|
||||
while (isspace(*s)) {
|
||||
if (entry) *s = 0;
|
||||
s++;
|
||||
}
|
||||
|
||||
if (TT.n && TT.entries >= TT.n)
|
||||
return *s ? s : (char *)1;
|
||||
|
||||
for (s = data; *s; TT.entries++) {
|
||||
while (isspace(*s)) s++;
|
||||
if (TT.n && TT.entries >= TT.n) return *s ? s : (char *)1;
|
||||
if (!*s) break;
|
||||
save = s;
|
||||
|
||||
// We ought to add sizeof(char *) to TT.bytes to be correct, but we don't
|
||||
// for bug compatibility with busybox 1.30.1 and findutils 4.7.0.
|
||||
save = ss = s;
|
||||
|
||||
// Specifying -s can cause "argument too long" errors.
|
||||
if (!FLAG(s)) TT.bytes += sizeof(void *)+1;
|
||||
for (;;) {
|
||||
if (++TT.bytes >= TT.s && TT.s) return save;
|
||||
if (++TT.bytes >= TT.s) return save;
|
||||
if (!*s || isspace(*s)) break;
|
||||
s++;
|
||||
}
|
||||
if (TT.E && strstart(&save, TT.E)) return (char *)2;
|
||||
if (entry) entry[TT.entries] = save;
|
||||
++TT.entries;
|
||||
if (TT.E && strstart(&ss, TT.E) && ss == s) return (char *)2;
|
||||
if (entry) {
|
||||
entry[TT.entries] = save;
|
||||
if (*s) *s++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -0 support
|
||||
} else {
|
||||
TT.bytes += sizeof(char *)+strlen(data)+1;
|
||||
if ((TT.s && TT.bytes >= TT.s) || (TT.n && TT.entries >= TT.n)) return data;
|
||||
long bytes = TT.bytes+sizeof(char *)+strlen(data)+1;
|
||||
|
||||
if (bytes >= TT.s || (TT.n && TT.entries >= TT.n)) return data;
|
||||
TT.bytes = bytes;
|
||||
if (entry) entry[TT.entries] = data;
|
||||
TT.entries++;
|
||||
}
|
||||
@@ -94,20 +91,44 @@ static char *handle_entries(char *data, char **entry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle SIGUSR1 and SIGUSR2 for -P
|
||||
static void signal_P(int sig)
|
||||
{
|
||||
if (sig == SIGUSR2 && TT.P>1) TT.P--;
|
||||
else TT.P++;
|
||||
}
|
||||
|
||||
static void waitchild(int options)
|
||||
{
|
||||
int ii, status;
|
||||
|
||||
if (1>waitpid(-1, &status, options)) return;
|
||||
TT.np--;
|
||||
ii = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+128;
|
||||
if (ii == 255) {
|
||||
error_msg("%s: exited with status 255; aborting", *toys.optargs);
|
||||
toys.exitval = 124;
|
||||
} else if ((ii|1)==127) toys.exitval = ii;
|
||||
else if (ii>127) toys.exitval = 125;
|
||||
else if (ii) toys.exitval = 123;
|
||||
}
|
||||
|
||||
void xargs_main(void)
|
||||
{
|
||||
struct double_list *dlist = 0, *dtemp;
|
||||
int entries, bytes, done = 0, ran_once = 0, status;
|
||||
char *data = 0, **out;
|
||||
pid_t pid;
|
||||
int entries, bytes, done = 0;
|
||||
char *data = 0, **out = 0;
|
||||
pid_t pid = 0;
|
||||
|
||||
xsignal_flags(SIGUSR1, signal_P, SA_RESTART);
|
||||
xsignal_flags(SIGUSR2, signal_P, SA_RESTART);
|
||||
|
||||
// POSIX requires that we never hit the ARG_MAX limit, even if we try to
|
||||
// with -s. POSIX also says we have to reserve 2048 bytes "to guarantee
|
||||
// that the invoked utility has room to modify its environment variables
|
||||
// and command line arguments and still be able to invoke another utility",
|
||||
// though obviously that's not really something you can guarantee.
|
||||
bytes = sysconf(_SC_ARG_MAX) - environ_bytes() - 2048;
|
||||
if (!TT.s || TT.s > bytes) TT.s = bytes;
|
||||
if (!FLAG(s)) TT.s = sysconf(_SC_ARG_MAX) - environ_bytes() - 4096;
|
||||
|
||||
TT.delim = '\n'*!FLAG(0);
|
||||
|
||||
@@ -119,30 +140,36 @@ void xargs_main(void)
|
||||
}
|
||||
|
||||
// count entries
|
||||
for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++)
|
||||
bytes += strlen(toys.optargs[entries]);
|
||||
if (bytes >= TT.s) error_exit("argument too long");
|
||||
for (entries = 0, bytes = -1; entries < toys.optc; entries++)
|
||||
bytes += strlen(toys.optargs[entries])+1+sizeof(char *)*!FLAG(s);
|
||||
if (bytes >= TT.s) error_exit("command too long");
|
||||
|
||||
// Loop through exec chunks.
|
||||
while (data || !done) {
|
||||
TT.entries = 0;
|
||||
TT.bytes = bytes;
|
||||
if (TT.np) waitchild(WNOHANG*!(TT.np==TT.P||(!data && done)));
|
||||
if (toys.exitval==124) break;
|
||||
|
||||
// Arbitrary number of execs, can't just leak memory each time...
|
||||
llist_traverse(dlist, llist_free_double);
|
||||
dlist = 0;
|
||||
free(out);
|
||||
out = 0;
|
||||
|
||||
// Loop reading input
|
||||
for (;;) {
|
||||
|
||||
// Read line
|
||||
if (!data) {
|
||||
ssize_t l = 0;
|
||||
if (getdelim(&data, (size_t *)&l, TT.delim, stdin)<0) {
|
||||
size_t l = 0;
|
||||
|
||||
if (getdelim(&data, &l, TT.delim, stdin)<0) {
|
||||
data = 0;
|
||||
done++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
dlist_add(&dlist, data);
|
||||
|
||||
// Count data used
|
||||
if (!(data = handle_entries(data, 0))) continue;
|
||||
if (data == (char *)2) done++;
|
||||
@@ -154,16 +181,15 @@ void xargs_main(void)
|
||||
|
||||
if (!TT.entries) {
|
||||
if (data) error_exit("argument too long");
|
||||
else if (ran_once) return;
|
||||
else if (FLAG(r)) continue;
|
||||
if (pid || FLAG(r)) break;
|
||||
}
|
||||
|
||||
// Fill out command line to exec
|
||||
out = xzalloc((entries+TT.entries+1)*sizeof(char *));
|
||||
memcpy(out, toys.optargs, entries*sizeof(char *));
|
||||
out = xzalloc((toys.optc+TT.entries+1)*sizeof(char *));
|
||||
memcpy(out, toys.optargs, toys.optc*sizeof(char *));
|
||||
TT.entries = 0;
|
||||
TT.bytes = bytes;
|
||||
if (dlist) dlist->prev->next = 0;
|
||||
dlist_terminate(dlist);
|
||||
for (dtemp = dlist; dtemp; dtemp = dtemp->next)
|
||||
handle_entries(dtemp->data, out+entries);
|
||||
|
||||
@@ -174,43 +200,17 @@ void xargs_main(void)
|
||||
if (FLAG(p)) {
|
||||
fprintf(stderr, "?");
|
||||
if (!TT.tty) TT.tty = xfopen("/dev/tty", "re");
|
||||
if (!fyesno(TT.tty, 0)) goto skip;
|
||||
if (!fyesno(TT.tty, 0)) continue;
|
||||
} else fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if (!(pid = XVFORK())) {
|
||||
close(0);
|
||||
xopen_stdio(FLAG(o) ? "/dev/tty" : "/dev/null", O_RDONLY);
|
||||
xopen_stdio(FLAG(o) ? "/dev/tty" : "/dev/null", O_RDONLY|O_CLOEXEC);
|
||||
xexec(out);
|
||||
}
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
// xargs is yet another weird collection of exit value special cases,
|
||||
// different from all the others.
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) == 126 || WEXITSTATUS(status) == 127) {
|
||||
toys.exitval = WEXITSTATUS(status);
|
||||
return;
|
||||
} else if (WEXITSTATUS(status) >= 1 && WEXITSTATUS(status) <= 125) {
|
||||
toys.exitval = 123;
|
||||
} else if (WEXITSTATUS(status) == 255) {
|
||||
error_msg("%s: exited with status 255; aborting", out[0]);
|
||||
toys.exitval = 124;
|
||||
return;
|
||||
}
|
||||
} else toys.exitval = 127;
|
||||
|
||||
// Abritrary number of execs, can't just leak memory each time...
|
||||
skip:
|
||||
ran_once = 1;
|
||||
while (dlist) {
|
||||
struct double_list *dtemp = dlist->next;
|
||||
|
||||
free(dlist->data);
|
||||
free(dlist);
|
||||
dlist = dtemp;
|
||||
}
|
||||
free(out);
|
||||
TT.np++;
|
||||
}
|
||||
while (TT.np) waitchild(0);
|
||||
if (TT.tty) fclose(TT.tty);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user