From 37024c0af711daa01e0c4384dbe378919efb9fa6 Mon Sep 17 00:00:00 2001 From: Kiita Date: Mon, 7 Jun 2021 20:29:03 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=B0=86toybox=E4=BE=B5=E5=85=A5?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E6=94=B9=E8=BF=9B=E8=A1=8C=E5=88=86=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【背景】当前liteos_a支持的toybox命令与linux存在差别,需要特别实现,为 避免侵入式修改,采用porting的方式将修改分离。 【修改方案】 在third_party/toybox下新增porting目录,以达到分离修改的目的。本次提交 为纯净的未作修改的toybox源码,其存在的意义为给后续修改提供对比。 【影响】 对现有的产品暂无影响。 Change-Id: I1a6afb0341aa05afb46dff9e4f5bcffa12962e58 --- porting/liteos_a/Makefile | 87 ++ porting/liteos_a/kconfig/Makefile | 77 + porting/liteos_a/lib/dirtree.c | 199 +++ porting/liteos_a/toys.h | 128 ++ porting/liteos_a/toys/lsb/mount.c | 407 ++++++ porting/liteos_a/toys/lsb/umount.c | 152 ++ porting/liteos_a/toys/net/ifconfig.c | 550 ++++++++ porting/liteos_a/toys/net/ping.c | 265 ++++ porting/liteos_a/toys/other/free.c | 58 + porting/liteos_a/toys/posix/cat.c | 102 ++ porting/liteos_a/toys/posix/chgrp.c | 106 ++ porting/liteos_a/toys/posix/chmod.c | 65 + porting/liteos_a/toys/posix/cp.c | 529 +++++++ porting/liteos_a/toys/posix/date.c | 174 +++ porting/liteos_a/toys/posix/du.c | 168 +++ porting/liteos_a/toys/posix/echo.c | 100 ++ porting/liteos_a/toys/posix/kill.c | 154 ++ porting/liteos_a/toys/posix/ls.c | 604 ++++++++ porting/liteos_a/toys/posix/mkdir.c | 54 + porting/liteos_a/toys/posix/ps.c | 1935 ++++++++++++++++++++++++++ porting/liteos_a/toys/posix/rm.c | 109 ++ porting/liteos_a/toys/posix/rmdir.c | 50 + porting/liteos_a/toys/posix/touch.c | 85 ++ porting/liteos_a/toys/posix/uname.c | 90 ++ 24 files changed, 6248 insertions(+) create mode 100644 porting/liteos_a/Makefile create mode 100644 porting/liteos_a/kconfig/Makefile create mode 100644 porting/liteos_a/lib/dirtree.c create mode 100644 porting/liteos_a/toys.h create mode 100644 porting/liteos_a/toys/lsb/mount.c create mode 100644 porting/liteos_a/toys/lsb/umount.c create mode 100644 porting/liteos_a/toys/net/ifconfig.c create mode 100644 porting/liteos_a/toys/net/ping.c create mode 100644 porting/liteos_a/toys/other/free.c create mode 100644 porting/liteos_a/toys/posix/cat.c create mode 100644 porting/liteos_a/toys/posix/chgrp.c create mode 100644 porting/liteos_a/toys/posix/chmod.c create mode 100644 porting/liteos_a/toys/posix/cp.c create mode 100644 porting/liteos_a/toys/posix/date.c create mode 100644 porting/liteos_a/toys/posix/du.c create mode 100644 porting/liteos_a/toys/posix/echo.c create mode 100644 porting/liteos_a/toys/posix/kill.c create mode 100644 porting/liteos_a/toys/posix/ls.c create mode 100644 porting/liteos_a/toys/posix/mkdir.c create mode 100644 porting/liteos_a/toys/posix/ps.c create mode 100644 porting/liteos_a/toys/posix/rm.c create mode 100644 porting/liteos_a/toys/posix/rmdir.c create mode 100644 porting/liteos_a/toys/posix/touch.c create mode 100644 porting/liteos_a/toys/posix/uname.c diff --git a/porting/liteos_a/Makefile b/porting/liteos_a/Makefile new file mode 100644 index 0000000..69d2487 --- /dev/null +++ b/porting/liteos_a/Makefile @@ -0,0 +1,87 @@ +# Makefile for toybox. +# Copyright 2006 Rob Landley + +# If people set these on the make command line, use 'em +# Note that CC defaults to "cc" so the one in configure doesn't get +# used when scripts/make.sh and care called through "make". + +HOSTCC?=cc + +export CROSS_COMPILE CFLAGS OPTIMIZE LDOPTIMIZE CC HOSTCC V STRIP + +all: toybox + +KCONFIG_CONFIG ?= .config + +toybox_stuff: $(KCONFIG_CONFIG) *.[ch] lib/*.[ch] toys/*/*.c scripts/*.sh + +toybox generated/unstripped/toybox: toybox_stuff + scripts/make.sh + +.PHONY: clean distclean baseline bloatcheck install install_flat \ + uinstall uninstall_flat tests help toybox_stuff change \ + list list_working list_pending root run_root + +include kconfig/Makefile +-include .singlemake + +$(KCONFIG_CONFIG): $(KCONFIG_TOP) + @if [ -e "$(KCONFIG_CONFIG)" ]; then make silentoldconfig; \ + else echo "Not configured (run 'make defconfig' or 'make menuconfig')";\ + exit 1; fi + +$(KCONFIG_TOP): generated/Config.in generated/Config.probed +generated/Config.probed: generated/Config.in +generated/Config.in: toys/*/*.c scripts/genconfig.sh + scripts/genconfig.sh + +# Development targets +baseline: generated/unstripped/toybox + @cp generated/unstripped/toybox generated/unstripped/toybox_old + +bloatcheck: generated/unstripped/toybox_old generated/unstripped/toybox + @scripts/bloatcheck generated/unstripped/toybox_old generated/unstripped/toybox + +install_flat: + scripts/install.sh --symlink --force + +install_airlock: + scripts/install.sh --symlink --force --airlock + +install: + scripts/install.sh --long --symlink --force + +uninstall_flat: + scripts/install.sh --uninstall + +uninstall: + scripts/install.sh --long --uninstall + +change: + scripts/change.sh + +root_clean: + @rm -rf root + @echo root cleaned + +clean:: + @rm -rf toybox generated change .singleconfig* cross-log-*.* + @echo cleaned + +# If singlemake was in generated/ "make clean; make test_ls" wouldn't work. +distclean: clean root_clean + @rm -f toybox* .config* .singlemake + @echo removed .config + +tests: + scripts/test.sh + +root: + scripts/mkroot.sh $(MAKEFLAGS) + +run_root: + C=$$(basename "$$CROSS_COMPILE" | sed 's/-.*//'); \ + cd root/"$${C:-host}" && ./qemu-*.sh $(MAKEFLAGS) || exit 1 + +help:: + @cat scripts/help.txt diff --git a/porting/liteos_a/kconfig/Makefile b/porting/liteos_a/kconfig/Makefile new file mode 100644 index 0000000..90a8148 --- /dev/null +++ b/porting/liteos_a/kconfig/Makefile @@ -0,0 +1,77 @@ +# =========================================================================== +# Kernel configuration targets +# These targets are used from top-level makefile + +KCONFIG_TOP = Config.in +KCONFIG_PROJECT = ToyBox +obj = ./kconfig +PHONY += clean help oldconfig menuconfig config silentoldconfig \ + randconfig allyesconfig allnoconfig allmodconfig defconfig + +menuconfig: $(obj)/mconf $(KCONFIG_TOP) + $< $(KCONFIG_TOP) + +config: $(obj)/conf $(KCONFIG_TOP) + $< $(KCONFIG_TOP) + +oldconfig: $(obj)/conf $(KCONFIG_TOP) + $< -o $(KCONFIG_TOP) + +silentoldconfig: $(obj)/conf $(KCONFIG_TOP) + yes | $< -o $(KCONFIG_TOP) > /dev/null + +randconfig: $(obj)/conf $(KCONFIG_TOP) + $< -r $(KCONFIG_TOP) > /dev/null + +allyesconfig: $(obj)/conf $(KCONFIG_TOP) + $< -y $(KCONFIG_TOP) > /dev/null + +allnoconfig: $(obj)/conf $(KCONFIG_TOP) + $< -n $(KCONFIG_TOP) > /dev/null + +defconfig: $(obj)/conf $(KCONFIG_TOP) + $< -D /dev/null $(KCONFIG_TOP) > /dev/null + +macos_defconfig: $(obj)/conf $(KCONFIG_TOP) + KCONFIG_ALLCONFIG=$(obj)/macos_miniconfig $< -n $(KCONFIG_TOP) > /dev/null + +bsd_defconfig: $(obj)/conf $(KCONFIG_TOP) + KCONFIG_ALLCONFIG=$(obj)/freebsd_miniconfig $< -n $(KCONFIG_TOP) > /dev/null + +# Help text used by make help +help:: + @echo ' config - Update current config utilising a line-oriented program' + @echo ' menuconfig - Update current config utilising a menu based program' + @echo ' oldconfig - Update current config utilising a provided .config as base' + @echo ' silentoldconfig - Same as oldconfig, but quietly' + @echo ' randconfig - New config with random answer to all options' + @echo ' defconfig - New config with default answer to all options' + @echo ' This is the maximum sane configuration.' + @echo ' allyesconfig - New config where all options are accepted with yes' + @echo " This may not actually compile, it's a starting point" + @echo ' for further configuration (probably with menuconfig)' + @echo ' allnoconfig - New config where all options are answered with no' + @echo ' (NOP binary, starting point for further configuration)' + @echo ' macos_defconfig - Select commands known to build on macosx' + @echo ' bsd_defconfig - Select commands known to build on freebsd' + +# Cheesy build + +SHIPPED = kconfig/zconf.tab.c kconfig/lex.zconf.c kconfig/zconf.hash.c + +%.c: %.c_shipped + @ln -s $(notdir $<) $@ + +gen_config.h: .config + +kconfig/mconf: $(SHIPPED) + $(HOSTCC) -o $@ kconfig/mconf.c kconfig/zconf.tab.c \ + kconfig/lxdialog/*.c -lcurses -DCURSES_LOC="" \ + -DKBUILD_NO_NLS=1 -DPROJECT_NAME=\"$(KCONFIG_PROJECT)\" + +kconfig/conf: $(SHIPPED) + $(HOSTCC) -o $@ kconfig/conf.c kconfig/zconf.tab.c -DKBUILD_NO_NLS=1 \ + -DPROJECT_NAME=\"$(KCONFIG_PROJECT)\" + +clean:: + @rm -f $(wildcard kconfig/*zconf*.c) kconfig/conf kconfig/mconf diff --git a/porting/liteos_a/lib/dirtree.c b/porting/liteos_a/lib/dirtree.c new file mode 100644 index 0000000..df38b25 --- /dev/null +++ b/porting/liteos_a/lib/dirtree.c @@ -0,0 +1,199 @@ +/* dirtree.c - Functions for dealing with directory trees. + * + * Copyright 2007 Rob Landley + */ + +#include "toys.h" + +int isdotdot(char *name) +{ + if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 1; + + return 0; +} + +// Default callback, filters out "." and ".." except at top level. + +int dirtree_notdotdot(struct dirtree *catch) +{ + // Should we skip "." and ".."? + return (!catch->parent||!isdotdot(catch->name)) + *(DIRTREE_SAVE|DIRTREE_RECURSE); +} + +// Create a dirtree node from a path, with stat and symlink info. +// (This doesn't open directory filehandles yet so as not to exhaust the +// filehandle space on large trees, dirtree_handle_callback() does that.) + +struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, int flags) +{ + struct dirtree *dt = 0; + struct stat st; + int len = 0, linklen = 0, statless = 0; + + if (name) { + // open code this because haven't got node to call dirtree_parentfd() on yet + int fd = parent ? parent->dirfd : AT_FDCWD; + + if (fstatat(fd, name, &st,AT_SYMLINK_NOFOLLOW*!(flags&DIRTREE_SYMFOLLOW))) { + if (flags&DIRTREE_STATLESS) statless++; + else goto error; + } + if (S_ISLNK(st.st_mode)) { + if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error; + libbuf[linklen++]=0; + } + len = strlen(name); + } + + // Allocate/populate return structure + dt = xmalloc((len = sizeof(struct dirtree)+len+1)+linklen); + memset(dt, 0, statless ? offsetof(struct dirtree, again) + : offsetof(struct dirtree, st)); + dt->parent = parent; + dt->again = statless ? 2 : 0; + if (!statless) memcpy(&dt->st, &st, sizeof(struct stat)); + strcpy(dt->name, name ? name : ""); + if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen); + + return dt; + +error: + if (!(flags&DIRTREE_SHUTUP) && !isdotdot(name)) { + char *path = parent ? dirtree_path(parent, 0) : ""; + + perror_msg("%s%s%s", path, parent ? "/" : "", name); + if (parent) free(path); + } + if (parent) parent->symlink = (char *)1; + free(dt); + return 0; +} + +// Return path to this node, assembled recursively. + +// Initial call can pass in NULL to plen, or point to an int initialized to 0 +// to return the length of the path, or a value greater than 0 to allocate +// extra space if you want to append your own text to the string. + +char *dirtree_path(struct dirtree *node, int *plen) +{ + char *path; + int len; + + if (!node) { + path = xmalloc(*plen); + *plen = 0; + return path; + } + + len = (plen ? *plen : 0)+strlen(node->name)+1; + path = dirtree_path(node->parent, &len); + if (len && path[len-1] != '/') path[len++]='/'; + len = stpcpy(path+len, node->name) - path; + if (plen) *plen = len; + + return path; +} + +int dirtree_parentfd(struct dirtree *node) +{ + return node->parent ? node->parent->dirfd : AT_FDCWD; +} + +// Handle callback for a node in the tree. Returns saved node(s) if +// callback returns DIRTREE_SAVE, otherwise frees consumed nodes and +// returns NULL. If !callback return top node unchanged. +// If !new return DIRTREE_ABORTVAL + +struct dirtree *dirtree_handle_callback(struct dirtree *new, + int (*callback)(struct dirtree *node)) +{ + int flags; + + if (!new) return DIRTREE_ABORTVAL; + if (!callback) return new; + flags = callback(new); + + if (S_ISDIR(new->st.st_mode) && (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN))) + flags = dirtree_recurse(new, callback, + openat(dirtree_parentfd(new), new->name, O_CLOEXEC), flags); + + // If this had children, it was callback's job to free them already. + if (!(flags & DIRTREE_SAVE)) { + free(new); + new = 0; + } + + return (flags & DIRTREE_ABORT)==DIRTREE_ABORT ? DIRTREE_ABORTVAL : new; +} + +// Recursively read/process children of directory node, filtering through +// callback(). Uses and closes supplied ->dirfd. + +int dirtree_recurse(struct dirtree *node, + int (*callback)(struct dirtree *node), int dirfd, int flags) +{ + struct dirtree *new, **ddt = &(node->child); + struct dirent *entry; + DIR *dir; + + node->dirfd = dirfd; + if (node->dirfd == -1 || !(dir = fdopendir(node->dirfd))) { + if (!(flags & DIRTREE_SHUTUP)) { + char *path = dirtree_path(node, 0); + perror_msg_raw(path); + free(path); + } + close(node->dirfd); + + return flags; + } + + // according to the fddir() man page, the filehandle in the DIR * can still + // be externally used by things that don't lseek() it. + + // The extra parentheses are to shut the stupid compiler up. + while ((entry = readdir(dir))) { + if ((flags&DIRTREE_PROC) && !isdigit(*entry->d_name)) continue; + if (!(new = dirtree_add_node(node, entry->d_name, flags))) continue; + if (!new->st.st_blksize && !new->st.st_mode) + new->st.st_mode = entry->d_type<<12; + new = dirtree_handle_callback(new, callback); + if (new == DIRTREE_ABORTVAL) break; + if (new) { + *ddt = new; + ddt = &((*ddt)->next); + } + } + + if (flags & DIRTREE_COMEAGAIN) { + node->again |= 1; + flags = callback(node); + } + + // This closes filehandle as well, so note it + closedir(dir); + node->dirfd = -1; + + return flags; +} + +// Create dirtree from path, using callback to filter nodes. If !callback +// return just the top node. Use dirtree_notdotdot callback to allocate a +// tree of struct dirtree nodes and return pointer to root node for later +// processing. +// Returns DIRTREE_ABORTVAL if path didn't exist (use DIRTREE_SHUTUP to handle +// error message yourself). + +struct dirtree *dirtree_flagread(char *path, int flags, + int (*callback)(struct dirtree *node)) +{ + return dirtree_handle_callback(dirtree_add_node(0, path, flags), callback); +} + +// Common case +struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node)) +{ + return dirtree_flagread(path, 0, callback); +} diff --git a/porting/liteos_a/toys.h b/porting/liteos_a/toys.h new file mode 100644 index 0000000..177918a --- /dev/null +++ b/porting/liteos_a/toys.h @@ -0,0 +1,128 @@ +/* Toybox infrastructure. + * + * Copyright 2006 Rob Landley + */ + +// Stuff that needs to go before the standard headers + +#include "generated/config.h" +#include "lib/portability.h" + +// General posix-2008 headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Posix networking + +#include +#include +#include +#include +#include +#include +#include +#include + +// Internationalization support (also in POSIX and LSB) + +#include +#include +#include + +// LSB 4.1 headers +#include + +#include "lib/lib.h" +#include "lib/lsm.h" +#include "lib/toyflags.h" + +// Get list of function prototypes for all enabled command_main() functions. + +#define NEWTOY(name, opts, flags) void name##_main(void); +#define OLDTOY(name, oldname, flags) void oldname##_main(void); +#include "generated/newtoys.h" +#include "generated/flags.h" +#include "generated/globals.h" +#include "generated/tags.h" + +// These live in main.c + +struct toy_list *toy_find(char *name); +void toy_init(struct toy_list *which, char *argv[]); +void toy_exec(char *argv[]); + +// Array of available commands + +extern struct toy_list { + char *name; + void (*toy_main)(void); + char *options; + unsigned flags; +} toy_list[]; + +// Global context shared by all commands. + +extern struct toy_context { + struct toy_list *which; // Which entry in toy_list is this one? + char **argv; // Original command line arguments + char **optargs; // Arguments left over from get_optflags() + unsigned long long optflags; // Command line option flags from get_optflags() + int optc; // Count of optargs + int envc; // Count of original environ entries + int old_umask; // Old umask preserved by TOYFLAG_UMASK + short toycount; // Total number of commands in this build + short signal; // generic_signal() records what signal it saw here + int signalfd; // and writes signal to this fd, if set + char exitval; // Value error_exit feeds to exit() + char wasroot; // dropped setuid + + // This is at the end so toy_init() doesn't zero it. + sigjmp_buf *rebound; // siglongjmp here instead of exit when do_rebound + struct arg_list *xexit; // atexit() functions for xexit(), set by sigatexit() + void *stacktop; // nested toy_exec() call count, or 0 if vforked +} toys; + +// Two big temporary buffers: one for use by commands, one for library functions + +extern char toybuf[4096], libbuf[4096]; + +extern char **environ; + +#define FLAG(x) (toys.optflags&FLAG_##x) + +#define GLOBALS(...) +#define ARRAY_LEN(array) (sizeof(array)/sizeof(*array)) +#define TAGGED_ARRAY(X, ...) {__VA_ARGS__} diff --git a/porting/liteos_a/toys/lsb/mount.c b/porting/liteos_a/toys/lsb/mount.c new file mode 100644 index 0000000..296d686 --- /dev/null +++ b/porting/liteos_a/toys/lsb/mount.c @@ -0,0 +1,407 @@ +/* mount.c - mount filesystems + * + * Copyright 2014 Rob Landley + * + * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/mount.html + * + * Note: -hV is bad spec, haven't implemented -FsLU yet + * no mtab (/proc/mounts does it) so -n is NOP. + * TODO mount -o loop,autoclear (linux git 96c5865559ce) + +USE_MOUNT(NEWTOY(mount, "?O:afnrvwt:o*[-rw]", TOYFLAG_BIN|TOYFLAG_STAYROOT)) +//USE_NFSMOUNT(NEWTOY(nfsmount, "?<2>2", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) + +config MOUNT + bool "mount" + default y + help + usage: mount [-afFrsvw] [-t TYPE] [-o OPTION,] [[DEVICE] DIR] + + Mount new filesystem(s) on directories. With no arguments, display existing + mounts. + + -a Mount all entries in /etc/fstab (with -t, only entries of that TYPE) + -O Only mount -a entries that have this option + -f Fake it (don't actually mount) + -r Read only (same as -o ro) + -w Read/write (default, same as -o rw) + -t Specify filesystem type + -v Verbose + + OPTIONS is a comma separated list of options, which can also be supplied + as --longopts. + + Autodetects loopback mounts (a file on a directory) and bind mounts (file + on file, directory on directory), so you don't need to say --bind or --loop. + You can also "mount -a /path" to mount everything in /etc/fstab under /path, + even if it's noauto. DEVICE starting with UUID= is identified by blkid -U. + +#config SMBMOUNT +# bool "smbmount" +# deault n +# helo +# usage: smbmount SHARE DIR +# +# Mount smb share with user/pasword prompt as necessary. +# +#config NFSMOUNT +# bool "nfsmount" +# default n +# help +# usage: nfsmount SHARE DIR +# +# Invoke an eldrich horror from the dawn of time. +*/ + +#define FOR_mount +#include "toys.h" + +GLOBALS( + struct arg_list *optlist; + char *type; + char *bigO; + + unsigned long flags; + char *opts; + int okuser; +) + +// mount.tests should check for all of this: +// TODO detect existing identical mount (procfs with different dev name?) +// TODO user, users, owner, group, nofail +// TODO -p (passfd) +// TODO -a -t notype,type2 +// TODO --subtree +// TODO --rbind, -R +// TODO make "mount --bind,ro old new" work (implicit -o remount) +// TODO mount -a +// TODO mount -o remount +// TODO fstab: lookup default options for mount +// TODO implement -v +// TODO "mount -a -o remount,ro" should detect overmounts +// TODO work out how that differs from "mount -ar" +// TODO what if you --bind mount a block device somewhere (file, dir, dev) +// TODO "touch servername; mount -t cifs servername path" +// TODO mount -o remount a user mount +// TODO mount image.img sub (auto-loopback) then umount image.img +// TODO mount UUID=blah + +// Strip flags out of comma separated list of options, return flags,. +static long flag_opts(char *new, long flags, char **more) +{ + struct { + char *name; + long flags; + } opts[] = { + // NOPs (we autodetect --loop and --bind) + {"loop", 0}, {"bind", 0}, {"defaults", 0}, {"quiet", 0}, + {"user", 0}, {"nouser", 0}, // checked in fstab, ignored in -o + {"ro", MS_RDONLY}, {"rw", ~MS_RDONLY}, + {"nosuid", MS_NOSUID}, {"suid", ~MS_NOSUID}, + {"nodev", MS_NODEV}, {"dev", ~MS_NODEV}, + {"noexec", MS_NOEXEC}, {"exec", ~MS_NOEXEC}, + {"sync", MS_SYNCHRONOUS}, {"async", ~MS_SYNCHRONOUS}, + {"noatime", MS_NOATIME}, {"atime", ~MS_NOATIME}, + {"norelatime", ~MS_RELATIME}, {"relatime", MS_RELATIME}, + {"nodiratime", MS_NODIRATIME}, {"diratime", ~MS_NODIRATIME}, + {"loud", ~MS_SILENT}, + {"shared", MS_SHARED}, {"rshared", MS_SHARED|MS_REC}, + {"slave", MS_SLAVE}, {"rslave", MS_SLAVE|MS_REC}, + {"private", MS_PRIVATE}, {"rprivate", MS_SLAVE|MS_REC}, + {"unbindable", MS_UNBINDABLE}, {"runbindable", MS_UNBINDABLE|MS_REC}, + {"remount", MS_REMOUNT}, {"move", MS_MOVE}, + // mand dirsync rec iversion strictatime + }; + + if (new) for (;;) { + char *comma = strchr(new, ','); + int i; + + if (comma) *comma = 0; + + // If we recognize an option, apply flags + for (i = 0; i < ARRAY_LEN(opts); i++) if (!strcasecmp(opts[i].name, new)) { + long ll = opts[i].flags; + + if (ll < 0) flags &= ll; + else flags |= ll; + + break; + } + + // If we didn't recognize it, keep string version + if (more && i == ARRAY_LEN(opts)) { + i = *more ? strlen(*more) : 0; + *more = xrealloc(*more, i + strlen(new) + 2); + if (i) (*more)[i++] = ','; + strcpy(i+*more, new); + } + + if (!comma) break; + *comma = ','; + new = comma + 1; + } + + return flags; +} + +// Shell out to a program, returning the output string or NULL on error +static char *tortoise(int loud, char **cmd) +{ + int rc, pipe, len; + pid_t pid; + + pid = xpopen(cmd, &pipe, 1); + len = readall(pipe, toybuf, sizeof(toybuf)-1); + rc = xpclose(pid, pipe); + if (!rc && len > 1) { + if (toybuf[len-1] == '\n') --len; + toybuf[len] = 0; + return toybuf; + } + if (loud) error_msg("%s failed %d", *cmd, rc); + + return 0; +} + +static void mount_filesystem(char *dev, char *dir, char *type, + unsigned long flags, char *opts) +{ + FILE *fp = 0; + int rc = EINVAL; + char *buf = 0; + + if (FLAG(f)) return; + + if (getuid()) { + if (TT.okuser) TT.okuser = 0; + else { + error_msg("'%s' not user mountable in fstab", dev); + + return; + } + } + + if (strstart(&dev, "UUID=")) { + char *s = tortoise(0, (char *[]){"blkid", "-U", dev, 0}); + + if (!dev) return error_msg("No uuid %s", dev); + dev = s; + } + + // Autodetect bind mount or filesystem type + + if (type && !strcmp(type, "auto")) type = 0; + if (flags & MS_MOVE) { + if (type) error_exit("--move with -t"); + } else if (!type) { + struct stat stdev, stdir; + + // file on file or dir on dir is a --bind mount. + if (!stat(dev, &stdev) && !stat(dir, &stdir) + && ((S_ISREG(stdev.st_mode) && S_ISREG(stdir.st_mode)) + || (S_ISDIR(stdev.st_mode) && S_ISDIR(stdir.st_mode)))) + { + flags |= MS_BIND; + } else fp = xfopen("/proc/filesystems", "r"); + } else if (!strcmp(type, "ignore")) return; + else if (!strcmp(type, "swap")) + toys.exitval |= xrun((char *[]){"swapon", "--", dev, 0}); + + for (;;) { + int fd = -1, ro = 0; + + // If type wasn't specified, try all of them in order. + if (fp && !buf) { + size_t i; + + if (getline(&buf, &i, fp)<0) { + error_msg("%s: need -t", dev); + break; + } + type = buf; + // skip nodev devices + if (!isspace(*type)) { + free(buf); + buf = 0; + + continue; + } + // trim whitespace + while (isspace(*type)) type++; + i = strlen(type); + if (i) type[i-1] = 0; + } + if (FLAG(v)) + printf("try '%s' type '%s' on '%s'\n", dev, type, dir); + for (;;) { + rc = mount(dev, dir, type, flags, opts); + // Did we succeed, fail unrecoverably, or already try read-only? + if (!rc || (errno != EACCES && errno != EROFS) || (flags&MS_RDONLY)) + break; + // If we haven't already tried it, use the BLKROSET ioctl to ensure + // that the underlying device isn't read-only. + if (fd == -1) { + if (FLAG(v)) + printf("trying BLKROSET ioctl on '%s'\n", dev); + if (-1 != (fd = open(dev, O_RDONLY))) { + rc = ioctl(fd, BLKROSET, &ro); + close(fd); + if (!rc) continue; + } + } + fprintf(stderr, "'%s' is read-only\n", dev); + flags |= MS_RDONLY; + } + + // Trying to autodetect loop mounts like bind mounts above (file on dir) + // isn't good enough because "mount -t ext2 fs.img dir" is valid, but if + // you _do_ accept loop mounts with -t how do you tell "-t cifs" isn't + // looking for a block device if it's not in /proc/filesystems yet + // because the fs module won't be loaded until you try the mount, and + // if you can't then DEVICE existing as a file would cause a false + // positive loopback mount (so "touch servername" becomes a potential + // denial of service attack...) + // + // Solution: try the mount, let the kernel tell us it wanted a block + // device, then do the loopback setup and retry the mount. + + if (rc && errno == ENOTBLK) { + dev = tortoise(1, (char *[]){"losetup", + (flags&MS_RDONLY) ? "-fsr" : "-fs", dev, 0}); + if (!dev) break; + } + + free(buf); + buf = 0; + if (!rc) break; + if (fp && (errno == EINVAL || errno == EBUSY)) continue; + + perror_msg("'%s'->'%s'", dev, dir); + + break; + } + if (fp) fclose(fp); +} + +void mount_main(void) +{ + char *opts = 0, *dev = 0, *dir = 0, **ss; + long flags = MS_SILENT; + struct arg_list *o; + struct mtab_list *mtl, *mtl2 = 0, *mm, *remount; + +// TODO +// remount +// - overmounts +// shared subtree +// -o parsed after fstab options +// test if mountpoint already exists (-o noremount?) + + // First pass; just accumulate string, don't parse flags yet. (This is so + // we can modify fstab entries with -a, or mtab with remount.) + for (o = TT.optlist; o; o = o->next) comma_collate(&opts, o->arg); + if (FLAG(r)) comma_collate(&opts, "ro"); + if (FLAG(w)) comma_collate(&opts, "rw"); + + // Treat each --option as -o option + for (ss = toys.optargs; *ss; ss++) { + char *sss = *ss; + + // If you realy, really want to mount a file named "--", we support it. + if (sss[0]=='-' && sss[1]=='-' && sss[2]) comma_collate(&opts, sss+2); + else if (!dev) dev = sss; + else if (!dir) dir = sss; + // same message as lib/args.c ">2" which we can't use because --opts count + else error_exit("Max 2 arguments\n"); + } + + if (FLAG(a) && dir) error_exit("-a with >1 arg"); + + // For remount we need _last_ match (in case of overmounts), so traverse + // in reverse order. (Yes I'm using remount as a boolean for a bit here, + // the double cast is to get gcc to shut up about it.) + remount = (void *)(long)comma_scan(opts, "remount", 0); + if ((FLAG(a) && !access("/proc/mounts", R_OK)) || remount) { + mm = dlist_terminate(mtl = mtl2 = xgetmountlist(0)); + if (remount) remount = mm; + } + + // Do we need to do an /etc/fstab trawl? + // This covers -a, -o remount, one argument, all user mounts + if (FLAG(a) || (dev && (!dir || getuid() || remount))) { + if (!remount) dlist_terminate(mtl = xgetmountlist("/etc/fstab")); + + for (mm = remount ? remount : mtl; mm; mm = (remount ? mm->prev : mm->next)) + { + char *aopts = 0; + struct mtab_list *mmm = 0; + int aflags, noauto, len; + + // Check for noauto and get it out of the option list. (Unknown options + // that make it to the kernel give filesystem drivers indigestion.) + noauto = comma_scan(mm->opts, "noauto", 1); + + if (FLAG(a)) { + // "mount -a /path" to mount all entries under /path + if (dev) { + len = strlen(dev); + if (strncmp(dev, mm->dir, len) + || (mm->dir[len] && mm->dir[len] != '/')) continue; + } else if (noauto) continue; // never present in the remount case + if (!mountlist_istype(mm,TT.type) || !comma_scanall(mm->opts,TT.bigO)) + continue; + } else { + if (dir && strcmp(dir, mm->dir)) continue; + if (dev && strcmp(dev, mm->device) && (dir || strcmp(dev, mm->dir))) + continue; + } + + // Don't overmount the same dev on the same directory + // (Unless root explicitly says to in non -a mode.) + if (mtl2 && !remount) + for (mmm = mtl2; mmm; mmm = mmm->next) + if (!strcmp(mm->dir, mmm->dir) && !strcmp(mm->device, mmm->device)) + break; + + // user only counts from fstab, not opts. + if (!mmm) { + TT.okuser = comma_scan(mm->opts, "user", 1); + aflags = flag_opts(mm->opts, flags, &aopts); + aflags = flag_opts(opts, aflags, &aopts); + + mount_filesystem(mm->device, mm->dir, mm->type, aflags, aopts); + } // TODO else if (getuid()) error_msg("already there") ? + free(aopts); + + if (!FLAG(a)) break; + } + if (CFG_TOYBOX_FREE) { + llist_traverse(mtl, free); + llist_traverse(mtl2, free); + } + if (!mm && !FLAG(a)) + error_exit("'%s' not in %s", dir ? dir : dev, + remount ? "/proc/mounts" : "fstab"); + + // show mounts from /proc/mounts + } else if (!dev) { + for (mtl = xgetmountlist(0); mtl && (mm = dlist_pop(&mtl)); free(mm)) { + char *s = 0; + + if (TT.type && strcmp(TT.type, mm->type)) continue; + if (*mm->device == '/') s = xabspath(mm->device, 0); + xprintf("%s on %s type %s (%s)\n", + s ? s : mm->device, mm->dir, mm->type, mm->opts); + free(s); + } + + // two arguments + } else { + char *more = 0; + + flags = flag_opts(opts, flags, &more); + mount_filesystem(dev, dir, TT.type, flags, more); + if (CFG_TOYBOX_FREE) free(more); + } +} diff --git a/porting/liteos_a/toys/lsb/umount.c b/porting/liteos_a/toys/lsb/umount.c new file mode 100644 index 0000000..3c9e3ca --- /dev/null +++ b/porting/liteos_a/toys/lsb/umount.c @@ -0,0 +1,152 @@ +/* umount.c - Unmount a mount point. + * + * Copyright 2012 Rob Landley + * + * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/umount.html + * + * Note: -n (/etc/mtab) is obsolete, /proc/mounts replaced it. Neither chroot + * nor per-process mount namespaces can work sanely with mtab. The kernel + * tracks mount points now, a userspace application can't do so anymore. + +USE_UMOUNT(NEWTOY(umount, "cndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT)) + +config UMOUNT + bool "umount" + default y + help + usage: umount [-a [-t TYPE[,TYPE...]]] [-vrfD] [DIR...] + + Unmount the listed filesystems. + + -a Unmount all mounts in /proc/mounts instead of command line list + -D Don't free loopback device(s) + -f Force unmount + -l Lazy unmount (detach from filesystem now, close when last user does) + -n Don't use /proc/mounts + -r Remount read only if unmounting fails + -t Restrict "all" to mounts of TYPE (or use "noTYPE" to skip) + -v Verbose +*/ + +#define FOR_umount +#include "toys.h" + +GLOBALS( + struct arg_list *t; + + char *types; +) + +// todo (done?) +// borrow df code to identify filesystem? +// umount -a from fstab +// umount when getpid() not 0, according to fstab +// lookup mount: losetup -d, bind, file, block +// loopback delete +// fstab -o user + +// TODO +// swapon, swapoff + +static void do_umount(char *dir, char *dev, int flags) +{ + // is it ok for this user to umount this mount? + if (CFG_TOYBOX_SUID && getuid()) { + struct mtab_list *mt = dlist_terminate(xgetmountlist("/etc/fstab")); + int len, user = 0; + + while (mt) { + struct mtab_list *mtemp = mt; + char *s; + + if (!strcmp(mt->dir, dir)) while ((s = comma_iterate(&mt->opts, &len))) { + if (len == 4 && strncmp(s, "user", 4)) user = 1; + else if (len == 6 && strncmp(s, "nouser", 6)) user = 0; + } + + mt = mt->next; + free(mtemp); + } + + if (!user) { + error_msg("not root"); + + return; + } + } + + if (!umount2(dir, flags)) { + if (toys.optflags & FLAG_v) xprintf("%s unmounted\n", dir); + + // Attempt to disassociate loopback device. This ioctl should be ignored + // for anything else, because lanana allocated ioctl range 'L' to loopback + if (dev && !(toys.optflags & FLAG_D)) { + int lfd = open(dev, O_RDONLY); + + if (lfd != -1) { + // This is LOOP_CLR_FD, fetching it from headers is awkward + if (!ioctl(lfd, 0x4C01) && (toys.optflags & FLAG_v)) + xprintf("%s cleared\n", dev); + close(lfd); + } + } + + return; + } + + if (toys.optflags & FLAG_r) { + if (!mount("", dir, "", MS_REMOUNT|MS_RDONLY, "")) { + if (toys.optflags & FLAG_v) xprintf("%s remounted ro\n", dir); + return; + } + } + + perror_msg_raw(dir); +} + +void umount_main(void) +{ + char **optargs, *pm = "/proc/mounts"; + struct mtab_list *mlsave = 0, *mlrev = 0, *ml; + int flags=0; + + if (!toys.optc && !(toys.optflags & FLAG_a)) + error_exit("Need 1 arg or -a"); + + if (toys.optflags & FLAG_f) flags |= MNT_FORCE; + if (toys.optflags & FLAG_l) flags |= MNT_DETACH; + + // Load /proc/mounts and get a reversed list (newest first) + // We use the list both for -a, and to umount /dev/name or do losetup -d + if (!(toys.optflags & FLAG_n) && !access(pm, R_OK)) + mlrev = dlist_terminate(mlsave = xgetmountlist(pm)); + + // Unmount all: loop through mounted filesystems, skip -t, unmount the rest + if (toys.optflags & FLAG_a) { + char *typestr = 0; + struct arg_list *tal; + + for (tal = TT.t; tal; tal = tal->next) comma_collate(&typestr, tal->arg); + for (ml = mlrev; ml; ml = ml->prev) + if (mountlist_istype(ml, typestr)) do_umount(ml->dir, ml->device, flags); + if (CFG_TOYBOX_FREE) { + free(typestr); + llist_traverse(mlsave, free); + } + // TODO: under what circumstances do we umount non-absolute path? + } else for (optargs = toys.optargs; *optargs; optargs++) { + char *abs = xabspath(*optargs, 0); + + for (ml = abs ? mlrev : 0; ml; ml = ml->prev) { + if (!strcmp(ml->dir, abs)) break; + if (!strcmp(ml->device, abs)) { + free(abs); + abs = ml->dir; + break; + } + } + + do_umount(abs ? abs : *optargs, ml ? ml->device : 0, flags); + if (ml && abs != ml->dir) free(abs); + } +} diff --git a/porting/liteos_a/toys/net/ifconfig.c b/porting/liteos_a/toys/net/ifconfig.c new file mode 100644 index 0000000..f8f1277 --- /dev/null +++ b/porting/liteos_a/toys/net/ifconfig.c @@ -0,0 +1,550 @@ +/* ifconfig.c - Configure network interface. + * + * Copyright 2012 Ranjan Kumar + * Copyright 2012 Kyungwan Han + * Reviewed by Kyungsu Kim + * + * Not in SUSv4. + * + * Obsolete fields included for historical purposes: + * irq|io_addr|mem_start ADDR - micromanage obsolete hardware + * outfill|keepalive INTEGER - SLIP analog dialup line quality monitoring + * metric INTEGER - added to Linux 0.9.10 with comment "never used", still true + +USE_IFCONFIG(NEWTOY(ifconfig, "^?aS", TOYFLAG_SBIN)) + +config IFCONFIG + bool "ifconfig" + default y + help + usage: ifconfig [-aS] [INTERFACE [ACTION...]] + + Display or configure network interface. + + With no arguments, display active interfaces. First argument is interface + to operate on, one argument by itself displays that interface. + + -a All interfaces displayed, not just active ones + -S Short view, one line per interface + + Standard ACTIONs to perform on an INTERFACE: + + ADDR[/MASK] - set IPv4 address (1.2.3.4/5) and activate interface + add|del ADDR[/LEN] - add/remove IPv6 address (1111::8888/128) + up|down - activate or deactivate interface + + Advanced ACTIONs (default values usually suffice): + + default - remove IPv4 address + netmask ADDR - set IPv4 netmask via 255.255.255.0 instead of /24 + txqueuelen LEN - number of buffered packets before output blocks + mtu LEN - size of outgoing packets (Maximum Transmission Unit) + broadcast ADDR - Set broadcast address + pointopoint ADDR - PPP and PPPOE use this instead of "route add default gw" + hw TYPE ADDR - set hardware (mac) address (type = ether|infiniband) + + Flags you can set on an interface (or -remove by prefixing with -): + + arp - don't use Address Resolution Protocol to map LAN routes + promisc - don't discard packets that aren't to this LAN hardware address + multicast - force interface into multicast mode if the driver doesn't + allmulti - promisc for multicast packets +*/ + +#define FOR_ifconfig +#include "toys.h" + +#include +#include + +GLOBALS( + int sockfd; +) + +// Convert hostname to binary address for AF_INET or AF_INET6 +// return /prefix (or range max if none) +static int get_addrinfo(char *host, sa_family_t af, void *addr) +{ + struct addrinfo hints, *result, *rp = 0; + int status, len; + char *from, *slash; + + memset(&hints, 0 , sizeof(struct addrinfo)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + + slash = strchr(host, '/'); + if (slash) *slash = 0; + + status = getaddrinfo(host, NULL, &hints, &result); + if (!status) + for (rp = result; rp; rp = rp->ai_next) + if (rp->ai_family == af) break; + if (!rp) error_exit("bad address '%s' : %s", host, gai_strerror(status)); + + // ai_addr isn't struct in_addr or in6_addr, it's struct sockaddr. Of course. + // You'd think ipv4 and ipv6 would have some basic compatibility, but no. + from = ((char *)rp->ai_addr) + 4; + if (af == AF_INET6) { + len = 16; + from += 4; // skip "flowinfo" field ipv6 puts before address + } else len = 4; + memcpy(addr, from, len); + freeaddrinfo(result); + + len = -1; + if (slash) len = atolx_range(slash+1, 0, (af == AF_INET) ? 32 : 128); + + return len; +} + +static void display_ifconfig(char *name, int always, unsigned long long val[]) +{ + struct ifreq ifre; + struct sockaddr_in *si = (void *)&ifre.ifr_addr; + struct { + int type; + char *title; + } types[] = { + {ARPHRD_LOOPBACK, "Local Loopback"}, {ARPHRD_ETHER, "Ethernet"}, + {ARPHRD_PPP, "Point-to-Point Protocol"}, {ARPHRD_INFINIBAND, "InfiniBand"}, + {ARPHRD_SIT, "IPv6-in-IPv4"}, {-1, "UNSPEC"} + }; + int i; + char *pp; + FILE *fp; + short flags; + + xstrncpy(ifre.ifr_name, name, IFNAMSIZ); + if (ioctl(TT.sockfd, SIOCGIFFLAGS, &ifre)<0) perror_exit_raw(name); + flags = ifre.ifr_flags; + if (!always && !(flags & IFF_UP)) return; + + if (toys.optflags&FLAG_S) { + unsigned uu = 0; + int len; + + ioctl(TT.sockfd, SIOCGIFADDR, &ifre); + len = printf("%*s %s", -9, name, inet_ntoa(si->sin_addr)); + if (!ioctl(TT.sockfd, SIOCGIFNETMASK, &ifre)) + uu = htonl(*(unsigned *)&(si->sin_addr)); + for (i = 0; uu; i++) uu <<= 1; + len += printf("/%d", i); + printf("%*c", 29-len, ' '); + } + + // Query hardware type and hardware address. + // Not xioctl because you don't have permission for this on Android. + ioctl(TT.sockfd, SIOCGIFHWADDR, &ifre); + + if (toys.optflags&FLAG_S) + for (i=0; i<6; i++) printf(":%02x"+!i, ifre.ifr_hwaddr.sa_data[i]); + else { + for (i=0; i < ARRAY_LEN(types)-1; i++) + if (ifre.ifr_hwaddr.sa_family == types[i].type) break; + xprintf("%-9s Link encap:%s ", name, types[i].title); + if(ifre.ifr_hwaddr.sa_family == ARPHRD_ETHER) { + xprintf("HWaddr "); + for (i=0; i<6; i++) xprintf(":%02x"+!i, ifre.ifr_hwaddr.sa_data[i]); + } + sprintf(toybuf, "/sys/class/net/%.15s/device/driver", name); + if (readlink0(toybuf, toybuf, sizeof(toybuf))>0) + if ((pp = strrchr(toybuf, '/'))) xprintf(" Driver %s", pp+1); + xputc('\n'); + } + + // If an address is assigned record that. + + ifre.ifr_addr.sa_family = AF_INET; + memset(&ifre.ifr_addr, 0, sizeof(ifre.ifr_addr)); + ioctl(TT.sockfd, SIOCGIFADDR, &ifre); + pp = (char *)&ifre.ifr_addr; + for (i = 0; isin_family == AF_INET) ? "inet" : + (si->sin_family == AF_INET6) ? "inet6" : "unspec"); + + for (i=0; isin_family = 0; + xprintf(" %s:%s ", addr[i].name, + (si->sin_family == 0xFFFF || !si->sin_family) + ? "[NOT SET]" : inet_ntoa(si->sin_addr)); + } + } + + xputc('\n'); + } + + fp = fopen(pp = "/proc/net/if_inet6", "r"); + if (fp) { + char iface_name[IFNAMSIZ]; + int plen, iscope; + + while (fgets(toybuf, sizeof(toybuf), fp)) { + int nitems; + char ipv6_addr[40]; + + nitems = sscanf(toybuf, "%32s %*08x %02x %02x %*02x %15s\n", + ipv6_addr, &plen, &iscope, iface_name); + if (nitems<0 && feof(fp)) break; + if (nitems != 4) perror_exit("bad %s", pp); + + if (!strcmp(name, iface_name)) { + struct sockaddr_in6 s6; + char *ptr = ipv6_addr+sizeof(ipv6_addr)-1; + + // convert giant hex string into colon-spearated ipv6 address by + // inserting ':' every 4 characters. + for (i = 32; i; i--) + if ((*(ptr--) = ipv6_addr[i])) if (!(i&3)) *(ptr--) = ':'; + + // Convert to binary and back to get abbreviated :: version + if (inet_pton(AF_INET6, ipv6_addr, (void *)&s6.sin6_addr) > 0) { + if (inet_ntop(AF_INET6, &s6.sin6_addr, toybuf, sizeof(toybuf))) { + char *scopes[] = {"Global","Host","Link","Site","Compat"}, + *scope = "Unknown"; + + for (i=0; i= 0) val[16] = ifre.ifr_qlen; + else val[16] = -1; + + for (i = 0; i= 0x100) // IO_MAP_INDEX + xprintf("Base address:0x%x ", ifre.ifr_map.base_addr); + if(ifre.ifr_map.mem_start) + xprintf("Memory:%lx-%lx ", ifre.ifr_map.mem_start, ifre.ifr_map.mem_end); + if(ifre.ifr_map.dma) xprintf("DMA chan:%x ", ifre.ifr_map.dma); + xputc('\n'); + } + xputc('\n'); +} + +static void show_iface(char *iface_name) +{ + char *name; + struct string_list *ifaces = 0, *sl; + int i, j; + FILE *fp; + + fp = xfopen("/proc/net/dev", "r"); + + for (i=0; fgets(toybuf, sizeof(toybuf), fp); i++) { + char *buf = toybuf; + unsigned long long val[17]; + + if (i<2) continue; + + while (isspace(*buf)) buf++; + name = strsep(&buf, ":"); + if(!buf) error_exit("bad name %s", name); + + errno = 0; + for (j=0; j<16 && !errno; j++) val[j] = strtoll(buf, &buf, 0); + if (errno) perror_exit("bad %s at %s", name, buf); + + if (iface_name) { + if (!strcmp(iface_name, name)) { + display_ifconfig(iface_name, 1, val); + + return; + } + } else { + sl = xmalloc(sizeof(*sl)+strlen(name)+1); + strcpy(sl->str, name); + sl->next = ifaces; + ifaces = sl; + + display_ifconfig(sl->str, toys.optflags & FLAG_a, val); + } + } + fclose(fp); + + if (iface_name) display_ifconfig(iface_name, 1, 0); + else { + struct ifconf ifcon; + struct ifreq *ifre; + int num; + + // Loop until buffer's big enough + ifcon.ifc_buf = NULL; + for (num = 30;;num += 10) { + ifcon.ifc_len = sizeof(struct ifreq)*num; + ifcon.ifc_buf = xrealloc(ifcon.ifc_buf, ifcon.ifc_len); + xioctl(TT.sockfd, SIOCGIFCONF, &ifcon); + if (ifcon.ifc_len != sizeof(struct ifreq)*num) break; + } + + ifre = ifcon.ifc_req; + for(num = 0; num < ifcon.ifc_len && ifre; num += sizeof(struct ifreq), ifre++) + { + // Skip duplicates + for(sl = ifaces; sl; sl = sl->next) + if(!strcmp(sl->str, ifre->ifr_name)) break; + + if(!sl) display_ifconfig(ifre->ifr_name, toys.optflags & FLAG_a, 0); + } + + free(ifcon.ifc_buf); + } + + llist_traverse(ifaces, free); +} + +// Encode offset and size of field into an int, and make result negative +#define IFREQ_OFFSZ(x) -(int)((offsetof(struct ifreq, x)<<16) + sizeof(ifre.x)) + +void ifconfig_main(void) +{ + char **argv = toys.optargs; + struct ifreq ifre; + int i; + + TT.sockfd = xsocket(AF_INET, SOCK_DGRAM, 0); + if(toys.optc < 2) { + show_iface(*argv); + return; + } + + // Open interface + memset(&ifre, 0, sizeof(struct ifreq)); + xstrncpy(ifre.ifr_name, *argv, IFNAMSIZ); + + // Perform operations on interface + while(*++argv) { + // Table of known operations + struct argh { + char *name; + int on, off; // set, clear + } try[] = { + {0, IFF_UP|IFF_RUNNING, SIOCSIFADDR}, + {"up", IFF_UP|IFF_RUNNING, 0}, + {"down", 0, IFF_UP}, + {"arp", 0, IFF_NOARP}, + {"promisc", IFF_PROMISC, 0}, + {"allmulti", IFF_ALLMULTI, 0}, + {"multicast", IFF_MULTICAST, 0}, + {"pointopoint", IFF_POINTOPOINT, SIOCSIFDSTADDR}, + {"broadcast", IFF_BROADCAST, SIOCSIFBRDADDR}, + {"netmask", 0, SIOCSIFNETMASK}, + {"dstaddr", 0, SIOCSIFDSTADDR}, + {"mtu", IFREQ_OFFSZ(ifr_mtu), SIOCSIFMTU}, + {"keepalive", IFREQ_OFFSZ(ifr_data), SIOCDEVPRIVATE}, // SIOCSKEEPALIVE + {"outfill", IFREQ_OFFSZ(ifr_data), SIOCDEVPRIVATE+2}, // SIOCSOUTFILL + {"metric", IFREQ_OFFSZ(ifr_metric), SIOCSIFMETRIC}, + {"txqueuelen", IFREQ_OFFSZ(ifr_qlen), SIOCSIFTXQLEN}, + {"mem_start", IFREQ_OFFSZ(ifr_map.mem_start), SIOCSIFMAP}, + {"io_addr", IFREQ_OFFSZ(ifr_map.base_addr), SIOCSIFMAP}, + {"irq", IFREQ_OFFSZ(ifr_map.irq), SIOCSIFMAP}, + {"inet", 0, 0}, + {"inet6", 0, 0} + }; + char *s = *argv; + int rev = (*s == '-'); + + s += rev; + + // "set hardware address" is oddball enough to special case + if (!strcmp(*argv, "hw")) { + char *hw_addr, *ptr, *p; + struct sockaddr *sock = &ifre.ifr_hwaddr; + int count = 6; + + ptr = p = (char *)sock->sa_data; + memset(sock, 0, sizeof(struct sockaddr)); + if (argv[1]) { + if (!strcmp("ether", *++argv)) sock->sa_family = ARPHRD_ETHER; + else if (!strcmp("infiniband", *argv)) { + sock->sa_family = ARPHRD_INFINIBAND; + count = 20; + p = ptr = toybuf; + } + } + if (!sock->sa_family || !argv[1]) help_exit("bad hw '%s'", *argv); + hw_addr = *++argv; + + // Parse and verify address. + while (*hw_addr && (p-ptr) < count) { + int val, len = 0; + + if (*hw_addr == ':') hw_addr++; + sscanf(hw_addr, "%2x%n", &val, &len); + if (!len || len > 2) break; // 1 nibble can be set e.g. C2:79:38:95:D:A + hw_addr += len; + *p++ = val; + } + + if ((p-ptr) != count || *hw_addr) + error_exit("bad hw-addr '%s'", *argv); + + // the linux kernel's "struct sockaddr" (include/linux/socket.h in the + // kernel source) only has 14 bytes of sa_data, and an infiniband address + // is 20. So if we go through the ioctl, the kernel will truncate + // infiniband addresses, meaning we have to go through sysfs instead. + if (sock->sa_family == ARPHRD_INFINIBAND && !strchr(ifre.ifr_name, '/')) { + int fd; + + sprintf(toybuf, "/sys/class/net/%s/address", ifre.ifr_name); + fd = xopen(toybuf, O_RDWR); + xwrite(fd, *argv, strlen(*argv)); + close(fd); + } else xioctl(TT.sockfd, SIOCSIFHWADDR, &ifre); + continue; + + // Add/remove ipv6 address to interface + + } else if (!strcmp(*argv, "add") || !strcmp(*argv, "del")) { + struct ifreq_inet6 { + struct in6_addr addr; + unsigned prefix; + int index; + } ifre6; + int plen, fd6 = xsocket(AF_INET6, SOCK_DGRAM, 0); + + if (!argv[1]) help_exit("%s", *argv); + + plen = get_addrinfo(argv[1], AF_INET6, &ifre6.addr); + if (plen < 0) plen = 128; + xioctl(fd6, SIOCGIFINDEX, &ifre); + ifre6.index = ifre.ifr_ifindex; + ifre6.prefix = plen; + xioctl(fd6, **(argv++)=='a' ? SIOCSIFADDR : SIOCDIFADDR, &ifre6); + + close(fd6); + continue; + // Iterate through table to find/perform operation + } else for (i = 0; ion, off = t->off; + + if (!t->name) { + if (isdigit(**argv) || !strcmp(*argv, "default")) argv--; + else continue; + } else if (strcmp(t->name, s)) continue; + + // Is this an SIOCSI entry? + if ((off|0xff) == 0x89ff) { + if (!rev) { + if (!*++argv) error_exit("%s needs argument", t->name); + + // Assign value to ifre field and call ioctl? (via IFREQ_OFFSZ.) + if (on < 0) { + long l = strtoul(*argv, 0, 0); + + if (off == SIOCSIFMAP) xioctl(TT.sockfd, SIOCGIFMAP, &ifre); + on = -on; + poke((on>>16) + (char *)&ifre, l, on&15); + xioctl(TT.sockfd, off, &ifre); + break; + } else { + struct sockaddr_in *si = (struct sockaddr_in *)&ifre.ifr_addr; + int mask = -1; + + si->sin_family = AF_INET; + + if (!strcmp(*argv, "default")) si->sin_addr.s_addr = INADDR_ANY; + else mask = get_addrinfo(*argv, AF_INET, &si->sin_addr); + xioctl(TT.sockfd, off, &ifre); + + // Handle /netmask + if (mask >= 0) { + // sin_addr probably isn't unaligned, but just in case... + mask = htonl((~0)<<(32-mask)); + memcpy(&si->sin_addr, &mask, 4); + xioctl(TT.sockfd, SIOCSIFNETMASK, &ifre); + } + } + } + off = 0; + } + + // Set flags + if (on || off) { + xioctl(TT.sockfd, SIOCGIFFLAGS, &ifre); + ifre.ifr_flags &= ~(rev ? on : off); + ifre.ifr_flags |= (rev ? off : on); + xioctl(TT.sockfd, SIOCSIFFLAGS, &ifre); + } + + break; + } + if (i == ARRAY_LEN(try)) help_exit("bad argument '%s'", *argv); + } + close(TT.sockfd); +} diff --git a/porting/liteos_a/toys/net/ping.c b/porting/liteos_a/toys/net/ping.c new file mode 100644 index 0000000..9ae7c85 --- /dev/null +++ b/porting/liteos_a/toys/net/ping.c @@ -0,0 +1,265 @@ +/* ping.c - check network connectivity + * + * Copyright 2014 Rob Landley + * + * Not in SUSv4. + * + * Note: ping_group_range should never have existed. To disable it, do: + * echo 0 $(((1<<31)-1)) > /proc/sys/net/ipv4/ping_group_range + * (Android does this by default in its init script.) + * + * Yes, I wimped out and capped -s at sizeof(toybuf), waiting for a complaint... + +// -s > 4088 = sizeof(toybuf)-sizeof(struct icmphdr), then kernel adds 20 bytes +USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4088=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN)) + +config PING + bool "ping" + default y + help + usage: ping [OPTIONS] HOST + + Check network connectivity by sending packets to a host and reporting + its response. + + Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each + echo it receives back, with round trip time. Returns true if host alive. + + Options: + -4, -6 Force IPv4 or IPv6 + -c CNT Send CNT many packets (default 3, 0 = infinite) + -f Flood (print . and \b to show drops, default -c 15 -i 0.2) + -i TIME Interval between packets (default 1, need root for < .2) + -I IFACE/IP Source interface or address + -m MARK Tag outgoing packets using SO_MARK + -q Quiet (stops after one returns true if host is alive) + -s SIZE Data SIZE in bytes (default 56) + -t TTL Set Time To Live (number of hops) + -W SEC Seconds to wait for response after last -c packet (default 3) + -w SEC Exit after this many seconds +*/ + +#define FOR_ping +#include "toys.h" + +#include +#include + +GLOBALS( + char *I; + long w, W, i, s, c, t, m; + + struct sockaddr *sa; + int sock; + unsigned long sent, recv, fugit, min, max; +) + +// Print a summary. Called as a single handler or at exit. +static void summary(int sig) +{ + if (!(toys.optflags&FLAG_q) && TT.sent && TT.sa) { + printf("\n--- %s ping statistics ---\n", ntop(TT.sa)); + printf("%lu packets transmitted, %lu received, %ld%% packet loss\n", + TT.sent, TT.recv, ((TT.sent-TT.recv)*100)/(TT.sent?TT.sent:1)); + printf("round-trip min/avg/max = %lu/%lu/%lu ms\n", + TT.min, TT.max, TT.fugit/(TT.recv?TT.recv:1)); + } + TT.sa = 0; +} + +// assumes aligned and can read even number of bytes +static unsigned short pingchksum(unsigned short *data, int len) +{ + unsigned short u = 0, d; + + // circular carry is endian independent: bits from high byte go to low byte + while (len>0) { + d = *data++; + if (len == 1) d &= 255<= (u += d)) u++; + len -= 2; + } + + return u; +} + +void ping_main(void) +{ + struct addrinfo *ai, *ai2; + struct ifaddrs *ifa, *ifa2 = 0; + struct icmphdr *ih = (void *)toybuf; + union socksaddr srcaddr, srcaddr2; + struct sockaddr *sa = (void *)&srcaddr; + int family = 0, len; + long long tnext, tW, tnow, tw; + unsigned short seq = 0, pkttime; + + // Set nonstatic default values + if (!(toys.optflags&FLAG_i)) TT.i = (toys.optflags&FLAG_f) ? 200 : 1000; + else if (TT.i<200 && getuid()) error_exit("need root for -i <200"); + if (!(toys.optflags&FLAG_s)) TT.s = 56; // 64-PHDR_LEN + if ((toys.optflags&(FLAG_f|FLAG_c)) == FLAG_f) TT.c = 15; + + // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.) + if (FLAG(6) || strchr(toys.which->name, '6')) family = AF_INET6; + else if (FLAG(4)) family = AF_INET; + else family = 0; + + // If -I srcaddr look it up. Allow numeric address of correct type. + memset(&srcaddr, 0, sizeof(srcaddr)); + if (TT.I) { + if (!(toys.optflags&FLAG_6) && inet_pton(AF_INET, TT.I, + (void *)&srcaddr.in.sin_addr)) + family = AF_INET; + else if (!(toys.optflags&FLAG_4) && inet_pton(AF_INET6, TT.I, + (void *)&srcaddr.in6.sin6_addr)) + family = AF_INET6; + else if (getifaddrs(&ifa2)) perror_exit("getifaddrs"); + } + + // Look up HOST address, filtering for correct type and interface. + // If -I but no -46 then find compatible type between -I and HOST + ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0); + for (ai = ai2; ai; ai = ai->ai_next) { + + // correct type? + if (family && family!=ai->ai_family) continue; + if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue; + + // correct interface? + if (!TT.I || !ifa2) break; + for (ifa = ifa2; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family + || strcmp(ifa->ifa_name, TT.I)) continue; + sa = (void *)ifa->ifa_addr; + + break; + } + if (ifa) break; + } + + if (!ai) + error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I); + TT.sa = ai->ai_addr; + + // Open DGRAM socket + sa->sa_family = ai->ai_family; + TT.sock = socket(ai->ai_family, SOCK_DGRAM, + len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6); + if (TT.sock == -1) { + perror_msg("socket SOCK_DGRAM %x", len); + if (errno == EACCES) { + fprintf(stderr, "Kernel bug workaround (as root):\n"); + fprintf(stderr, "echo 0 9999999 > /proc/sys/net/ipv4/ping_group_range\n"); + } + xexit(); + } + if (TT.I) xbind(TT.sock, sa, sizeof(srcaddr)); + + if (toys.optflags&FLAG_m) { + int mark = TT.m; + + xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)); + } + + if (TT.t) { + len = TT.t; + + if (ai->ai_family == AF_INET) + xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4); + else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, 4); + } + + if (!(toys.optflags&FLAG_q)) { + printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa)); + if (TT.I) { + *toybuf = 0; + printf(" from %s (%s)", TT.I, ntop(sa)); + } + // 20 byte TCP header, 8 byte ICMP header, plus data payload + printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28); + } + toys.exitval = 1; + + tW = tw = 0; + tnext = millitime(); + if (TT.w) tw = TT.w*1000+tnext; + + sigatexit(summary); + + // Send/receive packets + for (;;) { + int waitms = INT_MAX; + + // Exit due to timeout? (TODO: timeout is after last packet, waiting if + // any packets ever dropped. Not timeout since packet was dropped.) + tnow = millitime(); + if (tW) { + if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break; + waitms = tW-tnow; + } + if (tw) { + if (tnow>tw) break; + else if (waitms>tw-tnow) waitms = tw-tnow; + } + + // Time to send the next packet? + if (!tW && tnext-tnow <= 0) { + tnext += TT.i; + + memset(ih, 0, sizeof(*ih)); + ih->type = (ai->ai_family == AF_INET) ? 8 : 128; + ih->un.echo.id = getpid(); + ih->un.echo.sequence = ++seq; + if (TT.s >= 4) *(unsigned *)(ih+1) = tnow; + + ih->checksum = 0; + ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih)); + xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa); + TT.sent++; + if ((toys.optflags&(FLAG_f|FLAG_q)) == FLAG_f) xputc('.'); + + // last packet? + if (TT.c) if (!--TT.c) { + tW = tnow + TT.W*1000; + waitms = 1; // check for immediate return even when W=0 + } + } + + // This is down here so it's against new period if we just sent a packet + if (!tW && waitms>tnext-tnow) waitms = tnext-tnow; + + // wait for next packet or timeout + + if (waitms<0) waitms = 0; + if (!(len = xrecvwait(TT.sock, toybuf, sizeof(toybuf), &srcaddr2, waitms))) + continue; + + TT.recv++; + TT.fugit += (pkttime = millitime()-*(unsigned *)(ih+1)); + + // reply id == 0 for ipv4, 129 for ipv6 + + if (!(toys.optflags&FLAG_q)) { + if (toys.optflags&FLAG_f) xputc('\b'); + else { + printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(&srcaddr2.s), + ih->un.echo.sequence, 0); + if (len >= sizeof(*ih)+4) + printf(" time=%u ms", pkttime); + xputc('\n'); + } + } + + toys.exitval = 0; + } + + sigatexit(0); + summary(0); + + if (CFG_TOYBOX_FREE) { + freeaddrinfo(ai2); + if (ifa2) freeifaddrs(ifa2); + } +} diff --git a/porting/liteos_a/toys/other/free.c b/porting/liteos_a/toys/other/free.c new file mode 100644 index 0000000..ce0df02 --- /dev/null +++ b/porting/liteos_a/toys/other/free.c @@ -0,0 +1,58 @@ +/* free.c - Display amount of free and used memory in the system. + * + * Copyright 2012 Elie De Brauwer + +// Flag order is signifcant: b-t are units in order, FLAG_h-1 is unit mask +USE_FREE(NEWTOY(free, "htgmkb[!htgmkb]", TOYFLAG_USR|TOYFLAG_BIN)) + +config FREE + bool "free" + default y + help + usage: free [-bkmgt] + + Display the total, free and used amount of physical memory and swap space. + + -bkmgt Output units (default is bytes) + -h Human readable (K=1024) +*/ + +#define FOR_free +#include "toys.h" + +GLOBALS( + unsigned bits; + unsigned long long units; + char *buf; +) + +static char *convert(unsigned long d) +{ + long long ll = d*TT.units; + char *s = TT.buf; + + if (toys.optflags & FLAG_h) human_readable(s, ll, 0); + else sprintf(s, "%llu",ll>>TT.bits); + TT.buf += strlen(TT.buf)+1; + + return s; +} + +void free_main(void) +{ + struct sysinfo in; + + sysinfo(&in); + TT.units = in.mem_unit ? in.mem_unit : 1; + while ((toys.optflags&(FLAG_h-1)) && !(toys.optflags&(1< + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/cat.html + * + * And "Cat -v considered harmful" at + * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz + +USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN)) +USE_CATV(NEWTOY(catv, USE_CATV("vte"), TOYFLAG_USR|TOYFLAG_BIN)) + +config CAT + bool "cat" + default y + help + usage: cat [-u] [file...] + + Copy (concatenate) files to stdout. If no files listed, copy from stdin. + Filename "-" is a synonym for stdin. + + -u Copy one byte at a time (slow) + +config CAT_V + bool "cat -etv" + default n + depends on CAT + help + usage: cat [-evt] + + -e Mark each newline with $ + -t Show tabs as ^I + -v Display nonprinting characters as escape sequences with M-x for + high ascii characters (>127), and ^x for other nonprinting chars + +config CATV + bool "catv" + default y + help + usage: catv [-evt] [filename...] + + Display nonprinting characters as escape sequences. Use M-x for + high ascii characters (>127), and ^x for other nonprinting chars. + + -e Mark each newline with $ + -t Show tabs as ^I + -v Don't use ^x or M-x escapes +*/ + +#define FOR_cat +#define FORCE_FLAGS +#include "toys.h" + +static void do_cat(int fd, char *name) +{ + int i, len, size=(toys.optflags & FLAG_u) ? 1 : sizeof(toybuf); + + for(;;) { + len = read(fd, toybuf, size); + if (len < 0) { + toys.exitval = EXIT_FAILURE; + perror_msg_raw(name); + } + if (len < 1) break; + if ((CFG_CAT_V || CFG_CATV) && (toys.optflags&~FLAG_u)) { + for (i=0; i 126 && (toys.optflags & FLAG_v)) { + if (c > 127) { + printf("M-"); + c -= 128; + } + if (c == 127) { + printf("^?"); + continue; + } + } + if (c < 32) { + if (c == 10) { + if (toys.optflags & FLAG_e) xputc('$'); + } else if (toys.optflags & (c==9 ? FLAG_t : FLAG_v)) { + printf("^%c", c+'@'); + continue; + } + } + xputc(c); + } + } else xwrite(1, toybuf, len); + } +} + +void cat_main(void) +{ + loopfiles(toys.optargs, do_cat); +} + +void catv_main(void) +{ + toys.optflags ^= FLAG_v; + loopfiles(toys.optargs, do_cat); +} diff --git a/porting/liteos_a/toys/posix/chgrp.c b/porting/liteos_a/toys/posix/chgrp.c new file mode 100644 index 0000000..8c2e95b --- /dev/null +++ b/porting/liteos_a/toys/posix/chgrp.c @@ -0,0 +1,106 @@ +/* chgrp.c - Change user and group ownership + * + * Copyright 2012 Georgi Chorbadzhiyski + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/chown.html + * See http://opengroup.org/onlinepubs/9699919799/utilities/chgrp.html + +USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN)) +USE_CHOWN(OLDTOY(chown, chgrp, TOYFLAG_BIN)) + +config CHGRP + bool "chgrp" + default y + help + usage: chgrp/chown [-RHLP] [-fvh] group file... + + Change group of one or more files. + + -f Suppress most error messages + -h Change symlinks instead of what they point to + -R Recurse into subdirectories (implies -h) + -H With -R change target of symlink, follow command line symlinks + -L With -R change target of symlink, follow all symlinks + -P With -R change symlink, do not follow symlinks (default) + -v Verbose + +config CHOWN + bool "chown" + default y + help + see: chgrp +*/ + +#define FOR_chgrp +#define FORCE_FLAGS +#include "toys.h" + +GLOBALS( + uid_t owner; + gid_t group; + char *owner_name, *group_name; + int symfollow; +) + +static int do_chgrp(struct dirtree *node) +{ + int fd, ret, flags = toys.optflags; + + // Depth first search + if (!dirtree_notdotdot(node)) return 0; + if ((flags & FLAG_R) && !node->again && S_ISDIR(node->st.st_mode)) + return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!(flags&FLAG_L)); + + fd = dirtree_parentfd(node); + ret = fchownat(fd, node->name, TT.owner, TT.group, + AT_SYMLINK_NOFOLLOW*(!(flags&(FLAG_L|FLAG_H)) && (flags&(FLAG_h|FLAG_R)))); + + if (ret || (flags & FLAG_v)) { + char *path = dirtree_path(node, 0); + if (flags & FLAG_v) + xprintf("%s %s%s%s %s\n", toys.which->name, TT.owner_name, + (toys.which->name[2]=='o' && *TT.group_name) ? ":" : "", + TT.group_name, path); + if (ret == -1 && !(toys.optflags & FLAG_f)) + perror_msg("'%s' to '%s:%s'", path, TT.owner_name, TT.group_name); + free(path); + } + toys.exitval |= ret; + + return 0; +} + +void chgrp_main(void) +{ + int ischown = toys.which->name[2] == 'o'; + char **s, *own; + + TT.owner = TT.group = -1; + TT.owner_name = TT.group_name = ""; + + // Distinguish chown from chgrp + if (ischown) { + char *grp; + + own = xstrdup(*toys.optargs); + if ((grp = strchr(own, ':')) || (grp = strchr(own, '.'))) { + *(grp++) = 0; + TT.group_name = grp; + } + if (*own) TT.owner = xgetuid(TT.owner_name = own); + } else TT.group_name = *toys.optargs; + + if (TT.group_name && *TT.group_name) + TT.group = xgetgid(TT.group_name); + + for (s=toys.optargs+1; *s; s++) + dirtree_flagread(*s, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), + do_chgrp); + + if (CFG_TOYBOX_FREE && ischown) free(own); +} + +void chown_main() +{ + chgrp_main(); +} diff --git a/porting/liteos_a/toys/posix/chmod.c b/porting/liteos_a/toys/posix/chmod.c new file mode 100644 index 0000000..4292439 --- /dev/null +++ b/porting/liteos_a/toys/posix/chmod.c @@ -0,0 +1,65 @@ +/* chmod.c - Change file mode bits + * + * Copyright 2012 Rob Landley + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/chmod.html + +USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN)) + +config CHMOD + bool "chmod" + default y + help + usage: chmod [-R] MODE FILE... + + Change mode of listed file[s] (recursively with -R). + + MODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo] + + Stanzas are applied in order: For each category (u = user, + g = group, o = other, a = all three, if none specified default is a), + set (+), clear (-), or copy (=), r = read, w = write, x = execute. + s = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s). + suid/sgid: execute as the user/group who owns the file. + sticky: can't delete files you don't own out of this directory + X = x for directories or if any category already has x set. + + Or MODE can be an octal value up to 7777 ug uuugggooo top + + bit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom + + Examples: + chmod u+w file - allow owner of "file" to write to it. + chmod 744 file - user can read/write/execute, everyone else read only +*/ + +#define FOR_chmod +#include "toys.h" + +GLOBALS( + char *mode; +) + +static int do_chmod(struct dirtree *try) +{ + mode_t mode; + + if (!dirtree_notdotdot(try)) return 0; + + mode = string_to_mode(TT.mode, try->st.st_mode); + if (toys.optflags & FLAG_v) { + char *s = dirtree_path(try, 0); + printf("chmod '%s' to %04o\n", s, mode); + free(s); + } + wfchmodat(dirtree_parentfd(try), try->name, mode); + + return (toys.optflags & FLAG_R) ? DIRTREE_RECURSE : 0; +} + +void chmod_main(void) +{ + TT.mode = *toys.optargs; + char **file; + + for (file = toys.optargs+1; *file; file++) dirtree_read(*file, do_chmod); +} diff --git a/porting/liteos_a/toys/posix/cp.c b/porting/liteos_a/toys/posix/cp.c new file mode 100644 index 0000000..bba5480 --- /dev/null +++ b/porting/liteos_a/toys/posix/cp.c @@ -0,0 +1,529 @@ +/* Copyright 2008 Rob Landley + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/cp.html + * And http://opengroup.org/onlinepubs/9699919799/utilities/mv.html + * And http://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic.html#INSTALL + * + * Posix says "cp -Rf dir file" shouldn't delete file, but our -f does. + * + * Deviations from posix: -adlnrsvF, --preserve... about half the + * functionality in this cp isn't in posix. Posix is stuck in the 1970's. + * + * TODO: --preserve=links + * TODO: what's this _CP_mode system.posix_acl_ business? We chmod()? + +// options shared between mv/cp must be in same order (right to left) +// for FLAG macros to work out right in shared infrastructure. + +USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"D(parents)RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN)) +USE_MV(NEWTOY(mv, "<2vnF(remove-destination)fi[-ni]", TOYFLAG_BIN)) +USE_INSTALL(NEWTOY(install, "<1cdDpsvm:o:g:", TOYFLAG_USR|TOYFLAG_BIN)) + +config CP + bool "cp" + default y + help + usage: cp [-adlnrsvfipRHLP] SOURCE... DEST + + Copy files from SOURCE to DEST. If more than one SOURCE, DEST must + be a directory. + + -D Create leading dirs under DEST (--parents) + -f Delete destination files we can't write to + -F Delete any existing destination file first (--remove-destination) + -i Interactive, prompt before overwriting existing DEST + -p Preserve timestamps, ownership, and mode + -R Recurse into subdirectories (DEST must be a directory) + -H Follow symlinks listed on command line + -L Follow all symlinks + -P Do not follow symlinks [default] + -a Same as -dpr + -d Don't dereference symlinks + -l Hard link instead of copy + -n No clobber (don't overwrite DEST) + -r Synonym for -R + -s Symlink instead of copy + -v Verbose + +config CP_PRESERVE + bool "cp --preserve support" + default y + depends on CP + help + usage: cp [--preserve=motcxa] + + --preserve takes either a comma separated list of attributes, or the first + letter(s) of: + + mode - permissions (ignore umask for rwx, copy suid and sticky bit) + ownership - user and group + timestamps - file creation, modification, and access times. + context - security context + xattr - extended attributes + all - all of the above + +config MV + bool "mv" + default y + help + usage: mv [-fivn] SOURCE... DEST + + -f Force copy by deleting destination file + -i Interactive, prompt before overwriting existing DEST + -v Verbose + -n No clobber (don't overwrite DEST) + +config INSTALL + bool "install" + default y + help + usage: install [-dDpsv] [-o USER] [-g GROUP] [-m MODE] [SOURCE...] DEST + + Copy files and set attributes. + + -d Act like mkdir -p + -D Create leading directories for DEST + -g Make copy belong to GROUP + -m Set permissions to MODE + -o Make copy belong to USER + -p Preserve timestamps + -s Call "strip -p" + -v Verbose +*/ + +#define FORCE_FLAGS +#define FOR_cp +#include "toys.h" + +GLOBALS( + union { + // install's options + struct { + char *g, *o, *m; + } i; + // cp's options + struct { + char *preserve; + } c; + }; + + char *destname; + struct stat top; + int (*callback)(struct dirtree *try); + uid_t uid; + gid_t gid; + int pflags; +) + +struct cp_preserve { + char *name; +} static const cp_preserve[] = TAGGED_ARRAY(CP, + {"mode"}, {"ownership"}, {"timestamps"}, {"context"}, {"xattr"}, +); + +// Callback from dirtree_read() for each file/directory under a source dir. + +static int cp_node(struct dirtree *try) +{ + int fdout = -1, cfd = try->parent ? try->parent->extra : AT_FDCWD, + tfd = dirtree_parentfd(try); + unsigned flags = toys.optflags; + char *catch = try->parent ? try->name : TT.destname, *err = "%s"; + struct stat cst; + + if (!dirtree_notdotdot(try)) return 0; + + // If returning from COMEAGAIN, jump straight to -p logic at end. + if (S_ISDIR(try->st.st_mode) && try->again) { + fdout = try->extra; + err = 0; + } else { + + // -d is only the same as -r for symlinks, not for directories + if (S_ISLNK(try->st.st_mode) && (flags & FLAG_d)) flags |= FLAG_r; + + // Detect recursive copies via repeated top node (cp -R .. .) or + // identical source/target (fun with hardlinks). + if ((TT.top.st_dev == try->st.st_dev && TT.top.st_ino == try->st.st_ino + && (catch = TT.destname)) + || (!fstatat(cfd, catch, &cst, 0) && cst.st_dev == try->st.st_dev + && cst.st_ino == try->st.st_ino)) + { + error_msg("'%s' is '%s'", catch, err = dirtree_path(try, 0)); + free(err); + + return 0; + } + + // Handle -invF + + if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) { + char *s; + + if (S_ISDIR(try->st.st_mode)) { + error_msg("dir at '%s'", s = dirtree_path(try, 0)); + free(s); + return 0; + } else if ((flags & FLAG_F) && unlinkat(cfd, catch, 0)) { + error_msg("unlink '%s'", catch); + return 0; + } else if (flags & FLAG_n) return 0; + else if (flags & FLAG_i) { + fprintf(stderr, "%s: overwrite '%s'", toys.which->name, + s = dirtree_path(try, 0)); + free(s); + if (!yesno(1)) return 0; + } + } + + if (flags & FLAG_v) { + char *s = dirtree_path(try, 0); + printf("%s '%s'\n", toys.which->name, s); + free(s); + } + + // Loop for -f retry after unlink + do { + + // directory, hardlink, symlink, mknod (char, block, fifo, socket), file + + // Copy directory + + if (S_ISDIR(try->st.st_mode)) { + struct stat st2; + + if (!(flags & (FLAG_a|FLAG_r|FLAG_R))) { + err = "Skipped dir '%s'"; + catch = try->name; + break; + } + + // Always make directory writeable to us, so we can create files in it. + // + // Yes, there's a race window between mkdir() and open() so it's + // possible that -p can be made to chown a directory other than the one + // we created. The closest we can do to closing this is make sure + // that what we open _is_ a directory rather than something else. + + if (!mkdirat(cfd, catch, try->st.st_mode | 0200) || errno == EEXIST) + if (-1 != (try->extra = openat(cfd, catch, O_NOFOLLOW))) + if (!fstat(try->extra, &st2) && S_ISDIR(st2.st_mode)) + return DIRTREE_COMEAGAIN | (DIRTREE_SYMFOLLOW*!!FLAG(L)); + + // Hardlink + + } else if (flags & FLAG_l) { + if (!linkat(tfd, try->name, cfd, catch, 0)) err = 0; + + // Copy tree as symlinks. For non-absolute paths this involves + // appending the right number of .. entries as you go down the tree. + + } else if (flags & FLAG_s) { + char *s; + struct dirtree *or; + int dotdots = 0; + + s = dirtree_path(try, 0); + for (or = try; or->parent; or = or->parent) dotdots++; + + if (*or->name == '/') dotdots = 0; + if (dotdots) { + char *s2 = xmprintf("%*c%s", 3*dotdots, ' ', s); + free(s); + s = s2; + while(dotdots--) { + memcpy(s2, "../", 3); + s2 += 3; + } + } + if (!symlinkat(s, cfd, catch)) { + err = 0; + fdout = AT_FDCWD; + } + free(s); + + // Do something _other_ than copy contents of a file? + } else if (!S_ISREG(try->st.st_mode) + && (try->parent || (flags & (FLAG_a|FLAG_r)))) + { + int i; + + // make symlink, or make block/char/fifo/socket + if (S_ISLNK(try->st.st_mode) + ? ((i = readlinkat0(tfd, try->name, toybuf, sizeof(toybuf))) && + ((!unlinkat(cfd, catch, 0) || ENOENT == errno) && + !symlinkat(toybuf, cfd, catch))) + : !mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev)) + { + err = 0; + fdout = AT_FDCWD; + } + + // Copy contents of file. + } else { + int fdin; + + fdin = openat(tfd, try->name, O_RDONLY); + if (fdin < 0) { + catch = try->name; + break; + } + // When copying contents use symlink target's attributes + if (S_ISLNK(try->st.st_mode)) fstat(fdin, &try->st); + fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode); + if (fdout >= 0) { + xsendfile(fdin, fdout); + err = 0; + } + + // We only copy xattrs for files because there's no flistxattrat() + if (TT.pflags&(_CP_xattr|_CP_context)) { + ssize_t listlen = xattr_flist(fdin, 0, 0), len; + char *name, *value, *list; + + if (listlen>0) { + list = xmalloc(listlen); + xattr_flist(fdin, list, listlen); + list[listlen-1] = 0; // I do not trust this API. + for (name = list; name-list < listlen; name += strlen(name)+1) { + if (!(TT.pflags&_CP_xattr) && strncmp(name, "security.", 9)) + continue; + if ((len = xattr_fget(fdin, name, 0, 0))>0) { + value = xmalloc(len); + if (len == xattr_fget(fdin, name, value, len)) + if (xattr_fset(fdout, name, value, len, 0)) + perror_msg("%s setxattr(%s=%s)", catch, name, value); + free(value); + } + } + free(list); + } + } + + close(fdin); + } + } while (err && (flags & (FLAG_f|FLAG_n)) && !unlinkat(cfd, catch, 0)); + } + + // Did we make a thing? + if (fdout != -1) { + int rc; + + // Inability to set --preserve isn't fatal, some require root access. + + // ownership + if (TT.pflags & _CP_ownership) { + + // permission bits already correct for mknod and don't apply to symlink + // If we can't get a filehandle to the actual object, use racy functions + if (fdout == AT_FDCWD) + rc = fchownat(cfd, catch, try->st.st_uid, try->st.st_gid, + AT_SYMLINK_NOFOLLOW); + else rc = fchown(fdout, try->st.st_uid, try->st.st_gid); + if (rc && !geteuid()) { + char *pp; + + perror_msg("chown '%s'", pp = dirtree_path(try, 0)); + free(pp); + } + } + + // timestamp + if (TT.pflags & _CP_timestamps) { + struct timespec times[] = {try->st.st_atim, try->st.st_mtim}; + + if (fdout == AT_FDCWD) utimensat(cfd, catch, times, AT_SYMLINK_NOFOLLOW); + else futimens(fdout, times); + } + + // mode comes last because other syscalls can strip suid bit + if (fdout != AT_FDCWD) { + if (TT.pflags & _CP_mode) fchmod(fdout, try->st.st_mode); + xclose(fdout); + } + + if (CFG_MV && toys.which->name[0] == 'm') + if (unlinkat(tfd, try->name, S_ISDIR(try->st.st_mode) ? AT_REMOVEDIR :0)) + err = "%s"; + } + + if (err) { + char *f = 0; + + if (catch == try->name) { + f = dirtree_path(try, 0); + while (try->parent) try = try->parent; + catch = xmprintf("%s%s", TT.destname, f+strlen(try->name)); + free(f); + f = catch; + } + perror_msg(err, catch); + free(f); + } + return 0; +} + +void cp_main(void) +{ + char *destname = toys.optargs[--toys.optc]; + int i, destdir = !stat(destname, &TT.top) && S_ISDIR(TT.top.st_mode); + + if ((toys.optc>1 || FLAG(D)) && !destdir) + error_exit("'%s' not directory", destname); + + if (FLAG(a)||FLAG(p)) TT.pflags = _CP_mode|_CP_ownership|_CP_timestamps; + + // Not using comma_args() (yet?) because interpeting as letters. + if (CFG_CP_PRESERVE && FLAG(preserve)) { + char *pre = xstrdup(TT.c.preserve ? TT.c.preserve : "mot"), *s; + + if (comma_remove(pre, "all")) TT.pflags = ~0; + for (i=0; iname[0] == 'm') { + while (*++trail); + if (*--trail == '/') *trail = 0; + } + + if (destdir) { + char *s = FLAG(D) ? getdirname(src) : getbasename(src); + + TT.destname = xmprintf("%s/%s", destname, s); + if (FLAG(D)) { + free(s); + if (!(s = fileunderdir(TT.destname, destname))) { + error_msg("%s not under %s", TT.destname, destname); + continue; + } + // TODO: .. follows abspath, not links... + free(s); + mkpath(TT.destname); + } + } else TT.destname = destname; + + errno = EXDEV; + if (CFG_MV && toys.which->name[0] == 'm') { + int force = FLAG(f), no_clobber = FLAG(n); + + if (!force || no_clobber) { + struct stat st; + int exists = !stat(TT.destname, &st); + + // Prompt if -i or file isn't writable. Technically "is writable" is + // more complicated (022 is not writeable by the owner, just everybody + // _else_) but I don't care. + if (exists && (FLAG(i) || !(st.st_mode & 0222))) { + fprintf(stderr, "%s: overwrite '%s'", toys.which->name, TT.destname); + if (!yesno(1)) rc = 0; + else unlink(TT.destname); + } + // if -n and dest exists, don't try to rename() or copy + if (exists && no_clobber) rc = 0; + } + if (rc) rc = rename(src, TT.destname); + if (errno && !*trail) *trail = '/'; + } + + // Copy if we didn't mv, skipping nonexistent sources + if (rc) { + if (errno!=EXDEV || dirtree_flagread(src, DIRTREE_SHUTUP+ + DIRTREE_SYMFOLLOW*!!(FLAG(H)||FLAG(L)), TT.callback)) + perror_msg("bad '%s'", src); + } + if (destdir) free(TT.destname); + } +} + +void mv_main(void) +{ + toys.optflags |= FLAG_d|FLAG_p|FLAG_R; + + cp_main(); +} + +// Export cp flags into install's flag context. + +static inline int cp_flag_F(void) { return FLAG_F; }; +static inline int cp_flag_p(void) { return FLAG_p; }; +static inline int cp_flag_v(void) { return FLAG_v; }; + +// Switch to install's flag context +#define CLEANUP_cp +#define FOR_install +#include + +static int install_node(struct dirtree *try) +{ + try->st.st_mode = TT.i.m ? string_to_mode(TT.i.m, try->st.st_mode) : 0755; + if (TT.i.g) try->st.st_gid = TT.gid; + if (TT.i.o) try->st.st_uid = TT.uid; + + // Always returns 0 because no -r + cp_node(try); + + // No -r so always one level deep, so destname as set by cp_node() is correct + if (FLAG(s) && xrun((char *[]){"strip", "-p", TT.destname, 0})) + toys.exitval = 1; + + return 0; +} + +void install_main(void) +{ + char **ss; + int flags = toys.optflags; + + TT.uid = TT.i.o ? xgetuid(TT.i.o) : -1; + TT.gid = TT.i.g ? xgetgid(TT.i.g) : -1; + + if (flags & FLAG_d) { + for (ss = toys.optargs; *ss; ss++) { + if (mkpathat(AT_FDCWD, *ss, 0777, MKPATHAT_MKLAST | MKPATHAT_MAKE)) perror_msg_raw(*ss); + if (flags & (FLAG_g|FLAG_o)) + if (lchown(*ss, TT.uid, TT.gid)) perror_msg("chown '%s'", *ss); + if (flags & FLAG_v) printf("%s\n", *ss); + } + + return; + } + + if (FLAG(D)) { + TT.destname = toys.optargs[toys.optc-1]; + if (mkpathat(AT_FDCWD, TT.destname, 0, MKPATHAT_MAKE)) + perror_exit("-D '%s'", TT.destname); + if (toys.optc == 1) return; + } + if (toys.optc < 2) error_exit("needs 2 args"); + + // Translate flags from install to cp + toys.optflags = cp_flag_F(); + if (flags & FLAG_v) toys.optflags |= cp_flag_v(); + if (flags & (FLAG_p|FLAG_o|FLAG_g)) toys.optflags |= cp_flag_p(); + + TT.callback = install_node; + cp_main(); +} diff --git a/porting/liteos_a/toys/posix/date.c b/porting/liteos_a/toys/posix/date.c new file mode 100644 index 0000000..a176285 --- /dev/null +++ b/porting/liteos_a/toys/posix/date.c @@ -0,0 +1,174 @@ +/* date.c - set/get the date + * + * Copyright 2012 Andre Renaud + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/date.html + * + * 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)) + +config DATE + bool "date" + default y + help + usage: date [-u] [-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]) + -r Use modification time of FILE instead of current date + -u Use UTC instead of current timezone + + Supported input formats: + + MMDDhhmm[[CC]YY][.ss] POSIX + @UNIXTIME[.FRACTION] seconds since midnight 1970-01-01 + YYYY-MM-DD [hh:mm[:ss]] ISO 8601 + hh:mm[:ss] 24-hour time today + + 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. + + +FORMAT specifies display format string using strftime(3) syntax: + + %% literal % %n newline %t tab + %S seconds (00-60) %M minute (00-59) %m month (01-12) + %H hour (0-23) %I hour (01-12) %p AM/PM + %y short year (00-99) %Y year %C century + %a short weekday name %A weekday name %u day of week (1-7, 1=mon) + %b short month name %B month name %Z timezone name + %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) + + %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 +*/ + +#define FOR_date +#include "toys.h" + +GLOBALS( + char *r, *D, *d; + + unsigned nano; +) + +// 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; + + if (!strncmp(str, "TZ=\"", 4)) { + // Extract the time zone and skip any whitespace. + new_tz = str+4; + if (!(str = strchr(new_tz, '"'))) xvali_date(0, s); + *str++ = 0; + while (isspace(*str)) str++; + + // Switch $TZ. + old_tz = getenv("TZ"); + setenv("TZ", new_tz, 1); + tzset(); + } + time(t); + xparsedate(str, t, &TT.nano, 1); + if (new_tz) { + if (old_tz) setenv("TZ", old_tz, 1); + else unsetenv("TZ"); + } +} + +// Print strftime plus %N escape(s). note: modifies fmt for %N +static void puts_time(char *fmt, struct tm *tm) +{ + char *s, *snap; + long width = width; + + for (s = fmt;;s++) { + + // 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; + } + fputs(toybuf, stdout); + if (!*s || !*(fmt = s+1)) break; + } + xputc('\n'); +} + +void date_main(void) +{ + char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y", + *tz = NULL; + time_t t; + + if (FLAG(u)) { + tz = getenv("TZ"); + setenv("TZ", "UTC", 1); + tzset(); + } + + if (TT.d) { + if (TT.D) { + 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); + } else parse_date(TT.d, &t); + } else { + struct timespec ts; + struct stat st; + + if (TT.r) { + xstat(TT.r, &st); + ts = st.st_mtim; + } else clock_gettime(CLOCK_REALTIME, &ts); + + t = ts.tv_sec; + TT.nano = ts.tv_nsec; + } + + // Fall through if no arguments + if (!setdate); + // Display the date? + else if (*setdate == '+') { + format_string = toys.optargs[0]+1; + setdate = toys.optargs[1]; + + // Set the date + } else if (setdate) { + struct timeval tv; + + parse_date(setdate, &t); + tv.tv_sec = t; + tv.tv_usec = TT.nano/1000; + if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date"); + } + + puts_time(format_string, localtime(&t)); + + if (FLAG(u)) { + if (tz) setenv("TZ", tz, 1); + else unsetenv("TZ"); + tzset(); + } + + return; +} diff --git a/porting/liteos_a/toys/posix/du.c b/porting/liteos_a/toys/posix/du.c new file mode 100644 index 0000000..73ef6db --- /dev/null +++ b/porting/liteos_a/toys/posix/du.c @@ -0,0 +1,168 @@ +/* du.c - disk usage program. + * + * Copyright 2012 Ashwini Kumar + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/du.html + * + * TODO: cleanup + +USE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsx[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN)) + +config DU + bool "du" + default y + help + usage: du [-d N] [-askxHLlmc] [file...] + + Show disk usage, space consumed by files and directories. + + Size in: + -k 1024 byte blocks (default) + -K 512 byte blocks (posix) + -m Megabytes + -h Human readable (e.g., 1K 243M 2G) + + What to show: + -a All files, not just directories + -H Follow symlinks on cmdline + -L Follow all symlinks + -s Only total size of each argument + -x Don't leave this filesystem + -c Cumulative total + -d N Only depth < N + -l Disable hardlink filter +*/ + +#define FOR_du +#include "toys.h" + +GLOBALS( + long d; + + unsigned long depth, total; + dev_t st_dev; + void *inodes; +) + +typedef struct node_size { + struct dirtree *node; + long size; +} node_size; + +// Print the size and name, given size in bytes +static void print(long long size, struct dirtree *node) +{ + char *name = "total"; + + if (TT.depth > TT.d) return; + + if (toys.optflags & FLAG_h) { + human_readable(toybuf, size, 0); + printf("%s", toybuf); + } else { + int bits = 10; + + if (toys.optflags & FLAG_K) bits = 9; + else if (toys.optflags & FLAG_m) bits = 20; + + printf("%llu", (size>>bits)+!!(size&((1<st_mode) && st->st_nlink > 1) { + struct inode_list { + struct inode_list *next; + ino_t ino; + dev_t dev; + } *new; + + for (new = *list; new; new = new->next) + if(new->ino == st->st_ino && new->dev == st->st_dev) + return 1; + + new = xzalloc(sizeof(*new)); + new->ino = st->st_ino; + new->dev = st->st_dev; + new->next = *list; + *list = new; + } + + return 0; +} + +// dirtree callback, compute/display size of node +static int do_du(struct dirtree *node) +{ + unsigned long blocks; + + if (!node->parent) TT.st_dev = node->st.st_dev; + else if (!dirtree_notdotdot(node)) return 0; + + // detect swiching filesystems + if ((toys.optflags & FLAG_x) && (TT.st_dev != node->st.st_dev)) + return 0; + + // Don't loop endlessly on recursive directory symlink + if (toys.optflags & FLAG_L) { + struct dirtree *try = node; + + while ((try = try->parent)) + if (node->st.st_dev==try->st.st_dev && node->st.st_ino==try->st.st_ino) + return 0; + } + + // Don't count hard links twice + if (!(toys.optflags & FLAG_l) && !node->again) + if (seen_inode(&TT.inodes, &node->st)) return 0; + + // Collect child info before printing directory size + if (S_ISDIR(node->st.st_mode)) { + if (!node->again) { + TT.depth++; + return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!(toys.optflags&FLAG_L)); + } else TT.depth--; + } + + // Modern compilers' optimizers are insane and think signed overflow + // behaves differently than unsigned overflow. Sigh. Big hammer. + blocks = node->st.st_blocks + (unsigned long)node->extra; + node->extra = blocks; + if (node->parent) + node->parent->extra = (unsigned long)node->parent->extra+blocks; + else TT.total += node->extra; + + if ((toys.optflags & FLAG_a) || !node->parent + || (S_ISDIR(node->st.st_mode) && !(toys.optflags & FLAG_s))) + { + blocks = node->extra; + print(blocks*512LL, node); + } + + return 0; +} + +void du_main(void) +{ + char *noargs[] = {".", 0}, **args; + + // Loop over command line arguments, recursing through children + for (args = toys.optc ? toys.optargs : noargs; *args; args++) + dirtree_flagread(*args, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), + do_du); + if (toys.optflags & FLAG_c) print(TT.total*512, 0); + + if (CFG_TOYBOX_FREE) seen_inode(TT.inodes, 0); +} diff --git a/porting/liteos_a/toys/posix/echo.c b/porting/liteos_a/toys/posix/echo.c new file mode 100644 index 0000000..70f4ce5 --- /dev/null +++ b/porting/liteos_a/toys/posix/echo.c @@ -0,0 +1,100 @@ +/* echo.c - echo supporting -n and -e. + * + * Copyright 2007 Rob Landley + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/echo.html + * + * Deviations from posix: we parse command line options, as Linux has + * consistently done since 1992. Posix defaults -e to on, we require -e. + * 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)) + +config ECHO + bool "echo" + default y + help + usage: echo [-neE] [args...] + + Write each argument to stdout, with 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) +*/ + +#define FOR_echo +#include "toys.h" + +void echo_main(void) +{ + int i = 0, out; + char *arg, *c; + + for (;;) { + arg = toys.optargs[i]; + if (!arg) break; + if (i++) putchar(' '); + + // Should we output arg verbatim? + + if (!FLAG(e)) { + xprintf("%s", arg); + continue; + } + + // Handle -e + + for (c = arg;;) { + if (!(out = *(c++))) break; + + // 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); + } + } + + // Output "\n" if no -n + if (!FLAG(n)) putchar('\n'); +} diff --git a/porting/liteos_a/toys/posix/kill.c b/porting/liteos_a/toys/posix/kill.c new file mode 100644 index 0000000..c9de98e --- /dev/null +++ b/porting/liteos_a/toys/posix/kill.c @@ -0,0 +1,154 @@ +/* kill.c - a program to send signals to processes + * + * Copyright 2012 Daniel Walter + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/kill.html + * + * killall5.c - Send signal to all processes outside current session. + * + * Copyright 2014 Ranjan Kumar + * Copyright 2014 Kyungwan Han + * + * No Standard + +USE_KILL(NEWTOY(kill, "?ls: ", TOYFLAG_BIN)) +USE_KILLALL5(NEWTOY(killall5, "?o*ls: [!lo][!ls]", TOYFLAG_SBIN)) + +config KILL + bool "kill" + default y + help + usage: kill [-l [SIGNAL] | -s SIGNAL | -SIGNAL] pid... + + Send signal to process(es). + + -l List signal name(s) and number(s) + -s Send SIGNAL (default SIGTERM) + +config KILLALL5 + bool "killall5" + default y + depends on KILL + help + usage: killall5 [-l [SIGNAL]] [-SIGNAL|-s SIGNAL] [-o PID]... + + Send a signal to all processes outside current session. + + -l List signal name(s) and number(s) + -o PID Omit PID + -s Send SIGNAL (default SIGTERM) +*/ + +// This has to match the filename: +#define FOR_kill +#define FORCE_FLAGS +#include "toys.h" + +GLOBALS( + char *s; + struct arg_list *o; +) + +// But kill's flags are a subset of killall5's + +#define CLEANUP_kill +#define FOR_killall5 +#include "generated/flags.h" + +void kill_main(void) +{ + int signum; + char *tmp, **args = toys.optargs; + pid_t pid; + + // list signal(s) + if (FLAG(l)) { + if (*args) { + int signum = sig_to_num(*args); + char *s = NULL; + + if (signum>=0) s = num_to_sig(signum&127); + if (isdigit(**args)) puts(s ? s : "UNKNOWN"); + else printf("%d\n", signum); + } else list_signals(); + return; + } + + // signal must come before pids, so "kill -9 -1" isn't confusing. + + if (!TT.s && *args && **args=='-') TT.s = *(args++)+1; + if (TT.s) { + char *arg; + int i = strtol(TT.s, &arg, 10); + if (!*arg) arg = num_to_sig(i); + else arg = TT.s; + + if (!arg || -1 == (signum = sig_to_num(arg))) + error_exit("Unknown signal '%s'", arg); + } else signum = SIGTERM; + + // is it killall5? + if (CFG_KILLALL5 && toys.which->name[4]=='a') { + DIR *dp; + struct dirent *entry; + int pid, sid; + long *olist = 0, ocount = 0; + + // parse omit list + if (FLAG(o)) { + struct arg_list *ptr; + + for (ptr = TT.o; ptr; ptr = ptr->next) ocount++; + olist = xmalloc(ocount*sizeof(long)); + ocount = 0; + for (ptr = TT.o; ptr; ptr=ptr->next) olist[ocount++] = atolx(ptr->arg); + } + + sid = getsid(pid = getpid()); + + if (!(dp = opendir("/proc"))) perror_exit("/proc"); + while ((entry = readdir(dp))) { + int count, procpid, procsid; + + if (!(procpid = atoi(entry->d_name))) continue; + + snprintf(toybuf, sizeof(toybuf), "/proc/%d/stat", procpid); + if (!readfile(toybuf, toybuf, sizeof(toybuf))) continue; + if (sscanf(toybuf, "%*d %*s %*c %*d %*d %d", &procsid) != 1) continue; + if (pid == procpid || sid == procsid || procpid == 1) continue; + + // Check for kernel threads. + snprintf(toybuf, sizeof(toybuf), "/proc/%d/cmdline", procpid); + if (!readfile(toybuf, toybuf, sizeof(toybuf)) || !*toybuf) continue; + + // Check with omit list. + for (count = 0; count < ocount; count++) + if (procpid == olist[count]) break; + if (count != ocount) continue; + + kill(procpid, signum); + } + if (CFG_TOYBOX_FREE) { + closedir(dp); + free(olist); + } + + // is it kill? + } else { + + // "<1" in optstr wouldn't cover this because "-SIGNAL" + if (!*args) help_exit("missing argument"); + + while (*args) { + char *arg = *(args++); + + pid = strtol(arg, &tmp, 10); + if (*tmp || kill(pid, signum) < 0) error_msg("unknown pid '%s'", arg); + } + } +} + +void killall5_main(void) +{ + kill_main(); +} diff --git a/porting/liteos_a/toys/posix/ls.c b/porting/liteos_a/toys/posix/ls.c new file mode 100644 index 0000000..d4c0211 --- /dev/null +++ b/porting/liteos_a/toys/posix/ls.c @@ -0,0 +1,604 @@ +/* ls.c - list files + * + * Copyright 2012 Andre Renaud + * Copyright 2012 Rob Landley + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/ls.html + * + * Deviations from posix: + * add -b (and default to it instead of -q for an unambiguous representation + * that doesn't cause collisions) + * add -Z -ll --color + * Posix says the -l date format should vary based on how recent it is + * and we do --time-style=long-iso instead + +USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE)) + +config LS + bool "ls" + default y + help + usage: ls [-ACFHLRSZacdfhiklmnpqrstux1] [--color[=auto]] [directory...] + + List files. + + what to show: + -a all files including .hidden -b escape nongraphic chars + -c use ctime for timestamps -d directory, not contents + -i inode number -p put a '/' after dir names + -q unprintable chars as '?' -s storage used (1024 byte units) + -u use access time for timestamps -A list all files but . and .. + -H follow command line symlinks -L follow symlinks + -R recursively list in subdirs -F append /dir *exe @sym |FIFO + -Z security context + + output formats: + -1 list one file per line -C columns (sorted vertically) + -g like -l but no owner -h human readable sizes + -l long (show full details) -m comma separated + -n like -l but numeric uid/gid -o like -l but no group + -x columns (horizontal sort) -ll long with nanoseconds (--full-time) + --color device=yellow symlink=turquoise/red dir=blue socket=purple + files: exe=green suid=red suidfile=redback stickydir=greenback + =auto means detect if output is a tty. + + sorting (default is alphabetical): + -f unsorted -r reverse -t timestamp -S size +*/ + +#define FOR_ls +#include "toys.h" + +// test sst output (suid/sticky in ls flaglist) + +// ls -lR starts .: then ./subdir: + +GLOBALS( + long l; + char *color; + + struct dirtree *files, *singledir; + unsigned screen_width; + int nl_title; + char *escmore; +) + +// Callback from crunch_str to represent unprintable chars +static int crunch_qb(FILE *out, int cols, int wc) +{ + int len = 1; + char buf[32]; + + if (FLAG(q)) *buf = '?'; + else { + if (wc<256) *buf = wc; + // scrute the inscrutable, eff the ineffable, print the unprintable + else if ((len = wcrtomb(buf, wc, 0) ) == -1) len = 1; + if (FLAG(b)) { + char *to = buf, *from = buf+24; + int i, j; + + memcpy(from, to, 8); + for (i = 0; ist_mode; + if ((FLAG(F)||FLAG(p)) && S_ISDIR(mode)) return '/'; + if (FLAG(F)) { + if (S_ISLNK(mode)) return '@'; + if (S_ISREG(mode) && (mode&0111)) return '*'; + if (S_ISFIFO(mode)) return '|'; + if (S_ISSOCK(mode)) return '='; + } + return 0; +} + +static int numlen(long long ll) +{ + return snprintf(0, 0, "%llu", ll); +} + +static int print_with_h(char *s, long long value, int units) +{ + if (FLAG(h)) return human_readable(s, value*units, 0); + else return sprintf(s, "%lld", value); +} + +// Figure out size of printable entry fields for display indent/wrap + +static void entrylen(struct dirtree *dt, unsigned *len) +{ + struct stat *st = &(dt->st); + unsigned flags = toys.optflags; + char tmp[64]; + + *len = strwidth(dt->name); + if (endtype(st)) ++*len; + if (flags & FLAG_m) ++*len; + + len[1] = (flags & FLAG_i) ? numlen(st->st_ino) : 0; + if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) { + unsigned fn = flags & FLAG_n; + + len[2] = numlen(st->st_nlink); + len[3] = fn ? numlen(st->st_uid) : strwidth(getusername(st->st_uid)); + len[4] = fn ? numlen(st->st_gid) : strwidth(getgroupname(st->st_gid)); + if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { + // cheating slightly here: assuming minor is always 3 digits to avoid + // tracking another column + len[5] = numlen(dev_major(st->st_rdev))+5; + } else len[5] = print_with_h(tmp, st->st_size, 1); + } + + len[6] = (flags & FLAG_s) ? print_with_h(tmp, st->st_blocks, 512) : 0; + len[7] = (flags & FLAG_Z) ? strwidth((char *)dt->extra) : 0; +} + +static int compare(void *a, void *b) +{ + struct dirtree *dta = *(struct dirtree **)a; + struct dirtree *dtb = *(struct dirtree **)b; + int ret = 0, reverse = FLAG(r) ? -1 : 1; + + if (FLAG(S)) { + if (dta->st.st_size > dtb->st.st_size) ret = -1; + else if (dta->st.st_size < dtb->st.st_size) ret = 1; + } + if (FLAG(t)) { + if (dta->st.st_mtime > dtb->st.st_mtime) ret = -1; + else if (dta->st.st_mtime < dtb->st.st_mtime) ret = 1; + else if (dta->st.st_mtim.tv_nsec > dtb->st.st_mtim.tv_nsec) ret = -1; + else if (dta->st.st_mtim.tv_nsec < dtb->st.st_mtim.tv_nsec) ret = 1; + } + if (!ret) ret = strcmp(dta->name, dtb->name); + return ret * reverse; +} + +// callback from dirtree_recurse() determining how to handle this entry. + +static int filter(struct dirtree *new) +{ + int flags = toys.optflags; + + // Special case to handle enormous dirs without running out of memory. + if (flags == (FLAG_1|FLAG_f)) { + xprintf("%s\n", new->name); + return 0; + } + + if (flags & FLAG_Z) { + if (!CFG_TOYBOX_LSM_NONE) { + + // (Wouldn't it be nice if the lsm functions worked like openat(), + // fchmodat(), mknodat(), readlinkat() so we could do this without + // even O_PATH? But no, this is 1990's tech.) + int fd = openat(dirtree_parentfd(new), new->name, + O_PATH|(O_NOFOLLOW*!FLAG(L))); + + if (fd != -1) { + if (-1 == lsm_fget_context(fd, (char **)&new->extra) && errno == EBADF) + { + char hack[32]; + + // Work around kernel bug that won't let us read this "metadata" from + // the filehandle unless we have permission to read the data. (We can + // query the same data in by path, but can't do it through an O_PATH + // filehandle, because reasons. But for some reason, THIS is ok? If + // they ever fix the kernel, this should stop triggering.) + + sprintf(hack, "/proc/self/fd/%d", fd); + lsm_lget_context(hack, (char **)&new->extra); + } + close(fd); + } + } + if (CFG_TOYBOX_LSM_NONE || !new->extra) new->extra = (long)xstrdup("?"); + } + + if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime; + if (flags & FLAG_c) new->st.st_mtime = new->st.st_ctime; + new->st.st_blocks >>= 1; + + if (flags & (FLAG_a|FLAG_f)) return DIRTREE_SAVE; + if (!(flags & FLAG_A) && new->name[0]=='.') return 0; + + return dirtree_notdotdot(new) & DIRTREE_SAVE; +} + +// For column view, calculate horizontal position (for padding) and return +// index of next entry to display. + +static unsigned long next_column(unsigned long ul, unsigned long dtlen, + unsigned columns, unsigned *xpos) +{ + unsigned long transition; + unsigned height, widecols; + + // Horizontal sort is easy + if (!FLAG(C)) { + *xpos = ul % columns; + return ul; + } + + // vertical sort + + // For -x, calculate height of display, rounded up + height = (dtlen+columns-1)/columns; + + // Sanity check: does wrapping render this column count impossible + // due to the right edge wrapping eating a whole row? + if (height*columns - dtlen >= height) { + *xpos = columns; + return 0; + } + + // Uneven rounding goes along right edge + widecols = dtlen % height; + if (!widecols) widecols = height; + transition = widecols * columns; + if (ul < transition) { + *xpos = ul % columns; + return (*xpos*height) + (ul/columns); + } + + ul -= transition; + *xpos = ul % (columns-1); + + return (*xpos*height) + widecols + (ul/(columns-1)); +} + +static int color_from_mode(mode_t mode) +{ + int color = 0; + + if (S_ISDIR(mode)) color = 256+34; + else if (S_ISLNK(mode)) color = 256+36; + else if (S_ISBLK(mode) || S_ISCHR(mode)) color = 256+33; + else if (S_ISREG(mode) && (mode&0111)) color = 256+32; + else if (S_ISFIFO(mode)) color = 33; + else if (S_ISSOCK(mode)) color = 256+35; + + return color; +} + +static void zprint(int zap, char *pat, int len, unsigned long arg) +{ + char tmp[32]; + + sprintf(tmp, "%%*%s", zap ? "s" : pat); + if (zap && pat[strlen(pat)-1]==' ') strcat(tmp, " "); + printf(tmp, len, zap ? (unsigned long)"?" : arg); +} + +// Display a list of dirtree entries, according to current format +// Output types -1, -l, -C, or stream + +static void listfiles(int dirfd, struct dirtree *indir) +{ + struct dirtree *dt, **sort; + unsigned long dtlen, ul = 0; + unsigned width, flags = toys.optflags, totals[8], len[8], totpad = 0, + *colsizes = (unsigned *)toybuf, columns = sizeof(toybuf)/4; + char tmp[64]; + + if (-1 == dirfd) { + perror_msg_raw(indir->name); + + return; + } + + memset(totals, 0, sizeof(totals)); + if (CFG_TOYBOX_DEBUG) memset(len, 0, sizeof(len)); + + // Top level directory was already populated by main() + if (!indir->parent) { + // Silently descend into single directory listed by itself on command line. + // In this case only show dirname/total header when given -R. + dt = indir->child; + if (dt && S_ISDIR(dt->st.st_mode) && !dt->next && !(flags&(FLAG_d|FLAG_R))) + { + listfiles(open(dt->name, 0), TT.singledir = dt); + + return; + } + + // Do preprocessing (Dirtree didn't populate, so callback wasn't called.) + for (;dt; dt = dt->next) filter(dt); + if (flags == (FLAG_1|FLAG_f)) return; + // Read directory contents. We dup() the fd because this will close it. + // This reads/saves contents to display later, except for in "ls -1f" mode. + } else dirtree_recurse(indir, filter, dup(dirfd), + DIRTREE_STATLESS|DIRTREE_SYMFOLLOW*!!(flags&FLAG_L)); + + // Copy linked list to array and sort it. Directories go in array because + // we visit them in sorted order too. (The nested loops let us measure and + // fill with the same inner loop.) + for (sort = 0;;sort = xmalloc(dtlen*sizeof(void *))) { + for (dtlen = 0, dt = indir->child; dt; dt = dt->next, dtlen++) + if (sort) sort[dtlen] = dt; + if (sort || !dtlen) break; + } + + // Label directory if not top of tree, or if -R + if (indir->parent && (TT.singledir!=indir || (flags&FLAG_R))) + { + char *path = dirtree_path(indir, 0); + + if (TT.nl_title++) xputc('\n'); + xprintf("%s:\n", path); + free(path); + } + + // Measure each entry to work out whitespace padding and total blocks + if (!(flags & FLAG_f)) { + unsigned long long blocks = 0; + + qsort(sort, dtlen, sizeof(void *), (void *)compare); + for (ul = 0; ultotals[width]) totals[width] = len[width]; + blocks += sort[ul]->st.st_blocks; + } + totpad = totals[1]+!!totals[1]+totals[6]+!!totals[6]+totals[7]+!!totals[7]; + if ((flags&(FLAG_h|FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) && indir->parent) { + print_with_h(tmp, blocks, 512); + xprintf("total %s\n", tmp); + } + } + + // Find largest entry in each field for display alignment + if (flags & (FLAG_C|FLAG_x)) { + + // columns can't be more than toybuf can hold, or more than files, + // or > 1/2 screen width (one char filename, one space). + if (columns > TT.screen_width/2) columns = TT.screen_width/2; + if (columns > dtlen) columns = dtlen; + + // Try to fit as many columns as we can, dropping down by one each time + for (;columns > 1; columns--) { + unsigned c, totlen = columns; + + memset(colsizes, 0, columns*sizeof(unsigned)); + for (ul=0; ul colsizes[c]) { + totlen += (*len)-colsizes[c]; + colsizes[c] = *len; + if (totlen > TT.screen_width) break; + } + } + // If everything fit, stop here + if (ul == dtlen) break; + } + } + + // Loop through again to produce output. + width = 0; + for (ul = 0; ulst); + mode_t mode = st->st_mode; + char et = endtype(st), *ss; + + // If we couldn't stat, output ? for most fields + zap = !st->st_blksize && !st->st_dev && !st->st_ino; + + // Skip directories at the top of the tree when -d isn't set + if (S_ISDIR(mode) && !indir->parent && !(flags & FLAG_d)) continue; + TT.nl_title=1; + + // Handle padding and wrapping for display purposes + entrylen(sort[next], len); + if (ul) { + int mm = !!(flags & FLAG_m); + + if (mm) xputc(','); + if (flags & (FLAG_C|FLAG_x)) { + if (!curcol) xputc('\n'); + } else if ((flags & FLAG_1) || width+1+*len > TT.screen_width) { + xputc('\n'); + width = 0; + } else { + printf(" "+mm, 0); // shut up the stupid compiler + width += 2-mm; + } + } + width += *len; + + if (flags & FLAG_i) zprint(zap, "lu ", totals[1], st->st_ino); + + if (flags & FLAG_s) { + print_with_h(tmp, st->st_blocks, 512); + zprint(zap, "s ", totals[6], (unsigned long)tmp); + } + + if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) { + + // (long) is to coerce the st types into something we know we can print. + mode_to_string(mode, tmp); + if (zap) memset(tmp+1, '?', 9); + printf("%s", tmp); + zprint(zap, "ld", totals[2]+1, st->st_nlink); + + // print user + if (!(flags&FLAG_g)) { + putchar(' '); + ii = -totals[3]; + if (zap || (flags&FLAG_n)) zprint(zap, "lu", ii, st->st_uid); + else draw_trim_esc(getusername(st->st_uid), ii, abs(ii), TT.escmore, + crunch_qb); + } + + // print group + if (!(flags&FLAG_o)) { + putchar(' '); + ii = -totals[4]; + if (zap || (flags&FLAG_n)) zprint(zap, "lu", ii, st->st_gid); + else draw_trim_esc(getgroupname(st->st_gid), ii, abs(ii), TT.escmore, + crunch_qb); + } + } + if (FLAG(Z)) + printf(" %-*s "+!FLAG(l), -(int)totals[7], (char *)sort[next]->extra); + + if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) { + struct tm *tm; + + // print major/minor, or size + if (!zap && (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))) + printf("% *d,% 4d", totals[5]-4, dev_major(st->st_rdev), + dev_minor(st->st_rdev)); + else { + print_with_h(tmp, st->st_size, 1); + zprint(zap, "s", totals[5]+1, (unsigned long)tmp); + } + + // print time, always in --time-style=long-iso + tm = localtime(&(st->st_mtime)); + strftime(tmp, sizeof(tmp), " %F %H:%M", tm); + if (TT.l>1) { + char *s = tmp+strlen(tmp); + + s += sprintf(s, ":%02d.%09d ", tm->tm_sec, (int)st->st_mtim.tv_nsec); + strftime(s, sizeof(tmp)-(s-tmp), "%z", tm); + } + zprint(zap, "s ", 17+(TT.l>1)*13, (unsigned long)tmp); + } + + if (flags & FLAG_color) { + color = color_from_mode(st->st_mode); + if (color) printf("\033[%d;%dm", color>>8, color&255); + } + + ss = sort[next]->name; + crunch_str(&ss, INT_MAX, stdout, TT.escmore, crunch_qb); + if (color) printf("\033[0m"); + + if ((flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) && S_ISLNK(mode)) { + printf(" -> "); + if (!zap && (flags & FLAG_color)) { + struct stat st2; + + if (fstatat(dirfd, sort[next]->symlink, &st2, 0)) color = 256+31; + else color = color_from_mode(st2.st_mode); + + if (color) printf("\033[%d;%dm", color>>8, color&255); + } + + zprint(zap, "s", 0, (unsigned long)sort[next]->symlink); + if (!zap && color) printf("\033[0m"); + } + + if (et) xputc(et); + + // Pad columns + if (flags & (FLAG_C|FLAG_x)) { + curcol = colsizes[curcol]-(*len)-totpad; + if (curcol < 255) printf("%*c", curcol, ' '); + } + } + + if (width) xputc('\n'); + + // Free directory entries, recursing first if necessary. + + for (ul = 0; ulst.st_mode)) continue; + + // Recurse into dirs if at top of the tree or given -R + if (!indir->parent || ((flags&FLAG_R) && dirtree_notdotdot(sort[ul]))) + listfiles(openat(dirfd, sort[ul]->name, 0), sort[ul]); + free((void *)sort[ul]->extra); + } + free(sort); + if (dirfd != AT_FDCWD) close(dirfd); +} + +void ls_main(void) +{ + char **s, *noargs[] = {".", 0}; + struct dirtree *dt; + + if (FLAG(full_time)) { + toys.optflags |= FLAG_l; + TT.l = 2; + } + + // Do we have an implied -1 + if (isatty(1)) { + if (!FLAG(show_control_chars)) toys.optflags |= FLAG_b; + if (toys.optflags&(FLAG_l|FLAG_o|FLAG_n|FLAG_g)) toys.optflags |= FLAG_1; + else if (!(toys.optflags&(FLAG_1|FLAG_x|FLAG_m))) toys.optflags |= FLAG_C; + } else { + if (!FLAG(m)) toys.optflags |= FLAG_1; + if (TT.color) toys.optflags ^= FLAG_color; + } + + TT.screen_width = 80; + terminal_size(&TT.screen_width, NULL); + if (TT.screen_width<2) TT.screen_width = 2; + if (FLAG(b)) TT.escmore = " \\"; + + // The optflags parsing infrastructure should really do this for us, + // but currently it has "switch off when this is set", so "-dR" and "-Rd" + // behave differently + if (FLAG(d)) toys.optflags &= ~FLAG_R; + + // Iterate through command line arguments, collecting directories and files. + // Non-absolute paths are relative to current directory. Top of tree is + // a dummy node to collect command line arguments into pseudo-directory. + TT.files = dirtree_add_node(0, 0, 0); + TT.files->dirfd = AT_FDCWD; + for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) { + int sym = !(toys.optflags&(FLAG_l|FLAG_d|FLAG_F)) + || (toys.optflags&(FLAG_L|FLAG_H)); + + dt = dirtree_add_node(0, *s, DIRTREE_STATLESS|DIRTREE_SYMFOLLOW*sym); + + // note: double_list->prev temporarily goes in dirtree->parent + if (dt) { + if (dt->again&2) { + perror_msg_raw(*s); + free(dt); + } else dlist_add_nomalloc((void *)&TT.files->child, (void *)dt); + } else toys.exitval = 1; + } + + // Convert double_list into dirtree. + dlist_terminate(TT.files->child); + for (dt = TT.files->child; dt; dt = dt->next) dt->parent = TT.files; + + // Display the files we collected + listfiles(AT_FDCWD, TT.files); + + if (CFG_TOYBOX_FREE) free(TT.files); +} diff --git a/porting/liteos_a/toys/posix/mkdir.c b/porting/liteos_a/toys/posix/mkdir.c new file mode 100644 index 0000000..770300d --- /dev/null +++ b/porting/liteos_a/toys/posix/mkdir.c @@ -0,0 +1,54 @@ +/* mkdir.c - Make directories + * + * Copyright 2012 Georgi Chorbadzhiyski + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/mkdir.html + +USE_MKDIR(NEWTOY(mkdir, "<1"USE_MKDIR_Z("Z:")"vp(parent)(parents)m:", TOYFLAG_BIN|TOYFLAG_UMASK)) + +config MKDIR + bool "mkdir" + default y + help + usage: mkdir [-vp] [-m mode] [dirname...] + + Create one or more directories. + + -m Set permissions of directory to mode + -p Make parent directories as needed + -v Verbose + +config MKDIR_Z + bool + default y + depends on MKDIR && !TOYBOX_LSM_NONE + help + usage: [-Z context] + + -Z Set security context +*/ + +#define FOR_mkdir +#include "toys.h" + +GLOBALS( + char *m, *Z; +) + +void mkdir_main(void) +{ + char **s; + mode_t mode = (0777&~toys.old_umask); + + if (CFG_MKDIR_Z && (toys.optflags&FLAG_Z)) + if (0>lsm_set_create(TT.Z)) + perror_exit("-Z '%s' failed", TT.Z); + + if (TT.m) mode = string_to_mode(TT.m, 0777); + + // Note, -p and -v flags line up with mkpathat() flags + for (s=toys.optargs; *s; s++) { + if (mkpathat(AT_FDCWD, *s, mode, toys.optflags|MKPATHAT_MKLAST)) + perror_msg("'%s'", *s); + } +} diff --git a/porting/liteos_a/toys/posix/ps.c b/porting/liteos_a/toys/posix/ps.c new file mode 100644 index 0000000..0c8e8e8 --- /dev/null +++ b/porting/liteos_a/toys/posix/ps.c @@ -0,0 +1,1935 @@ +/* ps.c - show process list + * + * Copyright 2015 Rob Landley + * + * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html + * And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4 + * And linux kernel source fs/proc/array.c function do_task_stat() + * + * Deviations from posix: no -n because /proc/self/wchan exists; we use -n to + * mean "show numeric users and groups" instead. + * Posix says default output should have field named "TTY" but if you "-o tty" + * the same field should be called "TT" which is _INSANE_ and I'm not doing it. + * Similarly -f outputs USER but calls it UID (we call it USER). + * It also says that -o "args" and "comm" should behave differently but use + * the same title, which is not the same title as the default output. (No.) + * Select by session id is -s not -g. Posix doesn't say truncated fields + * should end with "+" but it's pretty common behavior. + * + * Posix defines -o ADDR as "The address of the process" but the process + * start address is a constant on any elf system with mmu. The procps ADDR + * field always prints "-" with an alignment of 1, which is why it has 11 + * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you + * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't + * be sanely implemented on 64 bit Linux systems. In procps there's ps -y + * which changes -l by removing the "F" column and swapping RSS for ADDR, + * leaving 9 chars for cmd, so we're using that as our -l output. + * + * Added a bunch of new -o fields posix doesn't mention, and we don't + * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't + * output argv[0] unmodified for -o comm or -o args (but procps violates + * posix for -o comm anyway, it's stat[2] not argv[0]). + * + * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io + * files (why they're not globally readable when the rest of proc + * data is...?) and get a global I/O picture. Normal top is NOT, + * even though you can -o AIO there, to give sysadmins the option + * to reduce security exposure.) + * + * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference) + * TODO: switch -fl to -y + * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l") + * TODO: iotop: Window size change: respond immediately. Why not padding + * at right edge? (Not adjusting to screen size at all? Header wraps?) + * TODO: top: thread support and SMP + * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf. + +USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_BIN|TOYFLAG_LOCALE)) +// stayroot because iotop needs root to read other process' proc/$$/io +// TOP and IOTOP have a large common option block used for common processing, +// the default values are different but the flags are in the same order. +USE_TOP(NEWTOY(top, ">0O*" "Hk*o*p*u*s#<1d%<100=3000m#n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE)) +USE_IOTOP(NEWTOY(iotop, ">0AaKO" "Hk*o*p*u*s#<1=7d%<100=3000m#n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE)) +USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN)) + +config PS + bool "ps" + default y + help + usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,] + + List processes. + + Which processes to show (-gGuUpPt selections may be comma separated lists): + + -A All -a Has terminal not session leader + -d All but session leaders -e Synonym for -A + -g In GROUPs -G In real GROUPs (before sgid) + -p PIDs (--pid) -P Parent PIDs (--ppid) + -s In session IDs -t Attached to selected TTYs + -T Show threads also -u Owned by selected USERs + -U Real USERs (before suid) + + Output modifiers: + + -k Sort FIELDs (-FIELD to reverse) -M Measure/pad future field widths + -n Show numeric USER and GROUP -w Wide output (don't truncate fields) + + Which FIELDs to show. (-o HELP for list, default = -o PID,TTY,TIME,CMD) + + -f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD) + -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD) + -o Output FIELDs instead of defaults, each with optional :size and =title + -O Add FIELDS to defaults + -Z Include LABEL + +config TOP + bool "top" + default y + help + usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,] + + Show process activity in real time. + + -H Show threads + -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID) + -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE) + -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default) + -s Sort by field number (1-X, default 9) + -b Batch mode (no tty) + -d Delay SECONDS between each cycle (default 3) + -m Maximum number of tasks to show + -n Exit after NUMBER iterations + -p Show these PIDs + -u Show these USERs + -q Quiet (no header lines) + + Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force + update, R to reverse sort, Q to exit. + +# Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io +config IOTOP + bool "iotop" + default y + help + usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] + + Rank processes by I/O. + + -A All I/O, not just disk + -a Accumulated I/O (not percentage) + -H Show threads + -K Kilobytes + -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID) + -m Maximum number of tasks to show + -O Only show processes doing I/O + -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM) + -s Sort by field number (0-X, default 6) + -b Batch mode (no tty) + -d Delay SECONDS between each cycle (default 3) + -n Exit after NUMBER iterations + -p Show these PIDs + -u Show these USERs + -q Quiet (no header lines) + + Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force + update, R to reverse sort, Q to exit. + +config PGREP + bool "pgrep" + default y + help + usage: pgrep [-clfnovx] [-d DELIM] [-L SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] + + Search for process(es). PATTERN is an extended regular expression checked + against command names. + + -c Show only count of matches + -d Use DELIM instead of newline + -L Send SIGNAL instead of printing name + -l Show command name + -f Check full command line for PATTERN + -G Match real Group ID(s) + -g Match Process Group(s) (0 is current user) + -n Newest match only + -o Oldest match only + -P Match Parent Process ID(s) + -s Match Session ID(s) (0 for current) + -t Match Terminal(s) + -U Match real User ID(s) + -u Match effective User ID(s) + -v Negate the match + -x Match whole command (not substring) + +config PKILL + bool "pkill" + default y + help + usage: pkill [-fnovx] [-SIGNAL|-l SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] + + -l Send SIGNAL (default SIGTERM) + -V Verbose + -f Check full command line for PATTERN + -G Match real Group ID(s) + -g Match Process Group(s) (0 is current user) + -n Newest match only + -o Oldest match only + -P Match Parent Process ID(s) + -s Match Session ID(s) (0 for current) + -t Match Terminal(s) + -U Match real User ID(s) + -u Match effective User ID(s) + -v Negate the match + -x Match whole command (not substring) +*/ + +#define FOR_ps +#include "toys.h" + +GLOBALS( + union { + struct { + struct arg_list *G, *g, *U, *u, *t, *s, *p, *O, *o, *P, *k; + } ps; + struct { + long n, m, d, s; + struct arg_list *u, *p, *o, *k, *O; + } top; + struct { + char *L; + struct arg_list *G, *g, *P, *s, *t, *U, *u; + char *d; + + void *regexes, *snapshot; + int signal; + pid_t self, match; + } pgrep; + }; + + struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU; + struct dirtree *threadparent; + unsigned width, height; + dev_t tty; + void *fields, *kfields; + long long ticks, bits, time; + int kcount, forcek, sortpos; + int (*match_process)(long long *slot); + void (*show_process)(void *tb); +) + +// Linked list of -o fields selected for display, in order, with :len and =title + +struct ofields { + struct ofields *next, *prev; + short which, len, reverse; + char *title; +}; + +/* The function get_ps() reads all the data about one process, saving it in + * toybox as a struct procpid. Simple ps calls then pass toybuf directly to + * show_ps(), but features like sorting append a copy to a linked list + * for further processing once all processes have been read. + * + * struct procpid contains a slot[] array of 64 bit values, with the following + * data at each position in the array. Most is read from /proc/$PID/stat (see + * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but + * we replace several fields with don't use with other data. */ + +enum { + SLOT_pid, /*process id*/ SLOT_ppid, // parent process id + SLOT_pgrp, /*process group*/ SLOT_sid, // session id + SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty + SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults + SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults + SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies + SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child utime + SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level + SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count + SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot + SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size + SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr + SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address + SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer + SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes + SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id + SLOT_ruid, /*real user id*/ SLOT_gid, // group id + SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent + SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority + SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time + SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child + SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss +// end of /proc/$PID/stat fields + SLOT_upticks, /*uptime-starttime*/ SLOT_argv0len, // argv[0] length + SLOT_uptime, /*sysinfo.uptime*/ SLOT_totalram, // sysinfo.totalram + SLOT_vsz, /*Virtual mem Size*/ SLOT_shr, // Shared memory + SLOT_pcy, /*Android sched pol*/ SLOT_rchar, // All bytes read + SLOT_wchar, /*All bytes written*/ SLOT_rbytes, // Disk bytes read + SLOT_wbytes, /*Disk bytes written*/ SLOT_swap, // Swap pages used + SLOT_bits, /*32 or 64*/ SLOT_tid, // Thread ID + SLOT_tcount, /*Thread count*/ + + SLOT_count /* Size of array */ +}; + +/* In addition to slot[], carevup contains 6 string fields to display + command name, tty device, selinux label... They're stored one after the + other in str[] (separated by null terminators), and offset[] contains the + starting position of each string after the first (which is always 0). */ + +// Data layout in toybuf +struct procpid { + long long slot[SLOT_count]; // data (see enum above) + unsigned short offset[6]; // offset of fields in str[] (skip CMD, always 0) + char state; + char str[]; // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME +}; + +/* The typos[] array lists all the types understood by "ps -o", I.E all the + * columns ps and top know how to display. Each entry has: + * + * name: the column name, displayed at top and used to select column with -o + * + * width: the display width. Fields are padded to this width when displaying + * to a terminal (negative means right justified). Strings are truncated + * to fit, numerical fields are padded but not truncated (although + * the display code reclaims unused padding from later fields to try to + * get the overflow back). + * + * slot: which slot[] out of procpid. Negative means it's a string field. + * value|XX requests extra display/sort processing. + * + * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the + * first string argument and the prefix is the first argument to TAGGED_ARRAY + * so in this case "NAME" becomes PS_NAME which is the offset into typos[] + * for that entry, and also _PS_NAME (the bit position, 1<str, rest are str+offset[1-slot]) + {"TTY", "Controlling terminal", -8, -2}, + {"WCHAN", "Wait location in kernel", -6, -3}, + {"LABEL", "Security label", -30, -4}, + {"COMM", "EXE filename (/proc/PID/exe)", -27, -5}, + {"NAME", "Process name (PID's argv[0])", -27, -7}, + {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5}, + {"CMDLINE", "Command line (argv[])", -27, -6}, + {"ARGS", "CMDLINE minus initial path", -27, -6}, + {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1}, + + // user/group (may call getpwuid() or similar) + {"UID", "User id", 5, SLOT_uid}, + {"USER", "User name", -12, XX|SLOT_uid}, + {"RUID", "Real (before suid) user ID", 4, SLOT_ruid}, + {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid}, + {"GID", "Group ID", 8, SLOT_gid}, + {"GROUP", "Group name", -8, XX|SLOT_gid}, + {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid}, + {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid}, + + // clock displays (00:00:00) + {"TIME", "CPU time consumed", 8, SLOT_utime}, + {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime}, + {"TIME+", "CPU time (high precision)", 9, SLOT_utime}, + + // Percentage displays (fixed point, one decimal digit. 123 -> 12.3) + {"C", "Total %CPU used since start", 1, SLOT_utime2}, + {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize}, + {"%MEM", "RSS as % of physical memory", 5, SLOT_rss}, + {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2}, + + // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc) + {"VIRT", "Virtual memory size", 4, SLOT_vsz}, + {"RES", "Short RSS", 4, SLOT_rss}, + {"SHR", "Shared memory", 4, SLOT_shr}, + {"READ", "Data read", 6, SLOT_rchar}, + {"WRITE", "Data written", 6, SLOT_wchar}, + {"IO", "Data I/O", 6, SLOT_iobytes}, + {"DREAD", "Data read from disk", 6, SLOT_rbytes}, + {"DWRITE", "Data written to disk", 6, SLOT_wbytes}, + {"SWAP", "Swap I/O", 6, SLOT_swap}, + {"DIO", "Disk I/O", 6, SLOT_diobytes}, + + // Misc (special cases) + {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime}, + {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags}, + {"S", "Process state:\n" + "\t R (running) S (sleeping) D (device I/O) T (stopped) t (trace stop)\n" + "\t X (dead) Z (zombie) P (parked) I (idle)\n" + "\t Also between Linux 2.6.33 and 3.13:\n" + "\t x (dead) K (wakekill) W (waking)\n", + -1, XX}, + {"STAT", "Process state (S) plus:\n" + "\t < high priority N low priority L locked memory\n" + "\t s session leader + foreground l multithreaded", + -5, XX}, + {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy}, +); + +// Show sorted "-o help" text for fields listed in toybuf[len] +static void help_fields(int len, int multi) +{ + int i, j, k, left = 0; + struct typography *t; + + // Quick and dirty sort of toybuf[] entries (see TODO below) + for (j = len; j--; ) { + k = -1; + + for (i=0; i0) { + k = toybuf[i]; + toybuf[i] = toybuf[i+1]; + toybuf[i+1] = k; + } + } + if (k == -1) break; + } + + // Display loop + for (i = j = 0; ihelp)>30) { + if (multi) printf(" %-8s%s\n", t->name, t->help); + else j--; + } else if (!multi) { + left = !(j&1); + printf(" %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left); + } + } + if (!multi && left) xputc('\n'); +} + +// Print help text for each -o field, with categories. +static void help_help(void) +{ + int i, jump = PS_CMD+1-PS_COMM; + + // TODO: sort the array of -o types so they're already alphabetical and + // don't need sorting here. A regex to find everything that currently cares + // about symbol order might be: "which *[><]=* *PS" + + // First show the half-dozen variants of command line display. + + printf("Command line field types:\n\n"); + for (i = 0; i=PS_COMM)*jump; + help_fields(ARRAY_LEN(typos)-jump, 1); + help_fields(ARRAY_LEN(typos)-jump, 0); + + xexit(); +} + +// process match filter for top/ps/pgrep: Return 0 to discard, nonzero to keep +static int shared_match_process(long long *slot) +{ + struct ptr_len match[] = { + {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid}, + {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr}, + {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid} + }; + int i, j; + long *ll = 0; + + // Do we have -g -G -p -P -s -t -u -U options selecting processes? + for (i = 0; i < ARRAY_LEN(match); i++) { + struct ptr_len *mm = match[i].ptr; + + if (mm->len) { + ll = mm->ptr; + for (j = 0; jlen; j++) if (ll[j] == slot[match[i].len]) return 1; + } + } + + return ll ? 0 : -1; +} + +// process match filter for ps: Return 0 to discard, nonzero to keep +static int ps_match_process(long long *slot) +{ + int i = shared_match_process(slot); + + if (i>0) return 1; + // If we had selections and didn't match them, don't display + if (!i) return 0; + + // Filter implicit categories for other display types + if ((FLAG(a)||FLAG(d)) && slot[SLOT_sid]==*slot) return 0; + if (FLAG(a) && !slot[SLOT_ttynr]) return 0; + if (!(FLAG(a)||FLAG(d)||FLAG(A)||FLAG(e)) && TT.tty!=slot[SLOT_ttynr]) + return 0; + + return 1; +} + +// Generate display string (260 bytes at end of toybuf) from struct ofield +static char *string_field(struct procpid *tb, struct ofields *field) +{ + char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s; + int which = field->which, sl = typos[which].slot; + long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0; + + // numbers, mostly from /proc/$PID/stat + if (which <= PS_BIT) { + char *fmt = "%lld"; + + if (which==PS_PRI) ll = 39-ll; + if (which==PS_ADDR) fmt = "%llx"; + else if (which==PS_SZ) ll >>= 12; + else if (which==PS_RSS) ll <<= 2; + else if (which==PS_VSZ) ll >>= 10; + else if (which==PS_PR && ll<-9) fmt="RT"; + else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-"; + sprintf(out, fmt, ll); + + // String fields + } else if (sl < 0) { + out = tb->str; + sl *= -1; + // First string slot has offset 0, others are offset[-slot-2] + if (--sl) out += tb->offset[--sl]; + if (which==PS_ARGS || which==PS_COMM) { + int i; + + s = out; + for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++) + if (out[i] == '/') s = out+i+1; + out = s; + } + if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str); + + // user/group + } else if (which <= PS_RGROUP) { + sprintf(out, "%lld", ll); + if (sl&XX) { + if (which > PS_RUSER) { + struct group *gr = bufgetgrgid(ll); + + if (gr) out = gr->gr_name; + } else { + struct passwd *pw = bufgetpwuid(ll); + + if (pw) out = pw->pw_name; + } + } + + // Clock displays + } else if (which <= PS_TIME_) { + int unit = 60, pad = 2, j = TT.ticks; + time_t seconds; + + if (which!=PS_TIME_) unit *= 60*24; + else pad = 0; + // top adjusts slot[SLOT_upticks], we want original meaning. + if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime]; + seconds = ll/j; + + // Output days-hours:mins:secs, skipping non-required fields with zero + // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top + for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) { + if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out; + if (s) { + s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit)); + pad = 2; + if ((*s = "-::"[j])) s++; + } + seconds %= unit; + unit /= j ? 60 : 24; + } + if (which==PS_TIME_ && s-out<8) + sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks); + + // Percentage displays + } else if (which <= PS__CPU) { + ll = slot[sl&(XX-1)]*1000; + if (which==PS__VSZ || which==PS__MEM) + ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096); + else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks]; + sl = ll; + if (which==PS_C) sl += 5; + sprintf(out, "%d", sl/10); + if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10); + + // Human readable + } else if (which <= PS_DIO) { + int i = abs(field->len); + + if (i<4) i = 4; + s = out; + if ((ll = slot[typos[which].slot])<0) { + ll = -ll; + *s++ = '-'; + if (i>4) i--; + } + if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE); + if (TT.forcek) sprintf(out, "%lldk", ll/1024); + else human_readable_long(s, ll, i-1, 0); + + // Posix doesn't specify what flags should say. Man page says + // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h + } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5); + else if (which==PS_S || which==PS_STAT) { + s = out; + *s++ = tb->state; + if (which==PS_STAT) { + // TODO l = multithreaded + if (slot[SLOT_nice]<0) *s++ = '<'; + else if (slot[SLOT_nice]>0) *s++ = 'N'; + if (slot[SLOT_sid]==*slot) *s++ = 's'; + if (slot[SLOT_vmlck]) *s++ = 'L'; + if (slot[SLOT_ttypgrp]==*slot) *s++ = '+'; + } + *s = 0; + } else if (which==PS_STIME) { + time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks; + + // Padding behavior's a bit odd: default field size is just hh:mm. + // Increasing stime:size reveals more data at left until full, + // so move start address so yyyy-mm-dd hh:mm revealed on left at :16, + // then add :ss on right for :19. + strftime(out, 260, "%F %T", localtime(&t)); + out = out+strlen(out)-3-abs(field->len); + if (outnext) { + char *out = string_field(tb, field); + + // Output the field, appropriately padded + + // Minimum one space between each field + if (width<2) break; + if (field != TT.fields) { + putchar(' '); + width--; + } + + // Don't truncate number fields, but try to reclaim extra offset from later + // fields that can naturally be shorter + abslen = abs(field->len); + sign = field->len<0 ? -1 : 1; + olen = (TT.tty) ? utf8len(out) : strlen(out); + if ((field->which<=PS_BIT || FLAG(w)) && olen>abslen) { + // overflow but remember by how much + extra += olen-abslen; + abslen = olen; + } else if (extra && olenextra) unused = extra; + abslen -= unused; + extra -= unused; + } + if (abslen>width) abslen = width; + len = pad = abslen; + pad *= sign; + + // If last field is left justified, no trailing spaces. + if (!field->next && sign<0) { + pad = -1; + len = width; + } + + // If we truncated a left-justified field, show + instead of last char + if (olen>len && len>1 && sign<0) { + width--; + len--; + if (field->next) pad++; + abslen = 0; + } + + if (TT.tty) width -= draw_trim(out, pad, len); + else width -= printf("%*.*s", pad, len, out); + if (!abslen) putchar('+'); + if (!width) break; + } + putchar(TT.time ? '\r' : '\n'); +} + +// dirtree callback: read data about a process, then display or store it. +// Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra +// (in -k mode) or calls show_ps directly on toybuf (for low memory systems). +static int get_ps(struct dirtree *new) +{ + struct { + char *name; // Path under /proc/$PID directory + long long bits; // Only fetch extra data if an -o field is displaying it + } fetch[] = { + // sources for procpid->offset[] data + {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL}, + {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}, + {"", _PS_NAME} + }; + struct procpid *tb = (void *)toybuf; + long long *slot = tb->slot; + char *name, *s, *buf = tb->str, *end = 0; + struct sysinfo si; + int i, j, fd; + off_t len; + + // Recurse one level into /proc children, skip non-numeric entries + if (!new->parent) + return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC + |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process)); + + // Grab PID and figure out if we're a thread or a process + memset(slot, 0, sizeof(tb->slot)); + slot[SLOT_tid] = *slot = atol(new->name); + if (TT.threadparent && TT.threadparent->extra) { + struct procpid *tb2 = (struct procpid *)TT.threadparent->extra; + + *slot = *tb2->slot; + // Parent also shows up as a thread, but we need to reread task/stat fields + // to get non-collated info for just parent thread (vs whole process). + if (*slot == slot[SLOT_tid]) slot = tb2->slot; + } + fd = dirtree_parentfd(new); + + // Read /proc/$PID/stat into half of toybuf. + len = 2048; + sprintf(buf, "%lld/stat", slot[SLOT_tid]); + if (!readfileat(fd, buf, buf, &len)) return 0; + + // parse oddball fields: the first field is same as new->name (skip it) + // and the second and third (name and state) are the only non-numeric fields. + // Name has (parentheses) around it, and can have embedded ')' so match + // _last_ ')' (VFS limits filenames to 255 bytes max, sanity check that). + // TODO: kernel task struct actually limits name to 16 chars? + if (!(name = strchr(buf, '('))) return 0; + for (s = ++name; *s; s++) if (*s == ')') end = s; + if (!end || end-name>255) return 0; + if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0; + + // All remaining fields should be numeric, parse them into slot[] array + // (skipping first 3 stat fields and first slot[], both were handled above) + // yes this means the alignment's off: stat[4] becomes slot[1] + for (j = SLOT_ppid; jsscanf(s += i, " %lld%n", slot+j, &i)) break; + + // Now we've read the data, move status and name right after slot[] array, + // and convert low chars to ? for non-tty display while we're at it. + for (i = 0; istr[i] = name[i]) < ' ') + if (!TT.tty) tb->str[i] = '?'; + buf = tb->str+i; + *buf++ = 0; + len = sizeof(toybuf)-(buf-toybuf); + + // Overwrite useless/obsolete stat fields with more interesting data. + + // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch + // or numeric wchan, and the remaining two are always zero), and vmlck into + // 18 (which is "obsolete, always 0" from stat) + slot[SLOT_uid] = new->st.st_uid; + slot[SLOT_gid] = new->st.st_gid; + + // TIME and TIME+ use combined value, ksort needs 'em added. + slot[SLOT_utime] += slot[SLOT_stime]; + slot[SLOT_utime2] = slot[SLOT_utime]; + + // Do we need to read "status"? + if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP + |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len) + { + off_t temp = len; + + sprintf(buf, "%lld/status", slot[SLOT_tid]); + if (!readfileat(fd, buf, buf, &temp)) *buf = 0; + s = strafter(buf, "\nUid:"); + slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid; + s = strafter(buf, "\nGid:"); + slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid; + if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s)*1024; + if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s)*1024; + } + + // Do we need to read "io"? + if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) { + off_t temp = len; + + sprintf(buf, "%lld/io", slot[SLOT_tid]); + if (!readfileat(fd, buf, buf, &temp)) *buf = 0; + if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s); + if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s); + if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s); + if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s); + slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap]; + slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap]; + } + + // If we were updating thread parent with its own task info, we're done. + if (slot != tb->slot) return 0; + + // We now know enough to skip processes we don't care about. + if (TT.match_process && !TT.match_process(slot)) return 0; + + // /proc data is generated as it's read, so for maximum accuracy on slow + // systems (or ps | more) we re-fetch uptime as we fetch each /proc line. + sysinfo(&si); + slot[SLOT_uptime] = si.uptime; + slot[SLOT_totalram] = si.totalram; + slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime]; + + // Do we need to read "statm"? + if (TT.bits&(_PS_VIRT|_PS_SHR)) { + off_t temp = len; + + sprintf(buf, "%lld/statm", slot[SLOT_tid]); + if (!readfileat(fd, buf, buf, &temp)) *buf = 0; + + // Skip redundant RSS field, we got it from stat. + slot[SLOT_vsz] = slot[SLOT_shr] = 0; + sscanf(buf, "%lld %*d %lld", &slot[SLOT_vsz], &slot[SLOT_shr]); + } + + // Do we need to read "exe"? + if (TT.bits&_PS_BIT) { + off_t temp = 6; + + sprintf(buf, "%lld/exe", slot[SLOT_tid]); + if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) { + if (buf[4] == 1) slot[SLOT_bits] = 32; + else if (buf[4] == 2) slot[SLOT_bits] = 64; + } + } + + // Do we need Android scheduling policy? + if (TT.bits&_PS_PCY) + get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]); + + // Done using buf[] (tb->str) as scratch space, now read string data, + // saving consective null terminated strings. (Save starting offsets into + // str->offset to avoid strlen() loop to find relevant string.) + + // Fetch string data while parentfd still available, appending to buf. + // (There's well over 3k of toybuf left. We could dynamically malloc, but + // it'd almost never get used, querying length of a proc file is awkward, + // fixed buffer is nommu friendly... Wait for somebody to complain. :) + + // The fetch[] array at the start of the function says what file to read + // and what -o display field outputs it (to skip the ones we don't need). + + slot[SLOT_argv0len] = 0; + for (j = 0; joffset[j] = buf-(tb->str); + if (!(TT.bits&fetch[j].bits)) { + *buf++ = 0; + continue; + } + + // Determine available space: reserve 256 bytes (guaranteed minimum) for + // each string we haven't checked yet, tb->str starts after the numeric + // arrays in struct procpid, and we reserve 260 bytes scratch space at the + // end of toybuf for output conversion in string_field(). Other than that, + // each use all available space, and future strings that don't use their + // guaranteed minimum add to the pool. + len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260; + sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name); + + // For exe (j==3) readlink() instead of reading file's contents + // for -o NAME (j==5) copy data from threadparent (PID) into thread (TID). + if (j==3 || j==5) { + struct procpid *ptb = 0; + int k; + + // Thread doesn't have exe or argv[0], so use parent's + if (TT.threadparent && TT.threadparent->extra) + ptb = (void *)TT.threadparent->extra; + + if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len); + else { + if (j==3) i = strlen(s = ptb->str+ptb->offset[3]); + else { + if (!ptb || slot[SLOT_argv0len]) ptb = tb; + i = ptb->slot[SLOT_argv0len]; + s = ptb->str+ptb->offset[4]; + while (-1!=(k = stridx(s, '/')) && k0) { + + // Trim trailing whitespace and NUL bytes + while (len) + if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0; + else break; + + // Turn NUL to space, other low ascii to ? (in non-tty mode), except + // cmdline has a trailing NUL that we don't want to turn to space. + for (i=0; iextra = (long)s; + memcpy(s, toybuf, buf-toybuf); + + return DIRTREE_SAVE; +} + +// wrapper for get_ps() that also collects threads under each processes +static int get_threads(struct dirtree *new) +{ + struct dirtree *dt; + struct procpid *tb; + unsigned pid, kcount; + + if (!new->parent) return get_ps(new); + pid = atol(new->name); + + TT.threadparent = new; + if (!get_ps(new)) { + // it exited out from under us + TT.threadparent = 0; + + return 0; + } + + // Recurse down into tasks, retaining thread groups. + // Disable show_process at least until we can calculate tcount + kcount = TT.kcount; + sprintf(toybuf, "/proc/%u/task", pid); + new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps); + if (new->child == DIRTREE_ABORTVAL) new->child = 0; + TT.threadparent = 0; + kcount = TT.kcount-kcount+1; + tb = (void *)new->extra; + tb->slot[SLOT_tcount] = kcount; + + // Fill out tid and thread count for each entry in group (if it didn't exit + // out from under us again; asynchronous reads of unlocked data are fun!) + if (new->child) for (dt = new->child->child; dt; dt = dt->next) { + tb = (void *)dt->extra; + tb->slot[SLOT_pid] = pid; + tb->slot[SLOT_tcount] = kcount; + } + + // Save or display + if (!TT.show_process) return DIRTREE_SAVE; + TT.show_process((void *)new->extra); + if ((dt = new->child)) { + new->child = 0; + while (dt->child) { + new = dt->child->next; + TT.show_process((void *)dt->child->extra); + free(dt->child); + dt->child = new; + } + free(dt); + } + + return 0; +} + +// Parse one FIELD argument (with optional =name :width) into struct ofields +static char *parse_ko(void *data, char *type, int length) +{ + struct ofields *field; + char *width, *title, *end, *s; + int i, j, k; + + // Caller's WOULD_EXIT catches -o help and prints help + if (length==4 && !strncasecmp(type, "HELP", length)) xexit(); + + // Get title, length of title, type, end of type, and display width + + // Chip off =name to display + if ((end = strchr(type, '=')) && length>(end-type)) { + title = end+1; + length -= (end-type)+1; + } else { + end = type+length; + title = 0; + } + + // Chip off :width to display + if ((width = strchr(type, ':')) && widthtitle = (char *)(field+1), title, length); + field->title[field->len = length] = 0; + } + + if (width) { + field->len = strtol(++width, &title, 10); + if (!isdigit(*width) || title != end) return title; + end = --width; + } + + // Find type + field->reverse = 1; + if (*type == '-') field->reverse = -1; + else if (*type != '+') type--; + type++; + for (i = 0; iwhich = i; + for (j = 0; j<2; j++) { + if (!j) s = typos[i].name; + // posix requires alternate names for some fields + else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU, + PS_VSZ, PS_USER, 0}, i))) continue; + else + s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k]; + + if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break; + } + if (j!=2) break; + } + if (i==ARRAY_LEN(typos)) return type; + if (!field->title) field->title = typos[field->which].name; + if (!field->len) field->len = typos[field->which].width; + else if (typos[field->which].width<0) field->len *= -1; + dlist_add_nomalloc(data, (void *)field); + + return 0; +} + +// Write FIELD list into display header string (truncating at blen), +// and return bitfield of which FIELDs are used. +static long long get_headers(struct ofields *field, char *buf, int blen) +{ + long long bits = 0; + int len = 0; + + for (; field; field = field->next) { + len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len, + field->title); + bits |= 1LL<which; + } + + return bits; +} + +// Parse command line options -p -s -t -u -U -g -G +static char *parse_rest(void *data, char *str, int len) +{ + struct ptr_len *pl = (struct ptr_len *)data; + long *ll = pl->ptr; + char *end; + int num = 0; + + // Allocate next chunk of data + if (!(15&pl->len)) + ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16)); + + // Parse numerical input + if (isdigit(*str)) { + ll[pl->len] = xstrtol(str, &end, 10); + if (end==(len+str)) num++; + // For pkill, -s 0 represents pkill's session id. + if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0); + } + + if (pl==&TT.pp || pl==&TT.ss) { + if (num && ll[pl->len]>0) { + pl->len++; + + return 0; + } + } else if (pl==&TT.tt) { + // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0 + if (!num) { + if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5; + if (strstart(&str, "pts/")) { + len -= 4; + num++; + } else if (strstart(&str, "tty")) len -= 3; + } + if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) { + struct stat st; + + end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty"); + memcpy(end, str, len); + end[len] = 0; + xstat(toybuf, &st); + ll[pl->len++] = st.st_rdev; + + return 0; + } + } else if (len<255) { + char name[256]; + + if (num) { + pl->len++; + + return 0; + } + + memcpy(name, str, len); + name[len] = 0; + if (pl==&TT.gg || pl==&TT.GG) { + struct group *gr = getgrnam(name); + if (gr) { + ll[pl->len++] = gr->gr_gid; + + return 0; + } + } else if (pl==&TT.uu || pl==&TT.UU) { + struct passwd *pw = getpwnam(name); + if (pw) { + ll[pl->len++] = pw->pw_uid; + + return 0; + } + } + } + + // Return error + return str; +} + +// sort processes by FIELD(s) listed in option -k +static int ksort(void *aa, void *bb) +{ + struct ofields *field; + struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb; + int ret = 0, slot; + + for (field = TT.kfields; field && !ret; field = field->next) { + slot = typos[field->which].slot; + + // Can we do numeric sort? + if (!(slot&XX)) { + if (ta->slot[slot]slot[slot]) ret = -1; + if (ta->slot[slot]>tb->slot[slot]) ret = 1; + } + + // fallback to string sort + if (!ret) { + memccpy(toybuf, string_field(ta, field), 0, 2048); + toybuf[2048] = 0; + ret = strcmp(toybuf, string_field(tb, field)); + } + ret *= field->reverse; + } + + return ret; +} + +// Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array +// (recursion because tree from get_thread() isn't flat list of siblings) +static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt) +{ + while (dt) { + struct dirtree *next = dt->next; + + if (dt->extra) *(tb++) = (void *)dt->extra; + if (dt->child) tb = collate_leaves(tb, dt->child); + free(dt); + dt = next; + } + + return tb; +} + +// Allocate struct procpid array of length count and populate it with ->extra +// fields from dirtree leaf nodes. (top diffs old & new array to show changes) +static struct procpid **collate(int count, struct dirtree *dt) +{ + struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *)); + + collate_leaves(tbsort, dt); + + return tbsort; +} + +// parse command line arguments (ala -k -o) with a comma separated FIELD list +static void default_ko(char *s, void *fields, char *err, struct arg_list *arg) +{ + struct arg_list def; + int x; + + memset(&def, 0, sizeof(struct arg_list)); + def.arg = s; + WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko)); + if (x) help_help(); +} + +void ps_main(void) +{ + char **arg; + struct dirtree *dt; + char *not_o; + int i; + + TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime + + if (-1 != (i = tty_fd())) { + struct stat st; + + if (!fstat(i, &st)) TT.tty = st.st_rdev; + } + + // If we can't query terminal size pad to 80 but do -w + TT.width = 80; + if (!isatty(1) || !terminal_size(&TT.width, 0)) toys.optflags |= FLAG_w; + if (FLAG(w)) TT.width = 99999; + + // parse command line options other than -o + comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest); + comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest); + comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest); + comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest); + comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest); + comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest); + comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest); + comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest); + comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko); + dlist_terminate(TT.kfields); + + // It's undocumented, but traditionally extra arguments are extra -p args + for (arg = toys.optargs; *arg; arg++) + if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit("bad %s", *arg); + + // Figure out which fields to display + not_o = "%sTTY,TIME,CMD"; + if (FLAG(f)) + sprintf(not_o = toybuf+128, + "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD", FLAG(T) ? "TCNT" :"C"); + else if (FLAG(l)) + not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD"; + else if (CFG_TOYBOX_ON_ANDROID) + sprintf(not_o = toybuf+128, + "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s", + FLAG(T) ? "CMD" : "NAME"); + sprintf(toybuf, not_o, FLAG(T) ? "PID,TID," : "PID,"); + + // Init TT.fields. This only uses toybuf if TT.ps.o is NULL + if (FLAG(Z)) default_ko("LABEL", &TT.fields, 0, 0); + default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o); + + if (TT.ps.O) { + if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->prev; + comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko); + if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->next; + } + dlist_terminate(TT.fields); + + // -f and -n change the meaning of some fields + if (FLAG(f)||FLAG(n)) { + struct ofields *field; + + for (field = TT.fields; field; field = field->next) { + if (FLAG(n) && field->which>=PS_UID + && field->which<=PS_RGROUP && (typos[field->which].slot&XX)) + field->which--; + } + } + + // Calculate seen fields bit array, and if we aren't deferring printing + // print headers now (for low memory/nommu systems). + TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf)); + if (!FLAG(M)) printf("%.*s\n", TT.width, toybuf); + if (!(FLAG(k)||FLAG(M))) TT.show_process = show_ps; + TT.match_process = ps_match_process; + dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, + (FLAG(T) || (TT.bits&(_PS_TID|_PS_TCNT))) + ? get_threads : get_ps); + + if ((dt != DIRTREE_ABORTVAL) && (FLAG(k)||FLAG(M))) { + struct procpid **tbsort = collate(TT.kcount, dt); + + if (FLAG(M)) { + for (i = 0; inext) { + int len = strlen(string_field(tbsort[i], field)); + + if (abs(field->len)len = (field->len<0) ? -len : len; + } + } + + // Now that we've recalculated field widths, re-pad headers again + get_headers(TT.fields, toybuf, sizeof(toybuf)); + printf("%.*s\n", TT.width, toybuf); + } + + if (FLAG(k)) qsort(tbsort, TT.kcount, sizeof(void *), (void *)ksort); + for (i = 0; inext) { + if ((TT.sortpos = i++)next) continue; + field2 = TT.kfields; + field2->which = field->which; + field2->len = field->len; + break; + } +} + +// If we have both, adjust slot[deltas[]] to be relative to previous +// measurement rather than process start. Stomping old.data is fine +// because we free it after displaying. +static int merge_deltas(long long *oslot, long long *nslot, int milis) +{ + char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar, + SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap}; + int i; + + for (i = 0; iwhence = millitime(); + dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, + (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps); + if (dt == DIRTREE_ABORTVAL) error_exit("no /proc"); + plnew->tb = collate(plnew->count = TT.kcount, dt); + TT.kcount = 0; + + if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) { + long long *st = stats+8*(tock&1); + + // user nice system idle iowait irq softirq host + sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld", + st, st+1, st+2, st+3, st+4, st+5, st+6, st+7); + } + + // First time, wait a quarter of a second to collect a little delta data. + if (!plold->tb) { + msleep(250); + continue; + } + + // Collate old and new into "mix", depends on /proc read in pid sort order + old = *plold; + new = *plnew; + mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid)); + mix.count = 0; + + while (old.count || new.count) { + struct procpid *otb = old.count ? *old.tb : 0, + *ntb = new.count ? *new.tb : 0; + + // If we just have old for this process, it exited. Discard it. + if (old.count && (!new.count || *otb->slot < *ntb->slot)) { + old.tb++; + old.count--; + + continue; + } + + // If we just have new, use it verbatim + if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb; + else { + // Keep or discard + if (filter(otb->slot, ntb->slot, new.whence-old.whence)) { + mix.tb[mix.count] = otb; + mix.count++; + } + old.tb++; + old.count--; + } + new.tb++; + new.count--; + } + + // Don't re-fetch data if it's not time yet, just re-display existing data. + for (;;) { + char was, is; + + if (recalc) { + qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort); + if (!FLAG(b)) { + printf("\033[H\033[J"); + if (toys.signal) { + toys.signal = 0; + terminal_probesize(&TT.width, &TT.height); + } + } + if (TT.top.m) TT.height = TT.top.m+5; + lines = TT.height; + } + if (recalc && !FLAG(q)) { + // Display "top" header. + if (*toys.which->name == 't') { + struct ofields field; + char hr[4][32]; + long long ll, up = 0; + long run[6]; + int j; + + // Count running, sleeping, stopped, zombie processes. + // The kernel has more states (and different sets in different + // versions), so we need to map them. (R)unning and (Z)ombie are + // easy enough, and since "stopped" is rare (just T and t as of + // Linux 4.20), we assume everything else is "sleeping". + field.which = PS_S; + memset(run, 0, sizeof(run)); + for (i = 0; i10); + + // If a processor goes idle it's powered down and its idle ticks don't + // advance, so calculate idle time as potential time - used. + if (mix.count) up = mix.tb[0]->slot[SLOT_upticks]; + if (!up) up = 1; + now = up*i; + ll = stats[3] = stats[11] = 0; + for (i = 0; i<8; i++) ll += stats[i]-stats[i+8]; + stats[3] = now - llabs(ll); + + for (i = 0; i<8; i++) { + ll = (llabs(stats[i]-stats[i+8])*1000)/up; + pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]); + } + lines = header_line(lines, 0); + } else { + struct ofields *field; + struct procpid tb; + + memset(&tb, 0, sizeof(struct procpid)); + pos = stpcpy(toybuf, "Totals:"); + for (field = TT.fields; field; field = field->next) { + long long ll, bits = 0; + int slot = typos[field->which].slot&(XX-1); + + if (field->whichwhich>PS_DIO) continue; + ll = 1LL<which; + if (bits&ll) continue; + bits |= ll; + for (i=0; islot[slot]; + pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf), + " %s: %*s,", typos[field->which].name, + field->len, string_field(&tb, field)); + } + *--pos = 0; + lines = header_line(lines, 0); + } + + get_headers(TT.fields, pos = toybuf, sizeof(toybuf)); + for (i = 0, is = ' '; *pos; pos++) { + was = is; + is = *pos; + if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf) + pos[-1] = '['; + if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']'; + } + if (FLAG(b)) while (isspace(*(pos-1))) --pos; + *pos = 0; + lines = header_line(lines, 1); + } + if (!recalc && !FLAG(b)) + printf("\033[%dH\033[J", 1+TT.height-lines); + recalc = 1; + + for (i = 0; istate == 'R'; + + if (!FLAG(b) && i) putchar('\n'); + if (bold) printf("\033[1m"); + show_ps(mix.tb[i+topoff]); + if (bold) printf("\033[m"); + } + + if (TT.top.n && !--TT.top.n) { + done++; + break; + } + + now = millitime(); + if (timeout<=now) timeout = new.whence+TT.top.d; + if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d; + + // In batch mode, we ignore the keyboard. + if (FLAG(b)) { + msleep(timeout-now); + // Make an obvious gap between datasets. + xputs("\n\n"); + break; + } else fflush(stdout); + + i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height); + if (i==-1 || i==3 || toupper(i)=='Q') { + done++; + break; + } + if (i==-2) break; + if (i==-3) continue; + + // Flush unknown escape sequences. + if (i==27) while (0reverse *= -1; + else { + i -= 256; + if (i == KEY_LEFT) setsort(TT.sortpos-1); + else if (i == KEY_RIGHT) setsort(TT.sortpos+1); + // KEY_UP is 0, so at end of strchr + else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) { + recalc = 0; + + if (i == KEY_UP) topoff--; + else if (i == KEY_DOWN) topoff++; + else if (i == KEY_PGDN) topoff += lines; + else if (i == KEY_PGUP) topoff -= lines; + if (topoff<0) topoff = 0; + if (topoff>mix.count) topoff = mix.count; + } + } + continue; + } + + free(mix.tb); + for (i=0; icount; i++) free(plold->tb[i]); + free(plold->tb); + } while (!done); + + if (!FLAG(b)) tty_reset(); +} + +static void top_setup(char *defo, char *defk) +{ + TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime + TT.tty = tty_fd() != -1; + + // Are we doing "batch" output or interactive? + if (FLAG(b)) TT.width = TT.height = 99999; + else { + // Grab starting time, make terminal raw, switch off cursor, + // set signal handler to put terminal/cursor back to normal at exit. + TT.time = millitime(); + start_redraw(&TT.width, &TT.height); + } + + comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest); + comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest); + TT.match_process = shared_match_process; + + default_ko(defo, &TT.fields, "bad -o", TT.top.o); + dlist_terminate(TT.fields); + + // First (dummy) sort field is overwritten by setsort() + default_ko("-S", &TT.kfields, 0, 0); + default_ko(defk, &TT.kfields, "bad -k", TT.top.k); + dlist_terminate(TT.kfields); + setsort(TT.top.s-1); +} + +void top_main(void) +{ + sprintf(toybuf, "%cID,USER,%s%%CPU,%%MEM,TIME+,%s", FLAG(H) ? 'T' : 'P', + TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,", + FLAG(H) ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS"); + if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9; + top_setup(toybuf, "-%CPU,-ETIME,-PID"); + if (TT.top.O) { + struct ofields *field = TT.fields; + + field = field->next->next; + comma_args(TT.top.O, &field, "bad -O", parse_ko); + } + + top_common(merge_deltas); +} + +#define CLEANUP_top +#define FOR_iotop +#include "generated/flags.h" + +// Compare old and new proces lists to measure changes +static int iotop_filter(long long *oslot, long long *nslot, int milis) +{ + // Current I/O, or accumulated since process start? + if (!FLAG(a)) merge_deltas(oslot, nslot, milis); + else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000; + + return !FLAG(O)||oslot[SLOT_iobytes+!FLAG(A)]; +} + +void iotop_main(void) +{ + char *s1 = 0, *s2 = 0, *d = "D"+!!FLAG(A); + + if (FLAG(K)) TT.forcek++; + + top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d), + s2 = xmprintf("-%sIO,-ETIME,-PID",d)); + free(s1); + free(s2); + top_common(iotop_filter); +} + +// pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag +// context, so force pgrep's flags on even when building pkill standalone. +// (All the pgrep/pkill functions drop out when building ps standalone.) +#define FORCE_FLAGS +#define CLEANUP_iotop +#define FOR_pgrep +#include "generated/flags.h" + +struct regex_list { + struct regex_list *next; + regex_t reg; +}; + +static void do_pgk(struct procpid *tb) +{ + if (TT.pgrep.signal) { + if (kill(*tb->slot, TT.pgrep.signal)) { + char *s = num_to_sig(TT.pgrep.signal); + + if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal); + perror_msg("%s->%lld", s, *tb->slot); + } + } + if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) { + printf("%lld", *tb->slot); + if (FLAG(l)) + printf(" %s", tb->str+tb->offset[4]*!!FLAG(f)); + + printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n"); + } +} + +static void match_pgrep(void *p) +{ + struct procpid *tb = p; + regmatch_t match; + struct regex_list *reg; + char *name = tb->str+tb->offset[4]*!!FLAG(f); + + // Never match ourselves. + if (TT.pgrep.self == *tb->slot) return; + + if (TT.pgrep.regexes) { + for (reg = TT.pgrep.regexes; reg; reg = reg->next) { + if (regexec(®->reg, name, 1, &match, 0)) continue; + if (FLAG(x)) + if (match.rm_so || match.rm_eo!=strlen(name)) continue; + break; + } + if (!FLAG(v) == !reg) return; + } + + // pgrep should return success if there's a match. + toys.exitval = 0; + + // Repurpose a field for -c count. + TT.sortpos++; + if (FLAG(n)||FLAG(o)) { + long long ll = tb->slot[SLOT_starttime]; + + if (FLAG(o)) ll *= -1; + if (TT.time && TT.time>ll) return; + TT.time = ll; + free(TT.pgrep.snapshot); + TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf); + } else do_pgk(tb); +} + +static int pgrep_match_process(long long *slot) +{ + return !FLAG(v) == !!shared_match_process(slot); +} + +void pgrep_main(void) +{ + char **arg; + struct regex_list *reg; + + TT.pgrep.self = getpid(); + + // No signal names start with "L", so no need for "L: " in optstr. + if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L))) + error_exit("bad -L '%s'", TT.pgrep.L); + + comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest); + comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest); + comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest); + comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest); + comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest); + comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest); + comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest); + + if ((toys.optflags&(FLAG_x|FLAG_f)) || + !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u))) + if (!toys.optc) help_exit("No PATTERN"); + + if (FLAG(f)) TT.bits |= _PS_CMDLINE; + for (arg = toys.optargs; *arg; arg++) { + reg = xmalloc(sizeof(struct regex_list)); + xregcomp(®->reg, *arg, REG_EXTENDED); + reg->next = TT.pgrep.regexes; + TT.pgrep.regexes = reg; + } + TT.match_process = pgrep_match_process; + TT.show_process = match_pgrep; + + // pgrep should return failure if there are no matches. + toys.exitval = 1; + + dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps); + if (FLAG(c)) printf("%d\n", TT.sortpos); + if (TT.pgrep.snapshot) { + do_pgk(TT.pgrep.snapshot); + if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot); + } + if (TT.pgrep.d) xputc('\n'); +} + +#define CLEANUP_pgrep +#define FOR_pkill +#include "generated/flags.h" + +void pkill_main(void) +{ + char **args = toys.optargs; + + if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1; + if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM; + if (FLAG(V)) TT.tty = 1; + pgrep_main(); +} diff --git a/porting/liteos_a/toys/posix/rm.c b/porting/liteos_a/toys/posix/rm.c new file mode 100644 index 0000000..8874b54 --- /dev/null +++ b/porting/liteos_a/toys/posix/rm.c @@ -0,0 +1,109 @@ +/* rm.c - remove files + * + * Copyright 2012 Rob Landley + * + * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html + +USE_RM(NEWTOY(rm, "fiRrv[-fi]", TOYFLAG_BIN)) + +config RM + bool "rm" + default y + help + usage: rm [-fiRrv] FILE... + + Remove each argument from the filesystem. + + -f Force: remove without confirmation, no error if it doesn't exist + -i Interactive: prompt for confirmation + -rR Recursive: remove directory contents + -v Verbose +*/ + +#define FOR_rm +#include "toys.h" + +static int do_rm(struct dirtree *try) +{ + int fd=dirtree_parentfd(try), dir=S_ISDIR(try->st.st_mode), or=0, using=0; + + // Skip . and .. (yes, even explicitly on the command line: posix says to) + if (isdotdot(try->name)) return 0; + + // Intentionally fail non-recursive attempts to remove even an empty dir + // (via wrong flags to unlinkat) because POSIX says to. + if (dir && !(toys.optflags & (FLAG_r|FLAG_R))) goto skip; + + // This is either the posix section 2(b) prompt or the section 3 prompt. + if (!FLAG(f) + && (!S_ISLNK(try->st.st_mode) && faccessat(fd, try->name, W_OK, 0))) or++; + if (!(dir && try->again) && ((or && isatty(0)) || FLAG(i))) { + char *s = dirtree_path(try, 0); + + fprintf(stderr, "rm %s%s%s", or ? "ro " : "", dir ? "dir " : "", s); + free(s); + or = yesno(0); + if (!or) goto nodelete; + } + + // handle directory recursion + if (dir) { + using = AT_REMOVEDIR; + // Handle chmod 000 directories when -f + if (faccessat(fd, try->name, R_OK, 0)) { + if (FLAG(f)) wfchmodat(fd, try->name, 0700); + else goto skip; + } + if (!try->again) return DIRTREE_COMEAGAIN; + if (try->symlink) goto skip; + if (FLAG(i)) { + char *s = dirtree_path(try, 0); + + // This is the section 2(d) prompt. (Yes, posix says to prompt twice.) + fprintf(stderr, "rmdir %s", s); + free(s); + or = yesno(0); + if (!or) goto nodelete; + } + } + +skip: + if (!unlinkat(fd, try->name, using)) { + if (FLAG(v)) { + char *s = dirtree_path(try, 0); + printf("%s%s '%s'\n", toys.which->name, dir ? "dir" : "", s); + free(s); + } + } else { + if (!dir || try->symlink != (char *)2) perror_msg_raw(try->name); +nodelete: + if (try->parent) try->parent->symlink = (char *)2; + } + + return 0; +} + +void rm_main(void) +{ + char **s; + + // Can't use <1 in optstring because zero arguments with -f isn't an error + if (!toys.optc && !FLAG(f)) help_exit("Needs 1 argument"); + + for (s = toys.optargs; *s; s++) { + if (!strcmp(*s, "/")) { + error_msg("rm /. if you mean it"); + continue; + } + + // Files that already don't exist aren't errors for -f, so try a quick + // unlink now to see if it succeeds or reports that it didn't exist. + if (FLAG(f) && (!unlink(*s) || errno == ENOENT)) continue; + + // There's a race here where a file removed between the above check and + // dirtree's stat would report the nonexistence as an error, but that's + // not a normal "it didn't exist" so I'm ok with it. + + dirtree_read(*s, do_rm); + } +} diff --git a/porting/liteos_a/toys/posix/rmdir.c b/porting/liteos_a/toys/posix/rmdir.c new file mode 100644 index 0000000..d04cf0d --- /dev/null +++ b/porting/liteos_a/toys/posix/rmdir.c @@ -0,0 +1,50 @@ +/* rmdir.c - remove directory/path + * + * Copyright 2008 Rob Landley + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/rmdir.html + +USE_RMDIR(NEWTOY(rmdir, "<1(ignore-fail-on-non-empty)p", TOYFLAG_BIN)) + +config RMDIR + bool "rmdir" + default y + help + usage: rmdir [-p] [dirname...] + + Remove one or more directories. + + -p Remove path + --ignore-fail-on-non-empty Ignore failures caused by non-empty directories +*/ + +#define FOR_rmdir +#include "toys.h" + +static void do_rmdir(char *name) +{ + char *temp; + + for (;;) { + if (rmdir(name)) { + if (!FLAG(ignore_fail_on_non_empty) || errno != ENOTEMPTY) + perror_msg_raw(name); + return; + } + + // Each -p cycle back up one slash, ignoring trailing and repeated /. + + if (!toys.optflags) return; + do { + if (!(temp = strrchr(name, '/'))) return; + *temp = 0; + } while (!temp[1]); + } +} + +void rmdir_main(void) +{ + char **s; + + for (s=toys.optargs; *s; s++) do_rmdir(*s); +} diff --git a/porting/liteos_a/toys/posix/touch.c b/porting/liteos_a/toys/posix/touch.c new file mode 100644 index 0000000..5e33d7f --- /dev/null +++ b/porting/liteos_a/toys/posix/touch.c @@ -0,0 +1,85 @@ +/* touch.c : change timestamp of a file + * + * Copyright 2012 Choubey Ji + * + * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html + * + * -f is ignored for BSD/macOS compatibility. busybox/coreutils also support + * this, but only coreutils documents it in --help output. + +USE_TOUCH(NEWTOY(touch, "<1acd:fmr:t:h[!dtr]", TOYFLAG_BIN)) + +config TOUCH + bool "touch" + default y + help + usage: touch [-amch] [-d DATE] [-t TIME] [-r FILE] FILE... + + Update the access and modification times of each FILE to the current time. + + -a Change access time + -m Change modification time + -c Don't create file + -h Change symlink + -d Set time to DATE (in YYYY-MM-DDThh:mm:SS[.frac][tz] format) + -t Set time to TIME (in [[CC]YY]MMDDhhmm[.ss][frac] format) + -r Set time same as reference FILE +*/ + +#define FOR_touch +#include "toys.h" + +GLOBALS( + char *t, *r, *d; +) + +void touch_main(void) +{ + struct timespec ts[2]; + char **ss; + int fd, i; + + // use current time if no -t or -d + ts[0].tv_nsec = UTIME_NOW; + + if (FLAG(t) || FLAG(d)) { + time_t t = time(0); + unsigned nano; + + xparsedate(TT.t ? TT.t : TT.d, &t, &nano, 0); + ts->tv_sec = t; + ts->tv_nsec = nano; + } + ts[1]=ts[0]; + + if (TT.r) { + struct stat st; + + xstat(TT.r, &st); + ts[0] = st.st_atim; + ts[1] = st.st_mtim; + } + + // Which time(s) should we actually change? + i = toys.optflags & (FLAG_a|FLAG_m); + if (i && i!=(FLAG_a|FLAG_m)) ts[i!=FLAG_m].tv_nsec = UTIME_OMIT; + + // Loop through files on command line + for (ss = toys.optargs; *ss;) { + char *s = *ss++; + + if (!strcmp(s, "-")) { + if (!futimens(1, ts)) continue; + } else { + // cheat: FLAG_h is rightmost flag, so its value is 1 + if (!utimensat(AT_FDCWD, s, ts, FLAG(h)*AT_SYMLINK_NOFOLLOW)) continue; + if (FLAG(c)) continue; + if (access(s, F_OK) && (-1!=(fd = open(s, O_CREAT, 0666)))) { + close(fd); + if (toys.optflags) ss--; + continue; + } + } + perror_msg("'%s'", s); + } +} diff --git a/porting/liteos_a/toys/posix/uname.c b/porting/liteos_a/toys/posix/uname.c new file mode 100644 index 0000000..d93fc7a --- /dev/null +++ b/porting/liteos_a/toys/posix/uname.c @@ -0,0 +1,90 @@ +/* uname.c - return system name + * + * Copyright 2008 Rob Landley + * + * See http://opengroup.org/onlinepubs/9699919799/utilities/uname.html + +USE_UNAME(NEWTOY(uname, "oamvrns[+os]", TOYFLAG_BIN)) +USE_ARCH(NEWTOY(arch, 0, TOYFLAG_USR|TOYFLAG_BIN)) + +config ARCH + bool "arch" + default y + help + usage: arch + + Print machine (hardware) name, same as uname -m. + +config UNAME + bool "uname" + default y + help + usage: uname [-asnrvm] + + Print system information. + + -s System name + -n Network (domain) name + -r Kernel Release number + -v Kernel Version + -m Machine (hardware) name + -a All of the above +*/ + +#define FOR_uname +#define FORCE_FLAGS +#include "toys.h" + +// If a 32 bit x86 build environment working in a chroot under an x86-64 +// kernel returns x86_64 for -m it confuses ./configure. Special case it. + +#if defined(__i686__) +#define GROSS "i686" +#elif defined(__i586__) +#define GROSS "i586" +#elif defined(__i486__) +#define GROSS "i486" +#elif defined(__i386__) +#define GROSS "i386" +#endif + +void uname_main(void) +{ + int i, flags = toys.optflags, needspace=0; + struct utsname u; + + uname(&u); + + if (!flags) flags = FLAG_s; + for (i=0; i<5; i++) { + char *c = ((char *) &u)+(sizeof(u.sysname)*i); + + if (flags & ((1<