summaryrefslogtreecommitdiff
path: root/toys
diff options
context:
space:
mode:
Diffstat (limited to 'toys')
-rw-r--r--toys/android/README5
-rw-r--r--toys/android/getenforce.c29
-rw-r--r--toys/android/getprop.c48
-rw-r--r--toys/android/load_policy.c32
-rw-r--r--toys/android/restorecon.c47
-rw-r--r--toys/android/runcon.c27
-rw-r--r--toys/android/setenforce.c32
-rw-r--r--toys/android/setprop.c49
-rw-r--r--toys/lsb/dmesg.c54
-rw-r--r--toys/lsb/mknod.c35
-rw-r--r--toys/lsb/mktemp.c45
-rw-r--r--toys/lsb/mount.c71
-rw-r--r--toys/lsb/pidof.c2
-rw-r--r--toys/lsb/su.c12
-rw-r--r--toys/other/acpi.c14
-rw-r--r--toys/other/base64.c87
-rw-r--r--toys/other/blkid.c26
-rw-r--r--toys/other/catv.c67
-rw-r--r--toys/other/chcon.c44
-rw-r--r--toys/other/chroot.c7
-rw-r--r--toys/other/chvt.c2
-rw-r--r--toys/other/clear.c2
-rw-r--r--toys/other/dos2unix.c22
-rw-r--r--toys/other/factor.c70
-rw-r--r--toys/other/hexedit.c250
-rw-r--r--toys/other/hwclock.c136
-rw-r--r--toys/other/ifconfig.c12
-rw-r--r--toys/other/insmod.c2
-rw-r--r--toys/other/ionice.c97
-rw-r--r--toys/other/login.c7
-rw-r--r--toys/other/losetup.c2
-rw-r--r--toys/other/lsmod.c2
-rw-r--r--toys/other/lspci.c3
-rw-r--r--toys/other/makedevs.c29
-rw-r--r--toys/other/mix.c68
-rw-r--r--toys/other/modinfo.c4
-rw-r--r--toys/other/mountpoint.c49
-rw-r--r--toys/other/nbd_client.c4
-rw-r--r--toys/other/netcat.c9
-rw-r--r--toys/other/nsenter.c167
-rw-r--r--toys/other/oneit.c99
-rw-r--r--toys/other/pivot_root.c5
-rw-r--r--toys/other/pmap.c2
-rw-r--r--toys/other/readlink.c2
-rw-r--r--toys/other/reboot.c6
-rw-r--r--toys/other/reset.c23
-rw-r--r--toys/other/rfkill.c4
-rw-r--r--toys/other/rmmod.c2
-rw-r--r--toys/other/setsid.c2
-rw-r--r--toys/other/shred.c106
-rw-r--r--toys/other/stat.c12
-rw-r--r--toys/other/swapoff.c2
-rw-r--r--toys/other/swapon.c2
-rw-r--r--toys/other/sysctl.c4
-rw-r--r--toys/other/taskset.c44
-rw-r--r--toys/other/timeout.c14
-rw-r--r--toys/other/truncate.c33
-rw-r--r--toys/other/unshare.c42
-rw-r--r--toys/other/vconfig.c2
-rw-r--r--toys/other/vmstat.c10
-rw-r--r--toys/other/xxd.c65
-rw-r--r--toys/pending/README15
-rw-r--r--toys/pending/arp.c4
-rw-r--r--toys/pending/arping.c14
-rw-r--r--toys/pending/bootchartd.c3
-rw-r--r--toys/pending/brctl.c16
-rw-r--r--toys/pending/compress.c266
-rw-r--r--toys/pending/dd.c4
-rw-r--r--toys/pending/dhcp.c16
-rw-r--r--toys/pending/dhcpd.c1238
-rw-r--r--toys/pending/diff.c4
-rw-r--r--toys/pending/dumpleases.c2
-rw-r--r--toys/pending/expr.c222
-rw-r--r--toys/pending/fold.c2
-rw-r--r--toys/pending/ftpget.c2
-rw-r--r--toys/pending/groupadd.c2
-rw-r--r--toys/pending/groupdel.c2
-rw-r--r--toys/pending/ip.c34
-rw-r--r--toys/pending/mdev.c74
-rw-r--r--toys/pending/mix.c64
-rw-r--r--toys/pending/more.c98
-rw-r--r--toys/pending/netstat.c11
-rw-r--r--toys/pending/pgrep.c2
-rw-r--r--toys/pending/printf.c269
-rw-r--r--toys/pending/ps.c688
-rw-r--r--toys/pending/reset.c34
-rw-r--r--toys/pending/route.c2
-rw-r--r--toys/pending/sh.c22
-rw-r--r--toys/pending/syslogd.c4
-rw-r--r--toys/pending/tar.c47
-rw-r--r--toys/pending/tcpsvd.c4
-rw-r--r--toys/pending/top.c13
-rw-r--r--toys/pending/traceroute.c2
-rw-r--r--toys/pending/useradd.c2
-rw-r--r--toys/pending/userdel.c2
-rw-r--r--toys/posix/cat.c76
-rw-r--r--toys/posix/chgrp.c61
-rw-r--r--toys/posix/cp.c165
-rw-r--r--toys/posix/cpio.c2
-rw-r--r--toys/posix/cut.c8
-rw-r--r--toys/posix/df.c45
-rw-r--r--toys/posix/du.c33
-rw-r--r--toys/posix/env.c19
-rw-r--r--toys/posix/find.c37
-rw-r--r--toys/posix/grep.c24
-rw-r--r--toys/posix/head.c11
-rw-r--r--toys/posix/id.c90
-rw-r--r--toys/posix/ln.c2
-rw-r--r--toys/posix/ls.c222
-rw-r--r--toys/posix/mkdir.c19
-rw-r--r--toys/posix/mkfifo.c19
-rw-r--r--toys/posix/nice.c2
-rw-r--r--toys/posix/nohup.c4
-rw-r--r--toys/posix/printf.c141
-rw-r--r--toys/posix/renice.c2
-rw-r--r--toys/posix/rm.c4
-rw-r--r--toys/posix/sed.c (renamed from toys/pending/sed.c)336
-rw-r--r--toys/posix/sort.c42
-rw-r--r--toys/posix/split.c2
-rw-r--r--toys/posix/tail.c22
-rw-r--r--toys/posix/tee.c4
-rw-r--r--toys/posix/time.c3
-rw-r--r--toys/posix/touch.c99
-rw-r--r--toys/posix/true.c2
-rw-r--r--toys/posix/uniq.c2
-rw-r--r--toys/posix/uuencode.c13
-rw-r--r--toys/posix/who.c2
-rw-r--r--toys/posix/xargs.c2
128 files changed, 4680 insertions, 2170 deletions
diff --git a/toys/android/README b/toys/android/README
new file mode 100644
index 0000000..d471a20
--- /dev/null
+++ b/toys/android/README
@@ -0,0 +1,5 @@
+Android
+
+Commands primarily used by Android, not vanilla Linux. (Also SELinux stuff.)
+
+Bug Elliott Hughes <enh@google.com> about this.
diff --git a/toys/android/getenforce.c b/toys/android/getenforce.c
new file mode 100644
index 0000000..b828ce5
--- /dev/null
+++ b/toys/android/getenforce.c
@@ -0,0 +1,29 @@
+/* getenforce.c - Get the current SELinux mode
+ *
+ * Copyright 2014 The Android Open Source Project
+
+USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config GETENFORCE
+ bool "getenforce"
+ default y
+ depends on TOYBOX_SELINUX
+ help
+ usage: getenforce
+
+ Shows whether SELinux is disabled, enforcing, or permissive.
+*/
+
+#define FOR_getenforce
+#include "toys.h"
+
+void getenforce_main(void)
+{
+ if (!is_selinux_enabled()) puts("Disabled");
+ else {
+ int ret = security_getenforce();
+
+ if (ret == -1) perror_exit("Couldn't get enforcing status");
+ else puts(ret ? "Enforcing" : "Permissive");
+ }
+}
diff --git a/toys/android/getprop.c b/toys/android/getprop.c
new file mode 100644
index 0000000..09bb0f0
--- /dev/null
+++ b/toys/android/getprop.c
@@ -0,0 +1,48 @@
+/* getprop.c - Get an Android system property
+ *
+ * Copyright 2015 The Android Open Source Project
+
+USE_GETPROP(NEWTOY(getprop, ">2", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config GETPROP
+ bool "getprop"
+ default y
+ depends on TOYBOX_ON_ANDROID
+ help
+ usage: getprop [NAME [DEFAULT]]
+
+ Gets an Android system property, or lists them all.
+*/
+
+#define FOR_getprop
+#include "toys.h"
+
+#include <cutils/properties.h>
+
+GLOBALS(
+ size_t size;
+ char **nv; // name/value pairs: even=name, odd=value
+)
+
+static void add_property(char *name, char *value, void *unused)
+{
+ if (!(TT.size&31)) TT.nv = xrealloc(TT.nv, (TT.size+32)*2*sizeof(char *));
+
+ TT.nv[2*TT.size] = xstrdup(name);
+ TT.nv[1+2*TT.size++] = xstrdup(value);
+}
+
+void getprop_main(void)
+{
+ if (*toys.optargs) {
+ property_get(*toys.optargs, toybuf, toys.optargs[1] ? toys.optargs[1] : "");
+ puts(toybuf);
+ } else {
+ size_t i;
+
+ if (property_list((void *)add_property, 0)) error_exit("property_list");
+ qsort(TT.nv, TT.size, 2*sizeof(char *), qstrcmp);
+ for (i = 0; i<TT.size; i++) printf("[%s]: [%s]\n", TT.nv[i*2],TT.nv[1+i*2]);
+ if (CFG_TOYBOX_FREE) free(TT.nv);
+ }
+}
diff --git a/toys/android/load_policy.c b/toys/android/load_policy.c
new file mode 100644
index 0000000..8496736
--- /dev/null
+++ b/toys/android/load_policy.c
@@ -0,0 +1,32 @@
+/* load_policy.c - Load a policy file
+ *
+ * Copyright 2015 The Android Open Source Project
+
+USE_LOAD_POLICY(NEWTOY(load_policy, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config LOAD_POLICY
+ bool "load_policy"
+ depends on TOYBOX_SELINUX
+ default y
+ help
+ usage: load_policy FILE
+
+ Load the specified policy file.
+*/
+
+#define FOR_load_policy
+#include "toys.h"
+
+void load_policy_main(void)
+{
+ char *path = *toys.optargs;
+ int fd = xopen(path, O_RDONLY);
+ off_t policy_len = fdlength(fd);
+ char *policy_data = mmap(0, policy_len, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ close(fd);
+ if (!policy_data || security_load_policy(policy_data, policy_len) < 0)
+ perror_exit("Couldn't %s %s", policy_data ? "load" : "read", path);
+
+ munmap(policy_data, policy_len);
+}
diff --git a/toys/android/restorecon.c b/toys/android/restorecon.c
new file mode 100644
index 0000000..5ea6b3f
--- /dev/null
+++ b/toys/android/restorecon.c
@@ -0,0 +1,47 @@
+/* restorecon.c - Restore default security contexts for files
+ *
+ * Copyright 2015 The Android Open Source Project
+
+USE_RESTORECON(NEWTOY(restorecon, "<1DFnRrv", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config RESTORECON
+ bool "restorecon"
+ depends on TOYBOX_SELINUX
+ default y
+ help
+ usage: restorecon [-D] [-F] [-R] [-n] [-v] FILE...
+
+ Restores the default security contexts for the given files.
+
+ -D apply to /data/data too
+ -F force reset
+ -R recurse into directories
+ -n don't make any changes; useful with -v to see what would change
+ -v verbose: show any changes
+*/
+
+#define FOR_restorecon
+#include "toys.h"
+
+#if defined(__ANDROID__)
+#include <selinux/android.h>
+#endif
+
+void restorecon_main(void)
+{
+#if defined(__ANDROID__)
+ char **s;
+ int flags = 0;
+
+ if (toys.optflags & FLAG_D) flags |= SELINUX_ANDROID_RESTORECON_DATADATA;
+ if (toys.optflags & FLAG_F) flags |= SELINUX_ANDROID_RESTORECON_FORCE;
+ if (toys.optflags & (FLAG_R|FLAG_r))
+ flags |= SELINUX_ANDROID_RESTORECON_RECURSE;
+ if (toys.optflags & FLAG_n) flags |= SELINUX_ANDROID_RESTORECON_NOCHANGE;
+ if (toys.optflags & FLAG_v) flags |= SELINUX_ANDROID_RESTORECON_VERBOSE;
+
+ for (s = toys.optargs; *s; s++)
+ if (selinux_android_restorecon(*s, flags) < 0)
+ perror_msg("restorecon failed: %s", *s);
+#endif
+}
diff --git a/toys/android/runcon.c b/toys/android/runcon.c
new file mode 100644
index 0000000..c2f71e2
--- /dev/null
+++ b/toys/android/runcon.c
@@ -0,0 +1,27 @@
+/* runcon.c - Run command in specified security context
+ *
+ * Copyright 2015 The Android Open Source Project
+
+USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config RUNCON
+ bool "runcon"
+ depends on TOYBOX_SELINUX
+ default y
+ help
+ usage: runcon CONTEXT COMMAND [ARGS...]
+
+ Run a command in a specified security context.
+*/
+
+#define FOR_runcon
+#include "toys.h"
+
+void runcon_main(void)
+{
+ char *context = *toys.optargs;
+
+ if (setexeccon(context)) perror_exit("Could not set context to %s", context);
+
+ xexec(++toys.optargs);
+}
diff --git a/toys/android/setenforce.c b/toys/android/setenforce.c
new file mode 100644
index 0000000..8194484
--- /dev/null
+++ b/toys/android/setenforce.c
@@ -0,0 +1,32 @@
+/* setenforce.c - Set the current SELinux mode
+ *
+ * Copyright 2014 The Android Open Source Project
+
+USE_SETENFORCE(NEWTOY(setenforce, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config SETENFORCE
+ bool "setenforce"
+ default y
+ depends on TOYBOX_SELINUX
+ help
+ usage: setenforce [enforcing|permissive|1|0]
+
+ Sets whether SELinux is enforcing (1) or permissive (0).
+*/
+
+#define FOR_setenforce
+#include "toys.h"
+
+void setenforce_main(void)
+{
+ char *new = *toys.optargs;
+ int state, ret;
+
+ if (!is_selinux_enabled()) error_exit("SELinux is disabled");
+ else if (!strcmp(new, "1") || !strcasecmp(new, "enforcing")) state = 1;
+ else if (!strcmp(new, "0") || !strcasecmp(new, "permissive")) state = 0;
+ else error_exit("Invalid state: %s", new);
+
+ ret = security_setenforce(state);
+ if (ret == -1) perror_msg("Couldn't set enforcing status to '%s'", new);
+}
diff --git a/toys/android/setprop.c b/toys/android/setprop.c
new file mode 100644
index 0000000..cbcd152
--- /dev/null
+++ b/toys/android/setprop.c
@@ -0,0 +1,49 @@
+/* setprop.c - Set an Android system property
+ *
+ * Copyright 2015 The Android Open Source Project
+
+USE_SETPROP(NEWTOY(setprop, "<2>2", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config SETPROP
+ bool "setprop"
+ default y
+ depends on TOYBOX_ON_ANDROID
+ help
+ usage: setprop NAME VALUE
+
+ Sets an Android system property.
+*/
+
+#define FOR_setprop
+#include "toys.h"
+
+#include <cutils/properties.h>
+
+void setprop_main(void)
+{
+ char *name = toys.optargs[0], *value = toys.optargs[1];
+ char *p;
+ size_t name_len = strlen(name), value_len = strlen(value);
+
+ // property_set doesn't tell us why it failed, and actually can't
+ // recognize most failures (because it doesn't wait for init), so
+ // we duplicate all of init's checks here to help the user.
+
+ if (name_len >= PROP_NAME_MAX)
+ error_exit("name '%s' too long; try '%.*s'",
+ name, PROP_NAME_MAX - 1, name);
+ if (value_len >= PROP_VALUE_MAX)
+ error_exit("value '%s' too long; try '%.*s'",
+ value, PROP_VALUE_MAX - 1, value);
+
+ if (*name == '.' || name[name_len - 1] == '.')
+ error_exit("property names must not start or end with '.'");
+ if (strstr(name, ".."))
+ error_exit("'..' is not allowed in a property name");
+ for (p = name; *p; ++p)
+ if (!isalnum(*p) && !strchr("_.-", *p))
+ error_exit("invalid character '%c' in name '%s'", *p, name);
+
+ if (property_set(name, value))
+ error_msg("failed to set property '%s' to '%s'", name, value);
+}
diff --git a/toys/lsb/dmesg.c b/toys/lsb/dmesg.c
index 1003256..d608446 100644
--- a/toys/lsb/dmesg.c
+++ b/toys/lsb/dmesg.c
@@ -4,19 +4,22 @@
*
* http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dmesg.html
-USE_DMESG(NEWTOY(dmesg, "s#n#c", TOYFLAG_BIN))
+// We care that FLAG_c is 1, so keep c at the end.
+USE_DMESG(NEWTOY(dmesg, "trs#<1n#c[!tr]", TOYFLAG_BIN))
config DMESG
bool "dmesg"
default y
help
- usage: dmesg [-n level] [-s bufsize] | -c
+ usage: dmesg [-c] [-r|-t] [-n LEVEL] [-s SIZE]
Print or control the kernel ring buffer.
- -n Set kernel logging level (1-9).
- -s Size of buffer to read (in bytes), default 16384.
- -c Clear the ring buffer after printing.
+ -c Clear the ring buffer after printing
+ -n Set kernel logging LEVEL (1-9)
+ -r Raw output (with <level markers>)
+ -s Show the last SIZE many bytes
+ -t Don't print kernel's timestamps
*/
#define FOR_dmesg
@@ -31,25 +34,38 @@ GLOBALS(
void dmesg_main(void)
{
// For -n just tell kernel to which messages to keep.
- if (toys.optflags & 2) {
- if (klogctl(8, NULL, TT.level)) error_exit("klogctl");
+ if (toys.optflags & FLAG_n) {
+ if (klogctl(8, NULL, TT.level)) perror_exit("klogctl");
} else {
- int size, i, last = '\n';
- char *data;
+ char *data, *to, *from;
+ int size;
// Figure out how much data we need, and fetch it.
size = TT.size;
- if (size<2) size = 16384;
- data = xmalloc(size);
- size = klogctl(3 + (toys.optflags&1), data, size);
- if (size < 0) error_exit("klogctl");
-
- // Display data, filtering out level markers.
- for (i=0; i<size; ) {
- if (last=='\n' && data[i]=='<') i += 3;
- else xputc(last = data[i++]);
+ if (!size && 1>(size = klogctl(10, 0, 0))) perror_exit("klogctl");;
+ data = to = from = xmalloc(size+1);
+ size = klogctl(3 + (toys.optflags & FLAG_c), data, size);
+ if (size < 0) perror_exit("klogctl");
+ data[size] = 0;
+
+ // Filter out level markers and optionally time markers
+ if (!(toys.optflags & FLAG_r)) while ((from - data) < size) {
+ if (from == data || from[-1] == '\n') {
+ char *to;
+
+ if (*from == '<' && (to = strchr(from, '>'))) from = ++to;
+ if ((toys.optflags&FLAG_t) && *from == '[' && (to = strchr(from, ']')))
+ from = to+1+(to[1]==' ');
+ }
+ *(to++) = *(from++);
+ } else to = data+size;
+
+ // Write result. The odds of somebody requesting a buffer of size 3 and
+ // getting "<1>" are remote, but don't segfault if they do.
+ if (to != data) {
+ xwrite(1, data, to-data);
+ if (to[-1] != '\n') xputc('\n');
}
- if (last!='\n') xputc('\n');
if (CFG_TOYBOX_FREE) free(data);
}
}
diff --git a/toys/lsb/mknod.c b/toys/lsb/mknod.c
index bf9288a..39073fa 100644
--- a/toys/lsb/mknod.c
+++ b/toys/lsb/mknod.c
@@ -4,28 +4,42 @@
*
* http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/mknod.html
-USE_MKNOD(NEWTOY(mknod, "<2>4", TOYFLAG_BIN))
+USE_MKNOD(NEWTOY(mknod, "<2>4m(mode):"USE_MKNOD_Z("Z:"), TOYFLAG_BIN|TOYFLAG_UMASK))
config MKNOD
bool "mknod"
default y
help
- usage: mknod NAME TYPE [MAJOR MINOR]
+ usage: mknod [-m MODE] NAME TYPE [MAJOR MINOR]
- Create a special file NAME with a given type, possible types are
- b block device
- c or u character device
- p named pipe (ignores MAJOR/MINOR)
+ Create a special file NAME with a given type. TYPE is b for block device,
+ c or u for character device, p for named pipe (which ignores MAJOR/MINOR).
+
+ -m Mode (file permissions) of new device, in octal or u+x format
+
+config MKNOD_Z
+ bool
+ default y
+ depends on MKNOD && !TOYBOX_LSM_NONE
+ help
+ usage: mknod [-Z CONTEXT] ...
+
+ -Z Set security context to created file
*/
#define FOR_mknod
#include "toys.h"
+GLOBALS(
+ char *arg_context;
+ char *m;
+)
+
void mknod_main(void)
{
mode_t modes[] = {S_IFIFO, S_IFCHR, S_IFCHR, S_IFBLK};
int major=0, minor=0, type;
- int mode = 0660;
+ int mode = TT.m ? string_to_mode(TT.m, 0777) : 0660;
type = stridx("pcub", *toys.optargs[1]);
if (type == -1) perror_exit("bad type '%c'", *toys.optargs[1]);
@@ -36,6 +50,9 @@ void mknod_main(void)
minor = atoi(toys.optargs[3]);
}
- if (mknod(toys.optargs[0], mode | modes[type], makedev(major, minor)))
- perror_exit("mknod %s failed", toys.optargs[0]);
+ if (toys.optflags & FLAG_Z)
+ if (-1 == lsm_set_create(TT.arg_context))
+ perror_exit("-Z '%s' failed", TT.arg_context);
+ if (mknod(*toys.optargs, mode|modes[type], makedev(major, minor)))
+ perror_exit("%s", *toys.optargs);
}
diff --git a/toys/lsb/mktemp.c b/toys/lsb/mktemp.c
index c1175fe..cee62c1 100644
--- a/toys/lsb/mktemp.c
+++ b/toys/lsb/mktemp.c
@@ -4,7 +4,7 @@
*
* http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/mktemp.html
-USE_MKTEMP(NEWTOY(mktemp, ">1q(directory)d(tmpdir)p:", TOYFLAG_BIN))
+USE_MKTEMP(NEWTOY(mktemp, ">1qd(directory)p(tmpdir):", TOYFLAG_BIN))
config MKTEMP
bool "mktemp"
@@ -12,41 +12,42 @@ config MKTEMP
help
usage: mktemp [-dq] [-p DIR] [TEMPLATE]
- Safely create new file and print its name. Default TEMPLATE is
- /tmp/tmp.XXXXXX and each trailing X is replaced with random char.
+ Safely create a new file "DIR/TEMPLATE" and print its name.
- -d, --directory Create directory instead of file
- -p DIR, --tmpdir=DIR Put new file in DIR
- -q Quiet
+ -d Create directory instead of file (--directory)
+ -p Put new file in DIR (--tmpdir)
+ -q Quiet, no error messages
+
+ Each X in TEMPLATE is replaced with a random printable character. The
+ default TEMPLATE is tmp.XXXXXX, and the default DIR is $TMPDIR if set,
+ else "/tmp".
*/
#define FOR_mktemp
#include "toys.h"
GLOBALS(
- char * tmpdir;
+ char *tmpdir;
)
void mktemp_main(void)
{
- int d_flag = toys.optflags & FLAG_d;
- char *tmp;
+ int d_flag = toys.optflags & FLAG_d;
+ char *template = *toys.optargs;
- tmp = *toys.optargs;
+ if (!template) template = "tmp.XXXXXX";
- if (!tmp) {
- if (!TT.tmpdir) TT.tmpdir = "/tmp";
- tmp = "tmp.xxxxxx";
- }
- if (TT.tmpdir) tmp = xmprintf("%s/%s", TT.tmpdir ? TT.tmpdir : "/tmp",
- *toys.optargs ? *toys.optargs : "tmp.XXXXXX");
+ if (!TT.tmpdir) TT.tmpdir = getenv("TMPDIR");
+ if (!TT.tmpdir || !*TT.tmpdir) TT.tmpdir = "/tmp";
- if (d_flag ? mkdtemp(tmp) == NULL : mkstemp(tmp) == -1)
- if (toys.optflags & FLAG_q)
- perror_exit("Failed to create temporary %s",
- d_flag ? "directory" : "file");
+ template = strchr(template, '/') ? xstrdup(template)
+ : xmprintf("%s/%s", TT.tmpdir, template);
- xputs(tmp);
+ if (d_flag ? !mkdtemp(template) : mkstemp(template) == -1) {
+ if (toys.optflags & FLAG_q) toys.exitval = 1;
+ else perror_exit("Failed to create %s %s/%s",
+ d_flag ? "directory" : "file", TT.tmpdir, template);
+ } else xputs(template);
- if (CFG_TOYBOX_FREE && TT.tmpdir) free(tmp);
+ if (CFG_TOYBOX_FREE) free(template);
}
diff --git a/toys/lsb/mount.c b/toys/lsb/mount.c
index 01f5c32..789d9a5 100644
--- a/toys/lsb/mount.c
+++ b/toys/lsb/mount.c
@@ -6,8 +6,8 @@
* Note: -hV is bad spec, haven't implemented -FsLU yet
* no mtab (/proc/mounts does it) so -n is NOP.
-USE_MOUNT(NEWTOY(mount, "?O:afnrvwt:o*[-rw]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
-USE_NFSMOUNT(NEWTOY(nfsmount, "?<2>2", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
+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"
@@ -34,13 +34,13 @@ config MOUNT
to say --bind or --loop. You can also "mount -a /path" to mount everything
in /etc/fstab under /path, even if it's noauto.
-config NFSMOUNT
- bool "nfsmount"
- default n
- help
- usage: nfsmount SHARE DIR
-
- Invoke an eldrich horror from the dawn of time.
+#config NFSMOUNT
+# bool "nfsmount"
+# default n
+# help
+# usage: nfsmount SHARE DIR
+#
+# Invoke an eldrich horror from the dawn of time.
*/
#define FOR_mount
@@ -56,6 +56,7 @@ GLOBALS(
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)
@@ -144,7 +145,8 @@ static void mount_filesystem(char *dev, char *dir, char *type,
if (getuid()) {
if (TT.okuser) TT.okuser = 0;
else {
- error_msg("'%s' not user mountable in fstab");
+ error_msg("'%s' not user mountable in fstab", dev);
+
return;
}
}
@@ -169,6 +171,8 @@ static void mount_filesystem(char *dev, char *dir, char *type,
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;
@@ -192,6 +196,14 @@ static void mount_filesystem(char *dev, char *dir, char *type,
for (;;) {
rc = mount(dev, dir, type, flags, opts);
if ((rc != EACCES && rc != EROFS) || (flags & MS_RDONLY)) break;
+ if (rc == EROFS && fd == -1) {
+ if (-1 != (fd = open(dev, O_RDONLY))) {
+ ioctl(fd, BLKROSET, &ro);
+ close(fd);
+
+ continue;
+ }
+ }
fprintf(stderr, "'%s' is read-only", dev);
flags |= MS_RDONLY;
}
@@ -246,8 +258,9 @@ void mount_main(void)
char *opts = 0, *dev = 0, *dir = 0, **ss;
long flags = MS_SILENT;
struct arg_list *o;
- struct mtab_list *mtl, *mm, *remount = 0;
+ struct mtab_list *mtl, *mtl2 = 0, *mm, *remount;
+// TODO
// remount
// - overmounts
// shared subtree
@@ -275,9 +288,13 @@ void mount_main(void)
if ((toys.optflags & FLAG_a) && dir) error_exit("-a with >1 arg");
// For remount we need _last_ match (in case of overmounts), so traverse
- // in reverse order.
- if (comma_scan(opts, "remount", 1))
- remount = dlist_terminate(mtl = xgetmountlist("/proc/mounts"));
+ // 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", 1);
+ if (((toys.optflags & 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
@@ -286,12 +303,14 @@ void mount_main(void)
for (mm = remount ? remount : mtl; mm; mm = (remount ? mm->prev : mm->next))
{
- int aflags, noauto, len;
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 (toys.optflags & FLAG_a) {
// "mount -a /path" to mount all entries under /path
if (dev) {
@@ -307,17 +326,29 @@ void mount_main(void)
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.
- TT.okuser = comma_scan(mm->opts, "user", 1);
- aflags = flag_opts(mm->opts, flags, &aopts);
- aflags = flag_opts(opts, aflags, &aopts);
+ 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);
+ mount_filesystem(mm->device, mm->dir, mm->type, aflags, aopts);
+ } // TODO else if (getuid()) error_msg("already there") ?
free(aopts);
if (!(toys.optflags & FLAG_a)) break;
}
- if (CFG_TOYBOX_FREE) llist_traverse(mtl, free);
+ if (CFG_TOYBOX_FREE) {
+ llist_traverse(mtl, free);
+ llist_traverse(mtl2, free);
+ }
if (!mm && !(toys.optflags & FLAG_a))
error_exit("'%s' not in %s", dir ? dir : dev,
remount ? "/proc/mounts" : "fstab");
diff --git a/toys/lsb/pidof.c b/toys/lsb/pidof.c
index 51b742f..a8fc8ef 100644
--- a/toys/lsb/pidof.c
+++ b/toys/lsb/pidof.c
@@ -5,7 +5,7 @@
*
* http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/pidof.html
-USE_PIDOF(NEWTOY(pidof, "<1so:", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PIDOF(NEWTOY(pidof, "<1so:", TOYFLAG_BIN))
config PIDOF
bool "pidof"
diff --git a/toys/lsb/su.c b/toys/lsb/su.c
index 616541b..612daef 100644
--- a/toys/lsb/su.c
+++ b/toys/lsb/su.c
@@ -54,11 +54,13 @@ void su_main()
else name = "root";
if (!(shp = getspnam(name))) perror_exit("no '%s'", name);
- if (*shp->sp_pwdp != '$') goto deny;
- if (read_password(toybuf, sizeof(toybuf), "Password: ")) goto deny;
- passhash = crypt(toybuf, shp->sp_pwdp);
- memset(toybuf, 0, sizeof(toybuf));
- if (!passhash || strcmp(passhash, shp->sp_pwdp)) goto deny;
+ if (getuid()) {
+ if (*shp->sp_pwdp != '$') goto deny;
+ if (read_password(toybuf, sizeof(toybuf), "Password: ")) goto deny;
+ passhash = crypt(toybuf, shp->sp_pwdp);
+ memset(toybuf, 0, sizeof(toybuf));
+ if (!passhash || strcmp(passhash, shp->sp_pwdp)) goto deny;
+ }
up = xgetpwnam(name);
xsetuser(up);
diff --git a/toys/other/acpi.c b/toys/other/acpi.c
index c293e84..44fd03b 100644
--- a/toys/other/acpi.c
+++ b/toys/other/acpi.c
@@ -38,7 +38,7 @@ int read_int_at(int dirfd, char *name)
FILE *fil;
if ((fd = openat(dirfd, name, O_RDONLY)) < 0) return -1;
- fscanf(fil = xfdopen(fd, "r"), "%d", &ret);
+ if (!fscanf(fil = xfdopen(fd, "r"), "%d", &ret)) perror_exit("%s", name);
fclose(fil);
return ret;
@@ -111,10 +111,11 @@ int temp_callback(struct dirtree *tree)
int cool_callback(struct dirtree *tree)
{
int dfd=5, cur, max;
+
errno = 0;
memset(toybuf, 0, sizeof(toybuf));
- if (tree->name[0]=='.') return 0;
+ if (*tree->name == '.') return 0;
if (!tree->parent) return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
@@ -136,14 +137,11 @@ int cool_callback(struct dirtree *tree)
void acpi_main(void)
{
- if (toys.optflags & FLAG_V)
- toys.optflags = FLAG_a|FLAG_b|FLAG_c|FLAG_t;
+ if (toys.optflags & FLAG_V) toys.optflags = FLAG_a|FLAG_b|FLAG_c|FLAG_t;
if (!toys.optflags) toys.optflags = FLAG_b;
if (toys.optflags & (FLAG_a|FLAG_b))
dirtree_read("/sys/class/power_supply", acpi_callback);
- if (toys.optflags & FLAG_t)
- dirtree_read("/sys/class", temp_callback);
- if (toys.optflags & FLAG_c)
- dirtree_read("/sys/class/thermal", cool_callback);
+ if (toys.optflags & FLAG_t) dirtree_read("/sys/class", temp_callback);
+ if (toys.optflags & FLAG_c) dirtree_read("/sys/class/thermal", cool_callback);
}
diff --git a/toys/other/base64.c b/toys/other/base64.c
new file mode 100644
index 0000000..7dc6114
--- /dev/null
+++ b/toys/other/base64.c
@@ -0,0 +1,87 @@
+/* base64.c - Encode and decode base64
+ *
+ * Copyright 2014 Rob Landley <rob@landley.net>
+ *
+ * No standard
+
+USE_BASE64(NEWTOY(base64, "diw#<1[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config BASE64
+ bool "base64"
+ default y
+ help
+ usage: base64 [-di] [-w COLUMNS] [FILE...]
+
+ Encode or decode in base64.
+
+ -d decode
+ -i ignore non-alphabetic characters
+ -w wrap output at COLUMNS (default 76)
+*/
+
+#define FOR_base64
+#include "toys.h"
+
+GLOBALS(
+ long columns;
+)
+
+static void do_base64(int fd, char *name)
+{
+ int out = 0, bits = 0, x = 0, i, len;
+ char *buf = toybuf+128;
+
+ for (;;) {
+ if (!(len = xread(fd, buf, sizeof(toybuf)-128))) {
+ if (!(toys.optflags & FLAG_d)) {
+ if (bits) {
+ putchar(toybuf[out<<(6-bits)]);
+ x++;
+ }
+ while (x++&3) putchar('=');
+ if (x != 1) xputc('\n');
+ }
+
+ return;
+ }
+ for (i=0; i<len; i++) {
+ if (toys.optflags & FLAG_d) {
+ if (buf[i] == '=') return;
+
+ if ((x = stridx(toybuf, buf[i])) != -1) {
+ out = (out<<6) + x;
+ bits += 6;
+ if (bits >= 8) {
+ putchar(out >> (bits -= 8));
+ out &= (1<<bits)-1;
+ if (ferror(stdout)) perror_exit(0);
+ }
+
+ continue;
+ }
+ if (buf[i] == '\n' || (toys.optflags & FLAG_i)) continue;
+
+ break;
+ } else {
+ out = (out<<8) + buf[i];
+ bits += 8;
+ while (bits >= 6) {
+ putchar(toybuf[out >> (bits -= 6)]);
+ out &= (1<<bits)-1;
+ if (TT.columns == ++x) {
+ xputc('\n');
+ x = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+void base64_main(void)
+{
+ if (!TT.columns) TT.columns = 76;
+
+ base64_init(toybuf);
+ loopfiles(toys.optargs, do_base64);
+}
diff --git a/toys/other/blkid.c b/toys/other/blkid.c
index 5c69a1e..725f163 100644
--- a/toys/other/blkid.c
+++ b/toys/other/blkid.c
@@ -5,15 +5,23 @@
* See ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.24/libblkid-docs/api-index-full.html
USE_BLKID(NEWTOY(blkid, "<1", TOYFLAG_BIN))
-USE_BLKID(OLDTOY(fstype, blkid, "<1", TOYFLAG_BIN))
+USE_FSTYPE(NEWTOY(fstype, "<1", TOYFLAG_BIN))
config BLKID
bool "blkid"
default y
help
- usage: blkid [block device...]
+ usage: blkid DEV...
- Prints type, label and UUID of filesystem.
+ Prints type, label and UUID of filesystem on a block device or image.
+
+config FSTYPE
+ bool "fstype"
+ default y
+ help
+ usage: fstype DEV...
+
+ Prints type of filesystem on a block device or image.
*/
#define FOR_blkid
@@ -26,9 +34,8 @@ struct fstype {
};
static const struct fstype fstypes[] = {
- // ext3 = buf[1116]&4 ext4 = buf[1120]&64
- {"ext2", 0xEF53, 2, 1080, 1128, 16, 1144},
- // label actually 8/16 0x4d80 but horrible: 16 bit wide characters via
+ {"ext2", 0xEF53, 2, 1080, 1128, 16, 1144}, // keep this first for ext3/4 check
+ // NTFS label actually 8/16 0x4d80 but horrible: 16 bit wide characters via
// codepage, something called a uuid that's only 8 bytes long...
{"ntfs", 0x5346544e, 4, 3, 0x48+(8<<24), 0, 0},
@@ -124,10 +131,15 @@ void do_blkid(int fd, char *name)
printf("\"");
}
- printf(" TYPE=\"%s\"\n", fstypes[i].name);
+ printf(" TYPE=\"%s\"\n", type);
}
void blkid_main(void)
{
loopfiles(toys.optargs, do_blkid);
}
+
+void fstype_main(void)
+{
+ blkid_main();
+}
diff --git a/toys/other/catv.c b/toys/other/catv.c
deleted file mode 100644
index 62520c4..0000000
--- a/toys/other/catv.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* cat -v implementation for toybox
- *
- * Copyright (C) 2006, 2007 Rob Landley <rob@landley.net>
- *
- * See "Cat -v considered harmful" at
- * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz
-
-USE_CATV(NEWTOY(catv, "vte", TOYFLAG_USR|TOYFLAG_BIN))
-
-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_catv
-#include "toys.h"
-
-// Callback function for loopfiles()
-
-static void do_catv(int fd, char *name)
-{
- for(;;) {
- int i, len;
-
- len = read(fd, toybuf, sizeof(toybuf));
- if (len < 0) toys.exitval = EXIT_FAILURE;
- if (len < 1) break;
- for (i=0; i<len; i++) {
- char c=toybuf[i];
-
- if (c > 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);
- }
- }
-}
-
-void catv_main(void)
-{
- toys.optflags ^= FLAG_v;
- loopfiles(toys.optargs, do_catv);
-}
diff --git a/toys/other/chcon.c b/toys/other/chcon.c
new file mode 100644
index 0000000..a2bbb66
--- /dev/null
+++ b/toys/other/chcon.c
@@ -0,0 +1,44 @@
+/* chcon.c - Change file security context
+ *
+ * Copyright 2014 The Android Open Source Project
+
+USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN))
+
+config CHCON
+ bool "chcon"
+ depends on TOYBOX_SELINUX
+ default y
+ help
+ usage: chcon [-hRv] CONTEXT FILE...
+
+ Change the SELinux security context of listed file[s].
+
+ -h change symlinks instead of what they point to.
+ -R recurse into subdirectories.
+ -v verbose output.
+*/
+
+#define FOR_chcon
+#include "toys.h"
+
+int do_chcon(struct dirtree *try)
+{
+ char *path, *con = *toys.optargs;
+
+ if (!dirtree_notdotdot(try)) return 0;
+
+ path = dirtree_path(try, 0);
+ if (toys.optflags & FLAG_v) printf("chcon '%s' to %s\n", path, con);
+ if (-1 == ((toys.optflags & FLAG_h) ? lsetfilecon : setfilecon)(path, con))
+ perror_msg("'%s' to %s", path, con);
+ free(path);
+
+ return (toys.optflags & FLAG_R)*DIRTREE_RECURSE;
+}
+
+void chcon_main(void)
+{
+ char **file;
+
+ for (file = toys.optargs+1; *file; file++) dirtree_read(*file, do_chcon);
+}
diff --git a/toys/other/chroot.c b/toys/other/chroot.c
index 00fb4e7..4260d98 100644
--- a/toys/other/chroot.c
+++ b/toys/other/chroot.c
@@ -1,6 +1,11 @@
/* chroot.c - Run command in new root directory.
*
* Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * TODO: The test for root is "==" so root can trivially escape a chroot by
+ * moving it below cwd, ala mkdir("sub"); chroot("sub"); chdir("../../../..")
+ * The container guys use pivot_root() to deal with this, which does actually
+ * edit mount tree. (New option? Kernel patch?)
USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN))
@@ -20,6 +25,6 @@ void chroot_main(void)
char *binsh[] = {"/bin/sh", "-i", 0};
if (chdir(*toys.optargs) || chroot(".")) perror_exit("%s", *toys.optargs);
- if (toys.optargs[1]) xexec_optargs(1);
+ if (toys.optargs[1]) xexec(toys.optargs+1);
else xexec(binsh);
}
diff --git a/toys/other/chvt.c b/toys/other/chvt.c
index 6544265..a93327f 100644
--- a/toys/other/chvt.c
+++ b/toys/other/chvt.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 David Anders <danders@amltd.com>
-USE_CHVT(NEWTOY(chvt, "<1", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_CHVT(NEWTOY(chvt, "<1", TOYFLAG_USR|TOYFLAG_BIN))
config CHVT
bool "chvt"
diff --git a/toys/other/clear.c b/toys/other/clear.c
index 2515f73..4061ea8 100644
--- a/toys/other/clear.c
+++ b/toys/other/clear.c
@@ -15,5 +15,5 @@ config CLEAR
void clear_main(void)
{
- write(1, "\e[2J\e[H", 7);
+ xwrite(1, "\e[2J\e[H", 7);
}
diff --git a/toys/other/dos2unix.c b/toys/other/dos2unix.c
index 3e1feb0..021ba38 100644
--- a/toys/other/dos2unix.c
+++ b/toys/other/dos2unix.c
@@ -2,16 +2,25 @@
*
* Copyright 2012 Rob Landley <rob@landley.net>
-USE_DOS2UNIX(NEWTOY(dos2unix, NULL, TOYFLAG_BIN))
-USE_DOS2UNIX(OLDTOY(unix2dos, dos2unix, NULL, TOYFLAG_BIN))
+USE_DOS2UNIX(NEWTOY(dos2unix, 0, TOYFLAG_BIN))
+USE_UNIX2DOS(NEWTOY(unix2dos, 0, TOYFLAG_BIN))
config DOS2UNIX
bool "dos2unix/unix2dos"
default y
help
- usage: dos2unix/unix2dos [file...]
+ usage: dos2unix [FILE...]
- Convert newline format between dos (\r\n) and unix (just \n)
+ Convert newline format from dos "\r\n" to unix "\n".
+ If no files listed copy from stdin, "-" is a synonym for stdin.
+
+config UNIX2DOS
+ bool "unix2dos"
+ default y
+ help
+ usage: unix2dos [FILE...]
+
+ Convert newline format from unix "\n" to dos "\r\n".
If no files listed copy from stdin, "-" is a synonym for stdin.
*/
@@ -60,3 +69,8 @@ void dos2unix_main(void)
{
loopfiles(toys.optargs, do_dos2unix);
}
+
+void unix2dos_main(void)
+{
+ dos2unix_main();
+}
diff --git a/toys/other/factor.c b/toys/other/factor.c
index 9dc1cca..570270e 100644
--- a/toys/other/factor.c
+++ b/toys/other/factor.c
@@ -21,46 +21,54 @@ static void factor(char *s)
{
long l, ll;
- l = strtol(s, &s, 0);
- if (*s) {
- error_msg("%s: not integer");
- return;
- }
+ for (;;) {
+ char *err = s;
- printf("%ld:", l);
+ while(isspace(*s)) s++;
+ if (!*s) return;
- // Negative numbers have -1 as a factor
- if (l < 0) {
- printf(" -1");
- l *= -1;
- }
+ l = strtol(s, &s, 0);
+ if (*s && !isspace(*s)) {
+ error_msg("%s: not integer", err);
- // Deal with 0 and 1 (and 2 since we're here)
- if (l < 3) {
- printf(" %ld\n", l);
- return;
- }
+ return;
+ }
- // Special case factors of 2
- while (l && !(l&1)) {
- printf(" 2");
- l >>= 1;
- }
+ printf("%ld:", l);
- // test odd numbers.
- for (ll=3; ;ll += 2) {
- long lll = ll*ll;
+ // Negative numbers have -1 as a factor
+ if (l < 0) {
+ printf(" -1");
+ l *= -1;
+ }
+
+ // Nothing below 4 has factors
+ if (l < 4) {
+ printf(" %ld\n", l);
+ continue;
+ }
- if (lll>l || lll<ll) {
- if (l>1) printf(" %ld", l);
- break;
+ // Special case factors of 2
+ while (l && !(l&1)) {
+ printf(" 2");
+ l >>= 1;
}
- while (!(l%ll)) {
- printf(" %ld", ll);
- l /= ll;
+
+ // test odd numbers.
+ for (ll=3; ;ll += 2) {
+ long lll = ll*ll;
+
+ if (lll>l || lll<ll) {
+ if (l>1) printf(" %ld", l);
+ break;
+ }
+ while (!(l%ll)) {
+ printf(" %ld", ll);
+ l /= ll;
+ }
}
+ xputc('\n');
}
- xputc('\n');
}
void factor_main(void)
diff --git a/toys/other/hexedit.c b/toys/other/hexedit.c
new file mode 100644
index 0000000..1f6b42e
--- /dev/null
+++ b/toys/other/hexedit.c
@@ -0,0 +1,250 @@
+/* hexedit.c - Hexadecimal file editor
+ *
+ * Copyright 2015 Rob Landley <rob@landley.net>
+ *
+ * No standard
+
+USE_HEXEDIT(NEWTOY(hexedit, "<1>1r", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+
+config HEXEDIT
+ bool "hexedit"
+ default y
+ help
+ usage: hexedit FILENAME
+
+ Hexadecimal file editor.
+
+ -r Read only (display but don't edit)
+*/
+
+#define FOR_hexedit
+#include "toys.h"
+
+GLOBALS(
+ char *data;
+ long long len, base;
+ int numlen, undo, undolen;
+ unsigned height;
+)
+
+#define UNDO_LEN (sizeof(toybuf)/(sizeof(long long)+1))
+
+// Render all characters printable, using color to distinguish.
+static void draw_char(char broiled)
+{
+ if (broiled<32 || broiled>=127) {
+ if (broiled>127) {
+ tty_esc("2m");
+ broiled &= 127;
+ }
+ if (broiled<32 || broiled==127) {
+ tty_esc("7m");
+ if (broiled==127) broiled = 32;
+ else broiled += 64;
+ }
+ printf("%c", broiled);
+ tty_esc("0m");
+ } else printf("%c", broiled);
+}
+
+static void draw_tail(void)
+{
+ int i = 0, width = 0, w, len;
+ char *start = *toys.optargs, *end;
+
+ tty_jump(0, TT.height);
+ tty_esc("K");
+
+ // First time, make sure we fit in 71 chars (advancing start as necessary).
+ // Second time, print from start to end, escaping nonprintable chars.
+ for (i=0; i<2; i++) {
+ for (end = start; *end;) {
+ wchar_t wc;
+
+ len = mbrtowc(&wc, end, 99, 0);
+ if (len<0 || wc<32 || (w = wcwidth(wc))<0) {
+ len = w = 1;
+ if (i) draw_char(*end);
+ } else if (i) fwrite(end, len, 1, stdout);
+ end += len;
+
+ if (!i) {
+ width += w;
+ while (width > 71) {
+ len = mbrtowc(&wc, start, 99, 0);
+ if (len<0 || wc<32 || (w = wcwidth(wc))<0) len = w = 1;
+ width -= w;
+ start += len;
+ }
+ }
+ }
+ }
+}
+
+static void draw_line(long long yy)
+{
+ int x, xx = 16;
+
+ yy = (TT.base+yy)*16;
+ if (yy+xx>=TT.len) xx = TT.len-yy;
+
+ if (yy<TT.len) {
+ printf("\r%0*llX ", TT.numlen, yy);
+ for (x=0; x<xx; x++) printf(" %02X", TT.data[yy+x]);
+ printf("%*s", 2+3*(16-xx), "");
+ for (x=0; x<xx; x++) draw_char(TT.data[yy+x]);
+ printf("%*s", 16-xx, "");
+ }
+ tty_esc("K");
+}
+
+static void draw_page(void)
+{
+ int y;
+
+ tty_jump(0, 0);
+ for (y = 0; y<TT.height; y++) {
+ if (y) printf("\r\n");
+ draw_line(y);
+ }
+ draw_tail();
+}
+
+// side: 0 = editing left, 1 = editing right, 2 = clear, 3 = read only
+static void highlight(int xx, int yy, int side)
+{
+ char cc = TT.data[16*(TT.base+yy)+xx];
+ int i;
+
+ // Display cursor
+ tty_jump(2+TT.numlen+3*xx, yy);
+ tty_esc("0m");
+ if (side!=2) tty_esc("7m");
+ if (side>1) printf("%02X", cc);
+ else for (i=0; i<2;) {
+ if (side==i) tty_esc("32m");
+ printf("%X", (cc>>(4*(1&++i)))&15);
+ }
+ tty_esc("0m");
+ tty_jump(TT.numlen+17*3+xx, yy);
+ draw_char(cc);
+}
+
+void hexedit_main(void)
+{
+ long long pos = 0, y;
+ int x, i, side = 0, key, ro = toys.optflags&FLAG_r,
+ fd = xopen(*toys.optargs, ro ? O_RDONLY : O_RDWR);
+ char keybuf[16];
+
+ // Terminal setup
+ TT.height = 25;
+ terminal_size(0, &TT.height);
+ if (TT.height) TT.height--;
+ sigatexit(tty_sigreset);
+ tty_esc("0m");
+ tty_esc("?25l");
+ fflush(0);
+ set_terminal(1, 1, 0);
+
+ if ((TT.len = fdlength(fd))<0) error_exit("bad length");
+ if (sizeof(long)==32 && TT.len>SIZE_MAX) TT.len = SIZE_MAX;
+ // count file length hex in digits, rounded up to multiple of 4
+ for (pos = TT.len, TT.numlen = 0; pos; pos >>= 4, TT.numlen++);
+ TT.numlen += (4-TT.numlen)&3;
+
+ TT.data = mmap(0, TT.len, PROT_READ|(PROT_WRITE*!ro), MAP_SHARED, fd, 0);
+ draw_page();
+
+ for (;;) {
+ // Scroll display if necessary
+ if (pos<0) pos = 0;
+ if (pos>TT.len) pos = TT.len-1;
+ x = pos&15;
+ y = pos/16;
+
+ i = 0;
+ while (y<TT.base) {
+ if (TT.base-y>(TT.height/2)) {
+ TT.base = y;
+ draw_page();
+ } else {
+ TT.base--;
+ i++;
+ tty_esc("1T");
+ tty_jump(0, 0);
+ draw_line(0);
+ }
+ }
+ while (y>=TT.base+TT.height) {
+ if (y-(TT.base+TT.height)>(TT.height/2)) {
+ TT.base = y-TT.height-1;
+ draw_page();
+ } else {
+ TT.base++;
+ i++;
+ tty_esc("1S");
+ tty_jump(0, TT.height-1);
+ draw_line(TT.height-1);
+ }
+ }
+ if (i) draw_tail();
+ y -= TT.base;
+
+ // Display cursor and flush output
+ highlight(x, y, ro ? 3 : side);
+ xprintf("");
+
+ // Wait for next key
+ key = scan_key(keybuf, 1);
+ // Exit for q, ctrl-c, ctrl-d, escape, or EOF
+ if (key==-1 || key==3 || key==4 || key==27 || key=='q') break;
+ highlight(x, y, 2);
+
+ // Hex digit?
+ if (key>='a' && key<='f') key-=32;
+ if (!ro && ((key>='0' && key<='9') || (key>='A' && key<='F'))) {
+ if (!side) {
+ long long *ll = (long long *)toybuf;
+
+ ll[TT.undo] = pos;
+ toybuf[(sizeof(long long)*UNDO_LEN)+TT.undo++] = TT.data[pos];
+ if (TT.undolen < UNDO_LEN) TT.undolen++;
+ TT.undo %= UNDO_LEN;
+ }
+
+ i = key - '0';
+ if (i>9) i -= 7;
+ TT.data[pos] &= 15<<(4*side);
+ TT.data[pos] |= i<<(4*!side);
+
+ if (++side==2) {
+ highlight(x, y, side);
+ side = 0;
+ ++pos;
+ }
+ } else side = 0;
+ if (key=='u') {
+ if (TT.undolen) {
+ long long *ll = (long long *)toybuf;
+
+ TT.undolen--;
+ if (!TT.undo) TT.undo = UNDO_LEN;
+ pos = ll[--TT.undo];
+ TT.data[pos] = toybuf[sizeof(long long)*UNDO_LEN+TT.undo];
+ }
+ } else if (key==KEY_UP) pos -= 16;
+ else if (key==KEY_DOWN) pos += 16;
+ else if (key==KEY_RIGHT) {
+ if (x<15) pos++;
+ } else if (key==KEY_LEFT) {
+ if (x) pos--;
+ } else if (key==KEY_PGUP) pos -= 16*TT.height;
+ else if (key==KEY_PGDN) pos += 16*TT.height;
+ else if (key==KEY_HOME) pos = 0;
+ else if (key==KEY_END) pos = TT.len-1;
+ }
+ munmap(TT.data, TT.len);
+ close(fd);
+ tty_reset();
+}
diff --git a/toys/other/hwclock.c b/toys/other/hwclock.c
new file mode 100644
index 0000000..75e0641
--- /dev/null
+++ b/toys/other/hwclock.c
@@ -0,0 +1,136 @@
+/* hwclock.c - get and set the hwclock
+ *
+ * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
+ *
+ * No standard, but see Documentation/rtc.txt in the linux kernel source..
+ *
+USE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HWCLOCK
+ bool "hwclock"
+ default y
+ help
+ usage: hwclock [-rswtluf]
+
+ -f FILE Use specified device file instead of /dev/rtc (--rtc)
+ -l Hardware clock uses localtime (--localtime)
+ -r Show hardware clock time (--show)
+ -s Set system time from hardware clock (--hctosys)
+ -t Set the system time based on the current timezone (--systz)
+ -u Hardware clock uses UTC (--utc)
+ -w Set hardware clock from system time (--systohc)
+*/
+
+#define FOR_hwclock
+#include "toys.h"
+#include <linux/rtc.h>
+
+GLOBALS(
+ char *fname;
+
+ int utc;
+)
+
+static int rtc_find(struct dirtree* node)
+{
+ FILE *fp;
+
+ if (!node->parent) return DIRTREE_RECURSE;
+
+ snprintf(toybuf, sizeof(toybuf), "/sys/class/rtc/%s/hctosys", node->name);
+ fp = fopen(toybuf, "r");
+ if (fp) {
+ int hctosys = 0, items = fscanf(fp, "%d", &hctosys);
+
+ fclose(fp);
+ if (items == 1 && hctosys == 1) {
+ snprintf(toybuf, sizeof(toybuf), "/dev/%s", node->name);
+ TT.fname = toybuf;
+
+ return DIRTREE_ABORT;
+ }
+ }
+
+ return 0;
+}
+
+void hwclock_main()
+{
+ struct timezone tzone;
+ struct timeval timeval;
+ struct tm tm;
+ time_t time;
+ int fd = -1;
+
+ // check for Grenich Mean Time
+ if (toys.optflags & FLAG_u) TT.utc = 1;
+ else {
+ FILE *fp;
+ char *s = 0;
+
+ for (fp = fopen("/etc/adjtime", "r");
+ fp && getline(&s, (void *)toybuf, fp)>0;
+ free(s), s = 0) TT.utc += !strncmp(s, "UTC", 3);
+ if (fp) fclose(fp);
+ }
+
+ if (!(toys.optflags&FLAG_t)) {
+ int w = toys.optflags & FLAG_w, flag = O_WRONLY*w;
+
+ // Open /dev/rtc (if your system has no /dev/rtc symlink, search for it).
+ if (!TT.fname && (fd = open("/dev/rtc", flag)) == -1) {
+ dirtree_read("/sys/class/rtc", rtc_find);
+ if (!TT.fname) TT.fname = "/dev/misc/rtc";
+ }
+ if (fd == -1) fd = xopen(TT.fname, flag);
+
+ // Get current time in seconds from rtc device. todo: get subsecond time
+ if (!w) {
+ char *s = s;
+
+ xioctl(fd, RTC_RD_TIME, &tm);
+ if (TT.utc) s = xtzset("UTC0");
+ if ((time = mktime(&tm)) < 0) error_exit("mktime failed");
+ if (TT.utc) {
+ free(xtzset(s));
+ free(s);
+ }
+ }
+ }
+
+ if (toys.optflags & (FLAG_w|FLAG_t)) {
+ if (gettimeofday(&timeval, 0)) perror_exit("gettimeofday failed");
+ if (!(TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm))
+ error_exit(TT.utc ? "gmtime_r failed" : "localtime_r failed");
+ }
+
+ if (toys.optflags & FLAG_w) {
+ /* The value of tm_isdst will positive if daylight saving time is in effect,
+ * zero if it is not and negative if the information is not available.
+ * todo: so why isn't this negative...? */
+ tm.tm_isdst = 0;
+ xioctl(fd, RTC_SET_TIME, &tm);
+ } else if (toys.optflags & FLAG_s) {
+ tzone.tz_minuteswest = timezone / 60 - 60 * daylight;
+ timeval.tv_sec = time;
+ timeval.tv_usec = 0; // todo: fixit
+ } else if (toys.optflags & FLAG_t) {
+ // Adjust seconds for timezone and daylight saving time
+ // extern long timezone is defined in header sys/time.h
+ tzone.tz_minuteswest = timezone / 60;
+ if (tm.tm_isdst) tzone.tz_minuteswest -= 60;
+ if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60;
+ } else {
+ char *c = ctime(&time), *s = strrchr(c, '\n');
+
+ if (s) *s = '\0';
+ // TODO: implement this.
+ xprintf("%s 0.000000 seconds\n", c);
+ }
+ if (toys.optflags & (FLAG_t|FLAG_s)) {
+ tzone.tz_dsttime = 0;
+ if (settimeofday(&timeval, &tzone)) perror_exit("settimeofday failed");
+ }
+
+ if (fd != -1) close(fd);
+}
diff --git a/toys/other/ifconfig.c b/toys/other/ifconfig.c
index 4d5c74d..948043e 100644
--- a/toys/other/ifconfig.c
+++ b/toys/other/ifconfig.c
@@ -6,7 +6,7 @@
*
* Not in SUSv4.
-USE_IFCONFIG(NEWTOY(ifconfig, "^?a", TOYFLAG_BIN))
+USE_IFCONFIG(NEWTOY(ifconfig, "^?a", TOYFLAG_SBIN))
config IFCONFIG
bool "ifconfig"
@@ -123,7 +123,7 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
xprintf("%-9s Link encap:%s ", name, types[i].title);
if(i >= 0 && ifre.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
xprintf("HWaddr ");
- for (i=0; i<6; i++) xprintf(":%02X"+!i, ifre.ifr_hwaddr.sa_data[i]);
+ for (i=0; i<6; i++) xprintf(":%02x"+!i, ifre.ifr_hwaddr.sa_data[i]);
}
xputc('\n');
@@ -254,7 +254,7 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
xprintf("%10c", ' ');
if(ifre.ifr_map.irq) xprintf("Interrupt:%d ", ifre.ifr_map.irq);
if(ifre.ifr_map.base_addr >= 0x100) // IO_MAP_INDEX
- xprintf("Base address:0x%lx ", ifre.ifr_map.base_addr);
+ 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);
@@ -449,7 +449,7 @@ void ifconfig_main(void)
if (!argv[1]) {
toys.exithelp++;
- error_exit(*argv);
+ error_exit("%s", *argv);
}
plen = get_addrinfo(argv[1], AF_INET6, &ifre6.addr);
@@ -462,7 +462,7 @@ void ifconfig_main(void)
close(fd6);
continue;
// Iterate through table to find/perform operation
- } else for (i = 0; i < sizeof(try)/sizeof(*try); i++) {
+ } else for (i = 0; i < ARRAY_LEN(try); i++) {
struct argh *t = try+i;
int on = t->on, off = t->off;
@@ -485,7 +485,7 @@ void ifconfig_main(void)
poke((on>>16) + (char *)&ifre, l, on&15);
xioctl(TT.sockfd, off, &ifre);
break;
- } else if (t->name || !strchr(ifre.ifr_name, ':')) {
+ } else {
struct sockaddr_in *si = (struct sockaddr_in *)&ifre.ifr_addr;
int mask = -1;
diff --git a/toys/other/insmod.c b/toys/other/insmod.c
index 81721a3..cb222a5 100644
--- a/toys/other/insmod.c
+++ b/toys/other/insmod.c
@@ -2,7 +2,7 @@
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_INSMOD(NEWTOY(insmod, "<1", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+USE_INSMOD(NEWTOY(insmod, "<1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
config INSMOD
bool "insmod"
diff --git a/toys/other/ionice.c b/toys/other/ionice.c
new file mode 100644
index 0000000..e44270a
--- /dev/null
+++ b/toys/other/ionice.c
@@ -0,0 +1,97 @@
+/* ionice.c - set or get process I/O scheduling class and priority
+ *
+ * Copyright 2015 Rob Landley <rob@landley.net>
+ *
+ * It would be really nice if there was a standard, but no. There is
+ * Documentation/block/ioprio.txt in the linux source.
+
+USE_IONICE(NEWTOY(ionice, "^tc#<0>3n#<0>7=5p#", TOYFLAG_USR|TOYFLAG_BIN))
+USE_IORENICE(NEWTOY(iorenice, "?<1>3", TOYFLAG_USR|TOYFLAG_BIN))
+
+config IONICE
+ bool "ionice"
+ default y
+ help
+ usage: ionice [-t] [-c CLASS] [-n LEVEL] [COMMAND...|-p PID]
+
+ Change the I/O scheduling priority of a process. With no arguments
+ (or just -p), display process' existing I/O class/priority.
+
+ -c CLASS = 1-3: 1(realtime), 2(best-effort, default), 3(when-idle)
+ -n LEVEL = 0-7: (0 is highest priority, default = 5)
+ -p Affect existing PID instead of spawning new child
+ -t Ignore failure to set I/O priority
+
+ System default iopriority is generally -c 2 -n 4.
+
+config IORENICE
+ bool "iorenice"
+ default y
+ help
+ usage: iorenice PID [CLASS] [PRIORITY]
+
+ Display or change I/O priority of existing process. CLASS can be
+ "rt" for realtime, "be" for best effort, "idle" for only when idle, or
+ "none" to leave it alone. PRIORITY can be 0-7 (0 is highest, default 4).
+*/
+
+#define FOR_ionice
+#include "toys.h"
+#include <sys/syscall.h>
+
+GLOBALS(
+ long pid;
+ long level;
+ long class;
+)
+
+static int ioprio_get(void)
+{
+ return syscall(__NR_ioprio_get, 1, (int)TT.pid);
+}
+
+static int ioprio_set(void)
+{
+ int prio = ((int)TT.class << 13) | (int)TT.level;
+
+ return syscall(__NR_ioprio_set, 1, (int)TT.pid, prio);
+}
+
+void ionice_main(void)
+{
+ if (!TT.pid && !toys.optc) error_exit("Need -p or COMMAND");
+ if (toys.optflags == FLAG_p) {
+ int p = ioprio_get();
+ xprintf("%s: prio %d\n",
+ (char *[]){"unknown", "Realtime", "Best-effort", "Idle"}[(p>>13)&3],
+ p&7);
+ } else {
+ if (-1 == ioprio_set() && !(toys.optflags&FLAG_t)) perror_exit("set");
+ if (!TT.pid) xexec(toys.optargs);
+ }
+}
+
+void iorenice_main(void)
+{
+ char *classes[] = {"none", "rt", "be", "idle"};
+
+ TT.pid = atolx(*toys.optargs);
+ if (toys.optc == 1) {
+ int p = ioprio_get();
+
+ if (p == -1) perror_exit("read priority");
+ TT.class = (p>>13)&3;
+ p &= 7;
+ xprintf("Pid %d, class %s (%d), prio %d\n",
+ TT.pid, classes[TT.class], TT.class, p);
+ return;
+ }
+
+ for (TT.class = 0; TT.class<4; TT.class++)
+ if (!strcmp(toys.optargs[toys.optc-1], classes[TT.class])) break;
+ if (toys.optc == 3 || TT.class == 4) TT.level = atolx(toys.optargs[1]);
+ else TT.level = 4;
+ TT.class &= 3;
+
+ if (-1 == ioprio_set()) perror_exit("set");
+}
diff --git a/toys/other/login.c b/toys/other/login.c
index 91523d4..b728286 100644
--- a/toys/other/login.c
+++ b/toys/other/login.c
@@ -4,6 +4,9 @@
*
* No support for PAM/securetty/selinux/login script/issue/utmp
* Relies on libcrypt for hash calculation.
+ *
+ * TODO: this command predates "pending" but needs cleanup. It #defines
+ * random stuff, calls exit() form a signal handler... yeah.
USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN))
@@ -156,7 +159,7 @@ void login_main(void)
if (!isatty(0) || !isatty(1) || !isatty(2)) error_exit("no tty");
openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
- signal(SIGALRM, login_timeout_handler);
+ xsignal(SIGALRM, login_timeout_handler);
alarm(TT.login_timeout = 60);
for (ss = forbid; *ss; ss++) unsetenv(*ss);
@@ -165,7 +168,7 @@ void login_main(void)
tcflush(0, TCIFLUSH);
username[sizeof(username)-1] = 0;
- if (*toys.optargs) strncpy(username, *toys.optargs, sizeof(username)-1);
+ if (*toys.optargs) xstrncpy(username, *toys.optargs, sizeof(username));
else {
read_user(username, sizeof(username));
if (!*username) continue;
diff --git a/toys/other/losetup.c b/toys/other/losetup.c
index e3094ef..9568627 100644
--- a/toys/other/losetup.c
+++ b/toys/other/losetup.c
@@ -112,7 +112,7 @@ static void loopback_setup(char *device, char *file)
if (ioctl(lfd, LOOP_SET_FD, ffd)) perror_exit("%s=%s", device, file);
loop->lo_offset = TT.offset;
loop->lo_sizelimit = TT.size;
- strncpy((char *)loop->lo_file_name, s, LO_NAME_SIZE);
+ xstrncpy((char *)loop->lo_file_name, s, LO_NAME_SIZE);
s[LO_NAME_SIZE-1] = 0;
if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file);
if (flags & FLAG_s) printf("%s", device);
diff --git a/toys/other/lsmod.c b/toys/other/lsmod.c
index b8f5d82..4d16048 100644
--- a/toys/other/lsmod.c
+++ b/toys/other/lsmod.c
@@ -2,7 +2,7 @@
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_BIN))
+USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN))
config LSMOD
bool "lsmod"
diff --git a/toys/other/lspci.c b/toys/other/lspci.c
index 40e0c0a..c9b22ab 100644
--- a/toys/other/lspci.c
+++ b/toys/other/lspci.c
@@ -50,9 +50,10 @@ int do_lspci(struct dirtree *new)
if (-1 == (dirfd = openat(dirtree_parentfd(new), new->name, O_RDONLY)))
return 0;
+ // it's ok for the driver link not to be there, whatever fortify says
*driver = 0;
if (toys.optflags & FLAG_k)
- readlinkat(dirfd, "driver", driver, sizeof(driver));
+ if (readlinkat(dirfd, "driver", driver, sizeof(driver))) {};
for (fields = (char*[]){"class", "vendor", "device", 0}; *fields; fields++) {
int fd, size = 6 + 2*((toys.optflags & FLAG_e) && p == toybuf);
diff --git a/toys/other/makedevs.c b/toys/other/makedevs.c
index f6e7ece..0f0a661 100644
--- a/toys/other/makedevs.c
+++ b/toys/other/makedevs.c
@@ -41,7 +41,7 @@ GLOBALS(
void makedevs_main()
{
- int value, fd = 0, line_no, i;
+ int fd = 0, line_no, i;
char *line = NULL;
// Open file and chdir, verbosely
@@ -78,33 +78,14 @@ void makedevs_main()
continue;
} else mode |= (mode_t[]){S_IFIFO, S_IFCHR, S_IFBLK, 0, 0}[i];
- if (*user) {
- struct passwd *usr;
-
- if (!(usr = getpwnam(user)) && isdigit(*user)) {
- sscanf(user, "%u", &value);
- usr = xgetpwuid(value);
- }
- if (!usr) error_exit("bad user '%s'", user);
- uid = usr->pw_uid;
- } else uid = getuid();
-
- if (*group) {
- struct group *grp;
-
- if (!(grp = getgrnam(group)) && isdigit(*group)) {
- sscanf (group, "%u", &value);
- grp = getgrgid(value);
- }
- if (!grp) error_exit("bad group '%s'", group);
- gid = grp->gr_gid;
- } else gid = getgid();
+ uid = *user ? xgetpwnamid(user)->pw_uid : getuid();
+ gid = *group ? xgetgrnamid(group)->gr_gid : getgid();
while (*node == '/') node++; // using relative path
for (i = 0; (!cnt && !i) || i < cnt; i++) {
- if (cnt) {
- snprintf(toybuf, sizeof(toybuf), "%s%u", node, st_val + i);
+ if (cnt>1) {
+ snprintf(toybuf, sizeof(toybuf), "%.999s%u", node, st_val + i);
ptr = toybuf;
} else ptr = node;
diff --git a/toys/other/mix.c b/toys/other/mix.c
new file mode 100644
index 0000000..845aac1
--- /dev/null
+++ b/toys/other/mix.c
@@ -0,0 +1,68 @@
+/* mix.c - A very basic mixer.
+ *
+ * Copyright 2014 Brad Conroy, dedicated to the Public Domain.
+ *
+
+USE_MIX(NEWTOY(mix, "c:d:l#r#", TOYFLAG_USR|TOYFLAG_BIN))
+
+config MIX
+ bool "mix"
+ default y
+ help
+ usage: mix [-d DEV] [-c CHANNEL] [-l VOL] [-r RIGHT]
+
+ List OSS sound channels (module snd-mixer-oss), or set volume(s).
+
+ -c CHANNEL Set/show volume of CHANNEL (default first channel found)
+ -d DEV Device node (default /dev/mixer)
+ -l VOL Volume level
+ -r RIGHT Volume of right stereo channel (with -r, -l sets left volume)
+*/
+
+#define FOR_mix
+#include "toys.h"
+#include <linux/soundcard.h>
+
+GLOBALS(
+ long right;
+ long level;
+ char *dev;
+ char *chan;
+)
+
+void mix_main(void)
+{
+ const char *channels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
+ int mask, channel = -1, level, fd;
+
+ if (!TT.dev) TT.dev = "/dev/mixer";
+ fd = xopen(TT.dev, O_RDWR|O_NONBLOCK);
+ xioctl(fd, SOUND_MIXER_READ_DEVMASK, &mask);
+
+ for (channel = 0; channel < SOUND_MIXER_NRDEVICES; channel++) {
+ if ((1<<channel) & mask) {
+ if (TT.chan) {
+ if (!strcmp(channels[channel], TT.chan)) break;
+ } else if (toys.optflags & FLAG_l) break;
+ else printf("%s\n", channels[channel]);
+ }
+ }
+
+ if (!(toys.optflags & (FLAG_c|FLAG_l))) return;
+ else if (channel == SOUND_MIXER_NRDEVICES) error_exit("bad -c '%s'", TT.chan);
+
+ if (!(toys.optflags & FLAG_l)) {
+ xioctl(fd, MIXER_READ(channel), &level);
+ if (level > 0xFF)
+ xprintf("%s:%s = left:%d\t right:%d\n",
+ TT.dev, channels[channel], level>>8, level & 0xFF);
+ else xprintf("%s:%s = %d\n", TT.dev, channels[channel], level);
+ } else {
+ level = TT.level;
+ if (!(toys.optflags & FLAG_r)) level = TT.right | (level<<8);
+
+ xioctl(fd, MIXER_WRITE(channel), &level);
+ }
+
+ if (CFG_TOYBOX_FREE) close(fd);
+}
diff --git a/toys/other/modinfo.c b/toys/other/modinfo.c
index f572a99..3a7e821 100644
--- a/toys/other/modinfo.c
+++ b/toys/other/modinfo.c
@@ -1,6 +1,8 @@
/* modinfo.c - Display module info
*
* Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ *
+ * TODO: cleanup
USE_MODINFO(NEWTOY(modinfo, "<1b:k:F:0", TOYFLAG_BIN))
@@ -27,7 +29,7 @@ GLOBALS(
static void output_field(char *field, char *value)
{
- if (!TT.field) xprintf("%s:%*c", field, 15 - strlen(field), ' ');
+ if (!TT.field) xprintf("%s:%*c", field, 15-(int)strlen(field), ' ');
else if (strcmp(TT.field, field)) return;
xprintf("%s", value);
xputc((toys.optflags & FLAG_0) ? 0 : '\n');
diff --git a/toys/other/mountpoint.c b/toys/other/mountpoint.c
index 29b8ae6..150865c 100644
--- a/toys/other/mountpoint.c
+++ b/toys/other/mountpoint.c
@@ -2,7 +2,7 @@
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_MOUNTPOINT(NEWTOY(mountpoint, "<1qdx", TOYFLAG_BIN))
+USE_MOUNTPOINT(NEWTOY(mountpoint, "<1qdx[-dx]", TOYFLAG_BIN))
config MOUNTPOINT
bool "mountpoint"
@@ -19,36 +19,45 @@ config MOUNTPOINT
#define FOR_mountpoint
#include "toys.h"
+static void die(char *gripe)
+{
+ if (!(toys.optflags & FLAG_q)) printf("%s: not a %s\n", *toys.optargs, gripe);
+
+ toys.exitval++;
+ xexit();
+}
+
void mountpoint_main(void)
{
struct stat st1, st2;
- int res = 0;
+ char *arg = *toys.optargs;
int quiet = toys.optflags & FLAG_q;
- toys.exitval = 1; // be pessimistic
- strncpy(toybuf, toys.optargs[0], sizeof(toybuf));
- if (((toys.optflags & FLAG_x) && lstat(toybuf, &st1)) || stat(toybuf, &st1))
- perror_exit("%s", toybuf);
- if (toys.optflags & FLAG_x){
+ if (lstat(arg, &st1)) perror_exit("%s", arg);
+
+ if (toys.optflags & FLAG_x) {
if (S_ISBLK(st1.st_mode)) {
if (!quiet) printf("%u:%u\n", major(st1.st_rdev), minor(st1.st_rdev));
- toys.exitval = 0;
+
return;
}
- if (!quiet) printf("%s: not a block device\n", toybuf);
- return;
+ die("block device");
}
- if(!S_ISDIR(st1.st_mode)){
- if (!quiet) printf("%s: not a directory\n", toybuf);
- return;
- }
- strncat(toybuf, "/..", sizeof(toybuf));
- stat(toybuf, &st2);
- res = (st1.st_dev != st2.st_dev) ||
- (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
- if (!quiet) printf("%s is %sa mountpoint\n", toys.optargs[0], res ? "" : "not ");
+ // TODO: Ignore the fact a file can be a mountpoint for --bind mounts.
+ if (!S_ISDIR(st1.st_mode)) die("directory");
+
+ arg = xmprintf("%s/..", arg);
+ xstat(arg, &st2);
+ if (CFG_TOYBOX_FREE) free(arg);
+
+ // If the device is different, it's a mount point. If the device _and_
+ // inode are the same, it's probably "/". This misses --bind mounts from
+ // elsewhere in the same filesystem, but so does the other one and in the
+ // absence of a spec I guess that's the expected behavior?
+ toys.exitval = !(st1.st_dev != st2.st_dev || st1.st_ino == st2.st_ino);
if (toys.optflags & FLAG_d)
printf("%u:%u\n", major(st1.st_dev), minor(st1.st_dev));
- toys.exitval = res ? 0 : 1;
+ else if (!quiet)
+ printf("%s is %sa mountpoint\n", *toys.optargs, toys.exitval ? "not " : "");
}
diff --git a/toys/other/nbd_client.c b/toys/other/nbd_client.c
index 6b437c7..c16585a 100644
--- a/toys/other/nbd_client.c
+++ b/toys/other/nbd_client.c
@@ -8,7 +8,7 @@
// things like prototype "nbd-client_main" which isn't a valid symbol. So
// we hide the underscore name and OLDTOY the name we want.
USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3ns", 0))
-USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, OPTSTR_nbd_client, TOYFLAG_USR|TOYFLAG_BIN))
+USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, TOYFLAG_USR|TOYFLAG_BIN))
config NBD_CLIENT
bool "nbd-client"
@@ -112,7 +112,7 @@ void nbd_client_main(void)
// Daemonize here.
- daemon(0,0);
+ if (daemon(0,0)) perror_exit("daemonize");
// Process NBD requests until further notice.
diff --git a/toys/other/netcat.c b/toys/other/netcat.c
index 2c1ec7b..4d70d17 100644
--- a/toys/other/netcat.c
+++ b/toys/other/netcat.c
@@ -4,8 +4,8 @@
*
* TODO: udp, ipv6, genericize for telnet/microcom/tail-f
-USE_NETCAT(OLDTOY(nc, netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
-USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
+USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
+USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#p#s:q#f:", TOYFLAG_BIN))
config NETCAT
bool "netcat"
@@ -55,12 +55,13 @@ GLOBALS(
static void timeout(int signum)
{
if (TT.wait) error_exit("Timeout");
+ // This should be xexit() but would need siglongjmp()...
exit(0);
}
static void set_alarm(int seconds)
{
- signal(SIGALRM, seconds ? timeout : SIG_DFL);
+ xsignal(SIGALRM, seconds ? timeout : SIG_DFL);
alarm(seconds);
}
@@ -186,7 +187,7 @@ void netcat_main(void)
set_alarm(0);
if (CFG_NETCAT_LISTEN && (toys.optflags&(FLAG_L|FLAG_l) && toys.optc))
- xexec_optargs(0);
+ xexec(toys.optargs);
// Poll loop copying stdin->socket and socket->stdout.
for (;;) {
diff --git a/toys/other/nsenter.c b/toys/other/nsenter.c
new file mode 100644
index 0000000..18a2cd2
--- /dev/null
+++ b/toys/other/nsenter.c
@@ -0,0 +1,167 @@
+/* nsenter.c - Enter existing namespaces
+ *
+ * Copyright 2014 andy Lutomirski <luto@amacapital.net>
+ *
+ * No standard
+ *
+ * unshare.c - run command in new context
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * No Standard
+ *
+
+// Note: flags go in same order (right to left) for shared subset
+USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
+USE_UNSHARE(NEWTOY(unshare, "<1^rimnpuU", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UNSHARE
+ bool "unshare"
+ default y
+ depends on TOYBOX_CONTAINER
+ help
+ usage: unshare [-imnpuUr] COMMAND...
+
+ Create new container namespace(s) for this process and its children, so
+ some attribute is not shared with the parent process.
+
+ -i SysV IPC (message queues, semaphores, shared memory)
+ -m Mount/unmount tree
+ -n Network address, sockets, routing, iptables
+ -p Process IDs and init
+ -r Become root (map current euid/egid to 0/0, implies -U)
+ -u Host and domain names
+ -U UIDs, GIDs, capabilities
+
+ A namespace allows a set of processes to have a different view of the
+ system than other sets of processes.
+
+config NSENTER
+ bool "nsenter"
+ default y
+ help
+ usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND...
+
+ Run COMMAND in an existing (set of) namespace(s).
+
+ -t PID to take namespaces from (--target)
+ -F don't fork, even if -p is used (--no-fork)
+
+ The namespaces to switch are:
+
+ -i SysV IPC: message queues, semaphores, shared memory (--ipc)
+ -m Mount/unmount tree (--mnt)
+ -n Network address, sockets, routing, iptables (--net)
+ -p Process IDs and init, will fork unless -F is used (--pid)
+ -u Host and domain names (--uts)
+ -U UIDs, GIDs, capabilities (--user)
+
+ If -t isn't specified, each namespace argument must provide a path
+ to a namespace file, ala "-i=/proc/$PID/ns/ipc"
+*/
+
+#define FOR_nsenter
+#include "toys.h"
+#include <linux/sched.h>
+int unshare(int flags);
+int setns(int fd, int nstype);
+
+GLOBALS(
+ char *nsnames[6];
+ long targetpid;
+)
+
+// Code that must run in unshare's flag context
+#define CLEANUP_nsenter
+#define FOR_unshare
+#include <generated/flags.h>
+
+static void write_ugid_map(char *map, unsigned eugid)
+{
+ int bytes = sprintf(toybuf, "0 %u 1", eugid), fd = xopen(map, O_WRONLY);
+
+ xwrite(fd, toybuf, bytes);
+ xclose(fd);
+}
+
+static void handle_r(int euid, int egid)
+{
+ int fd;
+
+ if ((fd = open("/proc/self/setgroups", O_WRONLY)) >= 0) {
+ xwrite(fd, "deny", 4);
+ close(fd);
+ }
+
+ write_ugid_map("/proc/self/uid_map", euid);
+ write_ugid_map("/proc/self/gid_map", egid);
+}
+
+static int test_r()
+{
+ return toys.optflags & FLAG_r;
+}
+
+// Shift back to the context GLOBALS lives in (I.E. matching the filename).
+#define CLEANUP_unshare
+#define FOR_nsenter
+#include <generated/flags.h>
+
+void unshare_main(void)
+{
+ unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET,
+ CLONE_NEWNS, CLONE_NEWIPC}, f = 0;
+ int i, fd;
+
+ // Create new namespace(s)?
+ if (CFG_UNSHARE && *toys.which->name=='u') {
+ // For -r, we have to save our original [ug]id before calling unshare()
+ int euid = geteuid(), egid = getegid();
+
+ // unshare -U does not imply -r, so we cannot use [+rU]
+ if (test_r()) toys.optflags |= FLAG_U;
+
+ for (i = 0; i<ARRAY_LEN(flags); i++)
+ if (toys.optflags & (1<<i)) f |= flags[i];
+
+ if (unshare(f)) perror_exit(0);
+ if (test_r()) handle_r(euid, egid);
+
+ // Bind to existing namespace(s)?
+ } else if (CFG_NSENTER) {
+ char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc";
+
+ for (i = 0; i<ARRAY_LEN(flags); i++) {
+ char *filename = TT.nsnames[i];
+
+ if (toys.optflags & (1<<i)) {
+ if (!filename || !*filename) {
+ if (!(toys.optflags & FLAG_t)) error_exit("need -t or =filename");
+ sprintf(toybuf, "/proc/%ld/ns/%s", TT.targetpid, nsnames);
+ filename = toybuf;
+ }
+
+ if (setns(fd = xopen(filename, O_RDONLY), flags[i]))
+ perror_exit("setns");
+ close(fd);
+ }
+ nsnames += strlen(nsnames)+1;
+ }
+
+ if ((toys.optflags & FLAG_p) && !(toys.optflags & FLAG_F)) {
+ pid_t pid = xfork();
+
+ if (pid) {
+ while (waitpid(pid, 0, 0) == -1 && errno == EINTR);
+ return;
+ }
+ }
+ }
+
+ xexec(toys.optargs);
+}
+
+void nsenter_main(void)
+{
+ unshare_main();
+}
diff --git a/toys/other/oneit.c b/toys/other/oneit.c
index 72395cc..8e4b713 100644
--- a/toys/other/oneit.c
+++ b/toys/other/oneit.c
@@ -2,7 +2,7 @@
*
* Copyright 2005, 2007 by Rob Landley <rob@landley.net>.
-USE_ONEIT(NEWTOY(oneit, "^<1c:p", TOYFLAG_SBIN))
+USE_ONEIT(NEWTOY(oneit, "^<1nc:p3[!pn]", TOYFLAG_SBIN))
config ONEIT
bool "oneit"
@@ -10,16 +10,18 @@ config ONEIT
help
usage: oneit [-p] [-c /dev/tty0] command [...]
- A simple init program that runs a single supplied command line with a
+ Simple init program that runs a single supplied command line with a
controlling tty (so CTRL-C can kill it).
+ -c Which console device to use (/dev/console doesn't do CTRL-C, etc).
-p Power off instead of rebooting when command exits.
- -c Which console device to use.
+ -r Restart child when it exits.
+ -3 Write 32 bit PID of each exiting reparented process to fd 3 of child.
+ (Blocking writes, child must read to avoid eventual deadlock.)
- The oneit command runs the supplied command line as a child process
- (because PID 1 has signals blocked), attached to /dev/tty0, in its
- own session. Then oneit reaps zombies until the child exits, at
- which point it reboots (or with -p, powers off) the system.
+ Spawns a single child process (because PID 1 has signals blocked)
+ in its own session, reaps zombies until the child exits, then
+ reboots the system (or powers off with -p, or restarts the child with -r).
*/
#define FOR_oneit
@@ -40,37 +42,70 @@ GLOBALS(
// PID 1 then reaps zombies until the child process it spawned exits, at which
// point it calls sync() and reboot(). I could stick a kill -1 in there.
+// Perform actions in response to signals. (Only root can send us signals.)
+static void oneit_signaled(int signal)
+{
+ int action = RB_AUTOBOOT;
+
+ toys.signal = signal;
+ if (signal == SIGUSR1) action = RB_HALT_SYSTEM;
+ if (signal == SIGUSR2) action = RB_POWER_OFF;
+
+ // PID 1 can't call reboot() because it kills the task that calls it,
+ // which causes the kernel to panic before the actual reboot happens.
+ sync();
+ if (!vfork()) reboot(action);
+}
void oneit_main(void)
{
- int i;
- pid_t pid;
-
- // Create a new child process.
- pid = vfork();
- if (pid) {
-
- // pid 1 just reaps zombies until it gets its child, then halts the system.
- while (pid != wait(&i));
- sync();
-
- // PID 1 can't call reboot() because it kills the task that calls it,
- // which causes the kernel to panic before the actual reboot happens.
- if (!vfork()) reboot((toys.optflags & FLAG_p) ? RB_POWER_OFF : RB_AUTOBOOT);
- sleep(5);
- _exit(1);
+ int i, pid, pipes[] = {SIGUSR1, SIGUSR2, SIGTERM, SIGINT};
+
+ if (FLAG_3) {
+ // Ensure next available filehandle is #3
+ while (open("/", 0) < 3);
+ close(3);
+ close(4);
+ if (pipe(pipes)) perror_exit("pipe");
+ fcntl(4, F_SETFD, FD_CLOEXEC);
}
- // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works.
- setsid();
- for (i=0; i<3; i++) {
- close(i);
- // Remember, O_CLOEXEC is backwards for xopen()
- xopen(TT.console ? TT.console : "/dev/tty0", O_RDWR|O_CLOEXEC);
+ // Setup signal handlers for signals of interest
+ for (i = 0; i<ARRAY_LEN(pipes); i++) xsignal(pipes[i], oneit_signaled);
+
+ while (!toys.signal) {
+
+ // Create a new child process.
+ pid = vfork();
+ if (pid) {
+
+ // pid 1 reaps zombies until it gets its child, then halts system.
+ // We ignore the return value of write (what would we do with it?)
+ // but save it in a variable we never read to make fortify shut up.
+ // (Real problem is if pid2 never reads, write() fills pipe and blocks.)
+ while (pid != wait(&i)) if (FLAG_3) i = write(4, &pid, 4);
+ if (toys.optflags & FLAG_n) continue;
+
+ oneit_signaled((toys.optflags & FLAG_p) ? SIGUSR2 : SIGTERM);
+ } else {
+ // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works.
+ setsid();
+ for (i=0; i<3; i++) {
+ close(i);
+ // Remember, O_CLOEXEC is backwards for xopen()
+ xopen(TT.console ? TT.console : "/dev/tty0", O_RDWR|O_CLOEXEC);
+ }
+
+ // Can't xexec() here, we vforked so we don't want to error_exit().
+ toy_exec(toys.optargs);
+ execvp(*toys.optargs, toys.optargs);
+ perror_msg("%s not in PATH=%s", *toys.optargs, getenv("PATH"));
+
+ break;
+ }
}
- // Can't xexec() here, because we vforked so we don't want to error_exit().
- toy_exec(toys.optargs);
- execvp(*toys.optargs, toys.optargs);
+ // Give reboot() time to kick in, or avoid rapid spinning if exec failed
+ sleep(5);
_exit(127);
}
diff --git a/toys/other/pivot_root.c b/toys/other/pivot_root.c
index 3e4beac..7748032 100644
--- a/toys/other/pivot_root.c
+++ b/toys/other/pivot_root.c
@@ -2,7 +2,7 @@
*
* Copyright 2012 Rob Landley <rob@landley.net>
-USE_PIVOT_ROOT(NEWTOY(pivot_root, "<2>2", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PIVOT_ROOT(NEWTOY(pivot_root, "<2>2", TOYFLAG_SBIN))
config PIVOT_ROOT
bool "pivot_root"
@@ -22,7 +22,8 @@ config PIVOT_ROOT
#define FOR_pivot_root
#include "toys.h"
-#include <linux/unistd.h>
+#include <sys/syscall.h>
+#include <unistd.h>
void pivot_root_main(void)
{
diff --git a/toys/other/pmap.c b/toys/other/pmap.c
index ab0b61c..a93ea3e 100644
--- a/toys/other/pmap.c
+++ b/toys/other/pmap.c
@@ -53,7 +53,7 @@ void pmap_main(void)
if ((toys.optflags & (FLAG_q|FLAG_x)) == FLAG_x)
xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n",
- (sizeof(long)*2)-4, ' ');
+ (int)(sizeof(long)*2)-4, ' ');
// Loop through mappings
for (;;) {
diff --git a/toys/other/readlink.c b/toys/other/readlink.c
index 1c33362..fecd1ef 100644
--- a/toys/other/readlink.c
+++ b/toys/other/readlink.c
@@ -2,7 +2,7 @@
*
* Copyright 2007 Rob Landley <rob@landley.net>
-USE_READLINK(NEWTOY(readlink, "<1>1fenq[-fe]", TOYFLAG_BIN))
+USE_READLINK(NEWTOY(readlink, "<1>1fenq[-fe]", TOYFLAG_USR|TOYFLAG_BIN))
config READLINK
bool "readlink"
diff --git a/toys/other/reboot.c b/toys/other/reboot.c
index 5cbc4f8..a135888 100644
--- a/toys/other/reboot.c
+++ b/toys/other/reboot.c
@@ -2,9 +2,9 @@
*
* Copyright 2013 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_REBOOT(NEWTOY(reboot, "n", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
-USE_REBOOT(OLDTOY(halt, reboot, "n", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
-USE_REBOOT(OLDTOY(poweroff, reboot, "n", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+USE_REBOOT(NEWTOY(reboot, "n", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
+USE_REBOOT(OLDTOY(halt, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
+USE_REBOOT(OLDTOY(poweroff, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
config REBOOT
bool "reboot"
diff --git a/toys/other/reset.c b/toys/other/reset.c
new file mode 100644
index 0000000..0c2089c
--- /dev/null
+++ b/toys/other/reset.c
@@ -0,0 +1,23 @@
+/* reset.c - reset the terminal.
+ *
+ * Copyright 2015 Rob Landley <rob@landley.net>
+ *
+ * No standard.
+
+USE_RESET(NEWTOY(reset, 0, TOYFLAG_USR|TOYFLAG_BIN))
+
+config RESET
+ bool "reset"
+ default y
+ help
+ usage: reset
+
+ reset the terminal
+*/
+#include "toys.h"
+
+void reset_main(void)
+{
+ // man 4 console codes: reset terminal is ESC (no left bracket) c
+ xwrite(xgettty(), "\033c", 2);
+}
diff --git a/toys/other/rfkill.c b/toys/other/rfkill.c
index af3efe1..36fe320 100644
--- a/toys/other/rfkill.c
+++ b/toys/other/rfkill.c
@@ -5,7 +5,7 @@
*
* No Standard
-USE_RFKILL(NEWTOY(rfkill, "<1>2", TOYFLAG_SBIN))
+USE_RFKILL(NEWTOY(rfkill, "<1>2", TOYFLAG_USR|TOYFLAG_SBIN))
config RFKILL
bool "rfkill"
@@ -49,7 +49,7 @@ void rfkill_main(void)
{"wimax", RFKILL_TYPE_WIMAX}, {"wwan", RFKILL_TYPE_WWAN},
{"gps", RFKILL_TYPE_GPS}, {"fm", 7}}; // RFKILL_TYPE_FM = 7
- if (!*++optargs) error_exit("'%s' needs IDENTIFIER");
+ if (!*++optargs) error_exit("'%s' needs IDENTIFIER", optargs[-1]);
for (i = 0; i < ARRAY_LEN(rftypes); i++)
if (!strcmp(rftypes[i].name, *optargs)) break;
if (i == ARRAY_LEN(rftypes)) idx = atolx_range(*optargs, 0, INT_MAX);
diff --git a/toys/other/rmmod.c b/toys/other/rmmod.c
index b789acc..10c134c 100644
--- a/toys/other/rmmod.c
+++ b/toys/other/rmmod.c
@@ -2,7 +2,7 @@
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_RMMOD(NEWTOY(rmmod, "<1wf", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+USE_RMMOD(NEWTOY(rmmod, "<1wf", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
config RMMOD
bool "rmmod"
diff --git a/toys/other/setsid.c b/toys/other/setsid.c
index 8f0a064..59a1d78 100644
--- a/toys/other/setsid.c
+++ b/toys/other/setsid.c
@@ -24,5 +24,5 @@ void setsid_main(void)
setpgid(0,0);
tcsetpgrp(0, getpid());
}
- xexec_optargs(0);
+ xexec(toys.optargs);
}
diff --git a/toys/other/shred.c b/toys/other/shred.c
new file mode 100644
index 0000000..64db5ed
--- /dev/null
+++ b/toys/other/shred.c
@@ -0,0 +1,106 @@
+/* shred.c - Overwrite a file to securely delete
+ *
+ * Copyright 2014 Rob Landley <rob@landley.net>
+ *
+ * No standard
+
+USE_SHRED(NEWTOY(shred, "<1zxus#<1n#<1o#<0f", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SHRED
+ bool "shred"
+ default y
+ help
+ usage: shred [-fuz] [-n COUNT] [-s SIZE] FILE...
+
+ Securely delete a file by overwriting its contents with random data.
+
+ -f Force (chmod if necessary)
+ -n COUNT Random overwrite iterations (default 1)
+ -o OFFSET Start at OFFSET
+ -s SIZE Use SIZE instead of detecting file size
+ -u unlink (actually delete file when done)
+ -x Use exact size (default without -s rounds up to next 4k)
+ -z zero at end
+
+ Note: data journaling filesystems render this command useless, you must
+ overwrite all free space (fill up disk) to erase old data on those.
+*/
+
+#define FOR_shred
+#include "toys.h"
+
+GLOBALS(
+ long offset;
+ long iterations;
+ long size;
+
+ int ufd;
+)
+
+void shred_main(void)
+{
+ char **try;
+
+ if (!(toys.optflags & FLAG_n)) TT.iterations++;
+ TT.ufd = xopen("/dev/urandom", O_RDONLY);
+
+ // We don't use loopfiles() here because "-" isn't stdin, and want to
+ // respond to files we can't open via chmod.
+
+ for (try = toys.optargs; *try; try++) {
+ off_t pos = 0, len = TT.size;
+ int fd = open(*try, O_RDWR), iter = 0, throw;
+
+ // do -f chmod if necessary
+ if (fd == -1 && (toys.optflags & FLAG_f)) {
+ chmod(*try, 0600);
+ fd = open(*try, O_RDWR);
+ }
+ if (fd == -1) {
+ perror_msg("%s", *try);
+ continue;
+ }
+
+ // determine length
+ if (!len) len = fdlength(fd);
+ if (len<1) {
+ error_msg("%s: needs -s", *try);
+ close(fd);
+ continue;
+ }
+
+ // Loop through, writing to this file
+ for (;;) {
+ // Advance to next -n or -z?
+
+ if (pos >= len) {
+ pos = -1;
+ if (++iter == TT.iterations && (toys.optargs && FLAG_z)) {
+ memset(toybuf, 0, sizeof(toybuf));
+ continue;
+ }
+ if (iter >= TT.iterations) break;
+ }
+
+ if (pos < TT.offset) {
+ if (TT.offset != lseek(fd, TT.offset, SEEK_SET)) {
+ perror_msg("%s", *try);
+ break;
+ }
+ pos = TT.offset;
+ }
+
+ // Determine length, read random data if not zeroing, write.
+
+ throw = sizeof(toybuf);
+ if (toys.optflags & FLAG_x)
+ if (len-pos < throw) throw = len-pos;
+
+ if (iter != TT.iterations) xread(TT.ufd, toybuf, throw);
+ if (throw != writeall(fd, toybuf, throw)) perror_msg("%s");
+ pos += throw;
+ }
+ if (toys.optflags & FLAG_u)
+ if (unlink(*try)) perror_msg("unlink '%s'", *try);
+ }
+}
diff --git a/toys/other/stat.c b/toys/other/stat.c
index d603316..d6db44d 100644
--- a/toys/other/stat.c
+++ b/toys/other/stat.c
@@ -82,7 +82,7 @@ static void print_stat(char type)
if (!stat->st_size && filetype == S_IFREG) t = "regular empty file";
xprintf("%s", t);
} else if (type == 'g') xprintf("%lu", stat->st_gid);
- else if (type == 'G') xprintf("%8s", TT.user_name->pw_name);
+ else if (type == 'G') xprintf("%8s", TT.group_name->gr_name);
else if (type == 'h') xprintf("%lu", stat->st_nlink);
else if (type == 'i') xprintf("%llu", stat->st_ino);
else if (type == 'N') {
@@ -106,11 +106,11 @@ static void print_stat(char type)
static void print_statfs(char type) {
struct statfs *statfs = (struct statfs *)&TT.stat;
- if (type == 'a') xprintf("%lu", statfs->f_bavail);
- else if (type == 'b') xprintf("%lu", statfs->f_blocks);
- else if (type == 'c') xprintf("%lu", statfs->f_files);
- else if (type == 'd') xprintf("%lu", statfs->f_ffree);
- else if (type == 'f') xprintf("%lu", statfs->f_bfree);
+ if (type == 'a') xprintf("%llu", statfs->f_bavail);
+ else if (type == 'b') xprintf("%llu", statfs->f_blocks);
+ else if (type == 'c') xprintf("%llu", statfs->f_files);
+ else if (type == 'd') xprintf("%llu", statfs->f_ffree);
+ else if (type == 'f') xprintf("%llu", statfs->f_bfree);
else if (type == 'l') xprintf("%ld", statfs->f_namelen);
else if (type == 't') xprintf("%lx", statfs->f_type);
else if (type == 'i')
diff --git a/toys/other/swapoff.c b/toys/other/swapoff.c
index b89e915..fb17130 100644
--- a/toys/other/swapoff.c
+++ b/toys/other/swapoff.c
@@ -2,7 +2,7 @@
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_SWAPOFF(NEWTOY(swapoff, "<1>1", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+USE_SWAPOFF(NEWTOY(swapoff, "<1>1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
config SWAPOFF
bool "swapoff"
diff --git a/toys/other/swapon.c b/toys/other/swapon.c
index 49f1249..838d382 100644
--- a/toys/other/swapon.c
+++ b/toys/other/swapon.c
@@ -2,7 +2,7 @@
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
-USE_SWAPON(NEWTOY(swapon, "<1>1p#<0>32767", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
+USE_SWAPON(NEWTOY(swapon, "<1>1p#<0>32767", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
config SWAPON
bool "swapon"
diff --git a/toys/other/sysctl.c b/toys/other/sysctl.c
index 8e57ca1..d4ed1b0 100644
--- a/toys/other/sysctl.c
+++ b/toys/other/sysctl.c
@@ -5,7 +5,7 @@
*
* No Standard
-USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN))
config SYSCTL
bool "sysctl"
@@ -97,7 +97,7 @@ static void process_key(char *key, char *value)
if (!value) value = split_key(key);
if ((toys.optflags & FLAG_w) && !value) {
- error_msg("'%s' not key=value");
+ error_msg("'%s' not key=value", key);
return;
}
diff --git a/toys/other/taskset.c b/toys/other/taskset.c
index bcc0347..2851923 100644
--- a/toys/other/taskset.c
+++ b/toys/other/taskset.c
@@ -3,6 +3,17 @@
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
USE_TASKSET(NEWTOY(taskset, "<1^pa", TOYFLAG_BIN|TOYFLAG_STAYROOT))
+USE_NPROC(NEWTOY(nproc, "(all)", TOYFLAG_USR|TOYFLAG_BIN))
+
+config NPROC
+ bool "nproc"
+ default y
+ help
+ usage: nproc [--all]
+
+ Print number of processors.
+
+ --all Show all processors, not just ones this task can run on.
config TASKSET
bool "taskset"
@@ -29,6 +40,10 @@ config TASKSET
#define sched_getaffinity(pid, size, cpuset) \
syscall(__NR_sched_getaffinity, (pid_t)pid, (size_t)size, (void *)cpuset)
+GLOBALS(
+ int nproc;
+)
+
// mask is an array of long, which makes the layout a bit weird on big
// endian systems but as long as it's consistent...
@@ -90,7 +105,7 @@ void taskset_main(void)
if (!(toys.optflags & FLAG_p)) {
if (toys.optc < 2) error_exit("Needs 2 args");
do_taskset(getpid(), 1);
- xexec_optargs(1);
+ xexec(toys.optargs+1);
} else {
char *c;
pid_t pid = strtol(toys.optargs[toys.optc-1], &c, 10);
@@ -104,3 +119,30 @@ void taskset_main(void)
} else do_taskset(pid, 0);
}
}
+
+int do_nproc(struct dirtree *new)
+{
+ if (!new->parent) return DIRTREE_RECURSE;
+ if (!strncmp(new->name, "cpu", 3) && isdigit(new->name[3])) TT.nproc++;
+
+ return 0;
+}
+
+void nproc_main(void)
+{
+ int i, j;
+
+ // This can only detect 32768 processors. Call getaffinity and count bits.
+ if (!toys.optflags && -1!=sched_getaffinity(getpid(), 4096, toybuf)) {
+ for (i = 0; i<4096; i++)
+ if (toybuf[i])
+ for (j=0; j<8; j++)
+ if (toybuf[i]&(1<<j))
+ TT.nproc++;
+ }
+
+ // If getaffinity failed or --all, count cpu entries in proc
+ if (!TT.nproc) dirtree_read("/sys/devices/system/cpu", do_nproc);
+
+ xprintf("%d\n", TT.nproc);
+}
diff --git a/toys/other/timeout.c b/toys/other/timeout.c
index f8acabf..06b1e89 100644
--- a/toys/other/timeout.c
+++ b/toys/other/timeout.c
@@ -4,7 +4,7 @@
*
* No standard
-USE_TIMEOUT(NEWTOY(timeout, "<2^k:s: ", TOYFLAG_BIN))
+USE_TIMEOUT(NEWTOY(timeout, "<2^vk:s: ", TOYFLAG_BIN))
config TIMEOUT
bool "timeout"
@@ -21,6 +21,7 @@ config TIMEOUT
-s Send specified signal (default TERM)
-k Send KILL signal if child still running this long after first signal.
+ -v Verbose
*/
#define FOR_timeout
@@ -38,14 +39,15 @@ GLOBALS(
static void handler(int i)
{
+ fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
kill(TT.pid, TT.nextsig);
if (TT.k_timeout) {
TT.k_timeout = 0;
TT.nextsig = SIGKILL;
- signal(SIGALRM, handler);
+ xsignal(SIGALRM, handler);
TT.itv.it_value = TT.ktv;
- setitimer(ITIMER_REAL, &TT.itv, (void *)&toybuf);
+ setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
}
}
@@ -60,12 +62,12 @@ void timeout_main(void)
if (TT.s_signal && -1 == (TT.nextsig = sig_to_num(TT.s_signal)))
error_exit("bad -s: '%s'", TT.s_signal);
- if (!(TT.pid = xfork())) xexec_optargs(1);
+ if (!(TT.pid = xfork())) xexec(toys.optargs+1);
else {
int status;
- signal(SIGALRM, handler);
- setitimer(ITIMER_REAL, &TT.itv, (void *)&toybuf);
+ xsignal(SIGALRM, handler);
+ setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
while (-1 == waitpid(TT.pid, &status, 0) && errno == EINTR);
toys.exitval = WIFEXITED(status)
? WEXITSTATUS(status) : WTERMSIG(status) + 127;
diff --git a/toys/other/truncate.c b/toys/other/truncate.c
index d09818f..bfe1f10 100644
--- a/toys/other/truncate.c
+++ b/toys/other/truncate.c
@@ -2,37 +2,62 @@
*
* Copyright 2011 Rob Landley <rob@landley.net>
-USE_TRUNCATE(NEWTOY(truncate, "<1s#|c", TOYFLAG_BIN))
+USE_TRUNCATE(NEWTOY(truncate, "<1s:|c", TOYFLAG_BIN))
config TRUNCATE
bool "truncate"
default y
help
- usage: truncate [-c] -s file...
+ usage: truncate [-c] -s SIZE file...
Set length of file(s), extending sparsely if necessary.
-c Don't create file if it doesn't exist.
- -s New size
+ -s New size (with optional prefix and suffix)
+
+ SIZE prefix: + add, - subtract, < shrink to, > expand to,
+ / multiple rounding down, % multiple rounding up
+ SIZE suffix: k=1024, m=1024^2, g=1024^3, t=1024^4, p=1024^5, e=1024^6
*/
#define FOR_truncate
#include "toys.h"
GLOBALS(
+ char *s;
+
long size;
+ int type;
)
static void do_truncate(int fd, char *name)
{
+ long long size;
+
if (fd<0) return;
- if (ftruncate(fd, TT.size)) perror_msg("'%s' to '%ld'", name, TT.size);
+
+ if (TT.type == -1) size = TT.size;
+ else {
+ size = fdlength(fd);
+ if (TT.type<2) size += TT.size*(1-(2*TT.type));
+ else if (TT.type<4) {
+ if ((TT.type==2) ? (size <= TT.size) : (size >= TT.size)) return;
+ size = TT.size;
+ } else {
+ size = (size+(TT.type-4)*(TT.size-1))/TT.size;
+ size *= TT.size;
+ }
+ }
+ if (ftruncate(fd, size)) perror_msg("'%s' to '%lld'", name, size);
}
void truncate_main(void)
{
int cr = !(toys.optflags&1);
+ if (-1 != (TT.type = stridx("+-<>/%", *TT.s))) TT.s++;
+ TT.size = atolx(TT.s);
+
// Create files with mask rwrwrw.
// Nonexistent files are only an error if we're supposed to create them.
loopfiles_rw(toys.optargs, O_WRONLY|O_CLOEXEC|(cr ? O_CREAT : 0), 0666, cr,
diff --git a/toys/other/unshare.c b/toys/other/unshare.c
deleted file mode 100644
index 68c1ebd..0000000
--- a/toys/other/unshare.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/* unshare.c - run command in new context
- *
- * Copyright 2011 Rob Landley <rob@landley.net>
-
-USE_UNSHARE(NEWTOY(unshare, "<1^imnpuU", TOYFLAG_USR|TOYFLAG_BIN))
-
-config UNSHARE
- bool "unshare"
- default y
- depends on TOYBOX_CONTAINER
- help
- usage: unshare [-imnpuU] COMMAND...
-
- Create new namespace(s) for this process and its children, so some
- attribute is not shared with the parent process. This is part of
- Linux Containers. Each process can have its own:
-
- -i SysV IPC (message queues, semaphores, shared memory)
- -m Mount/unmount tree
- -n Network address, sockets, routing, iptables
- -p Process IDs and init
- -u Host and domain names
- -U UIDs, GIDs, capabilities
-*/
-
-#include "toys.h"
-#include <linux/sched.h>
-extern int unshare (int __flags);
-
-void unshare_main(void)
-{
- unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET,
- CLONE_NEWNS, CLONE_NEWIPC, 0};
- unsigned f=0;
- int i;
-
- for (i=0; flags[i]; i++) if (toys.optflags & (1<<i)) f |= flags[i];
-
- if (unshare(f)) perror_exit("failed");
-
- xexec_optargs(0);
-}
diff --git a/toys/other/vconfig.c b/toys/other/vconfig.c
index eff918c..fd78527 100644
--- a/toys/other/vconfig.c
+++ b/toys/other/vconfig.c
@@ -4,6 +4,8 @@
* Copyright 2012 Kyungwan Han <asura321@gmail.com>
*
* No standard
+ *
+ * TODO: cleanup
USE_VCONFIG(NEWTOY(vconfig, "<2>4", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
diff --git a/toys/other/vmstat.c b/toys/other/vmstat.c
index 49c7cc7..c11e46b 100644
--- a/toys/other/vmstat.c
+++ b/toys/other/vmstat.c
@@ -1,6 +1,9 @@
/* vmstat.c - Report virtual memory statistics.
*
* Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * TODO: I have no idea how the "io" and "system" categories are calculated.
+ * whatever we're doing isn't matching what other implementations are doing.
USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
@@ -84,11 +87,13 @@ void vmstat_main(void)
if (toys.optc) loop_delay = atolx_range(toys.optargs[0], 0, INT_MAX);
if (toys.optc > 1) loop_max = atolx_range(toys.optargs[1], 1, INT_MAX) - 1;
- for (loop = 0; loop <= loop_max; loop++) {
+ for (loop = 0; !loop_max || loop <= loop_max; loop++) {
unsigned idx = loop&1, offset = 0, expected = 0;
uint64_t units, total_hz, *ptr = (uint64_t *)(top+idx),
*oldptr = (uint64_t *)(top+!idx);
+ if (loop && loop_delay) sleep(loop_delay);
+
// Print headers
if (rows>3 && !(loop % (rows-3))) {
if (isatty(1)) terminal_size(0, &rows);
@@ -147,7 +152,6 @@ void vmstat_main(void)
}
xputc('\n');
- if (loop_delay) sleep(loop_delay);
- else break;
+ if (!loop_delay) break;
}
}
diff --git a/toys/other/xxd.c b/toys/other/xxd.c
new file mode 100644
index 0000000..e9ad839
--- /dev/null
+++ b/toys/other/xxd.c
@@ -0,0 +1,65 @@
+/* xxd.c - hexdump.
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * No obvious standard, output looks like:
+ * 0000000: 4c69 6e75 7820 7665 7273 696f 6e20 332e Linux version 3.
+ *
+ * TODO: support for reversing a hexdump back into the original data.
+ * TODO: -s seek
+
+USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config XXD
+ bool "xxd"
+ default y
+ help
+ usage: xxd [-c n] [-g n] [-l n] [file]
+
+ Hexdump a file to stdout. If no file is listed, copy from stdin.
+ Filename "-" is a synonym for stdin.
+
+ -c n Show n bytes per line (default 16).
+ -g n Group bytes by adding a ' ' every n bytes (default 2).
+ -l n Limit of n bytes before stopping (default is no limit).
+*/
+
+#define FOR_xxd
+#include "toys.h"
+
+GLOBALS(
+ long g;
+ long l;
+ long c;
+)
+
+static void do_xxd(int fd, char *name)
+{
+ long long pos = 0;
+ int i, len, space;
+
+ while (0<(len = readall(fd, toybuf, (TT.l && TT.l-pos<TT.c)?TT.l-pos:TT.c))) {
+ printf("%08llx: ", pos);
+ pos += len;
+ space = 2*TT.c+TT.c/TT.g+1;
+
+ for (i=0; i<len;) {
+ space -= printf("%02x", toybuf[i]);
+ if (!(++i%TT.g)) {
+ putchar(' ');
+ space--;
+ }
+ }
+
+ printf("%*s", space, "");
+ for (i=0; i<len; i++)
+ putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.');
+ putchar('\n');
+ }
+ if (len<0) perror_exit("read");
+}
+
+void xxd_main(void)
+{
+ loopfiles(toys.optargs, do_xxd);
+}
diff --git a/toys/pending/README b/toys/pending/README
index f42f34b..2eb83e1 100644
--- a/toys/pending/README
+++ b/toys/pending/README
@@ -1,4 +1,4 @@
-pending
+pending (see toys/pending/README)
Commands in this directory are external submissions awaiting review and/or
cleanup before being "promoted" to one of the other directories.
@@ -7,12 +7,9 @@ Code in this directory may or may not work, some of the commands here are
unfinished stubs, others just need a more thorough inspection than we've had
time for yet. Everything in here defaults to "n" in defconfig.
-Library code awaiting cleanup lives in lib/pending.c
+Outside of this directory, several commands (and some library code) have
+TODO annotations.
-The following commands predate the pending directory, and are awaiting
-cleanup but don't live here:
-
- vmstat, login, du, vconfig, mountpoint, chroot, cut, touch,
- modinfo, expand, xargs
-
- lib/password.c
+This directory should go away before the 1.0 release. It's just a staging
+area so code submissions don't get lost while awaiting more thorough (and
+very time consuming) scrutiny as described in www/cleanup.html.
diff --git a/toys/pending/arp.c b/toys/pending/arp.c
index 1153ebc..e725112 100644
--- a/toys/pending/arp.c
+++ b/toys/pending/arp.c
@@ -175,7 +175,7 @@ static int set_entry(void)
flags = ATF_PERM | ATF_COM;
if (toys.optargs[2]) check_flags(&flags, (toys.optargs+2));
req.arp_flags = flags;
- strncpy(req.arp_dev, TT.device, sizeof(req.arp_dev));
+ xstrncpy(req.arp_dev, TT.device, sizeof(req.arp_dev));
xioctl(TT.sockfd, SIOCSARP, &req);
if (toys.optflags & FLAG_v) xprintf("Entry set for %s\n", toys.optargs[0]);
@@ -204,7 +204,7 @@ static int delete_entry(void)
flags = ATF_PERM;
if (toys.optargs[1]) check_flags(&flags, (toys.optargs+1));
req.arp_flags = flags;
- strncpy(req.arp_dev, TT.device, sizeof(req.arp_dev));
+ xstrncpy(req.arp_dev, TT.device, sizeof(req.arp_dev));
xioctl(TT.sockfd, SIOCDARP, &req);
if (toys.optflags & FLAG_v) xprintf("Delete entry for %s\n", toys.optargs[0]);
diff --git a/toys/pending/arping.c b/toys/pending/arping.c
index fe5c76b..3e522bd 100644
--- a/toys/pending/arping.c
+++ b/toys/pending/arping.c
@@ -63,7 +63,7 @@ static void get_interface(char *interface, int *ifindex, uint32_t *oip,
int fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
req.ifr_addr.sa_family = AF_INET;
- strncpy(req.ifr_name, interface, IFNAMSIZ);
+ xstrncpy(req.ifr_name, interface, IFNAMSIZ);
req.ifr_name[IFNAMSIZ-1] = '\0';
xioctl(fd, SIOCGIFFLAGS, &req);
@@ -116,18 +116,18 @@ static void send_packet()
ptr = mempcpy(ptr, &src_pk.sll_addr, src_pk.sll_halen);
ptr = mempcpy(ptr, &src_addr, 4);
- if (toys.optflags & FLAG_A)
- ptr = mempcpy(ptr, &src_pk.sll_addr, src_pk.sll_halen);
- else ptr = mempcpy(ptr, &dst_pk.sll_addr, src_pk.sll_halen);
-
+ ptr = mempcpy(ptr,
+ (toys.optflags & FLAG_A) ? &src_pk.sll_addr : &dst_pk.sll_addr,
+ src_pk.sll_halen);
ptr = mempcpy(ptr, &dest_addr, 4);
+
ret = sendto(TT.sockfd, sbuf, ptr - sbuf, 0,
(struct sockaddr *)&dst_pk, sizeof(dst_pk));
if (ret == ptr - sbuf) {
struct timeval tval;
gettimeofday(&tval, NULL);
- TT.sent_at = (tval.tv_sec * 1000000ULL + (tval.tv_usec));
+ TT.sent_at = tval.tv_sec * 1000000ULL + tval.tv_usec;
TT.sent_nr++;
if (!TT.unicast_flag) TT.brd_sent++;
}
@@ -215,7 +215,7 @@ void arping_main(void)
TT.sockfd = xsocket(AF_PACKET, SOCK_DGRAM, 0);
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, TT.iface, IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, TT.iface, IFNAMSIZ);
get_interface(TT.iface, &if_index, NULL, NULL);
src_pk.sll_ifindex = if_index;
diff --git a/toys/pending/bootchartd.c b/toys/pending/bootchartd.c
index 72ade69..d903405 100644
--- a/toys/pending/bootchartd.c
+++ b/toys/pending/bootchartd.c
@@ -294,7 +294,6 @@ void bootchartd_main()
putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin");
start_logging();
stop_logging(tmp_dir, bchartd_opt == 1 ? toys.optargs[1] : NULL);
- free(tmp_dir);
return;
}
waitpid(lgr_pid, NULL, WUNTRACED);
@@ -310,7 +309,7 @@ void bootchartd_main()
if (bchartd_opt == 1 && toys.optargs[1]) {
pid_t prog_pid;
- if (!(prog_pid = xfork())) xexec_optargs(1);
+ if (!(prog_pid = xfork())) xexec(toys.optargs+1);
waitpid(prog_pid, NULL, 0);
kill(lgr_pid, SIGUSR1);
}
diff --git a/toys/pending/brctl.c b/toys/pending/brctl.c
index fecbf68..e3b1526 100644
--- a/toys/pending/brctl.c
+++ b/toys/pending/brctl.c
@@ -49,7 +49,7 @@ static void get_ports(char *bridge, int *indices)
memset(ifindices, 0, MAX_BRIDGES);
args[1] = (unsigned long)ifindices;
- strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, bridge, IFNAMSIZ);
ifr.ifr_data = (char *)args;
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
if (indices) memcpy(indices, ifindices, sizeof(ifindices));
@@ -62,7 +62,7 @@ void get_br_info(char *bridge, struct __bridge_info *info)
(unsigned long) info, 0, 0 };
memset(info, 0, sizeof(*info));
- strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, bridge, IFNAMSIZ);
ifr.ifr_data = (char *)args;
if (ioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr) < 0) {
@@ -118,7 +118,7 @@ void br_addbr(char **argv)
#ifdef SIOCBRADDBR
xioctl(TT.sockfd, SIOCBRADDBR, argv[0]);
#else
- strncpy(br, argv[0], IFNAMSIZ);
+ xstrncpy(br, argv[0], IFNAMSIZ);
xioctl(TT.sockfd, SIOCSIFBR, args);
#endif
}
@@ -131,7 +131,7 @@ void br_delbr(char **argv)
#ifdef SIOCBRDELBR
xioctl(TT.sockfd, SIOCBRDELBR, argv[0]);
#else
- strncpy(br, argv[0], IFNAMSIZ);
+ xstrncpy(br, argv[0], IFNAMSIZ);
xioctl(TT.sockfd, SIOCSIFBR, args);
#endif
}
@@ -148,7 +148,7 @@ void br_addif(char **argv)
xioctl(TT.sockfd, SIOCBRADDIF, &ifr);
#else
args[1] = index;
- strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
ifr.ifr_data = (char *)args;
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
#endif
@@ -166,7 +166,7 @@ void br_delif(char **argv)
xioctl(TT.sockfd, SIOCBRDELIF, &ifr);
#else
args[1] = index;
- strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
ifr.ifr_data = (char *)args;
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
#endif
@@ -194,7 +194,7 @@ void set_time(char *br, unsigned long cmd, unsigned long val)
struct ifreq ifr;
unsigned long args[4] = {cmd, val, 0, 0};
- strncpy(ifr.ifr_name, br, IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, br, IFNAMSIZ);
ifr.ifr_data = (char *)args;
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
}
@@ -270,7 +270,7 @@ void set_cost_prio(char *br, char *port, unsigned long cmd, unsigned long val)
}
if (i >= MAX_BRIDGES) error_exit("%s not in bridge", port);
args[1] = i;
- strncpy(ifr.ifr_name, br, IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, br, IFNAMSIZ);
ifr.ifr_data = (char *)args;
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
}
diff --git a/toys/pending/compress.c b/toys/pending/compress.c
index bb4af46..1749ee4 100644
--- a/toys/pending/compress.c
+++ b/toys/pending/compress.c
@@ -5,7 +5,8 @@
* The inflate/deflate code lives here, so the various things that use it
* either live here or call these commands to pipe data through them.
*
- * Divergence from posix: replace obsolete "compress" with mutiplexer.
+ * Divergence from posix: replace obsolete/patented "compress" with mutiplexer.
+ * (gzip already replaces "uncompress".)
*
* See RFCs 1950 (zlib), 1951 (deflate), and 1952 (gzip)
* LSB 4.1 has gzip, gunzip, and zcat
@@ -14,8 +15,10 @@
// Accept many different kinds of command line argument.
// Leave Lrg at end so flag values line up.
-USE_COMPRESS(NEWTOY(compress, "zcd9Lrg[-cd][!zgLr]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_COMPRESS(NEWTOY(zcat, "aLrg[!aLrg]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_COMPRESS(NEWTOY(compress, "zcd9lrg[-cd][!zgLr]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_GZIP(NEWTOY(gzip, USE_GZIP_D("d")"19dcflqStvgLRz[!gLRz]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_ZCAT(NEWTOY(zcat, 0, TOYFLAG_USR|TOYFLAG_BIN))
+USE_GUNZIP(NEWTOY(gunzip, "cflqStv", TOYFLAG_USR|TOYFLAG_BIN))
//zip unzip gzip gunzip zcat
@@ -23,38 +26,114 @@ config COMPRESS
bool "compress"
default n
help
+ usage: compress [-zgLR19] [FILE]
+
+ Compress or decompress file (or stdin) using "deflate" algorithm.
+
+ -1 min compression
+ -9 max compression (default)
+ -g gzip (default)
+ -L zlib
+ -R raw
+ -z zip
+
+config GZIP
+ bool "gzip"
+ default y
+ depends on COMPRESS
+ help
+ usage: gzip [-19cfqStvzgLR] [FILE...]
+
+ Compess (deflate) file(s). With no files, compress stdin to stdout.
+
+ On successful decompression, compressed files are replaced with the
+ uncompressed version. The input file is removed and replaced with
+ a new file without the .gz extension (with same ownership/permissions).
+
+ -1 Minimal compression (fastest)
+ -9 Max compression (default)
+ -c cat to stdout (act as zcat)
+ -f force (if output file exists, input is tty, unrecognized extension)
+ -q quiet (no warnings)
+ -S specify exension (default .*)
+ -t test compressed file(s)
+ -v verbose (like -l, but compress files)
+
+ Compression type:
+ -g gzip (default) -L zlib -R raw -z zip
+
+config GZIP_D
+ bool
+ default y
+ depends on GZIP && DECOMPRESS
+ help
+ usage: gzip [-d]
+
+ -d decompress (act as gunzip)
+
+config DECOMPRESS
+ bool "decompress"
+ default n
+ help
usage: compress [-zglrcd9] [FILE]
Compress or decompress file (or stdin) using "deflate" algorithm.
- -c compress with -g gzip (default) -L zlib -r raw -z zip
+ -c compress with -g gzip (default) -l zlib -r raw -z zip
-d decompress (autodetects type)
+
config ZCAT
bool "zcat"
- default n
- depends on COMPRESS
+ default y
+ depends on DECOMPRESS
help
usage: zcat [FILE...]
Decompress deflated file(s) to stdout
+
+config GUNZIP
+ bool "gunzip"
+ default y
+ depends on DECOMPRESS
+ help
+ usage: gunzip [-cflqStv] [FILE...]
+
+ Decompess (deflate) file(s). With no files, compress stdin to stdout.
+
+ On successful decompression, compressed files are replaced with the
+ uncompressed version. The input file is removed and replaced with
+ a new file without the .gz extension (with same ownership/permissions).
+
+ -c cat to stdout (act as zcat)
+ -f force (output file exists, input is tty, unrecognized extension)
+ -l list compressed/uncompressed/ratio/name for each input file.
+ -q quiet (no warnings)
+ -S specify exension (default .*)
+ -t test compressed file(s)
+ -v verbose (like -l, but decompress files)
*/
#define FOR_compress
#include "toys.h"
GLOBALS(
- // base offset and extra bits tables (length and distance)
+ // Huffman codes: base offset and extra bits tables (length and distance)
char lenbits[29], distbits[30];
unsigned short lenbase[29], distbase[30];
void *fixdisthuff, *fixlithuff;
+ // CRC
void (*crcfunc)(char *data, int len);
- unsigned crc, len;
+ unsigned crc;
+
+ // Compressed data buffer
+ char *data;
+ unsigned pos, len;
+ int infd, outfd;
- char *outbuf;
- unsigned outlen;
- int outfd;
+ // Tables only used for deflation
+ unsigned short *hashhead, *hashchain;
)
// little endian bit buffer
@@ -66,9 +145,8 @@ struct bitbuf {
// malloc a struct bitbuf
struct bitbuf *bitbuf_init(int fd, int size)
{
- struct bitbuf *bb = xmalloc(sizeof(struct bitbuf)+size);
+ struct bitbuf *bb = xzalloc(sizeof(struct bitbuf)+size);
- memset(bb, 0, sizeof(struct bitbuf));
bb->max = size;
bb->fd = fd;
@@ -125,13 +203,44 @@ unsigned bitbuf_get(struct bitbuf *bb, int bits)
return result;
}
-static void outbuf_crc(char sym)
+void bitbuf_flush(struct bitbuf *bb)
+{
+ if (!bb->bitpos) return;
+
+ xwrite(bb->fd, bb->buf, (bb->bitpos+7)/8);
+ memset(bb->buf, 0, bb->max);
+ bb->bitpos = 0;
+}
+
+void bitbuf_put(struct bitbuf *bb, int data, int len)
+{
+ while (len) {
+ int click = bb->bitpos >> 3, blow, blen;
+
+ // Flush buffer if necessary
+ if (click == bb->max) {
+ bitbuf_flush(bb);
+ click = 0;
+ }
+ blow = bb->bitpos & 7;
+ blen = 8-blow;
+ if (blen > len) blen = len;
+ bb->buf[click] |= data << blow;
+ bb->bitpos += blen;
+ data >>= blen;
+ len -= blen;
+ }
+}
+
+static void output_byte(char sym)
{
- TT.outbuf[TT.outlen++ & 32767] = sym;
+ int pos = TT.pos++ & 32767;
- if (!(TT.outlen & 32767)) {
- xwrite(TT.outfd, TT.outbuf, 32768);
- if (TT.crcfunc) TT.crcfunc(0, 32768);
+ TT.data[pos] = sym;
+
+ if (!pos) {
+ xwrite(TT.outfd, TT.data, 32768);
+ if (TT.crcfunc) TT.crcfunc(TT.data, 32768);
}
}
@@ -146,7 +255,7 @@ struct huff {
// Create simple huffman tree from array of bit lengths.
-// The symbols in deflate's huffman trees are sorted (first by bit length
+// The symbols in the huffman trees are sorted (first by bit length
// of the code to reach them, then by symbol number). This means that given
// the bit length of each symbol, we can construct a unique tree.
static void len2huff(struct huff *huff, char bitlen[], int len)
@@ -166,9 +275,9 @@ static void len2huff(struct huff *huff, char bitlen[], int len)
}
// Fetch and decode next huffman coded symbol from bitbuf.
-// This takes advantage of the the sorting to navigate the tree as an array:
+// This takes advantage of the sorting to navigate the tree as an array:
// each time we fetch a bit we have all the codes at that bit level in
-// order with no gaps..
+// order with no gaps.
static unsigned huff_and_puff(struct bitbuf *bb, struct huff *huff)
{
unsigned short *length = huff->length;
@@ -185,7 +294,7 @@ static unsigned huff_and_puff(struct bitbuf *bb, struct huff *huff)
return huff->symbol[start + offset];
}
-// Decompress deflated data from bitbuf to filehandle.
+// Decompress deflated data from bitbuf to TT.outfd.
static void inflate(struct bitbuf *bb)
{
TT.crc = ~0;
@@ -216,7 +325,7 @@ static void inflate(struct bitbuf *bb)
// dump bytes until done or end of current bitbuf contents
if (bblen > len) bblen = len;
pos = bblen;
- while (pos--) outbuf_crc(*(p++));
+ while (pos--) output_byte(*(p++));
bitbuf_skip(bb, bblen << 3);
len -= bblen;
}
@@ -276,7 +385,7 @@ static void inflate(struct bitbuf *bb)
int sym = huff_and_puff(bb, lithuff);
// Literal?
- if (sym < 256) outbuf_crc(sym);
+ if (sym < 256) output_byte(sym);
// Copy range?
else if (sym > 256) {
@@ -286,9 +395,9 @@ static void inflate(struct bitbuf *bb)
len = TT.lenbase[sym] + bitbuf_get(bb, TT.lenbits[sym]);
sym = huff_and_puff(bb, disthuff);
dist = TT.distbase[sym] + bitbuf_get(bb, TT.distbits[sym]);
- sym = TT.outlen & 32767;
+ sym = TT.pos & 32767;
- while (len--) outbuf_crc(TT.outbuf[(TT.outlen-dist) & 32767]);
+ while (len--) output_byte(TT.data[(TT.pos-dist) & 32767]);
// End of block
} else break;
@@ -299,18 +408,62 @@ static void inflate(struct bitbuf *bb)
if (final) break;
}
- if (TT.outlen & 32767) {
- xwrite(TT.outfd, TT.outbuf, TT.outlen & 32767);
- if (TT.crcfunc) TT.crcfunc(0, TT.outlen & 32767);
+ if (TT.pos & 32767) {
+ xwrite(TT.outfd, TT.data, TT.pos & 32767);
+ if (TT.crcfunc) TT.crcfunc(TT.data, TT.pos & 32767);
+ }
+}
+
+// Deflate from TT.infd to bitbuf
+// For deflate, TT.len = input read, TT.pos = input consumed
+static void deflate(struct bitbuf *bb)
+{
+ char *data = TT.data;
+ int len, final = 0;
+
+ TT.crc = ~0;
+
+ while (!final) {
+ // Read next half-window of data if we haven't hit EOF yet.
+ len = readall(TT.infd, data+(TT.len&32768), 32768);
+ if (len < 0) perror_exit("read"); // todo: add filename
+ if (len != 32768) final++;
+ if (TT.crcfunc) TT.crcfunc(data+(TT.len&32768), len);
+ // TT.len += len; crcfunc advances len
+
+ // store block as literal
+ bitbuf_put(bb, final, 1);
+ bitbuf_put(bb, 0, 1);
+
+ bitbuf_put(bb, 0, (8-bb->bitpos)&7);
+ bitbuf_put(bb, len, 16);
+ bitbuf_put(bb, 0xffff & ~len, 16);
+
+ // repeat until spanked
+ while (TT.pos != TT.len) {
+ unsigned pos = TT.pos & 65535;
+
+ bitbuf_put(bb, data[pos], 8);
+
+ // need to refill buffer?
+ if (!(32767 & ++TT.pos) && !final) break;
+ }
}
+ bitbuf_flush(bb);
}
-static void init_deflate(void)
+// Allocate memory for deflate/inflate.
+static void init_deflate(int compress)
{
int i, n = 1;
- // Ye olde deflate window
- TT.outbuf = xmalloc(32768);
+ // compress needs 64k data and 32k each for hashhead and hashchain.
+ // decompress just needs 32k data.
+ TT.data = xmalloc(32768*(compress ? 4 : 1));
+ if (compress) {
+ TT.hashhead = (unsigned short *)(TT.data + 65536);
+ TT.hashchain = (unsigned short *)(TT.data + 65536 + 32768);
+ }
// Calculate lenbits, lenbase, distbits, distbase
*TT.lenbase = 3;
@@ -365,11 +518,39 @@ void gzip_crc(char *data, int len)
unsigned crc, *crc_table = (unsigned *)(toybuf+sizeof(toybuf)-1024);
crc = TT.crc;
- for (i=0; i<len; i++) crc = crc_table[(crc^TT.outbuf[i])&0xff] ^ (crc>>8);
+ for (i=0; i<len; i++) crc = crc_table[(crc^data[i])&0xff] ^ (crc>>8);
TT.crc = crc;
TT.len += len;
}
+static void do_gzip(int fd, char *name)
+{
+ struct bitbuf *bb = bitbuf_init(1, sizeof(toybuf));
+
+ // Header from RFC 1952 section 2.2:
+ // 2 ID bytes (1F, 8b), gzip method byte (8=deflate), FLAG byte (none),
+ // 4 byte MTIME (zeroed), Extra Flags (2=maximum compression),
+ // Operating System (FF=unknown)
+
+ TT.infd = fd;
+ xwrite(bb->fd, "\x1f\x8b\x08\0\0\0\0\0\x02\xff", 10);
+
+ // Use last 1k of toybuf for little endian crc table
+ crc_init((unsigned *)(toybuf+sizeof(toybuf)-1024), 1);
+ TT.crcfunc = gzip_crc;
+
+ deflate(bb);
+
+ // tail: crc32, len32
+
+ bitbuf_put(bb, 0, (8-bb->bitpos)&7);
+ bitbuf_put(bb, ~TT.crc, 32);
+ bitbuf_put(bb, TT.len, 32);
+
+ bitbuf_flush(bb);
+ free(bb);
+}
+
static void do_zcat(int fd, char *name)
{
struct bitbuf *bb = bitbuf_init(fd, sizeof(toybuf));
@@ -395,7 +576,8 @@ static void do_zcat(int fd, char *name)
void compress_main(void)
{
- zcat_main();
+ // todo: this
+ printf("hello world");
}
//#define CLEANUP_compress
@@ -404,7 +586,21 @@ void compress_main(void)
void zcat_main(void)
{
- init_deflate();
+ init_deflate(0);
loopfiles(toys.optargs, do_zcat);
}
+
+void gunzip_main(void)
+{
+ init_deflate(0);
+
+ loopfiles(toys.optargs, do_zcat);
+}
+
+void gzip_main(void)
+{
+ init_deflate(1);
+
+ loopfiles(toys.optargs, do_gzip);
+}
diff --git a/toys/pending/dd.c b/toys/pending/dd.c
index a5c2452..3449104 100644
--- a/toys/pending/dd.c
+++ b/toys/pending/dd.c
@@ -134,9 +134,9 @@ static void summary()
fprintf(stderr,"%llu+%llu records in\n%llu+%llu records out\n", st.in_full, st.in_part,
st.out_full, st.out_part);
human_readable(toybuf, st.bytes);
- fprintf(stderr, "%llu bytes (%s) copied,",st.bytes, toybuf);
+ fprintf(stderr, "%llu bytes (%s) copied, ",st.bytes, toybuf);
human_readable(toybuf, st.bytes/seconds);
- fprintf(stderr, "%f seconds, %s/s\n", seconds, toybuf);
+ fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
}
static void sig_handler(int sig)
diff --git a/toys/pending/dhcp.c b/toys/pending/dhcp.c
index bdaefca..f8226db 100644
--- a/toys/pending/dhcp.c
+++ b/toys/pending/dhcp.c
@@ -288,7 +288,7 @@ static int get_interface( char *interface, int *ifindex, uint32_t *oip, uint8_t
int fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
req.ifr_addr.sa_family = AF_INET;
- strncpy(req.ifr_name, interface, IFNAMSIZ);
+ xstrncpy(req.ifr_name, interface, IFNAMSIZ);
req.ifr_name[IFNAMSIZ-1] = '\0';
xioctl(fd, SIOCGIFFLAGS, &req);
@@ -628,7 +628,7 @@ static int mode_app(void)
close(state->sockfd);
return -1;
}
- strncpy(ifr.ifr_name, state->iface, IFNAMSIZ);
+ xstrncpy(ifr.ifr_name, state->iface, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ -1] = '\0';
setsockopt(state->sockfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
@@ -884,14 +884,16 @@ static uint8_t *dhcpc_addreqipaddr(struct in_addr *ipaddr, uint8_t *optptr)
}
// adds hostname to dhcp packet.
-static uint8_t *dhcpc_addfdnname(uint8_t *optptr, char *hname)
+static uint8_t *dhcpc_addfdnname(uint8_t *optptr, char *hname)
{
int size = strlen(hname);
+
*optptr++ = DHCP_OPTION_FQDN;
*optptr++ = size + 3;
*optptr++ = 0x1; //flags
optptr += 2; // two blank bytes
- strncpy((char*)optptr, hname, size); // name
+ strcpy((char*)optptr, hname); // name
+
return optptr + size;
}
@@ -1228,16 +1230,14 @@ static void renew(void)
// Sends a IP release request.
static void release(void)
{
- int len = sizeof("255.255.255.255\0");
- char buffer[len];
+ char buffer[sizeof("255.255.255.255\0")];
struct in_addr temp_addr;
mode_app();
// send release packet
if (state->status == STATE_BOUND || state->status == STATE_RENEWING || state->status == STATE_REBINDING) {
temp_addr.s_addr = htonl(server);
- strncpy(buffer, inet_ntoa(temp_addr), sizeof(buffer));
- buffer[len - 1] = '\0';
+ xstrncpy(buffer, inet_ntoa(temp_addr), sizeof(buffer));
temp_addr.s_addr = state->ipaddr.s_addr;
infomsg( infomode, "Unicasting a release of %s to %s", inet_ntoa(temp_addr), buffer);
dhcpc_sendmsg(DHCPRELEASE);
diff --git a/toys/pending/dhcpd.c b/toys/pending/dhcpd.c
index 7da5194..17f5009 100644
--- a/toys/pending/dhcpd.c
+++ b/toys/pending/dhcpd.c
@@ -2,19 +2,22 @@
*
* Copyright 2013 Madhur Verma <mad.flexi@gmail.com>
* Copyright 2013 Kyungwan Han <asura321@gamil.com>
+ * Copyright 2015 Yeongdeok Suh <skyducks111@gmail.com>
*
* No Standard
-USE_DHCPD(NEWTOY(dhcpd, ">1P#<0>65535=67fS", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
+USE_DHCPD(NEWTOY(dhcpd, ">1P#<0>65535=67fi:S46[!46]", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
config DHCPD
bool "dhcpd"
default n
help
- usage: dhcpd [-fS] [-P N] [CONFFILE]
+ usage: dhcpd [-46fS] [-i IFACE] [-P N] [CONFFILE]
-f Run in foreground
+ -i Interface to use
-S Log to syslog too
- -P N Use port N (default 67)
+ -P N Use port N (default ipv4 67, ipv6 547)
+ -4, -6 Run as a DHCPv4 or DHCPv6 server
config DEBUG_DHCP
bool "debugging messeges ON/OFF"
@@ -22,6 +25,15 @@ config DEBUG_DHCP
depends on DHCPD
*/
+/*
+ * Things to do
+ *
+ * - Working as an relay agent
+ * - Rapid commit option support
+ * - Additional packet options (commented on the middle of sources)
+ * - Create common modules
+ */
+
#define FOR_dhcpd
#include "toys.h"
@@ -30,6 +42,7 @@ config DEBUG_DHCP
// Todo: headers not in posix
#include <netinet/ip.h>
+#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <netpacket/packet.h>
@@ -57,6 +70,20 @@ config DEBUG_DHCP
#define DHCPRELEASE 7
#define DHCPINFORM 8
+#define DHCP6SOLICIT 1
+#define DHCP6ADVERTISE 2 // server -> client
+#define DHCP6REQUEST 3
+#define DHCP6CONFIRM 4
+#define DHCP6RENEW 5
+#define DHCP6REBIND 6
+#define DHCP6REPLY 7 // server -> client
+#define DHCP6RELEASE 8
+#define DHCP6DECLINE 9
+#define DHCP6RECONFIGURE 10 // server -> client
+#define DHCP6INFOREQUEST 11
+#define DHCP6RELAYFLOW 12 // relay -> relay/server
+#define DHCP6RELAYREPLY 13 // server/relay -> relay
+
#define DHCP_NUM8 (1<<8)
#define DHCP_NUM16 (1<<9)
#define DHCP_NUM32 DHCP_NUM16 | DHCP_NUM8
@@ -78,10 +105,39 @@ config DEBUG_DHCP
#define DHCP_OPT_PARAM_REQ DHCP_STRING | 0x37 // list of options client wants
#define DHCP_OPT_END 0xff
+// DHCPv6 option codes (partial). See RFC 3315
+#define DHCP6_OPT_CLIENTID 1
+#define DHCP6_OPT_SERVERID 2
+#define DHCP6_OPT_IA_NA 3
+#define DHCP6_OPT_IA_ADDR 5
+#define DHCP6_OPT_ORO 6
+#define DHCP6_OPT_PREFERENCE 7
+#define DHCP6_OPT_ELAPSED_TIME 8
+#define DHCP6_OPT_RELAY_MSG 9
+#define DHCP6_OPT_STATUS_CODE 13
+#define DHCP6_OPT_IA_PD 25
+#define DHCP6_OPT_IA_PREFIX 26
+
+#define DHCP6_STATUS_SUCCESS 0
+#define DHCP6_STATUS_NOADDRSAVAIL 2
+
+#define DHCP6_DUID_LLT 1
+#define DHCP6_DUID_EN 2
+#define DHCP6_DUID_LL 3
+#define DHCP6_DUID_UUID 4
+
GLOBALS(
+ char *iface;
long port;
);
+struct config_keyword {
+ char *keyword;
+ int (*handler)(const char *str, void *var);
+ void *var;
+ char *def;
+};
+
typedef struct __attribute__((packed)) dhcp_msg_s {
uint8_t op;
uint8_t htype;
@@ -101,18 +157,39 @@ typedef struct __attribute__((packed)) dhcp_msg_s {
uint8_t options[308];
} dhcp_msg_t;
+typedef struct __attribute__((packed)) dhcp6_msg_s {
+ uint8_t msgtype;
+ uint8_t transaction_id[3];
+ uint8_t options[524];
+} dhcp6_msg_t;
+
typedef struct __attribute__((packed)) dhcp_raw_s {
struct iphdr iph;
struct udphdr udph;
dhcp_msg_t dhcp;
} dhcp_raw_t;
+typedef struct __attribute__((packed)) dhcp6_raw_s {
+ struct ip6_hdr iph;
+ struct udphdr udph;
+ dhcp6_msg_t dhcp6;
+} dhcp6_raw_t;
+
typedef struct static_lease_s {
struct static_lease_s *next;
uint32_t nip;
int mac[6];
} static_lease;
+typedef struct static_lease6_s {
+ struct static_lease6_s *next;
+ uint16_t duid_len;
+ uint16_t ia_type;
+ uint32_t iaid;
+ uint32_t nip6[4];
+ uint8_t duid[20];
+} static_lease6;
+
typedef struct {
uint32_t expires;
uint32_t lease_nip;
@@ -121,6 +198,15 @@ typedef struct {
uint8_t pad[2];
} dyn_lease;
+typedef struct {
+ uint16_t duid_len;
+ uint16_t ia_type;
+ uint32_t expires;
+ uint32_t iaid;
+ uint32_t lease_nip6[4];
+ uint8_t duid[20];
+} dyn_lease6;
+
typedef struct option_val_s {
char *key;
uint16_t code;
@@ -128,9 +214,32 @@ typedef struct option_val_s {
size_t len;
} option_val_t;
+struct __attribute__((packed)) optval_duid_llt {
+ uint16_t type;
+ uint16_t hwtype;
+ uint32_t time;
+ uint8_t *lladdr;
+};
+
+struct __attribute__((packed)) optval_ia_na {
+ uint32_t iaid;
+ uint32_t t1, t2;
+ uint8_t *optval;
+};
+struct __attribute__((packed)) optval_ia_addr {
+ uint32_t ipv6_addr[4];
+ uint32_t pref_lifetime;
+ uint32_t valid_lifetime;
+};
+struct __attribute__((packed)) optval_status_code {
+ uint16_t status_code;
+ uint8_t *status_msg;
+};
+
typedef struct __attribute__((__may_alias__)) server_config_s {
char *interface; // interface to use
int ifindex;
+ uint32_t server_nip6[4];
uint32_t server_nip;
uint32_t port;
uint8_t server_mac[6]; // our MAC address (used only for ARP probing)
@@ -138,6 +247,8 @@ typedef struct __attribute__((__may_alias__)) server_config_s {
/* start,end are in host order: we need to compare start <= ip <= end*/
uint32_t start_ip; // start address of leases, in host order
uint32_t end_ip; // end of leases, in host order
+ uint32_t start_ip6[4]; // start address of leases, in IPv6 mode
+ uint32_t end_ip6[4]; // end of leases, in IPv6 mode
uint32_t max_lease_sec; // maximum lease time (host order)
uint32_t min_lease_sec; // minimum lease time a client can request
uint32_t max_leases; // maximum number of leases (including reserved addresses)
@@ -149,30 +260,36 @@ typedef struct __attribute__((__may_alias__)) server_config_s {
uint32_t offer_time; // how long an offered address is reserved
uint32_t siaddr_nip; // "next server" bootp option
char *lease_file;
+ char *lease6_file;
char *pidfile;
char *notify_file; // what to run whenever leases are written
char *sname; // bootp server name
char *boot_file; // bootp boot file option
+ uint32_t pref_lifetime;
+ uint32_t valid_lifetime;
+ uint32_t t1,t2;
struct static_lease *static_leases; // List of ip/mac pairs to assign static leases
} server_config_t;
typedef struct __attribute__((__may_alias__)) server_state_s {
uint8_t rqcode;
int listensock;
- dhcp_msg_t rcvd_pkt;
+ union {
+ dhcp_msg_t rcvd_pkt;
+ dhcp6_msg_t rcvd_pkt6;
+ } rcvd;
uint8_t* rqopt;
- dhcp_msg_t send_pkt;
- static_lease *sleases;
+ union {
+ dhcp_msg_t send_pkt;
+ dhcp6_msg_t send_pkt6;
+ } send;
+ union {
+ static_lease *sleases;
+ static_lease6 *sleases6;
+ } leases;
struct arg_list *dleases;
} server_state_t;
-struct config_keyword {
- char *keyword;
- int (*handler)(const char *str, void *var);
- void *var;
- char *def;
-};
-
static option_val_t options_list[] = {
{"lease" , DHCP_NUM32 | 0x33, NULL, 0},
{"subnet" , DHCP_IP | 0x01, NULL, 0},
@@ -211,12 +328,34 @@ static server_state_t gstate;
static uint8_t infomode;
static struct fd_pair sigfd;
static int constone = 1;
+static sa_family_t addr_version = AF_INET;
+
+static void htonl6(uint32_t *host_order, uint32_t *network_order)
+{
+ int i;
+ if(!host_order) {
+ error_msg("NULL ipv6 address");
+ } else {
+ for(i=0;i<4;i++) network_order[i] = htonl(host_order[i]);
+ }
+}
+
+static void ntohl6(uint32_t *network_order, uint32_t *host_order)
+{
+ int i;
+ if(!network_order) {
+ error_msg("NULL ipv6 address");
+ } else {
+ for(i=0;i<4;i++) host_order[i] = ntohl(network_order[i]);
+ }
+}
// calculate options size.
static int dhcp_opt_size(uint8_t *optionptr)
{
int i = 0;
- for(;optionptr[i] != 0xff; i++) if(optionptr[i] != 0x00) i += optionptr[i + 1] + 2 -1;
+ for(;optionptr[i] != 0xff; i++)
+ if(optionptr[i] != 0x00) i += optionptr[i + 1] + 2 -1;
return i;
}
@@ -239,25 +378,61 @@ static uint16_t dhcp_checksum(void *addr, int count)
}
// gets information of INTERFACE and updates IFINDEX, MAC and IP
-static int get_interface(const char *interface, int *ifindex, uint32_t *oip, uint8_t *mac)
+static int get_interface(const char *interface, int *ifindex, uint32_t *oip,
+ uint8_t *mac)
{
struct ifreq req;
struct sockaddr_in *ip;
- int fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ struct sockaddr_in6 ip6;
+ int fd = xsocket(addr_version, SOCK_RAW, IPPROTO_RAW);
+ char ipv6_addr[40] = {0,};
- req.ifr_addr.sa_family = AF_INET;
- strncpy(req.ifr_name, interface, IFNAMSIZ);
- req.ifr_name[IFNAMSIZ-1] = '\0';
+ req.ifr_addr.sa_family = addr_version;
+ xstrncpy(req.ifr_name, (char *)interface, IFNAMSIZ);
xioctl(fd, SIOCGIFFLAGS, &req);
-
+
if (!(req.ifr_flags & IFF_UP)) return -1;
- if (oip) {
- xioctl(fd, SIOCGIFADDR, &req);
- ip = (struct sockaddr_in*) &req.ifr_addr;
- dbg("IP %s\n", inet_ntoa(ip->sin_addr));
- *oip = ntohl(ip->sin_addr.s_addr);
+
+ if (addr_version == AF_INET6) {
+
+ FILE *fd6 = fopen("/proc/net/if_inet6", "r");
+ int i;
+
+ while(fgets(toybuf, sizeof(toybuf), fd6)) {
+ if (!strstr(toybuf, interface))
+ continue;
+
+ if (sscanf(toybuf, "%32s \n", ipv6_addr) != 1)
+ continue;
+
+ if (strstr(ipv6_addr, "fe80")) break;
+ }
+ fclose(fd6);
+
+ if (oip) {
+ 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--) = ':';
+
+ dbg("ipv6 %s\n", ipv6_addr);
+ if(inet_pton(AF_INET6, ipv6_addr, &ip6.sin6_addr) <= 0)
+ error_msg("inet : the ipv6 address is not proper");
+ else
+ ntohl6(ip6.sin6_addr.s6_addr32, oip);
+ }
+ } else {
+ if (oip) {
+ xioctl(fd, SIOCGIFADDR, &req);
+ ip = (struct sockaddr_in*) &req.ifr_addr;
+ dbg("IP %s\n", inet_ntoa(ip->sin_addr));
+ *oip = ntohl(ip->sin_addr.s_addr);
+ }
}
+
if (ifindex) {
xioctl(fd, SIOCGIFINDEX, &req);
dbg("Adapter index %d\n", req.ifr_ifindex);
@@ -268,6 +443,7 @@ static int get_interface(const char *interface, int *ifindex, uint32_t *oip, uin
memcpy(mac, req.ifr_hwaddr.sa_data, 6);
dbg("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
+
close(fd);
return 0;
}
@@ -351,6 +527,7 @@ static int strtou32(const char *str, void *var)
base = 16;
str+=2;
}
+
long ret_val = strtol(str, &endptr, base);
if (errno) infomsg(infomode, "config : Invalid num %s",str);
else if (endptr && (*endptr!='\0'||endptr == str))
@@ -371,17 +548,15 @@ static int strinvar(const char *str, void *var)
// IP String STR to binary data.
static int striptovar(const char *str, void *var)
{
- in_addr_t addr;
*((uint32_t*)(var)) = 0;
if(!str) {
error_msg("config : NULL address string \n");
return -1;
}
- if((addr = inet_addr(str)) == -1) {
- error_msg("config : wrong address %s \n",str );
+ if((inet_pton(AF_INET6, str, var)<=0) && (inet_pton(AF_INET, str, var)<=0)) {
+ error_msg("config : wrong address %s \n", str);
return -1;
}
- *((uint32_t*)(var)) = (uint32_t)addr;
return 0;
}
@@ -527,14 +702,14 @@ static int get_staticlease(const char *str, void *var)
}
}
striptovar(tkip, &sltmp->nip);
- sltmp->next = gstate.sleases;
- gstate.sleases = sltmp;
+ sltmp->next = gstate.leases.sleases;
+ gstate.leases.sleases = sltmp;
return 0;
}
static struct config_keyword keywords[] = {
-// keyword handler variable address default
+// keyword handler variable address default
{"start" , striptovar , (void*)&gconfig.start_ip , "192.168.0.20"},
{"end" , striptovar , (void*)&gconfig.end_ip , "192.168.0.254"},
{"interface" , strinvar , (void*)&gconfig.interface , "eth0"},
@@ -546,6 +721,7 @@ static struct config_keyword keywords[] = {
{"conflict_time", strtou32 , (void*)&gconfig.conflict_time, "3600"},
{"offer_time" , strtou32 , (void*)&gconfig.offer_time , "60"},
{"lease_file" , strinvar , (void*)&gconfig.lease_file , "/var/lib/misc/dhcpd.leases"}, //LEASES_FILE
+ {"lease6_file" , strinvar , (void*)&gconfig.lease6_file , "/var/lib/misc/dhcpd6.leases"}, //LEASES_FILE
{"pidfile" , strinvar , (void*)&gconfig.pidfile , "/var/run/dhcpd.pid"}, //DPID_FILE
{"siaddr" , striptovar , (void*)&gconfig.siaddr_nip , "0.0.0.0"},
{"option" , strtoopt , (void*)&gconfig.options , ""},
@@ -554,6 +730,12 @@ static struct config_keyword keywords[] = {
{"sname" , strinvar , (void*)&gconfig.sname , ""},
{"boot_file" , strinvar , (void*)&gconfig.boot_file , ""},
{"static_lease" , get_staticlease , (void*)&gconfig.static_leases, ""},
+ {"start6" , striptovar , (void*)&gconfig.start_ip6 , "2001:620:40b:555::100"},
+ {"end6" , striptovar , (void*)&gconfig.end_ip6 , "2001:620:40b:555::200"},
+ {"preferred_lifetime" , strtou32 , (void*)&gconfig.pref_lifetime, "3600"},
+ {"valid_lifetime" , strtou32 , (void*)&gconfig.valid_lifetime, "7200"},
+ {"t1" , strtou32 , (void*)&gconfig.t1 , "3600"},
+ {"t2" , strtou32 , (void*)&gconfig.t2 , "5400"},
};
// Parses the server config file and updates the global server config accordingly.
@@ -564,7 +746,8 @@ static int parse_server_config(char *config_file, struct config_keyword *confkey
int len, linelen, tcount, count, size = ARRAY_LEN(keywords);
for (count = 0; count < size; count++)
- if (confkey[count].handler) confkey[count].handler(confkey[count].def, confkey[count].var);
+ if (confkey[count].handler)
+ confkey[count].handler(confkey[count].def, confkey[count].var);
if (!(fs = fopen(config_file, "r"))) perror_msg("%s", config_file);
for (len = 0, linelen = 0; fs;) {
@@ -608,6 +791,54 @@ free_conf_continue:
return 0;
}
+// opens UDP socket for listen ipv6 packets
+static int open_listensock6(void)
+{
+ struct sockaddr_in6 addr6;
+ struct ipv6_mreq mreq;
+
+ if (gstate.listensock > 0) close(gstate.listensock);
+
+ dbg("Opening listen socket on *:%d %s\n", gconfig.port, gconfig.interface);
+
+ gstate.listensock = xsocket(PF_INET6, SOCK_DGRAM, 0);
+ setsockopt(gstate.listensock, SOL_SOCKET, SO_REUSEADDR, &constone, sizeof(constone));
+
+ if (setsockopt(gstate.listensock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &constone,
+ sizeof(constone)) == -1) {
+ error_msg("failed to receive ipv6 packets.\n");
+ close(gstate.listensock);
+ return -1;
+ }
+
+ setsockopt(gstate.listensock, SOL_SOCKET, SO_BINDTODEVICE, gconfig.interface, strlen(gconfig.interface)+1);
+
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = (flag_chk(FLAG_P))?htons(TT.port):htons(gconfig.port); //SERVER_PORT
+ addr6.sin6_scope_id = if_nametoindex(gconfig.interface);
+ //Listening for multicast packet
+ inet_pton(AF_INET6, "ff02::1:2", &addr6.sin6_addr);
+
+ if (bind(gstate.listensock, (struct sockaddr *) &addr6, sizeof(addr6)) == -1) {
+ close(gstate.listensock);
+ perror_exit("bind failed");
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.ipv6mr_interface = if_nametoindex(gconfig.interface);
+ memcpy(&mreq.ipv6mr_multiaddr, &addr6.sin6_addr, sizeof(addr6.sin6_addr));
+
+ if(setsockopt(gstate.listensock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) {
+ error_msg("failed to join a multicast group.\n");
+ close(gstate.listensock);
+ return -1;
+ }
+
+ dbg("OPEN : success\n");
+ return 0;
+}
+
// opens UDP socket for listen
static int open_listensock(void)
{
@@ -620,18 +851,17 @@ static int open_listensock(void)
gstate.listensock = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt(gstate.listensock, SOL_SOCKET, SO_REUSEADDR, &constone, sizeof(constone));
if (setsockopt(gstate.listensock, SOL_SOCKET, SO_BROADCAST, &constone, sizeof(constone)) == -1) {
- dbg("OPEN : brodcast ioctl failed.\n");
- close(gstate.listensock);
- return -1;
+ error_msg("failed to receive brodcast packets.\n");
+ close(gstate.listensock);
+ return -1;
}
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, gconfig.interface, IFNAMSIZ);
- ifr.ifr_name[IFNAMSIZ -1] = '\0';
+ xstrncpy(ifr.ifr_name, gconfig.interface, IFNAMSIZ);
setsockopt(gstate.listensock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
- addr.sin_port = (flag_chk(FLAG_P))?htons(TT.port):htons(67); //SERVER_PORT
+ addr.sin_port = (flag_chk(FLAG_P))?htons(TT.port):htons(gconfig.port); //SERVER_PORT
addr.sin_addr.s_addr = INADDR_ANY ;
if (bind(gstate.listensock, (struct sockaddr *) &addr, sizeof(addr))) {
@@ -642,6 +872,65 @@ static int open_listensock(void)
return 0;
}
+static int send_packet6(uint8_t relay, uint8_t *client_lla, uint16_t optlen)
+{
+ struct sockaddr_ll dest_sll;
+ dhcp6_raw_t packet;
+ unsigned padding;
+ int fd, result = -1;
+ uint32_t front, back;
+
+ memset(&packet, 0, sizeof(dhcp6_raw_t));
+ memcpy(&packet.dhcp6, &gstate.send.send_pkt6, sizeof(dhcp6_msg_t));
+ padding = sizeof(packet.dhcp6.options) - optlen;
+
+ if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6))) < 0) {
+ dbg("SEND : ipv6 socket failed\n");
+ return -1;
+ }
+ memset(&dest_sll, 0, sizeof(dest_sll));
+ dest_sll.sll_family = AF_PACKET;
+ dest_sll.sll_protocol = htons(ETH_P_IPV6);
+ dest_sll.sll_ifindex = gconfig.ifindex;
+ dest_sll.sll_halen = ETH_ALEN;
+ memcpy(dest_sll.sll_addr, client_lla, sizeof(client_lla));
+
+ if (bind(fd, (struct sockaddr *) &dest_sll, sizeof(dest_sll)) < 0) {
+ dbg("SEND : bind failed\n");
+ close(fd);
+ return -1;
+ }
+ memcpy(&packet.iph.ip6_src, &gconfig.server_nip6, sizeof(uint32_t)*4);
+ //HW addr to Link-Local addr
+ inet_pton(AF_INET6, "fe80::0200:00ff:fe00:0000", &packet.iph.ip6_dst);
+ ntohl6(packet.iph.ip6_dst.__in6_u.__u6_addr32,packet.iph.ip6_dst.__in6_u.__u6_addr32);
+ front = ntohl(*(uint32_t*)(client_lla+3) & 0x00ffffff) >> 8;
+ back = ntohl(*(uint32_t*)(client_lla) & 0x00ffffff);
+ packet.iph.ip6_dst.__in6_u.__u6_addr32[3] =
+ packet.iph.ip6_dst.__in6_u.__u6_addr32[3] | front;
+ packet.iph.ip6_dst.__in6_u.__u6_addr32[2] =
+ packet.iph.ip6_dst.__in6_u.__u6_addr32[2] | back;
+ htonl6(packet.iph.ip6_dst.__in6_u.__u6_addr32,packet.iph.ip6_dst.__in6_u.__u6_addr32);
+
+ packet.udph.source = htons(gconfig.port);
+ packet.udph.dest = htons(546);
+ packet.udph.len = htons(sizeof(dhcp6_raw_t) - sizeof(struct ip6_hdr) - padding);
+ packet.iph.ip6_ctlun.ip6_un1.ip6_un1_plen = htons(ntohs(packet.udph.len) + 0x11);
+ packet.udph.check = dhcp_checksum(&packet, sizeof(dhcp6_raw_t) - padding);
+ packet.iph.ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000);
+ packet.iph.ip6_ctlun.ip6_un1.ip6_un1_plen = packet.udph.len;
+ packet.iph.ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_UDP;
+ packet.iph.ip6_ctlun.ip6_un1.ip6_un1_hlim = 0x64;
+
+ result = sendto(fd, &packet, sizeof(dhcp6_raw_t)-padding,
+ 0, (struct sockaddr *) &dest_sll, sizeof(dest_sll));
+
+ dbg("sendto %d\n", result);
+ close(fd);
+ if (result < 0) dbg("PACKET send error\n");
+ return result;
+}
+
// Sends data through raw socket.
static int send_packet(uint8_t broadcast)
{
@@ -652,7 +941,7 @@ static int send_packet(uint8_t broadcast)
uint8_t bmacaddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
memset(&packet, 0, sizeof(dhcp_raw_t));
- memcpy(&packet.dhcp, &gstate.send_pkt, sizeof(dhcp_msg_t));
+ memcpy(&packet.dhcp, &gstate.send.send_pkt, sizeof(dhcp_msg_t));
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
dbg("SEND : socket failed\n");
@@ -663,17 +952,17 @@ static int send_packet(uint8_t broadcast)
dest_sll.sll_protocol = htons(ETH_P_IP);
dest_sll.sll_ifindex = gconfig.ifindex;
dest_sll.sll_halen = 6;
- memcpy(dest_sll.sll_addr, (broadcast)?bmacaddr:gstate.rcvd_pkt.chaddr , 6);
+ memcpy(dest_sll.sll_addr, (broadcast)?bmacaddr:gstate.rcvd.rcvd_pkt.chaddr , 6);
if (bind(fd, (struct sockaddr *) &dest_sll, sizeof(dest_sll)) < 0) {
dbg("SEND : bind failed\n");
close(fd);
return -1;
}
- padding = 308 - 1 - dhcp_opt_size(gstate.send_pkt.options);
+ padding = 308 - 1 - dhcp_opt_size(gstate.send.send_pkt.options);
packet.iph.protocol = IPPROTO_UDP;
packet.iph.saddr = gconfig.server_nip;
- packet.iph.daddr = (broadcast || (gstate.rcvd_pkt.ciaddr == 0))?INADDR_BROADCAST:gstate.rcvd_pkt.ciaddr;
+ packet.iph.daddr = (broadcast || (gstate.rcvd.rcvd_pkt.ciaddr == 0))?INADDR_BROADCAST:gstate.rcvd.rcvd_pkt.ciaddr;
packet.udph.source = htons(67);//SERVER_PORT
packet.udph.dest = htons(68); //CLIENT_PORT
packet.udph.len = htons(sizeof(dhcp_raw_t) - sizeof(struct iphdr) - padding);
@@ -694,26 +983,45 @@ static int send_packet(uint8_t broadcast)
return result;
}
+static int read_packet6(void)
+{
+ int ret;
+
+ memset(&gstate.rcvd.rcvd_pkt6, 0, sizeof(dhcp6_msg_t));
+ ret = read(gstate.listensock, &gstate.rcvd.rcvd_pkt6, sizeof(dhcp6_msg_t));
+ if (ret < 0) {
+ dbg("Packet read error, ignoring. \n");
+ return ret; // returns -1
+ }
+ if (gstate.rcvd.rcvd_pkt6.msgtype < 1) {
+ dbg("Bad message type, igroning. \n");
+ return -2;
+ }
+
+ dbg("Received an ipv6 packet. Size : %d \n", ret);
+ return ret;
+}
+
// Reads from UDP socket
static int read_packet(void)
{
int ret;
- memset(&gstate.rcvd_pkt, 0, sizeof(dhcp_msg_t));
- ret = read(gstate.listensock, &gstate.rcvd_pkt, sizeof(dhcp_msg_t));
+ memset(&gstate.rcvd.rcvd_pkt, 0, sizeof(dhcp_msg_t));
+ ret = read(gstate.listensock, &gstate.rcvd.rcvd_pkt, sizeof(dhcp_msg_t));
if (ret < 0) {
dbg("Packet read error, ignoring. \n");
return ret; // returns -1
}
- if (gstate.rcvd_pkt.cookie != htonl(DHCP_MAGIC)) {
+ if (gstate.rcvd.rcvd_pkt.cookie != htonl(DHCP_MAGIC)) {
dbg("Packet with bad magic, ignoring. \n");
return -2;
}
- if (gstate.rcvd_pkt.op != 1) { //BOOTPREQUEST
+ if (gstate.rcvd.rcvd_pkt.op != 1) { //BOOTPREQUEST
dbg("Not a BOOT REQUEST ignoring. \n");
return -2;
}
- if (gstate.rcvd_pkt.hlen != 6) {
+ if (gstate.rcvd.rcvd_pkt.hlen != 6) {
dbg("hlen != 6 ignoring. \n");
return -2;
}
@@ -724,16 +1032,24 @@ static int read_packet(void)
// Preapres a dhcp packet with defaults and configs
static uint8_t* prepare_send_pkt(void)
{
- memset((void*)&gstate.send_pkt, 0, sizeof(gstate.send_pkt));
- gstate.send_pkt.op = 2; //BOOTPREPLY
- gstate.send_pkt.htype = 1;
- gstate.send_pkt.hlen = 6;
- gstate.send_pkt.xid = gstate.rcvd_pkt.xid;
- gstate.send_pkt.cookie = htonl(DHCP_MAGIC);
- gstate.send_pkt.nsiaddr = gconfig.server_nip;
- memcpy(gstate.send_pkt.chaddr, gstate.rcvd_pkt.chaddr, 16);
- gstate.send_pkt.options[0] = DHCP_OPT_END;
- return gstate.send_pkt.options;
+ memset((void*)&gstate.send.send_pkt, 0, sizeof(gstate.send.send_pkt));
+ gstate.send.send_pkt.op = 2; //BOOTPREPLY
+ gstate.send.send_pkt.htype = 1;
+ gstate.send.send_pkt.hlen = 6;
+ gstate.send.send_pkt.xid = gstate.rcvd.rcvd_pkt.xid;
+ gstate.send.send_pkt.cookie = htonl(DHCP_MAGIC);
+ gstate.send.send_pkt.nsiaddr = gconfig.server_nip;
+ memcpy(gstate.send.send_pkt.chaddr, gstate.rcvd.rcvd_pkt.chaddr, 16);
+ gstate.send.send_pkt.options[0] = DHCP_OPT_END;
+ return gstate.send.send_pkt.options;
+}
+
+static uint8_t* prepare_send_pkt6(uint16_t opt)
+{
+ memset((void*)&gstate.send.send_pkt6, 0, sizeof(gstate.send.send_pkt6));
+ gstate.send.send_pkt6.msgtype = opt;
+ memcpy(gstate.send.send_pkt6.transaction_id, gstate.rcvd.rcvd_pkt6.transaction_id, 3);
+ return gstate.send.send_pkt6.options;
}
// Sets a option value in dhcp packet's option field
@@ -748,6 +1064,15 @@ static uint8_t* set_optval(uint8_t *optptr, uint16_t opt, void *var, size_t len)
return optptr;
}
+static uint8_t* set_optval6(uint8_t *optptr, uint16_t opt, void *var, size_t len)
+{
+ *((uint16_t*)optptr) = htons(opt);
+ *(uint16_t*)(optptr+2) = htons(len);
+ memcpy(optptr+4, var, len);
+ optptr += len+4;
+ return optptr;
+}
+
// Gets a option value from dhcp packet's option field
static uint8_t* get_optval(uint8_t *optptr, uint16_t opt, void *var)
{
@@ -788,8 +1113,35 @@ static uint8_t* get_optval(uint8_t *optptr, uint16_t opt, void *var)
}
optptr += len + 2;
}
- if ((overloaded == 1) | (overloaded == 3)) get_optval((uint8_t*)&gstate.rcvd_pkt.file, opt, var);
- if ((overloaded == 2) | (overloaded == 3)) get_optval((uint8_t*)&gstate.rcvd_pkt.sname, opt, var);
+ if ((overloaded == 1) | (overloaded == 3)) get_optval((uint8_t*)&gstate.rcvd.rcvd_pkt.file, opt, var);
+ if ((overloaded == 2) | (overloaded == 3)) get_optval((uint8_t*)&gstate.rcvd.rcvd_pkt.sname, opt, var);
+ return optptr;
+}
+
+static uint8_t* get_optval6(uint8_t *optptr, uint16_t opt, uint16_t *datalen, void **var)
+{
+ uint16_t optcode;
+ uint16_t len;
+
+ memcpy(&optcode, optptr, sizeof(uint16_t));
+ memcpy(&len, optptr+2, sizeof(uint16_t));
+ if(!optcode) {
+ dbg("Option %d is not exist.\n", opt);
+ return optptr;
+ }
+ optcode = ntohs(optcode);
+ len = ntohs(len);
+
+ if (opt == optcode) {
+ *var = xmalloc(len);
+ memcpy(*var, optptr+4, len);
+ optptr = optptr + len + 4;
+ memcpy(datalen, &len, sizeof(uint16_t));
+ }
+ else {
+ optptr = get_optval6(optptr+len+4, opt, datalen, var);
+ }
+
return optptr;
}
@@ -798,7 +1150,7 @@ static uint8_t get_reqparam(uint8_t **list)
{
uint8_t len, *optptr;
if(*list) free(*list);
- for (optptr = gstate.rcvd_pkt.options;
+ for (optptr = gstate.rcvd.rcvd_pkt.options;
*optptr && *optptr!=((DHCP_OPT_PARAM_REQ) & 0x00FF); optptr+=optptr[1]+2);
len = *++optptr;
*list = xzalloc(len+1);
@@ -859,7 +1211,7 @@ static void run_notify(char **argv)
dbg("script complete.\n");
}
-static int write_leasefile(void)
+static void write_leasefile(void)
{
int fd;
uint32_t curr, tmp_time;
@@ -867,35 +1219,70 @@ static int write_leasefile(void)
struct arg_list *listdls = gstate.dleases;
dyn_lease *dls;
- if ((fd = open(gconfig.lease_file, O_WRONLY | O_CREAT | O_TRUNC)) < 0) {
+ if ((fd = open(gconfig.lease_file, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
perror_msg("can't open %s ", gconfig.lease_file);
- return fd;
+ } else {
+ curr = timestamp = time(NULL);
+ timestamp = SWAP_BE64(timestamp);
+ writeall(fd, &timestamp, sizeof(timestamp));
+
+ while (listdls) {
+ dls = (dyn_lease*)listdls->arg;
+ tmp_time = dls->expires;
+ dls->expires -= curr;
+ if ((int32_t) dls->expires < 0) goto skip;
+ dls->expires = htonl(dls->expires);
+ writeall(fd, dls, sizeof(dyn_lease));
+skip:
+ dls->expires = tmp_time;
+ listdls = listdls->next;
+ }
+ close(fd);
+ if (gconfig.notify_file) {
+ char *argv[3];
+ argv[0] = gconfig.notify_file;
+ argv[1] = gconfig.lease_file;
+ argv[2] = NULL;
+ run_notify(argv);
+ }
}
+}
- curr = timestamp = time(NULL);
- timestamp = SWAP_BE64(timestamp);
- writeall(fd, &timestamp, sizeof(timestamp));
+static void write_lease6file(void)
+{
+ int fd;
+ uint32_t curr, tmp_time;
+ int64_t timestamp;
+ struct arg_list *listdls = gstate.dleases;
+ dyn_lease6 *dls6;
- while (listdls) {
- dls = (dyn_lease*)listdls->arg;
- tmp_time = dls->expires;
- dls->expires -= curr;
- if ((int32_t) dls->expires < 0) goto skip;
- dls->expires = htonl(dls->expires);
- writeall(fd, dls, sizeof(dyn_lease));
+ if ((fd = open(gconfig.lease6_file, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
+ perror_msg("can't open %s ", gconfig.lease6_file);
+ } else {
+ curr = timestamp = time(NULL);
+ timestamp = SWAP_BE64(timestamp);
+ writeall(fd, &timestamp, sizeof(timestamp));
+
+ while (listdls) {
+ dls6 = (dyn_lease6*)listdls->arg;
+ tmp_time = dls6->expires;
+ dls6->expires -= curr;
+ if ((int32_t) dls6->expires < 0) goto skip;
+ dls6->expires = htonl(dls6->expires);
+ writeall(fd, dls6, sizeof(dyn_lease6));
skip:
- dls->expires = tmp_time;
- listdls = listdls->next;
- }
- close(fd);
- if (gconfig.notify_file) {
- char *argv[3];
- argv[0] = gconfig.notify_file;
- argv[1] = gconfig.lease_file;
- argv[2] = NULL;
- run_notify(argv);
+ dls6->expires = tmp_time;
+ listdls = listdls->next;
+ }
+ close(fd);
+ if (gconfig.notify_file) {
+ char *argv[3];
+ argv[0] = gconfig.notify_file;
+ argv[1] = gconfig.lease6_file;
+ argv[2] = NULL;
+ run_notify(argv);
+ }
}
- return 0;
}
// Update max lease time from options.
@@ -915,15 +1302,52 @@ static uint32_t get_lease(uint32_t req_exp)
{
uint32_t now = time(NULL);
req_exp = req_exp - now;
- if ((req_exp <= 0) || (req_exp > gconfig.max_lease_sec))
- return gconfig.max_lease_sec;
+ if(addr_version == AF_INET6) {
+ if ((req_exp <= 0) || req_exp > gconfig.pref_lifetime ||
+ req_exp > gconfig.valid_lifetime) {
+ if ((gconfig.pref_lifetime > gconfig.valid_lifetime)) {
+ error_msg("The valid lifetime must be greater than the preferred lifetime, \
+ setting to valid lifetime", gconfig.valid_lifetime);
+ return gconfig.valid_lifetime;
+ }
+ return gconfig.pref_lifetime;
+ }
+ } else {
+ if ((req_exp <= 0) || (req_exp > gconfig.max_lease_sec))
+ return gconfig.max_lease_sec;
- if (req_exp < gconfig.min_lease_sec)
- return gconfig.min_lease_sec;
+ if (req_exp < gconfig.min_lease_sec)
+ return gconfig.min_lease_sec;
+ }
return req_exp;
}
+static int verifyip6_in_lease(uint32_t *nip6, uint8_t *duid, uint16_t ia_type, uint32_t iaid)
+{
+ static_lease6 *sls6;
+ struct arg_list *listdls;
+ uint32_t tmpnip6[4] = {0,};
+
+ for (listdls = gstate.dleases; listdls; listdls = listdls->next) {
+ if (!memcmp(((dyn_lease6*) listdls->arg)->lease_nip6, nip6, sizeof(uint32_t)*4))
+ return -1;
+
+ if (!memcmp(((dyn_lease6*) listdls->arg)->duid, duid, ((dyn_lease6*) listdls->arg)->duid_len)
+ && ((dyn_lease6*) listdls->arg)->ia_type == ia_type)
+ return -1;
+ }
+ for (sls6 = gstate.leases.sleases6; sls6; sls6 = sls6->next)
+ if (memcmp(sls6->nip6, nip6, sizeof(uint32_t)*4)==0) return -2;
+
+ ntohl6(nip6, tmpnip6);
+ if (memcmp(tmpnip6, gconfig.start_ip6, sizeof(tmpnip6)) < 0 ||
+ memcmp(tmpnip6, gconfig.end_ip6, sizeof(tmpnip6)) > 0)
+ return -3;
+
+ return 0;
+}
+
// Verify ip NIP in current leases ( assigned or not)
static int verifyip_in_lease(uint32_t nip, uint8_t mac[6])
{
@@ -938,7 +1362,7 @@ static int verifyip_in_lease(uint32_t nip, uint8_t mac[6])
}
if (!memcmp(((dyn_lease*) listdls->arg)->lease_mac, mac, 6)) return -1;
}
- for (sls = gstate.sleases; sls; sls = sls->next)
+ for (sls = gstate.leases.sleases; sls; sls = sls->next)
if (sls->nip == nip) return -2;
if ((ntohl(nip) < gconfig.start_ip) || (ntohl(nip) > gconfig.end_ip))
@@ -979,6 +1403,39 @@ static int addip_to_lease(uint32_t assigned_nip, uint8_t mac[6], uint32_t *req_e
return 0;
}
+static int addip6_to_lease(uint32_t *assigned_nip, uint8_t *duid, uint16_t ia_type, uint32_t iaid, uint32_t *lifetime, uint8_t update)
+{
+ dyn_lease6 *dls6;
+ struct arg_list *listdls = gstate.dleases;
+ uint32_t now = time(NULL);
+
+ while (listdls) {
+ if (!memcmp(((dyn_lease6*) listdls->arg)->duid, duid, ((dyn_lease6*) listdls->arg)->duid_len)) {
+ if (update) *lifetime = get_lease(*lifetime + ((dyn_lease6*) listdls->arg)->expires);
+ ((dyn_lease6*) listdls->arg)->expires = *lifetime + now;
+ return 0;
+ }
+ listdls = listdls->next;
+ }
+
+ dls6 = xzalloc(sizeof(dyn_lease6));
+ dls6->duid_len = sizeof(duid);
+ memcpy(dls6->duid, duid, dls6->duid_len);
+ dls6->ia_type = ia_type;
+ dls6->iaid = iaid;
+ memcpy(dls6->lease_nip6, assigned_nip, sizeof(uint32_t)*4);
+
+ if (update) *lifetime = get_lease(*lifetime + now);
+ dls6->expires = *lifetime + now;
+
+ listdls = xzalloc(sizeof(struct arg_list));
+ listdls->next = gstate.dleases;
+ listdls->arg = (char*)dls6;
+ gstate.dleases = listdls;
+
+ return 0;
+}
+
// delete ip assigned_nip from dynamic lease.
static int delip_from_lease(uint32_t assigned_nip, uint8_t mac[6], uint32_t del_time)
{
@@ -998,7 +1455,7 @@ static int delip_from_lease(uint32_t assigned_nip, uint8_t mac[6], uint32_t del_
static uint32_t getip_from_pool(uint32_t req_nip, uint8_t mac[6], uint32_t *req_exp, char *hostname)
{
uint32_t nip = 0;
- static_lease *sls = gstate.sleases;
+ static_lease *sls = gstate.leases.sleases;
struct arg_list *listdls = gstate.dleases, *tmp = NULL;
if (req_nip && (!verifyip_in_lease(req_nip, mac))) nip = req_nip;
@@ -1042,44 +1499,142 @@ static uint32_t getip_from_pool(uint32_t req_nip, uint8_t mac[6], uint32_t *req_
return nip;
}
-static int read_leasefile(void)
+static uint32_t *getip6_from_pool(uint8_t *duid, uint16_t duid_len, uint16_t ia_type, uint32_t iaid, uint32_t *lifetime)
+{
+ uint32_t nip6[4] = {0,};
+ static_lease6 *sls6 = gstate.leases.sleases6;
+ struct arg_list *listdls6 = gstate.dleases, *tmp = NULL;
+
+ while(listdls6) {
+ if (!memcmp(((dyn_lease6*)listdls6->arg)->duid, duid, duid_len)) {
+ memcpy(nip6, ((dyn_lease6*)listdls6->arg)->lease_nip6, sizeof(nip6));
+ if(tmp) tmp->next = listdls6->next;
+ else gstate.dleases = listdls6->next;
+ free(listdls6->arg);
+ free(listdls6);
+
+ if(verifyip6_in_lease(nip6, duid, ia_type, iaid) < 0)
+ memset(nip6, 0, sizeof(nip6));
+ break;
+ }
+ tmp = listdls6;
+ listdls6 = listdls6->next;
+ }
+
+ if(!nip6[0] && !nip6[1] && !nip6[2] && !nip6[3]) {
+ while(sls6) {
+ if(!memcmp(sls6->duid, duid, 6)) {
+ memcpy(nip6, sls6->nip6, sizeof(nip6));
+ break;
+ }
+ sls6 = sls6->next;
+ }
+ }
+
+ if(!nip6[0] && !nip6[1] && !nip6[2] && !nip6[3]) {
+ uint32_t tmpip6[4] = {0,};
+ int i=3;
+ memcpy(tmpip6, gconfig.start_ip6, sizeof(tmpip6));
+ htonl6(gconfig.start_ip6, nip6);
+ while(memcmp(tmpip6, gconfig.end_ip6, sizeof(tmpip6))<=0) {
+ if(!verifyip6_in_lease(nip6, duid, ia_type, iaid)) break;
+ ntohl6(nip6, tmpip6);
+ while(i--) {
+ if (tmpip6[i] == 0xffff) {
+ tmpip6[i] = 0x0001;
+ } else {
+ ++tmpip6[i];
+ break;
+ }
+ }
+ htonl6(tmpip6, nip6);
+ }
+
+ ntohl6(nip6, tmpip6);
+ if (memcmp(tmpip6, gconfig.end_ip6, sizeof(tmpip6))>0) {
+ memset(nip6, 0, sizeof(nip6));
+ infomsg(infomode, "can't find free IP in IPv6 Pool.");
+ }
+ }
+
+ if(nip6[0] && nip6[1] && nip6[2] && nip6[3])
+ addip6_to_lease(nip6, duid, ia_type, iaid, lifetime, 1);
+ return nip6;
+}
+
+static void read_leasefile(void)
{
uint32_t passed, ip;
int32_t tmp_time;
int64_t timestamp;
dyn_lease *dls;
- int ret = -1, fd = open(gconfig.lease_file, O_RDONLY);
+ int fd = open(gconfig.lease_file, O_RDONLY);
- if (fd < 0) return fd;
dls = xzalloc(sizeof(dyn_lease));
- if (read(fd, &timestamp, sizeof(timestamp)) != sizeof(timestamp)) goto error_exit;
+ if (read(fd, &timestamp, sizeof(timestamp)) != sizeof(timestamp))
+ goto lease_error_exit;
timestamp = SWAP_BE64(timestamp);
passed = time(NULL) - timestamp;
- if ((uint64_t)passed > 12 * 60 * 60) goto error_exit;
+ if ((uint64_t)passed > 12 * 60 * 60) goto lease_error_exit;
while (read(fd, dls, sizeof(dyn_lease)) == sizeof(dyn_lease)) {
ip = ntohl(dls->lease_nip);
if (ip >= gconfig.start_ip && ip <= gconfig.end_ip) {
tmp_time = ntohl(dls->expires) - passed;
if (tmp_time < 0) continue;
- addip_to_lease(dls->lease_nip, dls->lease_mac, (uint32_t*)&tmp_time, dls->hostname, 0);
+ addip_to_lease(dls->lease_nip, dls->lease_mac,
+ (uint32_t*)&tmp_time, dls->hostname, 0);
}
}
- ret = 0;
-error_exit:
+lease_error_exit:
free(dls);
close(fd);
- return ret;
+}
+
+static void read_lease6file(void)
+{
+ uint32_t passed, ip6[4];
+ uint32_t tmp_time;
+ int64_t timestamp;
+ dyn_lease6 *dls6;
+ int fd = open(gconfig.lease6_file, O_RDONLY);
+
+ dls6 = xzalloc(sizeof(dyn_lease6));
+
+ if (read(fd, &timestamp, sizeof(timestamp)) != sizeof(timestamp))
+ goto lease6_error_exit;
+
+ timestamp = SWAP_BE64(timestamp);
+ passed = time(NULL) - timestamp;
+ if ((uint64_t)passed > 12 * 60 * 60) goto lease6_error_exit;
+
+ while (read(fd, dls6, sizeof(dyn_lease6)) == sizeof(dyn_lease6)) {
+ ntohl6(dls6->lease_nip6, ip6);
+ if (memcmp(ip6, gconfig.start_ip6, sizeof(ip6))<0 &&
+ memcmp(ip6, gconfig.end_ip6, sizeof(ip6))>9) {
+ tmp_time = ntohl(dls6->expires) - passed;
+ if (tmp_time < 0U) continue;
+ addip6_to_lease(dls6->lease_nip6, dls6->duid, dls6->ia_type, dls6->iaid,
+ (uint32_t*)&tmp_time, 0);
+ }
+ }
+
+lease6_error_exit:
+ free(dls6);
+ close(fd);
}
void dhcpd_main(void)
{
struct timeval tv;
- int retval;
+ int retval, i;
uint8_t *optptr, msgtype = 0;
+ uint16_t optlen = 0;
uint32_t waited = 0, serverid = 0, requested_nip = 0;
+ uint32_t requested_nip6[4] = {0,};
+ uint8_t transactionid[3] = {0,};
uint32_t reqested_lease = 0, ip_pool_size = 0;
char *hstname = NULL;
fd_set rfds;
@@ -1094,26 +1649,50 @@ void dhcpd_main(void)
infomode |= LOG_SYSTEM;
}
setlinebuf(stdout);
- parse_server_config((toys.optc==1)?toys.optargs[0]:"/etc/dhcpd.conf", keywords); //DHCPD_CONF_FILE
+ //DHCPD_CONF_FILE
+ parse_server_config((toys.optc==1)?toys.optargs[0]:"/etc/dhcpd.conf", keywords);
infomsg(infomode, "toybox dhcpd started");
- gconfig.start_ip = ntohl(gconfig.start_ip);
- gconfig.end_ip = ntohl(gconfig.end_ip);
- ip_pool_size = gconfig.end_ip - gconfig.start_ip + 1;
+
+ if (flag_chk(FLAG_6)){
+ addr_version = AF_INET6;
+ ntohl6(gconfig.start_ip6, gconfig.start_ip6);
+ ntohl6(gconfig.end_ip6, gconfig.end_ip6);
+ gconfig.t1 = ntohl(gconfig.t1);
+ gconfig.t2 = ntohl(gconfig.t2);
+ gconfig.pref_lifetime = ntohl(gconfig.pref_lifetime);
+ gconfig.valid_lifetime = ntohl(gconfig.valid_lifetime);
+ for(i=0;i<4;i++)
+ ip_pool_size += (gconfig.end_ip6[i]-gconfig.start_ip6[i])<<((3-i)*8);
+ } else {
+ gconfig.start_ip = ntohl(gconfig.start_ip);
+ gconfig.end_ip = ntohl(gconfig.end_ip);
+ ip_pool_size = gconfig.end_ip - gconfig.start_ip + 1;
+ }
+
if (gconfig.max_leases > ip_pool_size) {
- error_msg("max_leases=%u is too big, setting to %u", (unsigned) gconfig.max_leases, ip_pool_size);
+ error_msg("max_leases=%u is too big, setting to %u",
+ (unsigned) gconfig.max_leases, ip_pool_size);
gconfig.max_leases = ip_pool_size;
}
write_pid(gconfig.pidfile);
set_maxlease();
read_leasefile();
+ if(TT.iface) gconfig.interface = TT.iface;
+ (addr_version==AF_INET6) ? read_lease6file() : read_leasefile();
- if (get_interface(gconfig.interface, &gconfig.ifindex, &gconfig.server_nip,
+ if (get_interface(gconfig.interface, &gconfig.ifindex,
+ (addr_version==AF_INET6)? gconfig.server_nip6 : &gconfig.server_nip,
gconfig.server_mac)<0)
perror_exit("Failed to get interface %s", gconfig.interface);
- gconfig.server_nip = htonl(gconfig.server_nip);
-
setup_signal();
- open_listensock();
+ if (addr_version==AF_INET6) {
+ htonl6(gconfig.server_nip6, gconfig.server_nip6);
+ open_listensock6();
+ } else {
+ gconfig.server_nip = htonl(gconfig.server_nip);
+ open_listensock();
+ }
+
fcntl(gstate.listensock, F_SETFD, FD_CLOEXEC);
for (;;) {
@@ -1141,10 +1720,14 @@ void dhcpd_main(void)
if (!retval) { // Timed out
dbg("select wait Timed Out...\n");
waited = 0;
- write_leasefile();
- if (get_interface(gconfig.interface, &gconfig.ifindex, &gconfig.server_nip, gconfig.server_mac)<0)
- perror_exit("Interface lost %s\n", gconfig.interface);
- gconfig.server_nip = htonl(gconfig.server_nip);
+ (addr_version==AF_INET6)? write_lease6file() : write_leasefile();
+ if (get_interface(gconfig.interface, &gconfig.ifindex,
+ (addr_version==AF_INET6)? gconfig.server_nip6 : &gconfig.server_nip,
+ gconfig.server_mac)<0)
+ perror_exit("Failed to get interface %s", gconfig.interface);
+ if(addr_version == AF_INET6) {
+ htonl6(gconfig.server_nip6, gconfig.server_nip6);
+ } else gconfig.server_nip = htonl(gconfig.server_nip);
continue;
}
if (FD_ISSET(sigfd.rd, &rfds)) { // Some Activity on RDFDs : is signal
@@ -1154,94 +1737,363 @@ void dhcpd_main(void)
continue;
}
switch (sig) {
- case SIGUSR1:
- infomsg(infomode, "Received SIGUSR1");
- write_leasefile();
- continue;
- case SIGTERM:
- infomsg(infomode, "Received SIGTERM");
- write_leasefile();
- unlink(gconfig.pidfile);
- exit(0);
- break;
- default: break;
+ case SIGUSR1:
+ infomsg(infomode, "Received SIGUSR1");
+ (addr_version==AF_INET6)? write_lease6file() : write_leasefile();
+ continue;
+ case SIGTERM:
+ infomsg(infomode, "received sigterm");
+ (addr_version==AF_INET6)? write_lease6file() : write_leasefile();
+ unlink(gconfig.pidfile);
+ exit(0);
+ break;
+ default: break;
}
}
- if (FD_ISSET(gstate.listensock, &rfds)) { // Some Activity on RDFDs : is socket
+ if (FD_ISSET(gstate.listensock, &rfds)) { // Some Activity on RDFDs : is socke
dbg("select listen sock read\n");
- if (read_packet() < 0) {
- open_listensock();
- continue;
- }
- waited += time(NULL) - timestmp;
- get_optval((uint8_t*)&gstate.rcvd_pkt.options, DHCP_OPT_MESSAGE_TYPE, &gstate.rqcode);
- if (gstate.rqcode == 0 || gstate.rqcode < DHCPDISCOVER
- || gstate.rqcode > DHCPINFORM) {
- dbg("no or bad message type option, ignoring packet.\n");
- continue;
- }
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_SERVER_ID, &serverid);
- if (serverid && (serverid != gconfig.server_nip)) {
- dbg("server ID doesn't match, ignoring packet.\n");
- continue;
- }
- switch (gstate.rqcode) {
- case DHCPDISCOVER:
- msgtype = DHCPOFFER;
- dbg("Message Type : DHCPDISCOVER\n");
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_REQUESTED_IP, &requested_nip);
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_HOST_NAME, &hstname);
- reqested_lease = gconfig.offer_time;
- get_reqparam(&gstate.rqopt);
- optptr = prepare_send_pkt();
- gstate.send_pkt.yiaddr = getip_from_pool(requested_nip, gstate.rcvd_pkt.chaddr, &reqested_lease, hstname);
- if(!gstate.send_pkt.yiaddr){
- msgtype = DHCPNAK;
+ if(addr_version==AF_INET6) {
+ void *client_duid, *server_duid, *client_ia_na, *server_ia_na,
+ *client_ia_pd, *server_ia_pd;
+ uint8_t client_lla[6] = {0,};
+ uint16_t client_duid_len = 0, server_duid_len = 0, server_ia_na_len = 0,
+ client_ia_na_len = 0, client_ia_pd_len = 0;
+
+ if(read_packet6() < 0) {
+ open_listensock6();
+ continue;
+ }
+ waited += time(NULL) - timestmp;
+
+ memcpy(&gstate.rqcode, &gstate.rcvd.rcvd_pkt6.msgtype, sizeof(uint8_t));
+ memcpy(&transactionid, &gstate.rcvd.rcvd_pkt6.transaction_id,
+ sizeof(transactionid));
+
+ if (!gstate.rqcode || gstate.rqcode < DHCP6SOLICIT ||
+ gstate.rqcode > DHCP6RELAYREPLY) {
+ dbg("no or bad message type option, ignoring packet.\n");
+ continue;
+ }
+ if (!gstate.rcvd.rcvd_pkt6.transaction_id ||
+ memcmp(gstate.rcvd.rcvd_pkt6.transaction_id, transactionid, 3)) {
+ dbg("no or bad transaction id, ignoring packet.\n");
+ continue;
+ }
+
+ waited += time(NULL) - timestmp;
+ switch (gstate.rqcode) {
+ case DHCP6SOLICIT:
+ dbg("Message Type: DHCP6SOLICIT\n");
+ optptr = prepare_send_pkt6(DHCP6ADVERTISE);
+ optlen = 0;
+
+ //TODO policy check
+ //TODO Receive: ORO check (e.g. DNS)
+
+ //Receive: Identity Association for Non-temporary Address
+ if(get_optval6((uint8_t*)&gstate.rcvd.rcvd_pkt6.options,
+ DHCP6_OPT_IA_NA, &client_ia_na_len, &client_ia_na)) {
+ uint16_t ia_addr_len = sizeof(struct optval_ia_addr);
+ void *ia_addr, *status_code;
+ char *status_code_msg;
+ uint16_t status_code_len = 0;
+ server_ia_na_len = sizeof(struct optval_ia_na)-sizeof(uint8_t*);
+
+ //IA Address
+ ia_addr = xzalloc(ia_addr_len);
+ struct optval_ia_addr *ia_addr_p = (struct optval_ia_addr*)ia_addr;
+ (*ia_addr_p).pref_lifetime = gconfig.pref_lifetime;
+ (*ia_addr_p).valid_lifetime = gconfig.valid_lifetime;
+ memcpy(&(*ia_addr_p).ipv6_addr,
+ getip6_from_pool(client_duid, client_duid_len,
+ DHCP6_OPT_IA_NA, (*(struct optval_ia_na*) client_ia_na).iaid,
+ &(*ia_addr_p).pref_lifetime), sizeof(uint32_t)*4);
+ server_ia_na_len += (ia_addr_len+4);
+
+ //Status Code
+ if(*(*ia_addr_p).ipv6_addr) {
+ status_code_msg = xstrdup("Assigned an address.");
+ status_code_len = strlen(status_code_msg)+1;
+ status_code = xzalloc(status_code_len);
+ struct optval_status_code *status_code_p =
+ (struct optval_status_code*)status_code;
+ (*status_code_p).status_code = htons(DHCP6_STATUS_SUCCESS);
+ memcpy(&(*status_code_p).status_msg, status_code_msg,
+ status_code_len);
+ server_ia_na_len += (status_code_len+4);
+ free(status_code_msg);
+ } else {
+ status_code_msg = xstrdup("There's no available address.");
+ status_code_len = strlen(status_code_msg)+1;
+ status_code = xzalloc(status_code_len);
+ struct optval_status_code *status_code_p =
+ (struct optval_status_code*)status_code;
+ (*status_code_p).status_code = htons(DHCP6_STATUS_NOADDRSAVAIL);
+ memcpy(&(*status_code_p).status_msg, status_code_msg,
+ status_code_len);
+ server_ia_na_len += (status_code_len+4);
+ server_ia_na_len -= (ia_addr_len+4);
+ ia_addr_len = 0;
+ free(ia_addr);
+ free(status_code_msg);
+ //TODO send failed status code
+ break;
+ }
+
+ //combine options
+ server_ia_na = xzalloc(server_ia_na_len);
+ struct optval_ia_na *ia_na_p = (struct optval_ia_na*)server_ia_na;
+ (*ia_na_p).iaid = (*(struct optval_ia_na*)client_ia_na).iaid;
+ (*ia_na_p).t1 = gconfig.t1;
+ (*ia_na_p).t2 = gconfig.t2;
+
+ uint8_t* ia_na_optptr = &(*ia_na_p).optval;
+ if(ia_addr_len) {
+ set_optval6(ia_na_optptr, DHCP6_OPT_IA_ADDR, ia_addr, ia_addr_len);
+ ia_na_optptr += (ia_addr_len + 4);
+ free(ia_addr);
+ }
+ if(status_code_len) {
+ set_optval6(ia_na_optptr, DHCP6_OPT_STATUS_CODE, status_code,
+ status_code_len);
+ ia_na_optptr += (status_code_len);
+ free(status_code);
+ }
+
+ //Response: Identity Association for Non-temporary Address
+ optptr = set_optval6(optptr, DHCP6_OPT_IA_NA, server_ia_na,
+ server_ia_na_len);
+ optlen += (server_ia_na_len + 4);
+ free(client_ia_na);free(server_ia_na);
+ }
+ //Receive: Identity Association for Prefix Delegation
+ else if(get_optval6((uint8_t*)&gstate.rcvd.rcvd_pkt6.options,
+ DHCP6_OPT_IA_PD, &client_ia_pd_len, &client_ia_pd)) {
+
+ //TODO
+ //Response: Identity Association for Prefix Delegation
+ }
+
+ //Receive: Client Identifier (DUID)
+ get_optval6((uint8_t*)&gstate.rcvd.rcvd_pkt6.options,
+ DHCP6_OPT_CLIENTID, &client_duid_len, &client_duid);
+
+ //DUID type: link-layer address plus time
+ if(ntohs((*(struct optval_duid_llt*)client_duid).type) ==
+ DHCP6_DUID_LLT) {
+ server_duid_len = 8+sizeof(gconfig.server_mac);
+ server_duid = xzalloc(server_duid_len);
+ struct optval_duid_llt *server_duid_p =
+ (struct optval_duid_llt*)server_duid;
+ (*server_duid_p).type = htons(1);
+ (*server_duid_p).hwtype = htons(1);
+ (*server_duid_p).time = htonl((uint32_t)
+ (time(NULL) - 946684800) & 0xffffffff);
+ memcpy(&(*server_duid_p).lladdr, gconfig.server_mac,
+ sizeof(gconfig.server_mac));
+ memcpy(&client_lla, &(*(struct optval_duid_llt*)client_duid).lladdr,
+ sizeof(client_lla));
+
+ //Response: Server Identifier (DUID)
+ optptr = set_optval6(optptr, DHCP6_OPT_SERVERID, server_duid,
+ server_duid_len);
+ optlen += (server_duid_len + 4);
+ //Response: Client Identifier
+ optptr = set_optval6(optptr, DHCP6_OPT_CLIENTID, client_duid,
+ client_duid_len);
+ optlen += (client_duid_len + 4);
+ free(client_duid);free(server_duid);
+ }
+
+ send_packet6(0, client_lla, optlen);
+ write_lease6file();
+ break;
+ case DHCP6REQUEST:
+ dbg("Message Type: DHCP6REQUEST\n");
+ optptr = prepare_send_pkt6(DHCP6REPLY);
+ optlen = 0;
+
+ //Receive: Client Identifier (DUID)
+ get_optval6((uint8_t*)&gstate.rcvd.rcvd_pkt6.options,
+ DHCP6_OPT_CLIENTID, &client_duid_len, &client_duid);
+ optptr = set_optval6(optptr, DHCP6_OPT_CLIENTID, client_duid,
+ client_duid_len);
+ optlen += (client_duid_len + 4);
+ memcpy(&client_lla, &(*(struct optval_duid_llt*)client_duid).lladdr,
+ sizeof(client_lla));
+
+ //Receive: Identity Association for Non-temporary Address
+ if(get_optval6((uint8_t*)&gstate.rcvd.rcvd_pkt6.options,
+ DHCP6_OPT_IA_NA, &client_ia_na_len, &client_ia_na)) {
+ uint16_t ia_addr_len = 0, status_code_len = 0;
+ void *ia_addr, *status_code;
+ uint16_t server_ia_na_len =
+ sizeof(struct optval_ia_na)-sizeof(uint8_t*);
+ char *status_code_msg;
+
+ //Check IA Address
+ get_optval6((uint8_t*)&(*(struct optval_ia_na*)client_ia_na).optval,
+ DHCP6_OPT_IA_ADDR, &ia_addr_len, &ia_addr);
+ struct optval_ia_addr *ia_addr_p = (struct optval_ia_addr*)ia_addr;
+ if(verifyip6_in_lease((*ia_addr_p).ipv6_addr, client_duid,
+ DHCP6_OPT_IA_NA, (*(struct optval_ia_na*)client_ia_na).iaid)
+ == -1) {
+ server_ia_na_len += (ia_addr_len + 4);
+ //Add Status Code
+ status_code_msg = xstrdup("Assigned an address.");
+ status_code_len = strlen(status_code_msg) + 1;
+ status_code = xzalloc(status_code_len);
+ struct optval_status_code *status_code_p =
+ (struct optval_status_code*)status_code;
+ (*status_code_p).status_code = htons(DHCP6_STATUS_SUCCESS);
+ memcpy(&(*status_code_p).status_msg, status_code_msg,
+ status_code_len);
+ server_ia_na_len += (status_code_len+4);
+ } else {
+ //TODO send failed status code
+ break;
+ }
+
+ //combine options
+ server_ia_na = xzalloc(server_ia_na_len);
+ struct optval_ia_na *ia_na_p = (struct optval_ia_na*)server_ia_na;
+ (*ia_na_p).iaid = (*(struct optval_ia_na*)client_ia_na).iaid;
+ (*ia_na_p).t1 = gconfig.t1;
+ (*ia_na_p).t2 = gconfig.t2;
+
+ uint8_t* ia_na_optptr = &(*ia_na_p).optval;
+ ia_na_optptr = set_optval6(ia_na_optptr, DHCP6_OPT_IA_ADDR,
+ ia_addr, ia_addr_len);
+ free(ia_addr);
+
+ if(status_code_len) {
+ ia_na_optptr = set_optval6(ia_na_optptr, DHCP6_OPT_STATUS_CODE,
+ status_code, status_code_len);
+ free(status_code);
+ }
+
+ //Response: Identity Association for Non-temporary Address
+ //(Status Code added)
+ optptr = set_optval6(optptr, DHCP6_OPT_IA_NA,
+ server_ia_na, server_ia_na_len);
+ optlen += (server_ia_na_len + 4);
+ free(client_ia_na);free(server_ia_na);
+ }
+
+ //Receive: Server Identifier (DUID)
+ get_optval6((uint8_t*)&gstate.rcvd.rcvd_pkt6.options,
+ DHCP6_OPT_SERVERID, &server_duid_len, &server_duid);
+ optptr = set_optval6(optptr, DHCP6_OPT_SERVERID,
+ server_duid, server_duid_len);
+ optlen += (server_duid_len + 4);
+
+ free(client_duid); free(server_duid);
+
+ send_packet6(0, client_lla, optlen);
+ write_lease6file();
+ break;
+ case DHCP6RENEW: //TODO
+ case DHCP6REBIND: //TODO
+ case DHCP6RELEASE:
+ dbg("Message Type: DHCP6RELEASE\n");
+ optptr = prepare_send_pkt6(DHCP6REPLY);
+ break;
+ default:
+ dbg("Message Type : %u\n", gstate.rqcode);
+ break;
+ }
+
+ } else {
+ if(read_packet() < 0) {
+ open_listensock();
+ continue;
+ }
+ waited += time(NULL) - timestmp;
+
+ get_optval((uint8_t*)&gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_MESSAGE_TYPE, &gstate.rqcode);
+ if (gstate.rqcode == 0 || gstate.rqcode < DHCPDISCOVER
+ || gstate.rqcode > DHCPINFORM) {
+ dbg("no or bad message type option, ignoring packet.\n");
+ continue;
+ }
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_SERVER_ID, &serverid);
+ if (serverid && (serverid != gconfig.server_nip)) {
+ dbg("server ID doesn't match, ignoring packet.\n");
+ continue;
+ }
+
+ waited += time(NULL) - timestmp;
+ switch (gstate.rqcode) {
+ case DHCPDISCOVER:
+ msgtype = DHCPOFFER;
+ dbg("Message Type : DHCPDISCOVER\n");
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_REQUESTED_IP, &requested_nip);
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_HOST_NAME, &hstname);
+ reqested_lease = gconfig.offer_time;
+ get_reqparam(&gstate.rqopt);
+ optptr = prepare_send_pkt();
+ gstate.send.send_pkt.yiaddr = getip_from_pool(requested_nip,
+ gstate.rcvd.rcvd_pkt.chaddr, &reqested_lease, hstname);
+ if(!gstate.send.send_pkt.yiaddr){
+ msgtype = DHCPNAK;
+ optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+ send_packet(1);
+ break;
+ }
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_LEASE_TIME, &reqested_lease);
+ reqested_lease = htonl(get_lease(reqested_lease + time(NULL)));
optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+ optptr = set_optval(optptr, DHCP_OPT_SERVER_ID, &gconfig.server_nip, 4);
+ optptr = set_optval(optptr, DHCP_OPT_LEASE_TIME, &reqested_lease, 4);
+ optptr = set_reqparam(optptr, gstate.rqopt);
send_packet(1);
break;
- }
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_LEASE_TIME, &reqested_lease);
- reqested_lease = htonl(get_lease(reqested_lease + time(NULL)));
- optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
- optptr = set_optval(optptr, DHCP_OPT_SERVER_ID, &gconfig.server_nip, 4);
- optptr = set_optval(optptr, DHCP_OPT_LEASE_TIME, &reqested_lease, 4);
- optptr = set_reqparam(optptr, gstate.rqopt);
- send_packet(1);
- break;
- case DHCPREQUEST:
- msgtype = DHCPACK;
- dbg("Message Type : DHCPREQUEST\n");
- optptr = prepare_send_pkt();
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_REQUESTED_IP, &requested_nip);
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_LEASE_TIME, &reqested_lease);
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_HOST_NAME, &hstname);
- gstate.send_pkt.yiaddr = getip_from_pool(requested_nip, gstate.rcvd_pkt.chaddr, &reqested_lease, hstname);
- if (!serverid) reqested_lease = gconfig.max_lease_sec;
- if (!gstate.send_pkt.yiaddr) {
- msgtype = DHCPNAK;
+ case DHCPREQUEST:
+ msgtype = DHCPACK;
+ dbg("Message Type : DHCPREQUEST\n");
+ optptr = prepare_send_pkt();
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_REQUESTED_IP, &requested_nip);
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_LEASE_TIME, &reqested_lease);
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_HOST_NAME, &hstname);
+ gstate.send.send_pkt.yiaddr = getip_from_pool(requested_nip,
+ gstate.rcvd.rcvd_pkt.chaddr, &reqested_lease, hstname);
+ if (!serverid) reqested_lease = gconfig.max_lease_sec;
+ if (!gstate.send.send_pkt.yiaddr) {
+ msgtype = DHCPNAK;
+ optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+ send_packet(1);
+ break;
+ }
optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
+ optptr = set_optval(optptr, DHCP_OPT_SERVER_ID, &gconfig.server_nip, 4);
+ reqested_lease = htonl(reqested_lease);
+ optptr = set_optval(optptr, DHCP_OPT_LEASE_TIME, &reqested_lease, 4);
send_packet(1);
+ write_leasefile();
break;
- }
- optptr = set_optval(optptr, DHCP_OPT_MESSAGE_TYPE, &msgtype, 1);
- optptr = set_optval(optptr, DHCP_OPT_SERVER_ID, &gconfig.server_nip, 4);
- reqested_lease = htonl(reqested_lease);
- optptr = set_optval(optptr, DHCP_OPT_LEASE_TIME, &reqested_lease, 4);
- send_packet(1);
- write_leasefile();
- break;
- case DHCPDECLINE:// FALL THROUGH
- case DHCPRELEASE:
- dbg("Message Type : DHCPDECLINE or DHCPRELEASE \n");
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_SERVER_ID, &serverid);
- if (serverid != gconfig.server_nip) break;
- get_optval((uint8_t*) &gstate.rcvd_pkt.options, DHCP_OPT_REQUESTED_IP, &requested_nip);
- delip_from_lease(requested_nip, gstate.rcvd_pkt.chaddr, (gstate.rqcode==DHCPRELEASE)?0:gconfig.decline_time);
- break;
- default:
- dbg("Message Type : %u\n", gstate.rqcode);
- break;
+ case DHCPDECLINE:// FALL THROUGH
+ case DHCPRELEASE:
+ dbg("Message Type : DHCPDECLINE or DHCPRELEASE \n");
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_SERVER_ID, &serverid);
+ if (serverid != gconfig.server_nip) break;
+ get_optval((uint8_t*) &gstate.rcvd.rcvd_pkt.options,
+ DHCP_OPT_REQUESTED_IP, &requested_nip);
+ delip_from_lease(requested_nip, gstate.rcvd.rcvd_pkt.chaddr,
+ (gstate.rqcode==DHCPRELEASE)?0:gconfig.decline_time);
+ break;
+ default:
+ dbg("Message Type : %u\n", gstate.rqcode);
+ break;
+ }
}
}
}
diff --git a/toys/pending/diff.c b/toys/pending/diff.c
index 1b3f80b..8023861 100644
--- a/toys/pending/diff.c
+++ b/toys/pending/diff.c
@@ -768,7 +768,6 @@ void diff_main(void)
struct stat st[2];
int j = 0, k = 1, start[2] = {1, 1};
char *files[2];
- struct dirtree *root;
for (j = 0; j < 2; j++) {
files[j] = toys.optargs[j];
@@ -799,8 +798,7 @@ void diff_main(void)
if (S_ISDIR(st[0].st_mode) && S_ISDIR(st[1].st_mode)) {
for (j = 0; j < 2; j++) {
memset(&dir[j], 0, sizeof(dir));
- root = dirtree_add_node(0, files[j], 1);
- if (root) dirtree_handle_callback(root, list_dir);
+ dirtree_handle_callback(dirtree_start(files[j], 1), list_dir);
dir[j].nr_elm = TT.size; //size updated in list_dir
qsort(&(dir[j].list[1]), (TT.size - 1), sizeof(char*), cmp);
diff --git a/toys/pending/dumpleases.c b/toys/pending/dumpleases.c
index fec3955..86cb4e7 100644
--- a/toys/pending/dumpleases.c
+++ b/toys/pending/dumpleases.c
@@ -41,7 +41,7 @@ void dumpleases_main(void)
int64_t written_time , current_time, exp;
int i, fd;
- if(!(toys.optflags & FLAG_f)) TT.file = "/var/lib/misc/udhcpd.leases"; //DEF_LEASE_FILE
+ if(!(toys.optflags & FLAG_f)) TT.file = "/var/lib/misc/dhcpd.leases"; //DEF_LEASE_FILE
fd = xopen(TT.file, O_RDONLY);
xprintf("Mac Address IP Address Host Name Expires %s\n", (toys.optflags & FLAG_a) ? "at" : "in");
xread(fd, &written_time, sizeof(written_time));
diff --git a/toys/pending/expr.c b/toys/pending/expr.c
index dd27d58..763ad02 100644
--- a/toys/pending/expr.c
+++ b/toys/pending/expr.c
@@ -3,6 +3,9 @@
* Copyright 2013 Daniel Verkamp <daniel@drv.nu>
*
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
+ *
+ * The web standard is incomplete (precedence grouping missing), see:
+ * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141
USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
@@ -10,15 +13,31 @@ config EXPR
bool "expr"
default n
help
- usage: expr args
+ usage: expr ARG1 OPERATOR ARG2...
+
+ Evaluate expression and print result. For example, "expr 1 + 2".
+
+ The supported operators are (grouped from highest to lowest priority):
- Evaluate expression and print result.
+ ( ) : * / % + - != <= < >= > = & |
- The supported operators, in order of increasing precedence, are:
+ Each constant and operator must be a separate command line argument.
+ All operators are infix, meaning they expect a constant (or expression
+ that resolves to a constant) on each side of the operator. Operators of
+ the same priority (within each group above) are evaluated left to right.
+ Parentheses may be used (as separate arguments) to elevate the priority
+ of expressions.
- | & = > >= < <= != + - * / %
+ Calling expr from a command shell requires a lot of \( or '*' escaping
+ to avoid interpreting shell control characters.
- In addition, parentheses () are supported for grouping.
+ The & and | operators are logical (not bitwise) and may operate on
+ strings (a blank string is "false"). Comparison operators may also
+ operate on strings (alphabetical sort).
+
+ Constants may be strings or integers. Comparison, logical, and regex
+ operators may operate on strings (a blank string is "false"), other
+ operators require integers.
*/
// TODO: int overflow checking
@@ -39,44 +58,10 @@ struct value {
long long i;
};
-static void parse_expr(struct value *ret, struct value *v);
-
-static void get_value(struct value *v)
-{
- char *endp, *arg;
-
- if (TT.argidx == toys.optc) {
- v->i = 0;
- v->s = ""; // signal end of expression
- return;
- }
-
- if (TT.argidx >= toys.optc) {
- error_exit("syntax error");
- }
-
- arg = toys.optargs[TT.argidx++];
-
- v->i = strtoll(arg, &endp, 10);
- v->s = *endp ? arg : NULL;
-}
-
-
-// check if v matches a token, and consume it if so
-static int match(struct value *v, const char *tok)
-{
- if (v->s && !strcmp(v->s, tok)) {
- get_value(v);
- return 1;
- }
-
- return 0;
-}
-
// check if v is the integer 0 or the empty string
-static int is_zero(const struct value *v)
+static int is_zero(struct value *v)
{
- return ((v->s && *v->s == '\0') || v->i == 0);
+ return v->s ? !*v->s : !v->i;
}
static char *num_to_str(long long num)
@@ -86,103 +71,106 @@ static char *num_to_str(long long num)
return num_buf;
}
-static int cmp(const struct value *lhs, const struct value *rhs)
+static int cmp(struct value *lhs, struct value *rhs)
{
if (lhs->s || rhs->s) {
// at least one operand is a string
char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
return strcmp(ls, rs);
- } else {
- return lhs->i - rhs->i;
- }
+ } else return lhs->i - rhs->i;
}
-
-// operators
-
-struct op {
- const char *tok;
-
- // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
- void (*calc)(struct value *lhs, const struct value *rhs);
-};
-
-
-static void re(struct value *lhs, const struct value *rhs)
+static void re(struct value *lhs, struct value *rhs)
{
- error_exit("regular expression match not implemented");
+ regex_t rp;
+ regmatch_t rm[2];
+
+ xregcomp(&rp, rhs->s, 0);
+ if (!regexec(&rp, lhs->s, 2, rm, 0) && rm[0].rm_so == 0) {
+ if (rp.re_nsub > 0 && rm[1].rm_so >= 0)
+ lhs->s = xmprintf("%.*s", rm[1].rm_eo - rm[1].rm_so, lhs->s+rm[1].rm_so);
+ else {
+ lhs->i = rm[0].rm_eo;
+ lhs->s = 0;
+ }
+ } else {
+ if (!rp.re_nsub) {
+ lhs->i = 0;
+ lhs->s = 0;
+ } else lhs->s = "";
+ }
}
-static void mod(struct value *lhs, const struct value *rhs)
+static void mod(struct value *lhs, struct value *rhs)
{
if (lhs->s || rhs->s) error_exit("non-integer argument");
if (is_zero(rhs)) error_exit("division by zero");
lhs->i %= rhs->i;
}
-static void divi(struct value *lhs, const struct value *rhs)
+static void divi(struct value *lhs, struct value *rhs)
{
if (lhs->s || rhs->s) error_exit("non-integer argument");
if (is_zero(rhs)) error_exit("division by zero");
lhs->i /= rhs->i;
}
-static void mul(struct value *lhs, const struct value *rhs)
+static void mul(struct value *lhs, struct value *rhs)
{
if (lhs->s || rhs->s) error_exit("non-integer argument");
lhs->i *= rhs->i;
}
-static void sub(struct value *lhs, const struct value *rhs)
+static void sub(struct value *lhs, struct value *rhs)
{
if (lhs->s || rhs->s) error_exit("non-integer argument");
lhs->i -= rhs->i;
}
-static void add(struct value *lhs, const struct value *rhs)
+static void add(struct value *lhs, struct value *rhs)
{
if (lhs->s || rhs->s) error_exit("non-integer argument");
lhs->i += rhs->i;
}
-static void ne(struct value *lhs, const struct value *rhs)
+static void ne(struct value *lhs, struct value *rhs)
{
lhs->i = cmp(lhs, rhs) != 0;
lhs->s = NULL;
}
-static void lte(struct value *lhs, const struct value *rhs)
+static void lte(struct value *lhs, struct value *rhs)
{
lhs->i = cmp(lhs, rhs) <= 0;
lhs->s = NULL;
}
-static void lt(struct value *lhs, const struct value *rhs)
+static void lt(struct value *lhs, struct value *rhs)
{
lhs->i = cmp(lhs, rhs) < 0;
lhs->s = NULL;
}
-static void gte(struct value *lhs, const struct value *rhs)
+static void gte(struct value *lhs, struct value *rhs)
{
lhs->i = cmp(lhs, rhs) >= 0;
lhs->s = NULL;
}
-static void gt(struct value *lhs, const struct value *rhs)
+static void gt(struct value *lhs, struct value *rhs)
{
lhs->i = cmp(lhs, rhs) > 0;
lhs->s = NULL;
}
-static void eq(struct value *lhs, const struct value *rhs)
+static void eq(struct value *lhs, struct value *rhs)
{
- lhs->i = cmp(lhs, rhs) == 0;
+ lhs->i = !cmp(lhs, rhs);
lhs->s = NULL;
}
-static void and(struct value *lhs, const struct value *rhs)
+static void and(struct value *lhs, struct value *rhs)
{
if (is_zero(lhs) || is_zero(rhs)) {
lhs->i = 0;
@@ -190,51 +178,71 @@ static void and(struct value *lhs, const struct value *rhs)
}
}
-static void or(struct value *lhs, const struct value *rhs)
+static void or(struct value *lhs, struct value *rhs)
{
- if (is_zero(lhs)) {
- *lhs = *rhs;
- }
+ if (is_zero(lhs)) *lhs = *rhs;
}
+static void get_value(struct value *v)
+{
+ char *endp, *arg;
-// operators in order of increasing precedence
-static const struct op ops[] = {
- {"|", or },
- {"&", and },
- {"=", eq },
- {">", gt },
- {">=", gte },
- {"<", lt },
- {"<=", lte },
- {"!=", ne },
- {"+", add },
- {"-", sub },
- {"*", mul },
- {"/", divi},
- {"%", mod },
- {":", re },
- {"(", NULL}, // special case - must be last
-};
+ if (TT.argidx == toys.optc) {
+ v->i = 0;
+ v->s = ""; // signal end of expression
+ return;
+ }
+
+// can't happen, the increment is after the == test
+// if (TT.argidx >= toys.optc) error_exit("syntax error");
+ arg = toys.optargs[TT.argidx++];
+
+ v->i = strtoll(arg, &endp, 10);
+ v->s = *endp ? arg : NULL;
+}
-static void parse_parens(struct value *ret, struct value *v)
+// check if v matches a token, and consume it if so
+static int match(struct value *v, char *tok)
{
- if (match(v, "(")) {
- parse_expr(ret, v);
- if (!match(v, ")")) error_exit("syntax error"); // missing closing paren
- } else {
- // v is a string or integer - return it and get the next token
- *ret = *v;
+ if (v->s && !strcmp(v->s, tok)) {
get_value(v);
+ return 1;
}
+
+ return 0;
}
-static void parse_op(struct value *lhs, struct value *tok, const struct op *op)
+// operators in order of increasing precedence
+static struct op {
+ char *tok;
+
+ // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
+ void (*calc)(struct value *lhs, struct value *rhs);
+} ops[] = {
+ {"|", or }, {"&", and }, {"=", eq }, {"==", eq }, {">", gt },
+ {">=", gte }, {"<", lt }, {"<=", lte }, {"!=", ne }, {"+", add },
+ {"-", sub }, {"*", mul }, {"/", divi}, {"%", mod }, {":", re },
+ {"(", NULL}, // special case - must be last
+};
+
+// "|,&,= ==> >=< <= !=,+-,*/%,:"
+
+static void parse_op(struct value *lhs, struct value *tok, struct op *op)
{
+ if (!op) op = ops;
+
// special case parsing for parentheses
if (*op->tok == '(') {
- parse_parens(lhs, tok);
+ if (match(tok, "(")) {
+ parse_op(lhs, tok, 0);
+ if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren
+ } else {
+ // tok is a string or integer - return it and get the next token
+ *lhs = *tok;
+ get_value(tok);
+ }
+
return;
}
@@ -247,11 +255,6 @@ static void parse_op(struct value *lhs, struct value *tok, const struct op *op)
}
}
-static void parse_expr(struct value *ret, struct value *v)
-{
- parse_op(ret, v, ops); // start at the top of the ops table
-}
-
void expr_main(void)
{
struct value tok, ret = {0};
@@ -261,9 +264,10 @@ void expr_main(void)
TT.argidx = 0;
get_value(&tok); // warm up the parser with the initial value
- parse_expr(&ret, &tok);
+ parse_op(&ret, &tok, 0);
- if (!tok.s || *tok.s) error_exit("syntax error"); // final token should be end of expression
+ // final token should be end of expression
+ if (!tok.s || *tok.s) error_exit("syntax error");
if (ret.s) printf("%s\n", ret.s);
else printf("%lld\n", ret.i);
diff --git a/toys/pending/fold.c b/toys/pending/fold.c
index 80bb241..0face64 100644
--- a/toys/pending/fold.c
+++ b/toys/pending/fold.c
@@ -4,7 +4,7 @@
*
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/fold.html
-USE_FOLD(NEWTOY(fold, "bsuw#", TOYFLAG_USR|TOYFLAG_BIN))
+USE_FOLD(NEWTOY(fold, "bsuw#<1", TOYFLAG_USR|TOYFLAG_BIN))
config FOLD
bool "fold"
diff --git a/toys/pending/ftpget.c b/toys/pending/ftpget.c
index 2a81a34..a144713 100644
--- a/toys/pending/ftpget.c
+++ b/toys/pending/ftpget.c
@@ -6,7 +6,7 @@
* No Standard.
*
USE_FTPGET(NEWTOY(ftpget, "<2cvu:p:P#<0=21>65535", TOYFLAG_BIN))
-USE_FTPGET(OLDTOY(ftpput,ftpget, "<2vu:p:P#<0=21>65535", TOYFLAG_BIN))
+USE_FTPGET(OLDTOY(ftpput, ftpget, TOYFLAG_BIN))
config FTPGET
bool "ftpget/ftpput"
diff --git a/toys/pending/groupadd.c b/toys/pending/groupadd.c
index 7df0a5c..615c12f 100644
--- a/toys/pending/groupadd.c
+++ b/toys/pending/groupadd.c
@@ -6,7 +6,7 @@
* See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/groupadd.html
USE_GROUPADD(NEWTOY(groupadd, "<1>2g#<0S", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
-USE_GROUPADD(OLDTOY(addgroup, groupadd, OPTSTR_groupadd, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
+USE_GROUPADD(OLDTOY(addgroup, groupadd, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
config GROUPADD
bool "groupadd"
diff --git a/toys/pending/groupdel.c b/toys/pending/groupdel.c
index 834e113..483ac59 100644
--- a/toys/pending/groupdel.c
+++ b/toys/pending/groupdel.c
@@ -6,7 +6,7 @@
* See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/groupdel.html
USE_GROUPDEL(NEWTOY(groupdel, "<1>2", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
-USE_GROUPDEL(OLDTOY(delgroup, groupdel, OPTSTR_groupdel, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
+USE_GROUPDEL(OLDTOY(delgroup, groupdel, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
config GROUPDEL
bool "groupdel"
diff --git a/toys/pending/ip.c b/toys/pending/ip.c
index 42d3a73..9a01d95 100644
--- a/toys/pending/ip.c
+++ b/toys/pending/ip.c
@@ -8,11 +8,11 @@
* No Standard.
*
USE_IP(NEWTOY(ip, NULL, TOYFLAG_SBIN))
-USE_IP(OLDTOY(ipaddr, ip, NULL, TOYFLAG_SBIN))
-USE_IP(OLDTOY(iplink, ip, NULL, TOYFLAG_SBIN))
-USE_IP(OLDTOY(iproute, ip, NULL, TOYFLAG_SBIN))
-USE_IP(OLDTOY(iprule, ip, NULL, TOYFLAG_SBIN))
-USE_IP(OLDTOY(iptunnel, ip, NULL, TOYFLAG_SBIN))
+USE_IP(OLDTOY(ipaddr, ip, TOYFLAG_SBIN))
+USE_IP(OLDTOY(iplink, ip, TOYFLAG_SBIN))
+USE_IP(OLDTOY(iproute, ip, TOYFLAG_SBIN))
+USE_IP(OLDTOY(iprule, ip, TOYFLAG_SBIN))
+USE_IP(OLDTOY(iptunnel, ip, TOYFLAG_SBIN))
config IP
bool "ip"
@@ -646,7 +646,7 @@ static int link_set(char **argv)
memset(&req, 0, sizeof(req));
if (!*argv) error_exit("\"dev\" missing");
- strncpy(req.ifr_name, *argv, IF_NAMESIZE);
+ xstrncpy(req.ifr_name, *argv, IF_NAMESIZE);
fd = xsocket(AF_INET, SOCK_DGRAM, 0);
xioctl(fd, SIOCGIFINDEX, &req);
for (++argv; *argv;) {
@@ -674,9 +674,9 @@ static int link_set(char **argv)
++argv;
break;
case 5:
- strncpy(req.ifr_ifru.ifru_newname, *argv, IF_NAMESIZE);
+ xstrncpy(req.ifr_ifru.ifru_newname, *argv, IF_NAMESIZE);
xioctl(fd, SIOCSIFNAME, &req);
- strncpy(req.ifr_name, *argv++, IF_NAMESIZE);
+ xstrncpy(req.ifr_name, *argv++, IF_NAMESIZE);
xioctl(fd, SIOCGIFINDEX, &req);
break;
case 6:
@@ -839,7 +839,7 @@ static int get_link_info(struct nlmsghdr* h,struct linkdata* link,char **argv)
link->next = link->prev = 0;
link->iface_type = iface->ifi_type;
if (!lname) error_exit("Invalid link.");
- strncpy(link->type, lname, IFNAMSIZ);
+ xstrncpy(link->type, lname, IFNAMSIZ);
free(lname);
link->iface_idx = iface->ifi_index;
link->flags = iface->ifi_flags;
@@ -886,7 +886,7 @@ static int get_link_info(struct nlmsghdr* h,struct linkdata* link,char **argv)
{"DORMANT", 5}, {"UP", 6}, {NULL, -1}};
if (!(lname = get_flag_string(flags, *((int*)(RTA_DATA(attr))), 0)))
error_exit("Invalid state.");
- strncpy(link->state, lname,IFNAMSIZ);
+ xstrncpy(link->state, lname,IFNAMSIZ);
free(lname);
}
break;
@@ -974,7 +974,7 @@ static int print_addrinfo(struct nlmsghdr *h, int flag_l)
if (flag_l && addrinfo.label && ifa->ifa_family == AF_INET6) return 0;
if ((rta_tb[IFA_LABEL])) {
- strncpy(label, RTA_DATA(rta_tb[IFA_LABEL]), 256);
+ xstrncpy(label, RTA_DATA(rta_tb[IFA_LABEL]), 256);
label[255] = '\0';
if (addrinfo.label && fnmatch(addrinfo.label, label, 0))
return 0;
@@ -2330,8 +2330,8 @@ static int tnl_ioctl(char *dev, int rtype, struct ip_tunnel_parm *ptnl)
int fd, ret = 0;
if ((rtype == SIOCCHGTUNNEL || rtype == SIOCDELTUNNEL) && *ptnl->name)
- strncpy(req.ifr_name, ptnl->name, IF_NAMESIZE);
- else strncpy(req.ifr_name, dev, IF_NAMESIZE);
+ xstrncpy(req.ifr_name, ptnl->name, IF_NAMESIZE);
+ else xstrncpy(req.ifr_name, dev, IF_NAMESIZE);
if (rtype != SIOCGIFHWADDR) req.ifr_ifru.ifru_data = (void*)ptnl;
fd = xsocket(AF_INET, SOCK_DGRAM, 0);
@@ -2448,7 +2448,7 @@ static void parse_iptunnel_args(struct ip_tunnel_parm *ptnl, char **argv,
// frag_off is measured in units of 8 octets (64 bits)
ptnl->iph.frag_off = htons(IP_DF);
if (*argv && ipt_opt_idx <= 2 && string_to_idx(*argv, opts) == -1) {
- strncpy(ptnl->name, *argv, IF_NAMESIZE);
+ xstrncpy(ptnl->name, *argv, IF_NAMESIZE);
if (ipt_opt_idx == 1) {
struct ip_tunnel_parm iptnl_old;
@@ -2545,7 +2545,7 @@ static void parse_iptunnel_args(struct ip_tunnel_parm *ptnl, char **argv,
struct ifreq req;
int fd;
- strncpy(req.ifr_name, *argv, IFNAMSIZ);
+ xstrncpy(req.ifr_name, *argv, IFNAMSIZ);
fd = xsocket(AF_INET, SOCK_DGRAM, 0);
xioctl(fd, SIOCGIFINDEX, &req);
close(fd);
@@ -2578,12 +2578,12 @@ static void parse_iptunnel_args(struct ip_tunnel_parm *ptnl, char **argv,
if (*ptnl->name) error_exit("invalid tunnel");
else {
if (!*++argv) error_exit("name is missing");
- strncpy(ptnl->name, *argv, IF_NAMESIZE);
+ xstrncpy(ptnl->name, *argv, IF_NAMESIZE);
}
break;
default:
if (*ptnl->name) error_exit("invalid tunnel");
- strncpy(ptnl->name, *argv, IF_NAMESIZE);
+ xstrncpy(ptnl->name, *argv, IF_NAMESIZE);
break;
}
}
diff --git a/toys/pending/mdev.c b/toys/pending/mdev.c
index 2d98c25..0c49633 100644
--- a/toys/pending/mdev.c
+++ b/toys/pending/mdev.c
@@ -31,34 +31,53 @@ config MDEV_CONF
#include "toys.h"
-// todo, open() block devices to trigger partition scanning.
-
// mknod in /dev based on a path like "/sys/block/hda/hda1"
static void make_device(char *path)
{
- char *device_name, *s, *temp;
+ char *device_name = NULL, *s, *temp;
int major, minor, type, len, fd;
int mode = 0660;
uid_t uid = 0;
gid_t gid = 0;
- // Try to read major/minor string
-
- temp = strrchr(path, '/');
- fd = open(path, O_RDONLY);
- *temp=0;
- temp = toybuf;
- len = read(fd, temp, 64);
- close(fd);
- if (len<1) return;
- temp[len] = 0;
-
- // Determine device name, type, major and minor
-
- device_name = strrchr(path, '/') + 1;
- type = path[5]=='c' ? S_IFCHR : S_IFBLK;
- major = minor = 0;
- sscanf(temp, "%u:%u", &major, &minor);
+ if (path) {
+ // Try to read major/minor string
+
+ temp = strrchr(path, '/');
+ fd = open(path, O_RDONLY);
+ *temp=0;
+ temp = toybuf;
+ len = read(fd, temp, 64);
+ close(fd);
+ if (len<1) return;
+ temp[len] = 0;
+
+ // Determine device type, major and minor
+
+ type = path[5]=='c' ? S_IFCHR : S_IFBLK;
+ major = minor = 0;
+ sscanf(temp, "%u:%u", &major, &minor);
+ } else {
+ // if (!path), do hotplug
+
+ if (!(temp = getenv("SUBSYSTEM")))
+ return;
+ type = strcmp(temp, "block") ? S_IFCHR : S_IFBLK;
+ major = minor = 0;
+ if (!(temp = getenv("MAJOR")))
+ return;
+ sscanf(temp, "%u", &major);
+ if (!(temp = getenv("MINOR")))
+ return;
+ sscanf(temp, "%u", &minor);
+ path = getenv("DEVPATH");
+ device_name = getenv("DEVNAME");
+ if (!path)
+ return;
+ temp = toybuf;
+ }
+ if (!device_name)
+ device_name = strrchr(path, '/') + 1;
// If we have a config file, look up permissions for this device
@@ -167,9 +186,20 @@ found_device:
}
sprintf(temp, "/dev/%s", device_name);
+
+ if (getenv("ACTION") && !strcmp(getenv("ACTION"), "remove")) {
+ unlink(temp);
+ return;
+ }
+
+ if (strchr(device_name, '/'))
+ mkpathat(AT_FDCWD, temp, 0, 2);
if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
perror_exit("mknod %s failed", temp);
+
+ if (type == S_IFBLK) close(open(temp, O_RDONLY)); // scan for partitions
+
if (CFG_MDEV_CONF) mode=chown(temp, uid, gid);
}
@@ -202,7 +232,7 @@ void mdev_main(void)
if (toys.optflags) {
dirtree_read("/sys/class", callback);
dirtree_read("/sys/block", callback);
+ } else { // hotplug support
+ make_device(NULL);
}
-
- // hotplug support goes here
}
diff --git a/toys/pending/mix.c b/toys/pending/mix.c
deleted file mode 100644
index 4a51fb9..0000000
--- a/toys/pending/mix.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* mix.c - A very basic mixer.
- *
- * Copyright 2014 Brad Conroy, dedicated to the Public Domain.
- *
-
-USE_MIX(NEWTOY(mix, "m:d:l#r#", TOYFLAG_USR|TOYFLAG_BIN))
-config MIX
- bool "mix"
- default n
- help
- usage: mix [-m mixer] [-d device] [-l level / left level] [-r right level]
-
- Lists/sets mixer devices/levels.
-*/
-
-#define FOR_mix
-#include <linux/soundcard.h>
-#include "toys.h"
-
-
-GLOBALS(
- int right;
- int level;
- char *device;
- char *mixer;
-)
-
-void mix_main(void)
-{
- const char *devices[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
- char *mixer_name=(toys.optflags & FLAG_m)?TT.mixer:"/dev/mixer";
- int i, mask, device=-1, level,
- mixer=xopen(mixer_name, O_RDWR|O_NONBLOCK);
-
- xioctl(mixer, SOUND_MIXER_READ_DEVMASK,&mask);
-
- if (!(toys.optflags & FLAG_d)){
- for (i = 0; i < SOUND_MIXER_NRDEVICES; ++i)
- if (1<<i & mask) printf("%s\n",devices[i]);
- return;
- }else{
- for (i = 0; i < SOUND_MIXER_NRDEVICES; ++i){
- if ((1<<i & mask) && !strcmp(devices[i], TT.device)){
- device=i;
- break;
- }
- }
- if (-1==device) return; //with error
- }
-
- if (!(toys.optflags & FLAG_l)){
- xioctl(mixer, MIXER_READ(device),&level);
- if (0xFF < level) printf("%s:%s = left:%d\t right:%d\n", mixer_name,
- devices[device], level>>8, level & 0xFF);
- else printf("%s:%s = %d\n",mixer_name, devices[device], level);
- return;
- }
-
- level=TT.level;
- if (!(toys.optflags & FLAG_r)) level = TT.right | (level<<8);
-
- xioctl(mixer, MIXER_WRITE(device),&level);
- close(mixer);
-}
diff --git a/toys/pending/more.c b/toys/pending/more.c
index 9d89626..f0e7907 100644
--- a/toys/pending/more.c
+++ b/toys/pending/more.c
@@ -17,7 +17,6 @@ config MORE
#define FOR_more
#include "toys.h"
-#include <signal.h>
GLOBALS(
struct termios inf;
@@ -33,31 +32,58 @@ static void signal_handler(int sig)
_exit(sig | 128);
}
+static void show_file_header(const char *name)
+{
+ printf(":::::::::::::::::::::::\n%s\n:::::::::::::::::::::::\n", name);
+}
+
+static int prompt(FILE *cin, const char* fmt, ...)
+{
+ int input_key;
+ va_list ap;
+
+ printf("\33[7m"); // Reverse video before printing the prompt.
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+
+ while (1) {
+ fflush(NULL);
+ input_key = tolower(getc(cin));
+ printf("\33[0m\33[1K\r"); // Reset all attributes, erase to start of line.
+ if (strchr(" \nrq", input_key)) {
+ fflush(NULL);
+ return input_key;
+ }
+ printf("\33[7m(Enter:Next line Space:Next page Q:Quit R:Show the rest)");
+ }
+}
+
static void do_cat_operation(int fd, char *name)
{
char *buf = NULL;
- if(toys.optc > 1) printf(":::::::::::::::::::::::\n"
- "%s\n:::::::::::::::::::::::\n",name);
+ if (toys.optc > 1) show_file_header(name);
for (; (buf = get_line(fd)); free(buf)) printf("%s\n", buf);
}
void more_main()
{
- int ch, lines, input_key = 0, disp_more, more_msg_len;
- unsigned rows = 24, cols = 80;
+ int ch, input_key = 0, show_prompt;
+ unsigned rows = 24, cols = 80, row = 0, col = 0;
struct stat st;
struct termios newf;
FILE *fp, *cin;
if (!isatty(STDOUT_FILENO) || !(cin = fopen("/dev/tty", "r"))) {
loopfiles(toys.optargs, do_cat_operation);
- toys.exitval = 0;
return;
}
TT.cin_fd = fileno(cin);
- tcgetattr(TT.cin_fd,&TT.inf);
+ tcgetattr(TT.cin_fd, &TT.inf);
+
//Prepare terminal for input
memcpy(&newf, &TT.inf, sizeof(struct termios));
newf.c_lflag &= ~(ICANON | ECHO);
@@ -70,52 +96,54 @@ void more_main()
do {
fp = stdin;
if (*toys.optargs && !(fp = fopen(*toys.optargs, "r"))) {
- perror_msg("'%s'", *toys.optargs);
- continue;
+ perror_msg("%s", *toys.optargs);
+ goto next_file;
}
- st.st_size = disp_more = more_msg_len = lines = 0;
+ st.st_size = show_prompt = col = row = 0;
fstat(fileno(fp), &st);
terminal_size(&cols, &rows);
rows--;
- if(toys.optc > 1) {
- printf(":::::::::::::::::::::::\n"
- "%s\n:::::::::::::::::::::::\n",*toys.optargs);
- rows -= 3;
+
+ if (toys.optc > 1) {
+ show_file_header(*toys.optargs);
+ row += 3;
}
while ((ch = getc(fp)) != EOF) {
- if (input_key != 'r' && disp_more) {
- more_msg_len = printf("--More-- ");
- if (st.st_size)
- more_msg_len += printf("(%d%% of %lld bytes)",
- (int) (100 * ( (double) ftell(fp) / (double) st.st_size)),
- st.st_size);
- fflush(NULL);
-
- while (1) {
- input_key = getc(cin);
- input_key = tolower(input_key);
- printf("\r%*s\r", more_msg_len, ""); // Remove previous msg
- if (input_key == ' ' || input_key == '\n' || input_key == 'q'
- || input_key == 'r') break;
- more_msg_len = printf("(Enter:Next line Space:Next page Q:Quit R:Show the rest)");
- }
- more_msg_len = lines = disp_more = 0;
+ if (input_key != 'r' && show_prompt) {
+ if (st.st_size)
+ input_key = prompt(cin, "--More--(%d%% of %lld bytes)",
+ (int) (100 * ( (double) ftell(fp) / (double) st.st_size)),
+ (long long)st.st_size);
+ else
+ input_key = prompt(cin, "--More--");
if (input_key == 'q') goto stop;
+
+ col = row = show_prompt = 0;
terminal_size(&cols, &rows);
rows--;
}
- if (ch == '\n')
- if (++lines >= rows || input_key == '\n') disp_more = 1;
putchar(ch);
+ if (ch == '\t') col = (col | 0x7) + 1; else col++;
+ if (col == cols) putchar(ch = '\n');
+ if (ch == '\n') {
+ col = 0;
+ if (++row >= rows || input_key == '\n') show_prompt = 1;
+ }
}
fclose(fp);
- fflush(NULL);
- } while (*toys.optargs && *++toys.optargs);
+
+next_file:
+ if (*toys.optargs && *++toys.optargs) {
+ input_key = prompt(cin, "--More--(Next file: %s)", *toys.optargs);
+ if (input_key == 'q') goto stop;
+ }
+ } while (*toys.optargs);
stop:
tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
fclose(cin);
+ // Even if optarg not found, exit value still 0
toys.exitval = 0;
}
diff --git a/toys/pending/netstat.c b/toys/pending/netstat.c
index 5935563..fbb9eb1 100644
--- a/toys/pending/netstat.c
+++ b/toys/pending/netstat.c
@@ -76,15 +76,6 @@ static const char *get_basename(char *name)
if (c) return c + 1;
return name;
}
-/*
- * copy string from src to dest -> only number of bytes.
- */
-static char *safe_strncpy(char *dst, char *src, size_t size)
-{
- if(!size) return dst;
- dst[--size] = '\0';
- return strncpy(dst, src, size);
-}
/*
* locate character in string.
@@ -410,7 +401,7 @@ static void add2list(long inode, char *progname)
}
PID_LIST *new = (PID_LIST *)xzalloc(sizeof(PID_LIST));
new->inode = inode;
- safe_strncpy(new->name, progname, PROGNAME_LEN-1);
+ xstrncpy(new->name, progname, PROGNAME_LEN);
new->next = pid_list;
pid_list = new;
}
diff --git a/toys/pending/pgrep.c b/toys/pending/pgrep.c
index 77b6ced..59767b9 100644
--- a/toys/pending/pgrep.c
+++ b/toys/pending/pgrep.c
@@ -5,7 +5,7 @@
*
USE_PGREP(NEWTOY(pgrep, "?P# s# xvonlf[!sP]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_PGREP(OLDTOY(pkill, pgrep, OPTSTR_pgrep, TOYFLAG_USR|TOYFLAG_BIN))
+USE_PGREP(OLDTOY(pkill, pgrep, TOYFLAG_USR|TOYFLAG_BIN))
config PGREP
bool "pgrep"
diff --git a/toys/pending/printf.c b/toys/pending/printf.c
deleted file mode 100644
index 0970c8c..0000000
--- a/toys/pending/printf.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/* printf.c - Format and Print the data.
- *
- * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com>
- * Copyright 2014 Kyungwan Han <asura321@gmail.com>
- *
- * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
-
-USE_PRINTF(NEWTOY(printf, "<1", TOYFLAG_USR|TOYFLAG_BIN))
-
-config PRINTF
- bool "printf"
- default n
- help
- usage: printf Format [Arguments..]
-
- Format and print ARGUMENT(s) according to FORMAT.
- Format is 'C' control output as 'C' printf.
-*/
-#define FOR_printf
-#include "toys.h"
-
-GLOBALS(
- char *hv_w;
- char *hv_p;
- int encountered;
-)
-
-// Calculate width and precision from format string
-static int get_w_p()
-{
- char *ptr, *str = *toys.optargs;
-
- errno = 0;
- if (*str == '-') str++;
- long value = strtol(str, &ptr, 10);
- if (errno || (ptr && (*ptr != '\0' || ptr == str)))
- perror_msg("Invalid num %s", *toys.optargs);
- if (*--str == '-') return (int)(-1 * value);
- return value;
-}
-
-// Add ll and L to Interger and floating point formats respectively.
-static char *get_format(char *f)
-{
- int len = strlen(f);
- char last = f[--len], *post = "";
-
- f[len] = '\0';
- if (strchr("diouxX", last)) post = "ll"; // add ll to integer modifier.
- else if (strchr("feEgG", last)) post = "L"; // add L to float modifier.
- return xmprintf("%s%s%c", f, post, last);
-}
-
-// Print arguments with corresponding conversion and width and precision.
-static void print(char *fmt, int w, int p, int l)
-{
- char *ptr = (fmt+l-1), *ep = NULL, *format = NULL;
- long long val;
- long double dval;
-
- errno = 0;
- switch (*ptr) {
- case 'd': /*FALL_THROUGH*/
- case 'i': /*FALL_THROUGH*/
- case 'o': /*FALL_THROUGH*/
- case 'u': /*FALL_THROUGH*/
- case 'x': /*FALL_THROUGH*/
- case 'X':
- if (!*toys.optargs) val = 0;
- else {
- if (**toys.optargs == '\'' || **toys.optargs == '"')
- val = *((*toys.optargs) + 1);
- else {
- val = strtoll(*toys.optargs, &ep, 0);
- if (errno || (ep && (*ep != '\0' || ep == *toys.optargs))) {
- perror_msg("Invalid num %s", *toys.optargs);
- val = 0;
- }
- }
- }
- format = get_format(fmt);
- TT.hv_w ? (TT.hv_p ? printf(format, w, p, val) : printf(format, w, val))
- : (TT.hv_p ? printf(format, p, val) : printf(format, val));
- break;
- case 'g': /*FALL_THROUGH*/
- case 'e': /*FALL_THROUGH*/
- case 'E': /*FALL_THROUGH*/
- case 'G': /*FALL_THROUGH*/
- case 'f':
- if (*toys.optargs) {
- dval = strtold(*toys.optargs, &ep);
- if (errno || (ep && (*ep != '\0' || ep == *toys.optargs))) {
- perror_msg("Invalid num %s", *toys.optargs);
- dval = 0;
- }
- } else dval = 0;
- format = get_format(fmt);
- TT.hv_w ? (TT.hv_p ? printf(format, w, p, dval):printf(format, w, dval))
- : (TT.hv_p ? printf(format, p, dval) : printf(format, dval));
- break;
- case 's':
- {
- char *str = (*toys.optargs ? *toys.optargs : "");
- TT.hv_w ? (TT.hv_p ? printf(fmt,w,p,str): printf(fmt, w, str))
- : (TT.hv_p ? printf(fmt, p, str) : printf(fmt, str));
- }
- break;
- case 'c':
- printf(fmt, (*toys.optargs ? **toys.optargs : '\0'));
- break;
- }
- if (format) free(format);
-}
-
-// Handle the escape sequences.
-static int handle_slash(char **esc_val)
-{
- char *ptr = *esc_val;
- int esc_length = 0;
- unsigned base = 0, num = 0, result = 0, count = 0;
-
- /*
- * Hex escape sequence have only 1 or 2 digits, xHH. Oct escape sequence
- * have 1,2 or 3 digits, xHHH. Leading "0" (\0HHH) we are ignoring.
- */
- if (*ptr == 'x') {
- ptr++;
- esc_length++;
- base = 16;
- } else if (isdigit(*ptr)) base = 8;
-
- while (esc_length < 3 && base) {
- num = tolower(*ptr) - '0';
- if (num > 10) num += ('0' - 'a' + 10);
- if (num >= base) {
- if (base == 16) {
- esc_length--;
- if (!esc_length) {// Invalid hex value eg. /xvd, print as it is /xvd
- result = '\\';
- ptr--;
- }
- }
- break;
- }
- esc_length++;
- count = result = (count * base) + num;
- ptr++;
- }
- if (base) {
- ptr--;
- *esc_val = ptr;
- return (char)result;
- } else {
- switch (*ptr) {
- case 'n': result = '\n'; break;
- case 't': result = '\t'; break;
- case 'e': result = (char)27; break;
- case 'b': result = '\b'; break;
- case 'a': result = '\a'; break;
- case 'f': result = '\f'; break;
- case 'v': result = '\v'; break;
- case 'r': result = '\r'; break;
- case '\\': result = '\\'; break;
- default :
- result = '\\';
- ptr--; // Let pointer pointing to / we will increment after returning.
- break;
- }
- }
- *esc_val = ptr;
- return (char)result;
-}
-
-// Handle "%b" option with '\' interpreted.
-static void print_esc_str(char *str)
-{
- for (; *str; str++) {
- if (*str == '\\') {
- str++;
- xputc(handle_slash(&str)); //print corresponding char
- } else xputc(*str);
- }
-}
-
-// Prase the format string and print.
-static void parse_print(char *f)
-{
- char *start, *p, format_specifiers[] = "diouxXfeEgGcs";
- int len = 0, width = 0, prec = 0;
-
- while (*f) {
- switch (*f) {
- case '%':
- start = f++;
- len++;
- if (*f == '%') {
- xputc('%');
- break;
- }
- if (*f == 'b') {
- if (*toys.optargs) {
- print_esc_str(*toys.optargs++);
- TT.encountered = 1;
- } else print_esc_str("");
- break;
- }
- if (strchr("-+# ", *f)) f++, len++;
- if (*f == '*') {
- f++, len++;
- if (*toys.optargs) {
- width = get_w_p();
- toys.optargs++;
- }
- } else {
- while (isdigit(*f)) f++, len++;
- }
- if (*f == '.') {
- f++, len++;
- if (*f == '*') {
- f++, len++;
- if (*toys.optargs) {
- prec = get_w_p();
- toys.optargs++;
- }
- } else {
- while (isdigit(*f)) f++, len++;
- }
- }
- if (!(p = strchr(format_specifiers, *f)))
- perror_exit("Missing OR Invalid format specifier");
- else {
- len++;
- TT.hv_p = strstr(start, ".*");
- TT.hv_w = strchr(start, '*');
- //pitfall: handle diff b/w * and .*
- if ((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL;
- memcpy((p = xzalloc(len+1)), start, len);
- print(p, width, prec, len);
- if (*toys.optargs) toys.optargs++;
- free(p);
- p = NULL;
- }
- TT.encountered = 1;
- break;
- case '\\':
- if (f[1]) {
- if (*++f == 'c') exit(0); //Got '\c', so no further output
- xputc(handle_slash(&f));
- } else xputc(*f);
- break;
- default:
- xputc(*f);
- break;
- }
- f++;
- len = 0;
- }
-}
-
-void printf_main(void)
-{
- char *format = *toys.optargs++;
-
- TT.encountered = 0;
- parse_print(format); //printf acc. to format.
- //Re-use FORMAT arg as necessary to convert all given ARGS.
- while (*toys.optargs && TT.encountered) parse_print(format);
- xflush();
-}
diff --git a/toys/pending/ps.c b/toys/pending/ps.c
index f6680bf..cb0f32c 100644
--- a/toys/pending/ps.c
+++ b/toys/pending/ps.c
@@ -1,426 +1,358 @@
-/* ps.c - Show running process statistics.
+/* ps.c - show process list
*
- * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
- * Copyright 2013 Kyungwan Han <asura321@gmail.com>
+ * Copyright 2015 Rob Landley <rob@landley.net>
*
* 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 to specify an alternate /etc/passwd (??!?)
+ * 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.
+ * 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.
-USE_PS(NEWTOY(ps, ">0o*T", TOYFLAG_BIN))
+USE_PS(NEWTOY(ps, "aAdeflo*[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN))
config PS
bool "ps"
default n
help
- usage: ps [-o COL1,COL2=HEADER] [-T]
-
- Show list of processes
-
- -o COL1,COL2=HEADER Select columns for display
- -T Show threads
+ usage: ps [-Aade] [-fl] [-gG GROUP] [-o FIELD] [-p PID] [-t TTY] [-u USER]
+
+ List processes.
+
+ -A All processes
+ -a Processes with terminals, except session leaders
+ -d Processes that aren't session leaders
+ -e Same as -A
+ -f Full listing
+ -l Long listing
+
+ -g Processes belonging to these session leaders
+ -G Processes with these real group IDs
+ -o Show FIELDS for each process
+ -p select by PID
+ -t select by TTY
+ -u select by USER
+ -U select by USER
+
+ GROUP, FIELD, PID, TTY, and USER are comma separated lists.
+
+ OUTPUT (-o) FIELDS:
+
+ "UID", "PID", "PPID", "C", "PRI", "NI", "ADDR", "SZ",
+ "WCHAN", "STIME", "TTY", "TIME", "CMD", "COMMAND", "ELAPSED", "GROUP",
+ "%CPU", "PGID", "RGROUP", "RUSER", "USER", "VSZ"
+
+ C Processor utilization for scheduling
+ F Process flags (PF_*) from linux source file include/sched.h
+ (in octal rather than hex because posix)
+ S Process state:
+ R (running) S (sleeping) D (disk sleep) T (stopped) t (tracing stop)
+ Z (zombie) X (dead) x (dead) K (wakekill) W (waking)
+ PID Process id
+ PPID Parent process id
+ PRI Priority
+ UID User id of process owner
+
+ Default output is -o PID,TTY,TIME,CMD
+ With -f USER=UID,PID,PPID,C,STIME,TTY,TIME,CMD
+ With -l F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD
*/
#define FOR_ps
#include "toys.h"
GLOBALS(
- struct arg_list *llist_o;
- unsigned screen_width;
+ struct arg_list *o;
+
+ unsigned width;
+ dev_t tty;
+ void *fields;
+ long uptime;
)
-#define BUFF_SIZE 1024
-struct header_list {
- char *name;
- char *header;
- char *format;
- int width;
- int position;
- struct header_list *next;
-};
+/*
+ l l fl a fl fl l l l l l f a a a
+ F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
+ ruser user rgroup group pid ppid pgid pcpu vsz nice etime time tty comm args
-struct header_list *o_list = NULL; //List of Header attributes.
+ todo: thread support /proc/$d/task/%d/stat
+ man page: F flags mean...
+*/
-/*
- * create list of header attributes taking care of -o (-o ooid=MOM..)
- * and width of attributes.
- */
-static void list_add(struct header_list **list, struct header_list *data, char *c_data)
-{
- struct header_list *temp = *list, *new = xzalloc(sizeof(struct header_list));
-
- new->name = data->name;
- if (c_data) new->header = c_data;
- else new->header = xstrdup(data->header);
- if (c_data && (strlen(c_data) > data->width)) new->width = strlen(c_data);
- else new->width = data->width;
- new->format = data->format;
- new->position = data->position;
-
- if (temp) {
- while (temp->next) temp = temp->next;
- temp->next = new;
- } else *list = new;
-}
+struct strawberry {
+ struct strawberry *next, *prev;
+ short which, len;
+ char title[];
+};
-//print the default header OR header with -o args
-static void print_header(struct header_list *hdr, int hdr_len)
+// dirtree callback.
+// toybuf used as: 1024 /proc/$PID/stat, 1024 slot[], 2048 /proc/$PID/cmdline
+static int do_ps(struct dirtree *new)
{
- int i = 0;
- char *ptr = NULL, *str, *temp;
- struct arg_list *node = TT.llist_o;
-
- // Default pid, user, time, comm
- if (!node) {
- list_add(&o_list, hdr+4, 0);
- list_add(&o_list, hdr, 0);
- list_add(&o_list, hdr+11, 0);
- list_add(&o_list, hdr+3, 0);
- }
+ struct strawberry *field;
+ long long *slot = (void *)(toybuf+1024);
+ char *name, *s, state;
+ int nlen, i, fd, len, width = TT.width;
+
+ if (!new->parent) return DIRTREE_RECURSE;
+ if (!(*slot = atol(new->name))) return 0;
+
+ // name field limited to 256 bytes by VFS, plus 40 fields * max 20 chars:
+ // 1000-ish total, but some forced zero so actually there's headroom.
+ sprintf(toybuf, "%lld/stat", *slot);
+ if (!readfileat(dirtree_parentfd(new), toybuf, toybuf, 1024)) return 0;
+
+ // parse oddball fields (name and state)
+ if (!(s = strchr(toybuf, '('))) return 0;
+ for (name = ++s; *s != ')'; s++) if (!*s) return 0;
+ nlen = s++-name;
+ if (1>sscanf(++s, " %c%n", &state, &i)) return 0;
+
+ // parse numeric fields
+ for (len = 1; len<100; len++)
+ if (1>sscanf(s += i, " %lld%n", slot+len, &i)) break;
+
+ // skip entries we don't care about.
+ if ((toys.optflags&(FLAG_a|FLAG_d)) && getsid(*slot)==*slot) return 0;
+ if ((toys.optflags&FLAG_a) && !slot[4]) return 0;
+ if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e)) && TT.tty!=slot[4])
+ return 0;
+
+ for (field = TT.fields; field; field = field->next) {
+ char *out = toybuf+2048;
+
+ // Default: unsupported (5 "C")
+ sprintf(out, "-");
+
+ // PID, PPID, PRI, NI, ADDR, SZ
+ if (-1 != (i = stridx((char[]){3,4,6,7,8,9,0}, field->which)))
+ sprintf(out, ((1<<i)&0x10) ? "%llx" : "%lld",
+ slot[((char[]){0,2,16,17,22})[i]]>>(((1<<i)&0x20) ? 12 : 0));
+ // F
+ else if (!(i = field->which)) sprintf(out, "%llo", slot[7]);
+ // S
+ else if (i == 1)
+ sprintf(out, "%c", state);
+ // UID and USER
+ else if (i == 2 || i == 22) {
+ sprintf(out, "%d", new->st.st_uid);
+ if (i == 22) {
+ struct passwd *pw = getpwuid(new->st.st_uid);
+
+ if (pw) out = pw->pw_name;
+ }
+ // WCHAN
+ } else if (i==10) {
+ sprintf(toybuf+512, "%lld/wchan", *slot);
+ readfileat(dirtree_parentfd(new), toybuf+512, out, 2047);
+
+ // STIME
+ // TTY
+ } else if (i==12) {
+
+ // Can we readlink() our way to a name?
+ for (i=0; i<3; i++) {
+ struct stat st;
+
+ sprintf(toybuf+512, "%lld/fd/%i", *slot, i);
+ fd = dirtree_parentfd(new);
+ if (!fstatat(fd, toybuf+512, &st, 0) && S_ISCHR(st.st_mode)
+ && st.st_rdev == slot[4]
+ && 0<(len = readlinkat(fd, toybuf+512, out, 2047)))
+ {
+ out[len] = 0;
+ if (!strncmp(out, "/dev/", 5)) out += 5;
- while (node) {
- char *s = str = xstrdup(node->arg);
-
- i = 0;
- while (str) {
- if ((ptr = strsep(&str, ","))) { //seprate list
- if ((temp = strchr(ptr, '='))) { // Handle ppid = MOM
- *temp = 0;
- temp++;
- while (hdr[i].name) {
- // search from default header
- if (!(strcmp(hdr[i].name, ptr))) {
- //handle condition like ppid = M,OM
- if (str) ptr = xmprintf("%s,%s", temp, str);
- else ptr = xmprintf("%s", temp);
- list_add(&o_list, &hdr[i], ptr);
- break;
- }
- i++;
- }
- if (!hdr[i].name) perror_exit("Invalid arg for -o option");
break;
- } else {
- while (hdr[i].name) {
- if (!(strcmp(hdr[i].name, ptr))) {
- list_add(&o_list, &hdr[i], 0);
- break;
- }
- i++;
- }
- if (!hdr[i].name) error_exit("bad -o");
- i = 0;
}
}
+
+ // Couldn't find it, show major:minor
+ if (i==3) {
+ i = slot[4];
+ sprintf(out, "%d:%d", (i>>8)&0xfff, ((i>>12)&0xfff00)|(i&0xff));
+ }
+
+ // TIME ELAPSED
+ } else if (i==13 || i==16) {
+ long seconds = (i==16) ? slot[20] : slot[11]+slot[12], ll = 60*60*24;
+
+ seconds /= sysconf(_SC_CLK_TCK);
+ if (i==16) seconds = TT.uptime-seconds;
+ for (s = out, i = 0; i<4; i++) {
+ if (i>1 || seconds > ll)
+ s += sprintf(s, (i==3) ? "%02ld" : "%ld%c", seconds/ll, "-::"[i]);
+ seconds %= ll;
+ ll /= i ? 60 : 24;
+ }
+
+//16 "ELAPSED", "GROUP", "%CPU", "PGID", "RGROUP",
+//21 "RUSER", -, "VSZ"
+
+ // Command line limited to 2k displayable. 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. :)
+ } else if (i == 14 || i == 15) {
+ int fd;
+
+ len = 0;
+ sprintf(out, "%lld/cmdline", *slot);
+ fd = openat(dirtree_parentfd(new), out, O_RDONLY);
+
+ if (fd != -1) {
+ if (0<(len = read(fd, out, 2047))) {
+ if (!out[len-1]) len--;
+ else out[len] = 0;
+ for (i = 0; i<len; i++) if (out[i] < ' ') out[i] = ' ';
+ }
+ close(fd);
+ }
+ if (len<1) sprintf(out, "[%.*s]", nlen, name);
}
- free(s);
- node = node->next;
- }
- for (hdr = o_list; hdr; hdr = hdr->next)
- printf(hdr->format , hdr->width, hdr->header);
+ i = width<field->len ? width : field->len;
+ width -= printf(" %*.*s", i, field->next ? i : width, out);
+ }
xputc('\n');
+
+ return 0;
}
-//get uid/gid for processes.
-static void get_uid_gid(char *p, char *id_str, unsigned *id)
+void ps_main(void)
{
- FILE *f;
-
- if(!p) return;
- f = xfopen(p, "r");
- while (fgets(toybuf, BUFF_SIZE, f)) {
- if (!strncmp(toybuf, id_str, strlen(id_str))) {
- sscanf(toybuf, "%*s %u", id);
- break;
- }
+ struct strawberry *field;
+ // Octal output code followed by header name
+ char widths[] = {1,1,5,5,5,2,3,3,4,5,6,5,8,8,27,27,11,8,4,5,8,8,8,6},
+ *typos[] = {
+ "F", "S", "UID", "PID", "PPID", "C", "PRI", "NI", "ADDR", "SZ",
+ "WCHAN", "STIME", "TTY", "TIME", "CMD", "COMMAND", "ELAPSED", "GROUP",
+ "%CPU", "PGID", "RGROUP", "RUSER", "USER", "VSZ"
+ };
+ int i, fd = -1;
+
+ TT.width = 80;
+ terminal_size(&TT.width, 0);
+ TT.width--;
+
+ // find controlling tty, falling back to /dev/tty if none
+ for (i = fd = 0; i < 4; i++) {
+ struct stat st;
+
+ if (i != 3 || -1 != (i = fd = open("/dev/tty", O_RDONLY))) {
+ if (isatty(i) && !fstat(i, &st)) {
+ TT.tty = st.st_rdev;
+ break;
+ }
+ }
}
- fclose(f);
-}
+ if (fd != -1) close(fd);
-//get etime for processes.
-void get_etime(unsigned long s_time)
-{
- unsigned long min;
- unsigned sec;
- struct sysinfo info;
- char *temp;
-
- sysinfo(&info);
- min = s_time/sysconf(_SC_CLK_TCK);
- min = info.uptime - min;
- sec = min % 60;
- min = min / 60;
- temp = xmprintf("%3lu:%02u", min,sec);
- xprintf("%*.*s",7,7,temp);
- free(temp);
-}
+ sysinfo((void *)toybuf);
+ // Because "TT.uptime = *(long *)toybuf;" triggers a bug in gcc.
+ {
+ long *sigh = (long *)toybuf;
+ TT.uptime = *sigh;
+ }
-//get time attributes for processes.
-void get_time(unsigned long s_time, unsigned long u_time)
-{
- unsigned long min;
- unsigned sec;
- char *temp;
-
- min = (s_time + u_time)/sysconf(_SC_CLK_TCK);
- sec = min % 60;
- min = min / 60;
- temp = xmprintf("%3lu:%02u", min,sec);
- xprintf("%*.*s",6,6,temp);
- free(temp);
-}
+ // Manual field selection via -o
+ if (toys.optflags&FLAG_o) {
+ struct arg_list *ol;
+ int length;
-/*
- * read command line taking care of in between NUL's
- * in command line
- */
-static void read_cmdline(int fd, char *cmd_ptr)
-{
- int size = read(fd, cmd_ptr, BUFF_SIZE); //sizeof(cmd_buf)
+ for (ol = TT.o; ol; ol = ol->next) {
+ char *width, *type, *title, *end, *arg = ol->arg;
- cmd_ptr[size] = '\0';
- while (--size > 0 && cmd_ptr[size] == '\0'); //reach to last char
+ // Set title, length of title, type, end of type, and display width
+ while ((type = comma_iterate(&arg, &length))) {
+ if ((end = strchr(type, '=')) && length<(end-type)) {
+ title = end+1;
+ length = (end-type)-1;
+ } else {
+ end = type+length;
+ title = 0;
+ }
- while (size >= 0) {
- if ((unsigned char)cmd_ptr[size] < ' ') cmd_ptr[size] = ' ';
- size--;
- }
-}
+ // If changing display width, trim title at the :
+ if ((width = strchr(type, ':')) && width<end) {
+ if (!title) length = width-type;
+ } else width = 0;
-/*
- * get the processes stats and print the stats
- * corresponding to header attributes.
- */
-static void do_ps_line(int pid, int tid)
-{
- char *stat_buff = toybuf + BUFF_SIZE, *cmd_buff = toybuf + (2*BUFF_SIZE);
- char state[4] = {0,};
- int tty, tty_major, tty_minor, fd, n, nice, width_counter = 0;
- struct stat stats;
- struct passwd *pw;
- struct group *gr;
- char *name, *user, *group, *ruser, *rgroup, *ptr;
- long rss;
- unsigned long stime, utime, start_time, vsz;
- unsigned ppid, ruid, rgid, pgid;
- struct header_list *p = o_list;
-
- sprintf(stat_buff, "/proc/%d", pid);
- if(stat(stat_buff, &stats)) return;
-
- if (tid) {
- if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/task/%d/stat", pid, tid) >= BUFF_SIZE) return;
- if (snprintf(cmd_buff, BUFF_SIZE, "/proc/%d/task/%d/cmdline", pid, tid) >= BUFF_SIZE) return;
- } else {
- if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/stat", pid) >= BUFF_SIZE) return;
- if (snprintf(cmd_buff, BUFF_SIZE, "/proc/%d/cmdline", pid) >= BUFF_SIZE) return;
- }
+ // Allocate structure, copy title
+ field = xmalloc(sizeof(struct strawberry)+length+1);
+ if (title) {
+ memcpy(field->title, title, length);
+ field->title[length] = 0;
+ }
+ field->len = length;
- fd = xopen(stat_buff, O_RDONLY);
- n = readall(fd, stat_buff, BUFF_SIZE);
- xclose(fd);
- if (n < 0) return;
- stat_buff[n] = 0; //Null terminate the buffer.
- ptr = strchr(stat_buff, '(');
- ptr++;
- name = ptr;
- ptr = strrchr(stat_buff, ')');
- *ptr = '\0'; //unecessary if?
- name = xmprintf("[%s]", name);
- ptr += 2; // goto STATE
- n = sscanf(ptr, "%c %u %u %*u %d %*s %*s %*s %*s %*s %*s "
- "%lu %lu %*s %*s %*s %d %*s %*s %lu %lu %ld",
- &state[0],&ppid, &pgid, &tty, &utime, &stime,
- &nice,&start_time, &vsz,&rss);
-
- if (tid) pid = tid;
- vsz >>= 10; //Convert into KB
- rss = rss * 4; //Express in pages
- tty_major = (tty >> 8) & 0xfff;
- tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
-
- if (vsz == 0 && state[0] != 'Z') state[1] = 'W';
- else state[1] = ' ';
- if (nice < 0 ) state[2] = '<';
- else if (nice) state[2] = 'N';
- else state[2] = ' ';
-
- if (tid) {
- if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/task/%d/status", pid, tid) >= BUFF_SIZE)
- goto clean;
- } else {
- if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/status", pid) >= BUFF_SIZE)
- goto clean;
- }
+ if (width) {
+ field->len = strtol(++width, &title, 10);
+ if (!isdigit(*width) || title != end)
+ error_exit("bad : in -o %s@%ld", ol->arg, title-ol->arg);
+ end = width;
+ }
- fd = -1;
- while (p) {
- int width;
- width = p->width;
- width_counter += (width + 1); //how much screen we hv filled, +1, extra space b/w headers
- switch (p->position) {
- case 0:
- pw = getpwuid(stats.st_uid);
- if (!pw) user = xmprintf("%d",(int)stats.st_uid);
- else user = xmprintf("%s", pw->pw_name);
- printf("%-*.*s", width, width, user);
- free(user);
- break;
- case 1:
- gr = getgrgid(stats.st_gid);
- if (!gr) group = xmprintf("%d",(int)stats.st_gid);
- else group = xmprintf("%s", gr->gr_name);
- printf("%-*.*s", width, width, group);
- free(group);
- break;
- case 2:
- name[strlen(name) - 1] = '\0';
- printf("%-*.*s", width,width, name + 1);
- name[strlen(name)] = ']'; //Refill it for further process.
- break;
- case 3:
- {
- int j = 0;
- width_counter -= width;
- if(p->next) j = width; //is args is in middle. ( -o pid,args,ppid)
- else j = (TT.screen_width - width_counter % TT.screen_width); //how much screen left.
- if (fd == -1) fd = open(cmd_buff, O_RDONLY); //don't want to die
- else xlseek(fd, 0, SEEK_SET);
- if (fd < 0) cmd_buff[0] = 0;
- else read_cmdline(fd, cmd_buff);
- if (cmd_buff[0]) printf("%-*.*s", j, j, cmd_buff);
- else printf("%-*.*s", j, j, name);
- width_counter += width;
- break;
+ // Find type (reuse width as temp because we're done with it)
+ for (i = 0; i<ARRAY_LEN(typos) && !field->which; i++) {
+ int j, k;
+ char *s;
+
+ field->which = i;
+ for (j = 0; j < 2; j++) {
+ if (!j) s = typos[i];
+ // posix requires alternate names for some fields
+ else if (-1 == (k = stridx((char []){7, 14, 15, 16}, i))) break;
+ else s = ((char *[]){"nice", "args", "comm", "etime"})[k];
+
+ if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
+ }
+ if (j!=2) break;
}
- case 4:
- printf("%*d", width, pid);
- break;
- case 5:
- printf("%*d", width, ppid);
- break;
- case 6:
- printf("%*d", width, pgid);
- break;
- case 7:
- get_etime(start_time);
- break;
- case 8:
- printf("%*d", width, nice);
- break;
- case 9:
- get_uid_gid(stat_buff, "Gid:", &rgid);
- gr = getgrgid(rgid);
- if (!gr) rgroup = xmprintf("%d",(int)stats.st_gid);
- else rgroup = xmprintf("%s", gr->gr_name);
- printf("%-*.*s", width,width, rgroup);
- free(rgroup);
- break;
- case 10:
- get_uid_gid(stat_buff, "Uid:", &ruid);
- pw = getpwuid(ruid);
- if (!pw) ruser = xmprintf("%d",(int)stats.st_uid);
- else ruser = xmprintf("%s", pw->pw_name);
- printf("%-*.*s", width, width, ruser);
- free(ruser);
- break;
- case 11:
- get_time(utime, stime);
- break;
- case 12:
- if (tty_major) {
- char *temp = xmprintf("%d,%d", tty_major,tty_minor);
- printf("%-*s", width, temp);
- free(temp);
- } else printf("%-*s", width, "?");
- break;
- case 13:
- printf("%*lu", width, vsz);
- break;
- case 14:
- printf("%-*s", width, state);
- break;
- case 15:
- printf("%*lu", width, rss);
- break;
+ if (i == ARRAY_LEN(typos)) error_exit("bad -o %.*s", end-type, type);
+ if (!field->title) strcpy(field->title, typos[field->which]);
+ dlist_add_nomalloc((void *)&TT.fields, (void *)field);
+ }
}
- p = p->next;
- xputc(' '); //space char
- }
- if (fd >= 0) xclose(fd);
- xputc('\n');
-clean:
- free(name);
-}
-//Do stats for threads (for -T option)
-void do_ps_threads(int pid)
-{
- DIR *d;
- int tid;
- struct dirent *de;
- char *tmp = xmprintf("/proc/%d/task",pid);
-
- if (!(d = opendir(tmp))) {
- free(tmp);
- return;
- }
- while ((de = readdir(d))) {
- if (isdigit(de->d_name[0])) {
- tid = atoi(de->d_name);
- if (tid == pid) continue;
- do_ps_line(pid, tid);
- }
- }
- closedir(d);
- free(tmp);
-}
+ // Default fields (also with -f and -l)
+ } else {
+ unsigned short def = 0x7008;
-void ps_main(void)
-{
- DIR *dp;
- struct dirent *entry;
- int pid;
- struct header_list def_header[] = {
- {"user", "USER", "%-*s ", 8, 0, NULL},
- {"group", "GROUP", "%-*s ", 8, 1, NULL},
- {"comm", "COMMAND", "%-*s ",16, 2, NULL},
- {"args", "COMMAND", "%-*s ",30, 3, NULL},
- {"pid", "PID", "%*s ", 5, 4, NULL},
- {"ppid","PPID", "%*s ", 5, 5, NULL},
- {"pgid", "PGID", "%*s ", 5, 6, NULL},
- {"etime","ELAPSED", "%*s ", 7, 7, NULL},
- {"nice", "NI", "%*s ", 5, 8, NULL},
- {"rgroup","RGROUP", "%-*s ", 8, 9, NULL},
- {"ruser","RUSER", "%-*s ", 8, 10, NULL},
- {"time", "TIME", "%*s ", 6, 11, NULL},
- {"tty", "TT", "%-*s ", 6, 12, NULL},
- {"vsz","VSZ", "%*s ", 7, 13, NULL},
- {"stat", "STAT", "%-*s ", 4, 14, NULL},
- {"rss", "RSS", "%*s ", 4, 15, NULL},
-{0,0,0,0,0,0}
- };
-
- TT.screen_width = 80; //default width
- terminal_size(&TT.screen_width, NULL);
- print_header(def_header, ARRAY_LEN(def_header));
-
- if (!(dp = opendir("/proc"))) perror_exit("opendir");
- while ((entry = readdir(dp))) {
- if (isdigit(*entry->d_name)) {
- pid = atoi(entry->d_name);
- do_ps_line(pid, 0);
- if (toys.optflags & FLAG_T)
- do_ps_threads(pid);
+ if (toys.optflags&FLAG_f) def = 0x783c;
+ if (toys.optflags&FLAG_l) def = 0x77ff;
+
+ // order of fields[] matches posix STDOUT section, so add enabled XSI
+ // defaults according to bitmask
+
+ for (i=0; def>>i; i++) {
+ if (!((def>>i)&1)) continue;
+
+ field = xmalloc(sizeof(struct strawberry)+strlen(typos[i])+1);
+ field->which = i;
+ field->len = widths[i];
+ strcpy(field->title, typos[i]);
+ dlist_add_nomalloc((void *)&TT.fields, (void *)field);
}
}
- closedir(dp);
- if (CFG_TOYBOX_FREE) {
- struct header_list *temp = o_list;
- while(temp) {
- o_list = o_list->next;
- free(temp->header);
- free(temp);
- temp = o_list;
- }
+ dlist_terminate(TT.fields);
+
+ // Print padded headers. (Numbers are right justified, everyting else left.
+ // time and pcpu count as numbers, tty does not)
+ for (field = TT.fields; field; field = field->next) {
+
+ // right justify F, UID, PID, PPID, PRI, NI, ADDR SZ, TIME, ELAPSED, %CPU
+ // todo: STIME? C?
+ if (!((1<<field->which)&0x523dd)) field->len *= -1;
+ printf(" %*s", field->len, field->title);
+
+ // -f prints USER but calls it UID (but "ps -o uid -f" is numeric...?)
+ if ((toys.optflags&(FLAG_f|FLAG_o))==FLAG_f && field->which==2)
+ field->which = 22;
}
+ xputc('\n');
+
+ dirtree_read("/proc", do_ps);
}
diff --git a/toys/pending/reset.c b/toys/pending/reset.c
deleted file mode 100644
index a12f0b6..0000000
--- a/toys/pending/reset.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* reset.c - A program to reset the terminal.
- *
- * Copyright 2014 Ashwini Kumar <ak.ashwini@gmail.com>
- * Copyright 2014 Kyungwan Han <asura321@gmail.com>
- *
- * No Standard.
-
-USE_RESET(NEWTOY(reset, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-
-config RESET
- bool "reset"
- default n
- help
- usage: reset
-
- A program to reset the terminal.
-*/
-#define FOR_reset
-#include "toys.h"
-
-void reset_main(void)
-{
- char *args[] = {"stty", "sane", NULL};
-
- /* \033c - reset the terminal with default setting
- * \033(B - set the G0 character set (B=US)
- * \033[2J - clear the whole screen
- * \033[0m - Reset all attributes
- */
- if (isatty(1)) xprintf("\033c\033(B\033[0m\033[J\033[?25h");
- fflush(stdout);
- // set the terminal to sane settings
- xexec(args);
-}
diff --git a/toys/pending/route.c b/toys/pending/route.c
index 163ce6b..ac1bbef 100644
--- a/toys/pending/route.c
+++ b/toys/pending/route.c
@@ -411,7 +411,7 @@ static void setroute_inet6(char **argv)
if (dev_name) {
char ifre_buf[sizeof(struct ifreq)] = {0,};
struct ifreq *ifre = (struct ifreq*)ifre_buf;
- strncpy(ifre->ifr_name, dev_name, IFNAMSIZ-1);
+ xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ);
xioctl(sockfd, SIOGIFINDEX, ifre);
rt.rtmsg_ifindex = ifre->ifr_ifindex;
}
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index 81f91a7..499c5db 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -24,8 +24,8 @@
USE_SH(NEWTOY(cd, NULL, TOYFLAG_NOFORK))
USE_SH(NEWTOY(exit, NULL, TOYFLAG_NOFORK))
-USE_SH(NEWTOY(sh, "c:"USE_SH_INTERACTIVE("i"), TOYFLAG_BIN))
-USE_SH(OLDTOY(toysh, sh, OPTSTR_sh, TOYFLAG_BIN))
+USE_SH(NEWTOY(sh, "c:i", TOYFLAG_BIN))
+USE_SH(OLDTOY(toysh, sh, TOYFLAG_BIN))
config SH
bool "sh (toysh)"
@@ -37,16 +37,6 @@ config SH
and responds to it.
-c command line to execute
-
-config SH_INTERACTIVE
- bool "Interactive shell"
- default n
- depends on SH
- help
- This shell supports terminal control (so the shell isn't killed by CTRL-C),
- job control (fg, bg, jobs), and reads /etc/profile and ~/.profile when
- running interactively.
-
-i interactive mode (default when STDIN is a tty)
config EXIT
@@ -248,7 +238,7 @@ static char *parse_pipeline(char *cmdline, struct pipeline *line)
if (!cmdline) return 0;
- if (CFG_SH_INTERACTIVE) line->cmdline = cmdline;
+ line->cmdline = cmdline;
// Parse command into argv[]
for (;;) {
@@ -257,7 +247,7 @@ static char *parse_pipeline(char *cmdline, struct pipeline *line)
// Skip leading whitespace and detect end of line.
while (isspace(*start)) start++;
if (!*start || *start=='#') {
- if (CFG_SH_INTERACTIVE) line->cmdlinelen = start-cmdline;
+ line->cmdlinelen = start-cmdline;
return 0;
}
@@ -281,7 +271,7 @@ static char *parse_pipeline(char *cmdline, struct pipeline *line)
start = end;
}
- if (CFG_SH_INTERACTIVE) line->cmdlinelen = start-cmdline;
+ line->cmdlinelen = start-cmdline;
return start;
}
@@ -375,7 +365,7 @@ void sh_main(void)
FILE *f;
// Set up signal handlers and grab control of this tty.
- if (CFG_SH_INTERACTIVE && isatty(0)) toys.optflags |= FLAG_i;
+ if (isatty(0)) toys.optflags |= FLAG_i;
f = *toys.optargs ? xfopen(*toys.optargs, "r") : NULL;
if (TT.command) handle(TT.command);
diff --git a/toys/pending/syslogd.c b/toys/pending/syslogd.c
index 7fb297f..450bd72 100644
--- a/toys/pending/syslogd.c
+++ b/toys/pending/syslogd.c
@@ -300,6 +300,10 @@ static int write_rotate(struct logfile *tf, int len)
unlink(tf->filename);
close(tf->logfd);
tf->logfd = open(tf->filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
+ if (tf->logfd < 0) {
+ perror_msg("can't open %s", tf->filename);
+ return -1;
+ }
}
ftruncate(tf->logfd, 0);
}
diff --git a/toys/pending/tar.c b/toys/pending/tar.c
index 5b060c6..c8b6dff 100644
--- a/toys/pending/tar.c
+++ b/toys/pending/tar.c
@@ -32,6 +32,7 @@ config TAR
X File with names to exclude
T File with names to include
*/
+
#define FOR_tar
#include "toys.h"
@@ -94,7 +95,7 @@ static void copy_in_out(int src, int dst, off_t size)
static void itoo(char *str, int len, off_t val)
{
char *t, tmp[sizeof(off_t)*3+1];
- int cnt = sprintf(tmp, "%0*llo", len, val);
+ int cnt = sprintf(tmp, "%0*llo", len, (unsigned long long)val);
t = tmp + cnt - len;
if (*t == '0') t++;
@@ -130,11 +131,11 @@ static void write_longname(struct archive_handler *tar, char *name, char type)
memset(&tmp, 0, sizeof(tmp));
strcpy(tmp.name, "././@LongLink");
- sprintf(tmp.mode, "%0*d", sizeof(tmp.mode)-1, 0);
- sprintf(tmp.uid, "%0*d", sizeof(tmp.uid)-1, 0);
- sprintf(tmp.gid, "%0*d", sizeof(tmp.gid)-1, 0);
- sprintf(tmp.size, "%0*d", sizeof(tmp.size)-1, 0);
- sprintf(tmp.mtime, "%0*d", sizeof(tmp.mtime)-1, 0);
+ sprintf(tmp.mode, "%0*d", (int)sizeof(tmp.mode)-1, 0);
+ sprintf(tmp.uid, "%0*d", (int)sizeof(tmp.uid)-1, 0);
+ sprintf(tmp.gid, "%0*d", (int)sizeof(tmp.gid)-1, 0);
+ sprintf(tmp.size, "%0*d", (int)sizeof(tmp.size)-1, 0);
+ sprintf(tmp.mtime, "%0*d", (int)sizeof(tmp.mtime)-1, 0);
itoo(tmp.size, sizeof(tmp.size), sz);
tmp.type = type;
memset(tmp.chksum, ' ', 8);
@@ -184,12 +185,12 @@ static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
while ((c = strstr(hname, "../"))) hname = c + 3;
if (warn && hname != name) {
printf("removing leading '%.*s' "
- "from member names\n",hname-name, name);
+ "from member names\n", (int)(hname-name), name);
warn = 0;
}
memset(&hdr, 0, sizeof(hdr));
- strncpy(hdr.name, hname, sizeof(hdr.name));
+ xstrncpy(hdr.name, hname, sizeof(hdr.name));
itoo(hdr.mode, sizeof(hdr.mode), st->st_mode &07777);
itoo(hdr.uid, sizeof(hdr.uid), st->st_uid);
itoo(hdr.gid, sizeof(hdr.gid), st->st_gid);
@@ -202,13 +203,14 @@ static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
hdr.type = '1';
if (strlen(node->arg) > sizeof(hdr.link))
write_longname(tar, hname, 'K'); //write longname LINK
- strncpy(hdr.link, node->arg, sizeof(hdr.link));
+ xstrncpy(hdr.link, node->arg, sizeof(hdr.link));
} else if (S_ISREG(st->st_mode)) {
hdr.type = '0';
if (st->st_size <= (off_t)0777777777777LL)
itoo(hdr.size, sizeof(hdr.size), st->st_size);
else {
- error_msg("can't store file '%s' of size '%d'\n", hname, st->st_size);
+ error_msg("can't store file '%s' of size '%lld'\n",
+ hname, (unsigned long long)st->st_size);
return;
}
} else if (S_ISLNK(st->st_mode)) {
@@ -219,7 +221,7 @@ static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
}
if (strlen(lnk) > sizeof(hdr.link))
write_longname(tar, hname, 'K'); //write longname LINK
- strncpy(hdr.link, lnk, sizeof(hdr.link));
+ xstrncpy(hdr.link, lnk, sizeof(hdr.link));
free(lnk);
}
else if (S_ISDIR(st->st_mode)) hdr.type = '5';
@@ -229,7 +231,7 @@ static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
itoo(hdr.major, sizeof(hdr.major), major(st->st_rdev));
itoo(hdr.minor, sizeof(hdr.minor), minor(st->st_rdev));
} else {
- error_msg("unknown file type '%s'");
+ error_msg("unknown file type '%o'", st->st_mode & S_IFMT);
return;
}
if (strlen(hname) > sizeof(hdr.name))
@@ -432,7 +434,8 @@ COPY:
if (pw) u = pw->pw_uid;
if (gr) g = gr->gr_gid;
}
- chown(file_hdr->name, u, g);
+ if (chown(file_hdr->name, u, g))
+ perror_msg("chown %d:%d '%s'", u, g, file_hdr->name);;
}
if (toys.optflags & FLAG_p) // || !(toys.optflags & FLAG_no_same_permissions))
@@ -744,15 +747,10 @@ SKIP:
void tar_main(void)
{
struct archive_handler *tar_hdl;
- int fd = 0, flags = O_RDONLY;
+ int fd = 0;
struct arg_list *tmp;
char **args = toys.optargs;
- if (!toys.argv[1]) {
- toys.exithelp++;
- error_exit(NULL);
- }
-
if (!geteuid()) toys.optflags |= FLAG_p;
for (tmp = TT.exc; tmp; tmp = tmp->next)
@@ -764,16 +762,16 @@ void tar_main(void)
if (toys.optflags & FLAG_c) {
if (!TT.inc) error_exit("empty archive");
- fd = 1, flags = O_WRONLY|O_CREAT|O_TRUNC;
+ fd = 1;
}
if ((toys.optflags & FLAG_f) && strcmp(TT.fname, "-"))
- fd = xcreate(TT.fname, flags, 0666);
+ fd = xcreate(TT.fname, fd*(O_WRONLY|O_CREAT|O_TRUNC), 0666);
if (toys.optflags & FLAG_C) xchdir(TT.dir);
tar_hdl = init_handler();
tar_hdl->src_fd = fd;
- if (toys.optflags & FLAG_x || toys.optflags & FLAG_t) {
+ if ((toys.optflags & FLAG_x) || (toys.optflags & FLAG_t)) {
if (toys.optflags & FLAG_O) tar_hdl->extract_handler = extract_to_stdout;
if (toys.optflags & FLAG_to_command) {
signal(SIGPIPE, SIG_IGN); //will be using pipe between child & parent
@@ -790,9 +788,8 @@ void tar_main(void)
for (tmp = TT.inc; tmp; tmp = tmp->next) {
TT.handle = tar_hdl;
//recurse thru dir and add files to archive
- struct dirtree *root = dirtree_add_node(0,tmp->arg,toys.optflags & FLAG_h);
-
- if (root) dirtree_handle_callback(root, add_to_tar);
+ dirtree_handle_callback(dirtree_start(tmp->arg, toys.optflags & FLAG_h),
+ add_to_tar);
}
memset(toybuf, 0, 1024);
writeall(tar_hdl->src_fd, toybuf, 1024);
diff --git a/toys/pending/tcpsvd.c b/toys/pending/tcpsvd.c
index d7e1f6c..16110e5 100644
--- a/toys/pending/tcpsvd.c
+++ b/toys/pending/tcpsvd.c
@@ -7,7 +7,7 @@
* No Standard.
USE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1C:b#=20<0u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN))
-USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, OPTSTR_tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
+USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
config TCPSVD
bool "tcpsvd"
@@ -392,7 +392,7 @@ void tcpsvd_main(void)
close(1);
dup2(newfd, 0);
dup2(newfd, 1);
- xexec_optargs(2); //skip IP PORT
+ xexec(toys.optargs+2); //skip IP PORT
} else {
insert(&pids, pid, addr);
xclose(newfd); //close and reopen for next client.
diff --git a/toys/pending/top.c b/toys/pending/top.c
index 700baba..fd7e879 100644
--- a/toys/pending/top.c
+++ b/toys/pending/top.c
@@ -60,11 +60,6 @@ struct cpu_info {
unsigned long long total;
};
-enum CODE{
- KEY_UP = 0x100, KEY_DOWN, KEY_HOME,
- KEY_END, KEY_PAGEUP, KEY_PAGEDN,
-};
-
struct keycode_map_s {
char *key;
int code;
@@ -208,8 +203,8 @@ static int get_key_code(char *ch, int i)
};
static struct keycode_map_s type3[] = {
- {"[1~", KEY_HOME}, {"[4~", KEY_END}, {"[5~", KEY_PAGEUP},
- {"[6~", KEY_PAGEDN}, {"[7~", KEY_HOME}, {"[8~", KEY_END},
+ {"[1~", KEY_HOME}, {"[4~", KEY_END}, {"[5~", KEY_PGUP},
+ {"[6~", KEY_PGDN}, {"[7~", KEY_HOME}, {"[8~", KEY_END},
{NULL, 0}
};
struct keycode_map_s *table, *keytable[3] = {type2, type3, NULL};
@@ -833,10 +828,10 @@ void top_main(void )
case KEY_END:
TT.scroll_offset = TT.num_new_procs - TT.rows/2;
break;
- case KEY_PAGEUP:
+ case KEY_PGUP:
TT.scroll_offset -= TT.rows/2;
break;
- case KEY_PAGEDN:
+ case KEY_PGDN:
TT.scroll_offset += TT.rows/2;
break;
}
diff --git a/toys/pending/traceroute.c b/toys/pending/traceroute.c
index 9250993..830331a 100644
--- a/toys/pending/traceroute.c
+++ b/toys/pending/traceroute.c
@@ -8,7 +8,7 @@
* No Standard
USE_TRACEROUTE(NEWTOY(traceroute, "<1>2i:f#<1>255=1z#<0>86400=0g*w#<0>86400=5t#<0>255=0s:q#<1>255=3p#<1>65535=33434m#<1>255=30rvndlIUF64", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
-USE_TRACEROUTE(OLDTOY(traceroute6,traceroute, OPTSTR_traceroute, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
+USE_TRACEROUTE(OLDTOY(traceroute6,traceroute, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
config TRACEROUTE
bool "traceroute"
default n
diff --git a/toys/pending/useradd.c b/toys/pending/useradd.c
index 4f2bcc6..78f083b 100644
--- a/toys/pending/useradd.c
+++ b/toys/pending/useradd.c
@@ -6,7 +6,7 @@
* See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/useradd.html
USE_USERADD(NEWTOY(useradd, "<1>2u#<0G:s:g:h:SDH", TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
-USE_USERADD(OLDTOY(adduser, useradd, OPTSTR_useradd, TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
+USE_USERADD(OLDTOY(adduser, useradd, TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
config USERADD
bool "useradd"
diff --git a/toys/pending/userdel.c b/toys/pending/userdel.c
index 8619a62..9c93a21 100644
--- a/toys/pending/userdel.c
+++ b/toys/pending/userdel.c
@@ -5,7 +5,7 @@
* See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/userdel.html
USE_USERDEL(NEWTOY(userdel, "<1>1r", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
-USE_USERDEL(OLDTOY(deluser, userdel, OPTSTR_userdel, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
+USE_USERDEL(OLDTOY(deluser, userdel, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
config USERDEL
bool "userdel"
diff --git a/toys/posix/cat.c b/toys/posix/cat.c
index 3644c4f..3aae4a1 100644
--- a/toys/posix/cat.c
+++ b/toys/posix/cat.c
@@ -3,8 +3,12 @@
* Copyright 2006 Rob Landley <rob@landley.net>
*
* 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", TOYFLAG_BIN))
+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"
@@ -16,19 +20,73 @@ config CAT
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. Use 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 len, size=toys.optflags ? 1 : sizeof(toybuf);
+ int i, len, size=(toys.optflags & FLAG_u) ? 1 : sizeof(toybuf);
- for (;;) {
+ for(;;) {
len = read(fd, toybuf, size);
- if (len<0) perror_msg("%s",name);
- if (len<1) break;
- xwrite(1, toybuf, len);
+ if (len < 0) {
+ toys.exitval = EXIT_FAILURE;
+ perror_msg("%s", name);
+ }
+ if (len < 1) break;
+ if ((CFG_CAT_V || CFG_CATV) && (toys.optflags&~FLAG_u)) {
+ for (i=0; i<len; i++) {
+ char c=toybuf[i];
+
+ if (c > 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);
}
}
@@ -36,3 +94,9 @@ 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/toys/posix/chgrp.c b/toys/posix/chgrp.c
index 3aa2514..6b95c6a 100644
--- a/toys/posix/chgrp.c
+++ b/toys/posix/chgrp.c
@@ -4,20 +4,17 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/chown.html
* See http://opengroup.org/onlinepubs/9699919799/utilities/chgrp.html
- *
- * TODO: group only one of [HLP]
-USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv", TOYFLAG_BIN))
-USE_CHGRP(OLDTOY(chown, chgrp, OPTSTR_chgrp, TOYFLAG_BIN))
+USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
+USE_CHOWN(OLDTOY(chown, chgrp, TOYFLAG_BIN))
config CHGRP
- bool "chgrp/chown"
+ bool "chgrp"
default y
help
- usage: chown [-RHLP] [-fvh] [owner][:group] file...
- usage: chgrp [-RHLP] [-fvh] group file...
+ usage: chgrp/chown [-RHLP] [-fvh] group file...
- Change ownership of one or more files.
+ Change group of one or more files.
-f suppress most error messages.
-h change symlinks instead of what they point to
@@ -26,9 +23,16 @@ config CHGRP
-L with -R change target of symlink, follow all symlinks
-P with -R change symlink, do not follow symlinks (default)
-v verbose output.
+
+config CHOWN
+ bool "chown"
+ default y
+ help
+ see: chgrp
*/
#define FOR_chgrp
+#define FORCE_FLAGS
#include "toys.h"
GLOBALS(
@@ -45,12 +49,11 @@ static int do_chgrp(struct dirtree *node)
// Depth first search
if (!dirtree_notdotdot(node)) return 0;
if ((flags & FLAG_R) && !node->again && S_ISDIR(node->st.st_mode))
- return DIRTREE_COMEAGAIN|((flags&FLAG_L) ? DIRTREE_SYMFOLLOW : 0);
+ return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
fd = dirtree_parentfd(node);
ret = fchownat(fd, node->name, TT.owner, TT.group,
- (flags&(FLAG_L|FLAG_H)) || !(flags&(FLAG_h|FLAG_R))
- ? 0 : AT_SYMLINK_NOFOLLOW);
+ AT_SYMLINK_NOFOLLOW*(!(flags&(FLAG_L|FLAG_H)) && (flags&(FLAG_h|FLAG_R))));
if (ret || (flags & FLAG_v)) {
char *path = dirtree_path(node, 0);
@@ -70,42 +73,34 @@ static int do_chgrp(struct dirtree *node)
void chgrp_main(void)
{
- int ischown = toys.which->name[2] == 'o', hl = toys.optflags&(FLAG_H|FLAG_L);
+ int ischown = toys.which->name[2] == 'o';
char **s, *own;
+ TT.owner = TT.group = -1;
+
// Distinguish chown from chgrp
if (ischown) {
char *grp;
- struct passwd *p;
own = xstrdup(*toys.optargs);
if ((grp = strchr(own, ':')) || (grp = strchr(own, '.'))) {
*(grp++) = 0;
TT.group_name = grp;
}
- if (*own) {
- TT.owner_name = own;
- p = getpwnam(own);
- // TODO: trailing garbage?
- if (!p && isdigit(*own)) p=getpwuid(atoi(own));
- if (!p) error_exit("no user '%s'", own);
- TT.owner = p->pw_uid;
- }
+ if (*own) TT.owner = xgetpwnamid(TT.owner_name = own)->pw_uid;
} else TT.group_name = *toys.optargs;
- if (TT.group_name) {
- struct group *g;
- g = getgrnam(TT.group_name);
- if (!g) g=getgrgid(atoi(TT.group_name));
- if (!g) error_exit("no group '%s'", TT.group_name);
- TT.group = g->gr_gid;
- }
+ if (TT.group_name && *TT.group_name)
+ TT.group = xgetgrnamid(TT.group_name)->gr_gid;
- for (s=toys.optargs+1; *s; s++) {
- struct dirtree *new = dirtree_add_node(0, *s, hl);
- if (new) dirtree_handle_callback(new, do_chgrp);
- else toys.exitval = 1;
- }
+ for (s=toys.optargs+1; *s; s++)
+ dirtree_handle_callback(dirtree_start(*s, toys.optflags&(FLAG_H|FLAG_L)),
+ do_chgrp);;
if (CFG_TOYBOX_FREE && ischown) free(own);
}
+
+void chown_main()
+{
+ chgrp_main();
+}
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 07a8f05..d5e92f2 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -4,13 +4,12 @@
*
* Posix says "cp -Rf dir file" shouldn't delete file, but our -f does.
-// This is subtle: MV options must be in same order (right to left) as CP
-// for FLAG_X macros to work out right.
+// 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, "<2RHLPp"USE_CP_MORE("rdaslvnF")"fi[-HLPd]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
-USE_CP_MV(OLDTOY(mv, cp, "<2"USE_CP_MORE("vnF")"fi"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_MV(NEWTOY(mv, "<2"USE_CP_MORE("vnF")"fi"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
USE_INSTALL(NEWTOY(install, "<1cdDpsvm:o:g:", TOYFLAG_USR|TOYFLAG_BIN))
-*
config CP
bool "cp"
@@ -22,7 +21,7 @@ config CP
be a directory.
-f delete destination files we can't write to
- -F delete any existing destination file first (breaks hardlinks)
+ -F delete any existing destination file first (--remove-destination)
-i interactive, prompt before overwriting existing DEST
-p preserve timestamps, ownership, and permissions
-R recurse into subdirectories (DEST must be a directory)
@@ -45,7 +44,22 @@ config CP_MORE
-s symlink instead of copy
-v verbose
-config CP_MV
+config CP_PRESERVE
+ bool "cp --preserve support"
+ default y
+ depends on CP_MORE
+ help
+ usage: cp [--preserve=mota]
+
+ --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.
+ all - all of the above
+
+config MV
bool "mv"
default y
depends on CP
@@ -55,10 +69,10 @@ config CP_MV
-f force copy by deleting destination file
-i interactive, prompt before overwriting existing DEST
-config CP_MV_MORE
+config MV_MORE
bool
default y
- depends on CP_MV && CP_MORE
+ depends on MV && CP_MORE
help
usage: mv [-vn]
@@ -88,16 +102,24 @@ config INSTALL
#include "toys.h"
GLOBALS(
- // install's options
- char *group;
- char *user;
- char *mode;
+ union {
+ struct {
+ // install's options
+ char *group;
+ char *user;
+ char *mode;
+ } i;
+ struct {
+ char *preserve;
+ } c;
+ };
char *destname;
struct stat top;
int (*callback)(struct dirtree *try);
uid_t uid;
gid_t gid;
+ int pflags;
)
// Callback from dirtree_read() for each file/directory under a source dir.
@@ -208,7 +230,7 @@ int cp_node(struct dirtree *try)
if (*or->name == '/') dotdots = 0;
if (dotdots) {
- char *s2 = xmprintf("% *c%s", 3*dotdots, ' ', s);
+ char *s2 = xmprintf("%*c%s", 3*dotdots, ' ', s);
free(s);
s = s2;
while(dotdots--) {
@@ -246,43 +268,55 @@ int cp_node(struct dirtree *try)
if (fdin < 0) {
catch = try->name;
break;
- } else {
- fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode);
- if (fdout >= 0) {
- xsendfile(fdin, fdout);
- err = 0;
- }
- close(fdin);
}
+ fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode);
+ if (fdout >= 0) {
+ xsendfile(fdin, fdout);
+ err = 0;
+ }
+ close(fdin);
}
} while (err && (flags & (FLAG_f|FLAG_n)) && !unlinkat(cfd, catch, 0));
}
+ // Did we make a thing?
if (fdout != -1) {
- if (flags & (FLAG_a|FLAG_p)) {
- struct timespec times[2];
+ int rc;
- // Inability to set these isn't fatal, some require root access.
+ // Inability to set --preserve isn't fatal, some require root access.
- times[0] = try->st.st_atim;
- times[1] = try->st.st_mtim;
+ // ownership
+ if (TT.pflags & 2) {
+ // 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) {
- fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
- AT_SYMLINK_NOFOLLOW);
- utimensat(cfd, catch, times, AT_SYMLINK_NOFOLLOW);
- // permission bits already correct for mknod, don't apply to symlink
- } else {
- fchown(fdout, try->st.st_uid, try->st.st_gid);
- futimens(fdout, times);
- fchmod(fdout, try->st.st_mode);
+ 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) {
+ char *pp;
+
+ perror_msg("chown '%s'", pp = dirtree_path(try, 0));
+ free(pp);
}
}
- if (fdout != AT_FDCWD) xclose(fdout);
+ // timestamp
+ if (TT.pflags & 4) {
+ 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 & 1) fchmod(fdout, try->st.st_mode);
+ xclose(fdout);
+ }
- if (CFG_CP_MV && toys.which->name[0] == 'm')
+ if (CFG_MV && toys.which->name[0] == 'm')
if (unlinkat(tfd, try->name, S_ISDIR(try->st.st_mode) ? AT_REMOVEDIR :0))
err = "%s";
}
@@ -293,17 +327,41 @@ int cp_node(struct dirtree *try)
void cp_main(void)
{
- char *destname = toys.optargs[--toys.optc];
+ char *destname = toys.optargs[--toys.optc],
+ *preserve[] = {"mode", "ownership", "timestamps"};
int i, destdir = !stat(destname, &TT.top) && S_ISDIR(TT.top.st_mode);
if (toys.optc>1 && !destdir) error_exit("'%s' not directory", destname);
- if (toys.which->name[0] == 'm') toys.optflags |= FLAG_d|FLAG_p|FLAG_R;
- if (toys.optflags & (FLAG_a|FLAG_p)) umask(0);
+ if (toys.optflags & (FLAG_a|FLAG_p)) {
+ TT.pflags = 7; // preserve=mot
+ umask(0);
+ }
+ if (CFG_CP_PRESERVE && TT.c.preserve) {
+ char *pre = xstrdup(TT.c.preserve), *s;
+
+ if (comma_scan(pre, "all", 1)) TT.pflags = ~0;
+ for (i=0; i<ARRAY_LEN(preserve); i++)
+ if (comma_scan(pre, preserve[i], 1)) TT.pflags |= 1<<i;
+ if (*pre) {
+
+ // Try to interpret as letters, commas won't set anything this doesn't.
+ for (s = TT.c.preserve; *s; s++) {
+ for (i=0; i<ARRAY_LEN(preserve); i++)
+ if (*s == *preserve[i]) TT.pflags |= 1<<i;
+ if (i == ARRAY_LEN(preserve)) {
+ if (*s == 'a') TT.pflags = ~0;
+ else break;
+ }
+ }
+
+ if (*s) error_exit("bad --preserve=%s", pre);
+ }
+ free(pre);
+ }
if (!TT.callback) TT.callback = cp_node;
// Loop through sources
-
for (i=0; i<toys.optc; i++) {
struct dirtree *new;
char *src = toys.optargs[i];
@@ -313,7 +371,7 @@ void cp_main(void)
else TT.destname = destname;
errno = EXDEV;
- if (CFG_CP_MV && toys.which->name[0] == 'm') {
+ if (CFG_MV && toys.which->name[0] == 'm') {
if (!(toys.optflags & FLAG_f)) {
struct stat st;
@@ -333,9 +391,8 @@ void cp_main(void)
// Skip nonexistent sources
if (rc) {
- int symfollow = toys.optflags & (FLAG_H|FLAG_L);
-
- if (errno != EXDEV || !(new = dirtree_add_node(0, src, symfollow)))
+ if (errno!=EXDEV ||
+ !(new = dirtree_start(src, toys.optflags&(FLAG_H|FLAG_L))))
perror_msg("bad '%s'", src);
else dirtree_handle_callback(new, TT.callback);
}
@@ -343,15 +400,22 @@ void cp_main(void)
}
}
+void mv_main(void)
+{
+ toys.optflags |= FLAG_d|FLAG_p|FLAG_R;
+
+ cp_main();
+}
+
#define CLEANUP_cp
#define FOR_install
#include <generated/flags.h>
static int install_node(struct dirtree *try)
{
- if (TT.mode) try->st.st_mode = string_to_mode(TT.mode, try->st.st_mode);
- if (TT.group) try->st.st_gid = TT.gid;
- if (TT.user) try->st.st_uid = TT.uid;
+ if (TT.i.mode) try->st.st_mode = string_to_mode(TT.i.mode, try->st.st_mode);
+ if (TT.i.group) try->st.st_gid = TT.gid;
+ if (TT.i.user) try->st.st_uid = TT.uid;
// Always returns 0 because no -r
cp_node(try);
@@ -378,6 +442,7 @@ void install_main(void)
}
if (toys.optflags & FLAG_D) {
+ TT.destname = toys.optargs[toys.optc-1];
if (mkpathat(AT_FDCWD, TT.destname, 0, 2))
perror_exit("-D '%s'", TT.destname);
if (toys.optc == 1) return;
@@ -389,8 +454,8 @@ void install_main(void)
if (flags & FLAG_v) toys.optflags |= 8; // cp's FLAG_v
if (flags & (FLAG_p|FLAG_o|FLAG_g)) toys.optflags |= 512; // cp's FLAG_p
- if (TT.user) TT.uid = xgetpwnam(TT.user)->pw_uid;
- if (TT.group) TT.gid = xgetgrnam(TT.group)->gr_gid;
+ if (TT.i.user) TT.uid = xgetpwnamid(TT.i.user)->pw_uid;
+ if (TT.i.group) TT.gid = xgetgrnamid(TT.i.group)->gr_gid;
TT.callback = install_node;
cp_main();
diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c
index 2a0f7d9..312bb93 100644
--- a/toys/posix/cpio.c
+++ b/toys/posix/cpio.c
@@ -256,7 +256,7 @@ void cpio_main(void)
xwrite(afd, toybuf, nlen);
}
llen = st.st_size & 3;
- if (llen) write(afd, &zero, 4-llen);
+ if (llen) xwrite(afd, &zero, 4-llen);
}
close(fd);
}
diff --git a/toys/posix/cut.c b/toys/posix/cut.c
index 7f10c5e..bb2b22d 100644
--- a/toys/posix/cut.c
+++ b/toys/posix/cut.c
@@ -3,9 +3,11 @@
* Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
* Copyright 2012 Kyungwan Han <asura321@gmail.com>
*
- * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html
+ *
+ * TODO: cleanup
-USE_CUT(NEWTOY(cut, "b:|c:|f:|d:sn[!cbf]", TOYFLAG_BIN))
+USE_CUT(NEWTOY(cut, "b:|c:|f:|d:sn[!cbf]", TOYFLAG_USR|TOYFLAG_BIN))
config CUT
bool "cut"
@@ -111,7 +113,7 @@ static void get_data(void)
else {
int fd = open(*argv, O_RDONLY, 0);
if(fd < 0) {//if file not present then continue with other files.
- perror_msg(*argv);
+ perror_msg("%s", *argv);
continue;
}
TT.do_cut(fd);
diff --git a/toys/posix/df.c b/toys/posix/df.c
index afb296b..0bec17f 100644
--- a/toys/posix/df.c
+++ b/toys/posix/df.c
@@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/df.html
-USE_DF(NEWTOY(df, "Pkt*a", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_DF(NEWTOY(df, "Pkt*a[-Pk]", TOYFLAG_SBIN))
config DF
bool "df"
@@ -16,17 +16,9 @@ config DF
each filesystem listed on the command line, or all currently mounted
filesystems.
- -t type Display only filesystems of this type.
-
-config DF_PEDANTIC
- bool "options -P and -k"
- default y
- depends on DF
- help
- usage: df [-Pk]
-
-P The SUSv3 "Pedantic" option
-k Sets units back to 1024 bytes (the default without -P)
+ -t type Display only filesystems of this type.
Pedantic provides a slightly less useful output format dictated by Posix,
and sets the units to 512 bytes instead of the default 1024 bytes.
@@ -68,8 +60,7 @@ static void show_mt(struct mtab_list *mt)
block = mt->statvfs.f_bsize ? mt->statvfs.f_bsize : 1;
size = (block * mt->statvfs.f_blocks) / TT.units;
used = (block * (mt->statvfs.f_blocks-mt->statvfs.f_bfree)) / TT.units;
- avail = (block * (getuid() ? mt->statvfs.f_bavail : mt->statvfs.f_bfree))
- / TT.units;
+ avail = (block*(getuid()?mt->statvfs.f_bavail:mt->statvfs.f_bfree))/TT.units;
if (!(used+avail)) percent = 0;
else {
percent = (used*100)/(used+avail);
@@ -82,13 +73,8 @@ static void show_mt(struct mtab_list *mt)
// Figure out appropriate spacing
len = 25 - strlen(device);
if (len < 1) len = 1;
- if (CFG_DF_PEDANTIC && (toys.optflags & FLAG_P)) {
- xprintf("%s %lld %lld %lld %lld%% %s\n", device, size, used, avail,
- percent, mt->dir);
- } else {
- xprintf("%s% *lld % 10lld % 9lld % 3lld%% %s\n", device, len,
- size, used, avail, percent, mt->dir);
- }
+ xprintf("%s% *lld % 10lld % 10lld % *lld%% %s\n", device, len,
+ size, used, avail, (toys.optflags & FLAG_P) ? 7 : 3, percent, mt->dir);
if (device != mt->device) free(device);
}
@@ -96,15 +82,12 @@ static void show_mt(struct mtab_list *mt)
void df_main(void)
{
struct mtab_list *mt, *mtstart, *mtend;
+ int p = toys.optflags & FLAG_P;
- // Handle -P and -k
- TT.units = 1024;
- if (CFG_DF_PEDANTIC && (toys.optflags & FLAG_P)) {
- // Units are 512 bytes if you select "pedantic" without "kilobytes".
- if ((toys.optflags&(FLAG_P|FLAG_k)) == FLAG_P) TT.units = 512;
- printf("Filesystem %ld-blocks Used Available Capacity Mounted on\n",
- TT.units);
- } else puts("Filesystem\t1K-blocks\tUsed Available Use% Mounted on");
+ // Units are 512 bytes if you select "pedantic" without "kilobytes".
+ TT.units = p ? 512 : 1024;
+ xprintf("Filesystem%8s-blocks\tUsed Available %s Mounted on\n",
+ p ? "512" : "1K", p ? "Capacity" : "Use%");
if (!(mtstart = xgetmountlist(0))) return;
mtend = dlist_terminate(mtstart);
@@ -118,14 +101,16 @@ void df_main(void)
// Stat it (complain if we can't).
if(stat(*next, &st)) {
- perror_msg("`%s'", *next);
+ perror_msg("'%s'", *next);
continue;
}
// Find and display this filesystem. Use _last_ hit in case of
// overmounts (which is first hit in the reversed list).
for (mt = mtend; mt; mt = mt->prev) {
- if (st.st_dev == mt->stat.st_dev) {
+ if (st.st_dev == mt->stat.st_dev
+ || (st.st_rdev && (st.st_rdev == mt->stat.st_dev)))
+ {
show_mt(mt);
break;
}
@@ -143,7 +128,7 @@ void df_main(void)
mt3 = mt;
for (mt2 = mt->prev; mt2; mt2 = mt2->prev) {
if (mt->stat.st_dev == mt2->stat.st_dev) {
- // For --bind mounts, take show earliest mount
+ // For --bind mounts, show earliest mount
if (!strcmp(mt->device, mt2->device)) {
if (!toys.optflags & FLAG_a) mt3->stat.st_dev = 0;
mt3 = mt2;
diff --git a/toys/posix/du.c b/toys/posix/du.c
index 22a26d3..4302997 100644
--- a/toys/posix/du.c
+++ b/toys/posix/du.c
@@ -3,6 +3,8 @@
* Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/du.html
+ *
+ * TODO: cleanup
USE_DU(NEWTOY(du, "d#<0hmlcaHkKLsx[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -104,12 +106,22 @@ static int seen_inode(void **list, struct stat *st)
// dirtree callback, comput/display size of node
static int do_du(struct dirtree *node)
{
- if (node->parent && !dirtree_notdotdot(node)) return 0;
+ 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;
@@ -118,7 +130,7 @@ static int do_du(struct dirtree *node)
if (S_ISDIR(node->st.st_mode)) {
if (!node->again) {
TT.depth++;
- return DIRTREE_COMEAGAIN | (DIRTREE_SYMFOLLOW*!!(toys.optflags & FLAG_L));
+ return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!(toys.optflags&FLAG_L));
} else TT.depth--;
}
@@ -137,21 +149,12 @@ static int do_du(struct dirtree *node)
void du_main(void)
{
- char *noargs[] = {".", 0};
- struct dirtree *root;
-
- if (!toys.optc) toys.optargs = noargs;
+ char *noargs[] = {".", 0}, **args;
// Loop over command line arguments, recursing through children
- while (*toys.optargs) {
- root = dirtree_add_node(0, *toys.optargs, toys.optflags & (FLAG_H|FLAG_L));
-
- if (root) {
- TT.st_dev = root->st.st_dev;
- dirtree_handle_callback(root, do_du);
- }
- toys.optargs++;
- }
+ for (args = toys.optc ? toys.optargs : noargs; *args; args++)
+ dirtree_handle_callback(dirtree_start(*args, 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/toys/posix/env.c b/toys/posix/env.c
index 4e819f2..f20517e 100644
--- a/toys/posix/env.c
+++ b/toys/posix/env.c
@@ -24,28 +24,17 @@ extern char **environ;
void env_main(void)
{
char **ev;
- char **command = NULL;
char *del = "=";
if (toys.optflags) clearenv();
for (ev = toys.optargs; *ev != NULL; ev++) {
- char *env, *val = NULL;
-
- env = strtok(*ev, del);
-
- if (env) val = strtok(NULL, del);
+ char *env = strtok(*ev, del), *val = 0;
+ if (env) val = strtok(0, del);
if (val) setenv(env, val, 1);
- else {
- command = ev;
- break;
- }
+ else xexec(ev);
}
- if (!command) {
- char **ep;
- if (environ) for (ep = environ; *ep; ep++) xputs(*ep);
- } else xexec_optargs(command - toys.optargs);
-
+ if (environ) for (ev = environ; *ev; ev++) xputs(*ev);
}
diff --git a/toys/posix/find.c b/toys/posix/find.c
index caec80e..a11a910 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -32,6 +32,7 @@ config FIND
-ctime N created N days ago -mtime N modified N days ago
-newer FILE newer mtime than FILE -mindepth # at least # dirs down
-depth ignore contents of dir -maxdepth # at most # dirs down
+ -inum N inode number N
-type [bcdflps] (block, char, dir, file, symlink, pipe, socket)
Numbers N may be prefixed by a - (less than) or + (greater than):
@@ -72,7 +73,7 @@ static int flush_exec(struct dirtree *new, struct exec_range *aa)
{
struct double_list **dl;
char **newargs;
- int rc;
+ int rc = 0;
if (!aa->namecount) return 0;
@@ -82,8 +83,13 @@ static int flush_exec(struct dirtree *new, struct exec_range *aa)
// switch to directory for -execdir, or back to top if we have an -execdir
// _and_ a normal -exec, or are at top of tree in -execdir
- if (aa->dir && new->parent) fchdir(new->parent->data);
- else if (TT.topdir != -1) fchdir(TT.topdir);
+ if (aa->dir && new->parent) rc = fchdir(new->parent->data);
+ else if (TT.topdir != -1) rc = fchdir(TT.topdir);
+ if (rc) {
+ perror_msg("%s", new->name);
+
+ return rc;
+ }
// execdir: accumulated execs in this directory's children.
newargs = xmalloc(sizeof(char *)*(aa->arglen+aa->namecount+1));
@@ -160,7 +166,7 @@ char *strlower(char *s)
// encode back to utf8, something is wrong with your libc. But just
// in case somebody finds an exploit...
len = wcrtomb(new, c, 0);
- if (len < 1) error_exit("bad utf8 %x", c);
+ if (len < 1) error_exit("bad utf8 %x", (int)c);
new += len;
}
}
@@ -179,7 +185,7 @@ static int do_find(struct dirtree *new)
struct double_list *argdata = TT.argdata;
char *s, **ss;
- recurse = DIRTREE_COMEAGAIN|((toys.optflags&FLAG_L) ? DIRTREE_SYMFOLLOW : 0);
+ recurse = DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!(toys.optflags&FLAG_L));
// skip . and .. below topdir, handle -xdev and -depth
if (new) {
@@ -336,6 +342,9 @@ static int do_find(struct dirtree *new)
test = compare_numsign(new->st.st_size, 512, ss[1]);
} else if (!strcmp(s, "links")) {
if (check) test = compare_numsign(new->st.st_nlink, 0, ss[1]);
+ } else if (!strcmp(s, "inum")) {
+ if (check)
+ test = compare_numsign(new->st.st_ino, 0, ss[1]);
} else if (!strcmp(s, "mindepth") || !strcmp(s, "maxdepth")) {
if (check) {
struct dirtree *dt = new;
@@ -366,8 +375,8 @@ static int do_find(struct dirtree *new)
udl = xmalloc(sizeof(*udl));
dlist_add_nomalloc(&TT.argdata, (void *)udl);
- if (*s == 'u') udl->u.uid = xgetpwnam(ss[1])->pw_uid;
- else if (*s == 'g') udl->u.gid = xgetgrnam(ss[1])->gr_gid;
+ if (*s == 'u') udl->u.uid = xgetpwnamid(ss[1])->pw_uid;
+ else if (*s == 'g') udl->u.gid = xgetgrnamid(ss[1])->gr_gid;
else {
struct stat st;
@@ -430,7 +439,7 @@ static int do_find(struct dirtree *new)
if (aa->dir && TT.topdir == -1) TT.topdir = xopen(".", 0);
// collect names and execute commands
- } else if (check) {
+ } else {
char *name, *ss1 = ss[1];
struct double_list **ddl;
@@ -438,11 +447,12 @@ static int do_find(struct dirtree *new)
aa = (void *)llist_pop(&argdata);
ss += aa->arglen + 1;
+ if (!check) goto cont;
// name is always a new malloc, so we can always free it.
name = aa->dir ? xstrdup(new->name) : dirtree_path(new, 0);
// Mark entry so COMEAGAIN can call flush_exec() in parent.
- // This is never a valid pointer valud for prev to have otherwise
+ // This is never a valid pointer value for prev to have otherwise
if (aa->dir) aa->prev = (void *)1;
if (*s == 'o') {
@@ -525,12 +535,9 @@ void find_main(void)
do_find(0);
// Loop through paths
- for (i = 0; i < len; i++) {
- struct dirtree *new;
-
- new = dirtree_add_node(0, ss[i], toys.optflags&(FLAG_H|FLAG_L));
- if (new) dirtree_handle_callback(new, do_find);
- }
+ for (i = 0; i < len; i++)
+ dirtree_handle_callback(dirtree_start(ss[i], toys.optflags&(FLAG_H|FLAG_L)),
+ do_find);
if (CFG_TOYBOX_FREE) {
close(TT.topdir);
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
index aba7087..ffc920c 100644
--- a/toys/posix/grep.c
+++ b/toys/posix/grep.c
@@ -5,8 +5,8 @@
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html
USE_GREP(NEWTOY(grep, "ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
-USE_GREP(OLDTOY(egrep, grep, OPTSTR_grep, TOYFLAG_BIN))
-USE_GREP(OLDTOY(fgrep, grep, OPTSTR_grep, TOYFLAG_BIN))
+USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN))
+USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
config GREP
bool "grep"
@@ -36,6 +36,16 @@ config GREP
output prefix (default: filename if checking more than 1 file)
-H force filename -b byte offset of match
-h hide filename -n line number of match
+
+config EGREP
+ bool
+ default y
+ depends on GREP
+
+config FGREP
+ bool
+ default y
+ depends on GREP
*/
#define FOR_grep
@@ -259,7 +269,7 @@ static int do_grep_r(struct dirtree *new)
void grep_main(void)
{
- char **ss;
+ char **ss = toys.optargs;
// Handle egrep and fgrep
if (*toys.which->name == 'e' || (toys.optflags & FLAG_w))
@@ -267,9 +277,9 @@ void grep_main(void)
if (*toys.which->name == 'f') toys.optflags |= FLAG_F;
if (!TT.e && !TT.f) {
- if (!*toys.optargs) error_exit("no REGEX");
+ if (!*ss) error_exit("no REGEX");
TT.e = xzalloc(sizeof(struct arg_list));
- TT.e->arg = *(toys.optargs++);
+ TT.e->arg = *(ss++);
toys.optc--;
}
@@ -284,9 +294,9 @@ void grep_main(void)
}
if (toys.optflags & FLAG_r) {
- for (ss=toys.optargs; *ss; ss++) {
+ for (ss = *ss ? ss : (char *[]){".", 0}; *ss; ss++) {
if (!strcmp(*ss, "-")) do_grep(0, *ss);
else dirtree_read(*ss, do_grep_r);
}
- } else loopfiles_rw(toys.optargs, O_RDONLY, 0, 1, do_grep);
+ } else loopfiles_rw(ss, O_RDONLY, 0, 1, do_grep);
}
diff --git a/toys/posix/head.c b/toys/posix/head.c
index e8517d4..5867dfb 100644
--- a/toys/posix/head.c
+++ b/toys/posix/head.c
@@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/head.html
-USE_HEAD(NEWTOY(head, "n#<0=10", TOYFLAG_BIN))
+USE_HEAD(NEWTOY(head, "?n#<0=10", TOYFLAG_USR|TOYFLAG_BIN))
config HEAD
bool "head"
@@ -50,5 +50,12 @@ static void do_head(int fd, char *name)
void head_main(void)
{
- loopfiles(toys.optargs, do_head);
+ char *arg = *toys.optargs;
+
+ // handle old "-42" style arguments
+ if (arg && *arg == '-' && arg[1]) {
+ TT.lines = atolx(arg+1);
+ toys.optc--;
+ } else arg = 0;
+ loopfiles(toys.optargs+!!arg, do_head);
}
diff --git a/toys/posix/id.c b/toys/posix/id.c
index 000d7b4..aa43072 100644
--- a/toys/posix/id.c
+++ b/toys/posix/id.c
@@ -6,10 +6,10 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/id.html
-USE_ID(NEWTOY(id, ">1nGgru[!Ggu]", TOYFLAG_BIN))
-USE_GROUPS(OLDTOY(groups, id, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-USE_LOGNAME(OLDTOY(logname, id, ">0", TOYFLAG_BIN))
-USE_WHOAMI(OLDTOY(whoami, id, ">0", TOYFLAG_BIN))
+USE_ID(NEWTOY(id, ">1"USE_ID_Z("Z")"nGgru[!"USE_ID_Z("Z")"Ggu]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_GROUPS(NEWTOY(groups, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN))
+USE_WHOAMI(OLDTOY(whoami, logname, TOYFLAG_USR|TOYFLAG_BIN))
config ID
bool "id"
@@ -25,6 +25,15 @@ config ID
-r Show real ID instead of effective ID
-u Show only the effective user ID
+config ID_Z
+ bool
+ default y
+ depends on ID && !TOYBOX_LSM_NONE
+ help
+ usage: id [-Z]
+
+ -Z Show only security context
+
config GROUPS
bool "groups"
default y
@@ -51,15 +60,16 @@ config WHOAMI
*/
#define FOR_id
+#define FORCE_FLAGS
#include "toys.h"
GLOBALS(
- int do_u, do_n, do_G, is_groups;
+ int is_groups;
)
static void s_or_u(char *s, unsigned u, int done)
{
- if (TT.do_n) printf("%s", s);
+ if (toys.optflags&FLAG_n) printf("%s", s);
else printf("%u", u);
if (done) {
xputc('\n');
@@ -92,12 +102,12 @@ void do_id(char *username)
i = flags & FLAG_r;
pw = xgetpwuid(i ? uid : euid);
- if (TT.do_u) s_or_u(pw->pw_name, pw->pw_uid, 1);
+ if (toys.optflags&FLAG_u) s_or_u(pw->pw_name, pw->pw_uid, 1);
grp = xgetgrgid(i ? gid : egid);
if (flags & FLAG_g) s_or_u(grp->gr_name, grp->gr_gid, 1);
- if (!TT.do_G) {
+ if (!(toys.optflags&(FLAG_g|FLAG_Z))) {
showid("uid=", pw->pw_uid, pw->pw_name);
showid(" gid=", grp->gr_gid, grp->gr_name);
@@ -115,33 +125,55 @@ void do_id(char *username)
showid(" groups=", grp->gr_gid, grp->gr_name);
}
- groups = (gid_t *)toybuf;
- i = sizeof(toybuf)/sizeof(gid_t);
- ngroups = username ? getgrouplist(username, gid, groups, &i)
- : getgroups(i, groups);
- if (ngroups<0) perror_exit(0);
-
- for (i = 0; i<ngroups; i++) {
- if (i || !TT.do_G) xputc(' ');
- if (!(grp = getgrgid(groups[i]))) perror_msg(0);
- else if (TT.do_G) s_or_u(grp->gr_name, grp->gr_gid, 0);
- else if (grp->gr_gid != egid) showid("", grp->gr_gid, grp->gr_name);
+ if (!(toys.optflags&FLAG_Z)) {
+ groups = (gid_t *)toybuf;
+ i = sizeof(toybuf)/sizeof(gid_t);
+ ngroups = username ? getgrouplist(username, gid, groups, &i)
+ : getgroups(i, groups);
+ if (ngroups<0) perror_exit(0);
+
+ int show_separator = !(toys.optflags&FLAG_G);
+ for (i = 0; i<ngroups; i++) {
+ if (show_separator) xputc((toys.optflags&FLAG_G) ? ' ' : ',');
+ show_separator = 1;
+ if (!(grp = getgrgid(groups[i]))) perror_msg(0);
+ else if (toys.optflags&FLAG_G) s_or_u(grp->gr_name, grp->gr_gid, 0);
+ else if (grp->gr_gid != egid) showid("", grp->gr_gid, grp->gr_name);
+ else show_separator = 0; // Because we didn't show anything this time.
+ }
+ if (toys.optflags&FLAG_G) {
+ xputc('\n');
+ exit(0);
+ }
+ }
+
+ if (!CFG_TOYBOX_LSM_NONE) {
+ if (lsm_enabled()) {
+ char *context = lsm_context();
+
+ printf(" context=%s"+!!(toys.optflags&FLAG_Z), context);
+ if (CFG_TOYBOX_FREE) free(context);
+ } else if (toys.optflags&FLAG_Z) error_exit("%s disabled", lsm_name());
}
+
xputc('\n');
}
void id_main(void)
{
- // FLAG macros can be 0 if "id" command not enabled, so snapshot them here.
- if (FLAG_u) TT.do_u = toys.optflags & FLAG_u;
- if (FLAG_n) TT.do_n = toys.optflags & FLAG_n;
- if (FLAG_G) TT.do_G = toys.optflags & FLAG_G;
-
- // And set the variables for non-id commands.
- TT.is_groups = toys.which->name[0] == 'g';
- if (TT.is_groups) TT.do_G = TT.do_n = 1;
- else if (toys.which->name[0] != 'i') TT.do_u = TT.do_n = 1;
-
if (toys.optc) while(*toys.optargs) do_id(*toys.optargs++);
else do_id(NULL);
}
+
+void groups_main(void)
+{
+ TT.is_groups = 1;
+ toys.optflags = FLAG_G|FLAG_n;
+ id_main();
+}
+
+void logname_main(void)
+{
+ toys.optflags = FLAG_u|FLAG_n;
+ id_main();
+}
diff --git a/toys/posix/ln.c b/toys/posix/ln.c
index 04b4f29..06700dd 100644
--- a/toys/posix/ln.c
+++ b/toys/posix/ln.c
@@ -40,7 +40,7 @@ void ln_main(void)
if (((toys.optflags&FLAG_n) ? lstat : stat)(dest, &buf)
|| !S_ISDIR(buf.st_mode))
{
- if (toys.optc>1) error_exit("'%s' not a directory");
+ if (toys.optc>1) error_exit("'%s' not a directory", dest);
buf.st_mode = 0;
}
diff --git a/toys/posix/ls.c b/toys/posix/ls.c
index d1a26ee..44915fa 100644
--- a/toys/posix/ls.c
+++ b/toys/posix/ls.c
@@ -5,13 +5,13 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/ls.html
-USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"goACFHLRSacdfiklmnpqrstux1[-1Cglmnox][-cu][-ftS][-HL]", TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"ZgoACFHLRSacdfiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]", TOYFLAG_BIN|TOYFLAG_LOCALE))
config LS
bool "ls"
default y
help
- usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...]
+ usage: ls [-ACFHLRSZacdfiklmnpqrstux1] [directory...]
list files
what to show:
@@ -22,6 +22,7 @@ config LS
-u use access time for timestamps -A list all files but . and ..
-H follow command line symlinks -L follow symlinks
-R recursively list files in subdirs -F append /dir *exe @sym |FIFO
+ -Z security context
output formats:
-1 list one file per line -C columns (sorted vertically)
@@ -54,7 +55,7 @@ config LS_COLOR
GLOBALS(
char *color;
- struct dirtree *files;
+ struct dirtree *files, *singledir;
unsigned screen_width;
int nl_title;
@@ -86,19 +87,6 @@ int strwidth(char *s)
return total;
}
-void dlist_to_dirtree(struct dirtree *parent)
-{
- // Turn double_list into dirtree
- struct dirtree *dt = parent->child;
- if (dt) {
- dt->parent->next = NULL;
- while (dt) {
- dt->parent = parent;
- dt = dt->next;
- }
- }
-}
-
static char endtype(struct stat *st)
{
mode_t mode = st->st_mode;
@@ -128,6 +116,11 @@ static char *getgroupname(gid_t gid)
return gr ? gr->gr_name : TT.gid_buf;
}
+static int numlen(long long ll)
+{
+ return snprintf(0, 0, "%llu", ll);
+}
+
// Figure out size of printable entry fields for display indent/wrap
static void entrylen(struct dirtree *dt, unsigned *len)
@@ -139,21 +132,21 @@ static void entrylen(struct dirtree *dt, unsigned *len)
if (endtype(st)) ++*len;
if (flags & FLAG_m) ++*len;
- if (flags & FLAG_i) *len += (len[1] = numlen(st->st_ino));
+ 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 ? snprintf(0, 0, "%u", (unsigned)st->st_uid)
- : strwidth(getusername(st->st_uid));
- len[4] = fn ? snprintf(0, 0, "%u", (unsigned)st->st_gid)
- : strwidth(getgroupname(st->st_gid));
+ 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(major(st->st_rdev))+5;
} else len[5] = numlen(st->st_size);
}
- if (flags & FLAG_s) *len += (len[6] = numlen(st->st_blocks));
+
+ len[6] = (flags & FLAG_s) ? numlen(st->st_blocks) : 0;
+ len[7] = (flags & FLAG_Z) ? strwidth((char *)dt->extra) : 0;
}
static int compare(void *a, void *b)
@@ -186,6 +179,42 @@ static int filter(struct dirtree *new)
return 0;
}
+ if (flags & FLAG_Z) {
+ if (!CFG_TOYBOX_LSM_NONE) {
+ int fd;
+
+ // Why not just openat(O_PATH|(O_NOFOLLOW*!!(toys.optflags&FLAG_L))) and
+ // lsm_fget_context() on that filehandle? Because the kernel is broken,
+ // and won't let us read this "metadata" from the filehandle unless we
+ // have permission to read the data. We _can_ read the same data in
+ // by path, we just can't do it through an O_PATH filehandle, because
+ // reasons. So as a bug workaround for the broken kernel, we do it
+ // both ways.
+ //
+ // The O_NONBLOCK is there to avoid triggering automounting (there's
+ // a rush of nostalgia for you) on directories we don't descend into,
+ // which O_PATH would have done for us but see "the kernel is broken".
+ if (S_ISSOCK(new->st.st_mode) ||
+ (S_ISLNK(new->st.st_mode) && !(toys.optflags & FLAG_L)) ||
+ -1 == (fd = openat(dirtree_parentfd(new), new->name,
+ O_RDONLY|O_NONBLOCK|O_NOATIME)))
+ {
+ char *path;
+
+ // 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.
+ path = dirtree_path(new, 0);
+ lsm_lget_context(path, (char **)&new->extra);
+ free(path);
+ } else {
+ lsm_fget_context(fd, (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;
if (flags & FLAG_k) new->st.st_blocks = (new->st.st_blocks + 1) / 2;
@@ -257,44 +286,46 @@ int color_from_mode(mode_t mode)
static void listfiles(int dirfd, struct dirtree *indir)
{
- struct dirtree *dt, **sort = 0;
- unsigned long dtlen = 0, ul = 0;
- unsigned width, flags = toys.optflags, totals[7], len[7],
+ struct dirtree *dt, **sort;
+ unsigned long dtlen, ul = 0;
+ unsigned width, flags = toys.optflags, totals[8], len[8], totpad = 0,
*colsizes = (unsigned *)(toybuf+260), columns = (sizeof(toybuf)-260)/4;
memset(totals, 0, sizeof(totals));
- // Silently descend into single directory listed by itself on command line.
- // In this case only show dirname/total header when given -R.
+ // Top level directory was already populated by main()
if (!indir->parent) {
- if (!(dt = indir->child)) return;
- if (S_ISDIR(dt->st.st_mode) && !dt->next && !(flags & FLAG_d)) {
- dt->extra = 1;
- listfiles(open(dt->name, 0), dt);
+ // 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;
} else {
// 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.
indir->data = dup(dirfd);
- dirtree_recurse(indir, filter, (flags&FLAG_L) ? DIRTREE_SYMFOLLOW : 0);
+ dirtree_recurse(indir, filter, DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
}
// Copy linked list to array and sort it. Directories go in array because
- // we visit them in sorted order.
-
- for (;;) {
- for (dt = indir->child; dt; dt = dt->next) {
+ // 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;
- dtlen++;
- }
- if (sort) break;
- sort = xmalloc(dtlen * sizeof(void *));
- dtlen = 0;
- continue;
+ if (sort || !dtlen) break;
}
// Label directory if not top of tree, or if -R
- if (indir->parent && (!indir->extra || (flags & FLAG_R)))
+ if (indir->parent && (TT.singledir!=indir || (flags&FLAG_R)))
{
char *path = dirtree_path(indir, 0);
@@ -303,7 +334,21 @@ static void listfiles(int dirfd, struct dirtree *indir)
free(path);
}
- if (!(flags & FLAG_f)) qsort(sort, dtlen, sizeof(void *), (void *)compare);
+ // 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; ul<dtlen; ul++) {
+ entrylen(sort[ul], len);
+ for (width = 0; width<8; width++)
+ if (len[width]>totals[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_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) && indir->parent)
+ xprintf("total %llu\n", blocks);
+ }
// Find largest entry in each field for display alignment
if (flags & (FLAG_C|FLAG_x)) {
@@ -320,29 +365,18 @@ static void listfiles(int dirfd, struct dirtree *indir)
memset(colsizes, 0, columns*sizeof(unsigned));
for (ul=0; ul<dtlen; ul++) {
entrylen(sort[next_column(ul, dtlen, columns, &c)], len);
+ *len += totpad;
if (c == columns) break;
- // Does this put us over budget?
+ // Expand this column if necessary, break if that puts us over budget
if (*len > colsizes[c]) {
- totlen += *len-colsizes[c];
+ totlen += (*len)-colsizes[c];
colsizes[c] = *len;
if (totlen > TT.screen_width) break;
}
}
- // If it fit, stop here
+ // If everything fit, stop here
if (ul == dtlen) break;
}
- } else if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) {
- unsigned long blocks = 0;
-
- for (ul = 0; ul<dtlen; ul++)
- {
- entrylen(sort[ul], len);
- for (width=0; width<6; width++)
- if (len[width] > totals[width]) totals[width] = len[width];
- blocks += sort[ul]->st.st_blocks;
- }
-
- if (indir->parent) xprintf("total %lu\n", blocks);
}
// Loop through again to produce output.
@@ -375,41 +409,46 @@ static void listfiles(int dirfd, struct dirtree *indir)
}
width += *len;
- if (flags & FLAG_i) xprintf("% *lu ", len[1], (unsigned long)st->st_ino);
- if (flags & FLAG_s) xprintf("% *lu ", len[6], (unsigned long)st->st_blocks);
+ if (flags & FLAG_i)
+ xprintf("%*lu ", totals[1], (unsigned long)st->st_ino);
+ if (flags & FLAG_s)
+ xprintf("%*lu ", totals[6], (unsigned long)st->st_blocks);
if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
struct tm *tm;
- char perm[11], thyme[64], *usr, *upad, *grp, *grpad;
+ char perm[11], thyme[64], *ss;
+ // (long) is to coerce the st types into something we know we can print.
mode_to_string(mode, perm);
+ printf("%s% *ld", perm, totals[2]+1, (long)st->st_nlink);
- if (flags&FLAG_o) grp = grpad = toybuf+256;
- else {
- if (flags&FLAG_n) sprintf(grp = thyme, "%u", (unsigned)st->st_gid);
- else strwidth(grp = getgroupname(st->st_gid));
- grpad = toybuf+256-(totals[4]-len[4]);
+ // print group
+ if (!(flags&FLAG_o)) {
+ if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_gid);
+ else strwidth(ss = getgroupname(st->st_gid));
+ printf(" %*s", (int)totals[4], ss);
}
- if (flags&FLAG_g) usr = upad = toybuf+256;
- else {
- upad = toybuf+255-(totals[3]-len[3]);
- if (flags&FLAG_n) sprintf(usr = TT.uid_buf, "%u", (unsigned)st->st_uid);
- else strwidth(usr = getusername(st->st_uid));
+ if (!(flags&FLAG_g)) {
+ if (flags&FLAG_n) sprintf(ss = thyme, "%u", (unsigned)st->st_uid);
+ else strwidth(ss = getusername(st->st_uid));
+ printf(" %*s", (int)totals[3], ss);
}
- // Coerce the st types into something we know we can print.
- printf("%s% *ld %s%s%s%s", perm, totals[2]+1, (long)st->st_nlink,
- usr, upad, grp, grpad);
+ if (flags & FLAG_Z)
+ printf(" %*s", -(int)totals[7], (char *)sort[next]->extra);
+ // print major/minor
if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
printf("% *d,% 4d", totals[5]-4, major(st->st_rdev),minor(st->st_rdev));
- else printf("% *"PRId64, totals[5]+1, (int64_t)st->st_size);
+ else printf("% *lld", totals[5]+1, (long long)st->st_size);
+ // print time, always in --time-style=long-iso
tm = localtime(&(st->st_mtime));
strftime(thyme, sizeof(thyme), "%F %H:%M", tm);
xprintf(" %s ", thyme);
- }
+ } else if (flags & FLAG_Z)
+ printf("%*s ", (int)totals[7], (char *)sort[next]->extra);
if (flags & FLAG_color) {
color = color_from_mode(st->st_mode);
@@ -441,7 +480,7 @@ static void listfiles(int dirfd, struct dirtree *indir)
// Pad columns
if (flags & (FLAG_C|FLAG_x)) {
- curcol = colsizes[curcol] - *len;
+ curcol = colsizes[curcol]-(*len)-totpad;
if (curcol < 255) xprintf("%s", toybuf+255-curcol);
}
}
@@ -451,12 +490,12 @@ static void listfiles(int dirfd, struct dirtree *indir)
// Free directory entries, recursing first if necessary.
for (ul = 0; ul<dtlen; free(sort[ul++])) {
- if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)
- || !dirtree_notdotdot(sort[ul])) continue;
+ if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)) continue;
// Recurse into dirs if at top of the tree or given -R
- if (!indir->parent || (flags & FLAG_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);
@@ -485,22 +524,19 @@ void ls_main(void)
// Iterate through command line arguments, collecting directories and files.
// Non-absolute paths are relative to current directory.
- TT.files = dirtree_add_node(0, 0, 0);
+ TT.files = dirtree_start(0, 0);
for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) {
- dt = dirtree_add_node(0, *s, !(toys.optflags & (FLAG_l|FLAG_d|FLAG_F))
- || (toys.optflags & (FLAG_L|FLAG_H)));
-
- if (!dt) {
- toys.exitval = 1;
- continue;
- }
+ dt = dirtree_start(*s, !(toys.optflags&(FLAG_l|FLAG_d|FLAG_F)) ||
+ (toys.optflags&(FLAG_L|FLAG_H)));
- // Typecast means double_list->prev temporarirly goes in dirtree->parent
- dlist_add_nomalloc((void *)&TT.files->child, (struct double_list *)dt);
+ // note: double_list->prev temporarirly goes in dirtree->parent
+ if (dt) dlist_add_nomalloc((void *)&TT.files->child, (void *)dt);
+ else toys.exitval = 1;
}
- // Turn double_list into dirtree
- dlist_to_dirtree(TT.files);
+ // 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);
diff --git a/toys/posix/mkdir.c b/toys/posix/mkdir.c
index 739f961..95bdf33 100644
--- a/toys/posix/mkdir.c
+++ b/toys/posix/mkdir.c
@@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/mkdir.html
-USE_MKDIR(NEWTOY(mkdir, "<1vpm:", TOYFLAG_BIN|TOYFLAG_UMASK))
+USE_MKDIR(NEWTOY(mkdir, "<1"USE_MKDIR_Z("Z:")"vpm:", TOYFLAG_BIN|TOYFLAG_UMASK))
config MKDIR
bool "mkdir"
@@ -17,6 +17,15 @@ config MKDIR
-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
@@ -24,6 +33,7 @@ config MKDIR
GLOBALS(
char *arg_mode;
+ char *arg_context;
)
void mkdir_main(void)
@@ -31,12 +41,15 @@ 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.arg_context))
+ perror_exit("-Z '%s' failed", TT.arg_context);
if (TT.arg_mode) mode = string_to_mode(TT.arg_mode, 0777);
// Note, -p and -v flags line up with mkpathat() flags
-
- for (s=toys.optargs; *s; s++)
+ for (s=toys.optargs; *s; s++) {
if (mkpathat(AT_FDCWD, *s, mode, toys.optflags|1))
perror_msg("'%s'", *s);
+ }
}
diff --git a/toys/posix/mkfifo.c b/toys/posix/mkfifo.c
index 15fab70..9fc3829 100644
--- a/toys/posix/mkfifo.c
+++ b/toys/posix/mkfifo.c
@@ -4,15 +4,24 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/mkfifo.html
-USE_MKFIFO(NEWTOY(mkfifo, "<1m:", TOYFLAG_BIN))
+USE_MKFIFO(NEWTOY(mkfifo, "<1"USE_MKFIFO_Z("Z:")"m:", TOYFLAG_USR|TOYFLAG_BIN))
config MKFIFO
bool "mkfifo"
default y
help
- usage: mkfifo [fifo_name...]
+ usage: mkfifo [NAME...]
Create FIFOs (named pipes).
+
+config MKFIFO_Z
+ bool
+ default y
+ depends on MKFIFO && !TOYBOX_LSM_NONE
+ help
+ usage: mkfifo [-Z CONTEXT]
+
+ -Z Security context
*/
#define FOR_mkfifo
@@ -20,6 +29,8 @@ config MKFIFO
GLOBALS(
char *m_string;
+ char *Z;
+
mode_t mode;
)
@@ -30,6 +41,10 @@ void mkfifo_main(void)
TT.mode = 0666;
if (toys.optflags & FLAG_m) TT.mode = string_to_mode(TT.m_string, 0);
+ if (CFG_MKFIFO_Z && (toys.optflags&FLAG_Z))
+ if (0>lsm_set_create(TT.Z))
+ perror_exit("-Z '%s' failed", TT.Z);
+
for (s = toys.optargs; *s; s++)
if (mknod(*s, S_IFIFO | TT.mode, 0) < 0) perror_msg("%s", *s);
}
diff --git a/toys/posix/nice.c b/toys/posix/nice.c
index bc25d35..4b587ee 100644
--- a/toys/posix/nice.c
+++ b/toys/posix/nice.c
@@ -34,5 +34,5 @@ void nice_main(void)
errno = 0;
if (nice(TT.priority)==-1 && errno) perror_exit("Can't set priority");
- xexec_optargs(0);
+ xexec(toys.optargs);
}
diff --git a/toys/posix/nohup.c b/toys/posix/nohup.c
index df264da..4d6d59f 100644
--- a/toys/posix/nohup.c
+++ b/toys/posix/nohup.c
@@ -21,7 +21,7 @@ config NOHUP
void nohup_main(void)
{
- signal(SIGHUP, SIG_IGN);
+ xsignal(SIGHUP, SIG_IGN);
if (isatty(1)) {
close(1);
if (-1 == open("nohup.out", O_CREAT|O_APPEND|O_WRONLY,
@@ -38,5 +38,5 @@ void nohup_main(void)
close(0);
open("/dev/null", O_RDONLY);
}
- xexec_optargs(0);
+ xexec(toys.optargs);
}
diff --git a/toys/posix/printf.c b/toys/posix/printf.c
new file mode 100644
index 0000000..365b8f3
--- /dev/null
+++ b/toys/posix/printf.c
@@ -0,0 +1,141 @@
+/* printf.c - Format and Print the data.
+ *
+ * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copyright 2014 Kyungwan Han <asura321@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
+ *
+ * todo: *m$ ala printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);
+
+USE_PRINTF(NEWTOY(printf, "<1?^", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PRINTF
+ bool "printf"
+ default y
+ help
+ usage: printf FORMAT [ARGUMENT...]
+
+ Format and print ARGUMENT(s) according to FORMAT, using C printf syntax
+ (% escapes for cdeEfgGiosuxX, \ escapes for abefnrtv0 or \OCTAL or \xHEX).
+*/
+
+#define FOR_printf
+#include "toys.h"
+
+// Detect matching character (return true/false) and advance pointer if match.
+static int eat(char **s, char c)
+{
+ int x = (**s == c);
+
+ if (x) ++*s;
+
+ return x;
+}
+
+// Parse escape sequences.
+static int handle_slash(char **esc_val)
+{
+ char *ptr = *esc_val;
+ int len, base = 0;
+ unsigned result = 0, num;
+
+ if (*ptr == 'c') xexit();
+
+ // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
+ if (eat(&ptr, 'x')) base = 16;
+ else if (*ptr >= '0' && *ptr <= '8') base = 8;
+ len = (char []){0,3,2}[base/8];
+
+ // Not a hex or octal escape? (This catches trailing \)
+ if (!len) {
+ if (!(result = unescape(*ptr))) result = '\\';
+ else ++*esc_val;
+
+ return result;
+ }
+
+ while (len) {
+ num = tolower(*ptr) - '0';
+ if (num >= 'a'-'0') num += '0'-'a'+10;
+ if (num >= base) {
+ // Don't parse invalid hex value ala "\xvd", print it verbatim
+ if (base == 16 && len == 2) {
+ ptr--;
+ result = '\\';
+ }
+ break;
+ }
+ result = (result*base)+num;
+ ptr++;
+ len--;
+ }
+ *esc_val = ptr;
+
+ return result;
+}
+
+void printf_main(void)
+{
+ char **arg = toys.optargs+1;
+
+ // Repeat format until arguments consumed
+ for (;;) {
+ int seen = 0;
+ char *f = *toys.optargs;
+
+ // Loop through characters in format
+ while (*f) {
+ if (eat(&f, '\\')) putchar(handle_slash(&f));
+ else if (!eat(&f, '%') || *f == '%') putchar(*f++);
+
+ // Handle %escape
+ else {
+ char c, *end = 0, *aa, *to = toybuf;
+ int wp[] = {0,-1}, i = 0;
+
+ // Parse width.precision between % and type indicator.
+ *to++ = '%';
+ while (strchr("-+# '0", *f) && (to-toybuf)<10) *to++ = *f++;
+ for (;;) {
+ if (eat(&f, '*')) {
+ if (*arg) wp[i] = atolx(*arg++);
+ } else while (*f >= '0' && *f <= '9') wp[i] = (wp[i]*10)+(*f++)-'0';
+ if (i++ || !eat(&f, '.')) break;
+ wp[1] = 0;
+ }
+ c = *f++;
+ seen = sprintf(to, "*.*%c", c);;
+ errno = 0;
+ aa = *arg ? *arg++ : "";
+
+ // Output %esc using parsed format string
+ if (c == 'b') {
+ while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa) : *aa++);
+
+ continue;
+ } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa);
+ else if (c == 's') printf(toybuf, wp[0], wp[1], aa);
+ else if (strchr("diouxX", c)) {
+ long ll;
+
+ if (*aa == '\'' || *aa == '"') ll = aa[1];
+ else ll = strtoll(aa, &end, 0);
+
+ sprintf(to, "*.*ll%c", c);
+ printf(toybuf, wp[0], wp[1], ll);
+ } else if (strchr("feEgG", c)) {
+ long double ld = strtold(aa, &end);
+
+ sprintf(to, "*.*L%c", c);
+ printf(toybuf, wp[0], wp[1], ld);
+ } else error_exit("bad %%%c@%ld", c, (long)(f-*toys.optargs));
+
+ if (end && (errno || *end)) perror_msg("bad %%%c %s", c, aa);
+ }
+ }
+
+ // Posix says to keep looping through format until we consume all args.
+ // This only works if the format actually consumed at least one arg.
+ if (!seen || !*arg) break;
+ }
+}
diff --git a/toys/posix/renice.c b/toys/posix/renice.c
index 8c20644..489eb13 100644
--- a/toys/posix/renice.c
+++ b/toys/posix/renice.c
@@ -4,7 +4,7 @@
*
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/renice.html
-USE_RENICE(NEWTOY(renice, "<1gpun#|", TOYFLAG_BIN))
+USE_RENICE(NEWTOY(renice, "<1gpun#|", TOYFLAG_USR|TOYFLAG_BIN))
config RENICE
bool "renice"
diff --git a/toys/posix/rm.c b/toys/posix/rm.c
index 9561a67..5523a98 100644
--- a/toys/posix/rm.c
+++ b/toys/posix/rm.c
@@ -47,13 +47,13 @@ static int do_rm(struct dirtree *try)
// handle directory recursion
if (dir) {
+ using = AT_REMOVEDIR;
// Handle chmod 000 directories when -f
- if (faccessat(fd, try->name, R_OK, AT_SYMLINK_NOFOLLOW)) {
+ if (faccessat(fd, try->name, R_OK, 0)) {
if (toys.optflags & FLAG_f) wfchmodat(fd, try->name, 0700);
else goto skip;
}
if (!try->again) return DIRTREE_COMEAGAIN;
- using = AT_REMOVEDIR;
if (try->symlink) goto skip;
if (flags & FLAG_i) {
char *s = dirtree_path(try, 0);
diff --git a/toys/pending/sed.c b/toys/posix/sed.c
index b98da05..2097532 100644
--- a/toys/pending/sed.c
+++ b/toys/posix/sed.c
@@ -4,16 +4,16 @@
*
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
*
- * TODO: lines > 2G could signed int wrap length counters. Not just getline()
+ * TODO: lines > 2G could wrap signed int length counters. Not just getline()
* but N and s///
-USE_SED(NEWTOY(sed, "(version)e*f*inr", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_SED(NEWTOY(sed, "(version)e*f*inEr[+Er]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
config SED
bool "sed"
- default n
+ default y
help
- usage: sed [-inr] [-e SCRIPT]...|SCRIPT [-f SCRIPT_FILE]... [FILE...]
+ usage: sed [-inrE] [-e SCRIPT]...|SCRIPT [-f SCRIPT_FILE]... [FILE...]
Stream editor. Apply one or more editing SCRIPTs to each line of input
(from FILE or stdin) producing output (by default to stdout).
@@ -23,6 +23,7 @@ config SED
-i Edit each file in place.
-n No default output. (Use the p command to output matched lines.)
-r Use extended regular expression syntax.
+ -E Alias for -r.
-s Treat input files separately (implied by -i)
A SCRIPT is a series of one or more COMMANDs separated by newlines or
@@ -83,9 +84,9 @@ config SED
H Remember this line (appending to remembered line, if any)
- l Print this line, escaping \abfrtv (but leaving \n as a newline),
- using octal escapes for other nonprintable characters, and
- wrapping lines to terminal width with a backslash and newline
+ l Print line, escaping \abfrtv (but not newline), octal escaping other
+ nonprintable characters, wrapping lines to terminal width with a
+ backslash, and appending $ to actual end of line.
n Print default output and read next line, replacing current line
(If no next line available, quit processing script)
@@ -175,6 +176,7 @@ GLOBALS(
void *restart, *lastregex;
long nextlen, rememberlen, count;
int fdout, noeol;
+ unsigned xx;
)
struct step {
@@ -254,7 +256,7 @@ static char *extend_string(char **old, char *new, int oldlen, int newlen)
memcpy(s+oldlen, new, newlen);
s[oldlen+newlen] = 0;
- return s+oldlen+newlen;
+ return s+oldlen+newlen+1;
}
// An empty regex repeats the previous one
@@ -296,7 +298,9 @@ static void walk_pattern(char **pline, long plen)
if (line[len-1] == '\n') line[--len] = eol++;
TT.count++;
- logrus = TT.restart ? TT.restart : (void *)TT.pattern;
+ // The restart-1 is because we added one to make sure it wasn't NULL,
+ // otherwise N as last command would restart script
+ logrus = TT.restart ? ((struct step *)TT.restart)-1 : (void *)TT.pattern;
TT.restart = 0;
while (logrus) {
@@ -326,11 +330,17 @@ static void walk_pattern(char **pline, long plen)
if (line && !ghostwheel(rm, line, len, 0, 0, 0)) logrus->hit++;
} else if (lm == TT.count || (lm == -1 && !pline)) logrus->hit++;
+
+ if (!logrus->lmatch[1] && !logrus->rmatch[1]) miss = 1;
}
// Didn't match?
- if (!(logrus->hit ^ logrus->not)) {
+ lm = !(logrus->hit ^ logrus->not);
+
+ // Deferred disable from regex end match
+ if (miss || logrus->lmatch[1] == TT.count) logrus->hit = 0;
+ if (lm) {
// Handle skipping curly bracket command group
if (c == '{') {
int curly = 1;
@@ -344,8 +354,6 @@ static void walk_pattern(char **pline, long plen)
logrus = logrus->next;
continue;
}
- // Deferred disable from regex end match
- if (miss) logrus->hit = 0;
}
// A deleted line can still update line match state for later commands
@@ -359,7 +367,7 @@ static void walk_pattern(char **pline, long plen)
if (c=='a' || c=='r') {
struct append *a = xzalloc(sizeof(struct append));
a->str = logrus->arg1+(char *)logrus;
- a->file = c== 'r';
+ a->file = c=='r';
dlist_add_nomalloc((void *)&append, (void *)a);
} else if (c=='b' || c=='t' || c=='T') {
int t = tea;
@@ -375,22 +383,30 @@ static void walk_pattern(char **pline, long plen)
}
} else if (c=='c') {
str = logrus->arg1+(char *)logrus;
- if (!logrus->hit || (!logrus->lmatch[1] && !logrus->rmatch[1]))
- emit(str, strlen(str), 1);
- goto done;
+ if (!logrus->hit) emit(str, strlen(str), 1);
+ free(line);
+ line = 0;
+ continue;
} else if (c=='d') {
free(line);
line = 0;
continue;
} else if (c=='D') {
// Delete up to \n or end of buffer
- for (str = line; !*str || *str=='\n'; str++);
+ str = line;
+ while ((str-line)<len) if (*(str++) == '\n') break;
len -= str - line;
memmove(line, str, len);
- line[len] = 0;
- // restart script
- logrus = (void *)TT.pattern;
+ // if "delete" blanks line, disable further processing
+ // otherwise trim and restart script
+ if (!len) {
+ free(line);
+ line = 0;
+ } else {
+ line[len] = 0;
+ logrus = (void *)TT.pattern;
+ }
continue;
} else if (c=='g') {
free(line);
@@ -413,17 +429,40 @@ static void walk_pattern(char **pline, long plen)
} else if (c=='i') {
str = logrus->arg1+(char *)logrus;
emit(str, strlen(str), 1);
-// } else if (c=='l') {
-// error_exit("todo: l");
+ } else if (c=='l') {
+ int i, x, off;
+
+ if (!TT.xx) {
+ terminal_size(&TT.xx, 0);
+ if (!TT.xx) TT.xx = 80;
+ if (TT.xx > sizeof(toybuf)-10) TT.xx = sizeof(toybuf)-10;
+ if (TT.xx > 4) TT.xx -= 4;
+ }
+
+ for (i = off = 0; i<len; i++) {
+ if (off >= TT.xx) {
+ toybuf[off++] = '\\';
+ emit(toybuf, off, 1);
+ off = 0;
+ }
+ x = stridx("\\\a\b\f\r\t\v", line[i]);
+ if (x != -1) {
+ toybuf[off++] = '\\';
+ toybuf[off++] = "\\abfrtv"[x];
+ } else if (line[i] >= ' ') toybuf[off++] = line[i];
+ else off += sprintf(toybuf+off, "\\%03o", line[i]);
+ }
+ toybuf[off++] = '$';
+ emit(toybuf, off, 1);
} else if (c=='n') {
- TT.restart = logrus->next;
+ TT.restart = logrus->next+1;
break;
} else if (c=='N') {
// Can't just grab next line because we could have multiple N and
// we need to actually read ahead to get N;$p EOF detection right.
if (pline) {
- TT.restart = logrus->next;
+ TT.restart = logrus->next+1;
extend_string(&line, TT.nextline, len, -TT.nextlen);
free(TT.nextline);
TT.nextline = line;
@@ -492,35 +531,36 @@ static void walk_pattern(char **pline, long plen)
// place because backrefs may refer to text after it's overwritten.)
len += newlen-mlen;
swap = xmalloc(len+1);
- rswap = swap+(rline-line);
+ rswap = swap+(rline-line)+match[0].rm_so;
memcpy(swap, line, (rline-line)+match[0].rm_so);
- memcpy(rswap+match[0].rm_so+newlen, rline+match[0].rm_eo,
- (rlen -= match[0].rm_eo)+1);
+ memcpy(rswap+newlen, rline+match[0].rm_eo, (rlen -= match[0].rm_eo)+1);
// copy in new replacement text
- rswap += match[0].rm_so;
for (off = mlen = 0; new[off]; off++) {
int cc = 0, ll;
- if ((rswap[mlen++] = new[off]) == '\\') {
+ if (new[off] == '\\') {
cc = new[++off] - '0';
if (cc<0 || cc>9) {
- if (!(rswap[mlen-1] = unescape(new[off])))
+ if (!(rswap[mlen++] = unescape(new[off])))
rswap[mlen-1] = new[off];
continue;
} else if (match[cc].rm_so == -1) error_exit("no s//\\%d/", cc);
- } else if (new[off] != '&') continue;
+ } else if (new[off] != '&') {
+ rswap[mlen++] = new[off];
+
+ continue;
+ }
ll = match[cc].rm_eo-match[cc].rm_so;
- memcpy(rswap+(--mlen), rline+match[cc].rm_so, ll);
+ memcpy(rswap+mlen, rline+match[cc].rm_so, ll);
mlen += ll;
}
rline = rswap+newlen;
free(line);
line = swap;
- len = rlen+(rline-line);
// Stop after first substitution unless we have flag g
if (!(logrus->sflags & 2)) break;
@@ -576,7 +616,7 @@ writenow:
} else if (c=='=') {
sprintf(toybuf, "%ld", TT.count);
emit(toybuf, strlen(toybuf), 1);
- } else if (!strchr(":{}", c)) error_exit("todo: %c", c);
+ }
logrus = logrus->next;
}
@@ -590,12 +630,15 @@ done:
struct append *a = append->next;
if (append->file) {
- int fd = xopen(append->str, O_RDONLY);
+ int fd = open(append->str, O_RDONLY);
// Force newline if noeol pending
- emit(0, 0, 0);
- xsendfile(fd, TT.fdout);
- close(fd);
+ if (fd != -1) {
+ if (TT.noeol) xwrite(TT.fdout, "\n", 1);
+ TT.noeol = 0;
+ xsendfile(fd, TT.fdout);
+ close(fd);
+ }
} else emit(append->str, strlen(append->str), 1);
free(append);
append = a;
@@ -635,7 +678,7 @@ static void do_sed(int fd, char *name)
struct step *primal;
if (!fd && *name=='-') {
- error_msg("no -i on stdin");
+ error_msg("-i on stdin");
return;
}
TT.fdout = copy_tempfile(fd, name, &tmp);
@@ -653,89 +696,79 @@ static void do_sed(int fd, char *name)
}
}
-// Note: removing backslash escapes and null terminating edits the source
-// string, which could be from the environment space via -e, which could
-// screw up what "ps" sees, and I'm ok with that. (Modifying the environment
-// space like that means sed is very, very not reentrant.)
-
-// Ok, what happens if we xexec() sed with constant arguments then?
-// TODO: ^^^ that
-// also screws up error reporting for bad patterns
-
-// returns length of processed string, *pstr advances to next unused char,
-// if delim (or *delim) is 0 uses starting char as delimiter, otherwise
-// parses and saves delimiter from first character(s)
-// if rexex, ignore delimiter in [ranges]
-static int unescape_delimited_string(char **pstr, char *delim, int regex)
+// Copy chunk of string between two delimiters, converting printf escapes.
+// returns processed copy of string (0 if error), *pstr advances to next
+// unused char. if delim (or *delim) is 0 uses/saves starting char as delimiter
+// if regxex, ignore delimiter in [ranges]
+static char *unescape_delimited_string(char **pstr, char *delim, int regex)
{
- char *to, *from, d;
- int rc;
+ char *to, *from, mode = 0, d;
to = from = *pstr;
if (!delim || !*delim) {
- if (!(d = *(from++))) return -1;
+ if (!(d = *(from++))) return 0;
if (d == '\\') d = *(from++);
- if (!d || d == '\\') return -1;
+ if (!d || d == '\\') return 0;
if (delim) *delim = d;
} else d = *delim;
+ to = delim = xmalloc(strlen(*pstr)+1);
- while (*from != d) {
- if (!*from) return -1;
+ while (mode || *from != d) {
+ if (!*from) return 0;
// delimiter in regex character range doesn't count
if (*from == '[') {
- int len = 1;
-
- if (from[len] == ']') len++;
- while (from[len] != ']') if (!from[len++]) return -1;
- memmove(to, from, ++len);
- to += len;
- from += len;
- continue;
- }
- if (*from == '\\') {
- if (!from[1]) return -1;
+ mode = '[';
+ if (from[1] == ']') *(to++) = *(from++);
+ } else if (mode && *from == ']') mode = 0;
+ else if (*from == '\\') {
+ if (!from[1]) return 0;
// Check escaped end delimiter before printf style escapes.
if (from[1] == d) from++;
- else if (from[1]!='\\') {
+ else if (from[1]=='\\') *(to++) = *(from++);
+ else {
char c = unescape(from[1]);
if (c) {
*(to++) = c;
from+=2;
continue;
- }
+ } else *(to++) = *(from++);
}
}
*(to++) = *(from++);
}
- rc = to-*pstr;
*to = 0;
*pstr = from+1;
- return rc;
+ return delim;
}
// Translate primal pattern into walkable form.
static void jewel_of_judgement(char **pline, long len)
{
struct step *corwin = (void *)TT.pattern;
- char *line = *pline, *reg, c;
+ char *line, *reg, c, *errstart;
int i;
+ line = errstart = pline ? *pline : "";
+ if (len && line[len-1]=='\n') line[--len] = 0;
+
// Append additional line to pattern argument string?
+ // We temporarily repurpose "hit" to indicate line continuations
if (corwin && corwin->prev->hit) {
+ if (!*pline) error_exit("unfinished %c", corwin->prev->c);;
// Remove half-finished entry from list so remalloc() doesn't confuse it
TT.pattern = TT.pattern->prev;
corwin = dlist_pop(&TT.pattern);
- corwin->hit = 0;
c = corwin->c;
reg = (char *)corwin;
reg += corwin->arg1 + strlen(reg + corwin->arg1);
- // Resume parsing
- goto append;
+ // Resume parsing for 'a' or 's' command
+ if (corwin->hit < 256) goto resume_s;
+ else goto resume_a;
}
// Loop through commands in line
@@ -744,9 +777,14 @@ static void jewel_of_judgement(char **pline, long len)
for (;;) {
if (corwin) dlist_add_nomalloc(&TT.pattern, (void *)corwin);
- while (isspace(*line) || *line == ';') line++;
- if (!*line || *line == '#') return;
+ for (;;) {
+ while (isspace(*line) || *line == ';') line++;
+ if (*line == '#') while (*line && *line != '\n') line++;
+ else break;
+ }
+ if (!*line) return;
+ errstart = line;
memset(toybuf, 0, sizeof(struct step));
corwin = (void *)toybuf;
reg = toybuf + sizeof(struct step);
@@ -763,13 +801,14 @@ static void jewel_of_judgement(char **pline, long len)
} else if (*line == '/' || *line == '\\') {
char *s = line;
- if (-1 == unescape_delimited_string(&line, 0, 1)) goto brand;
+ if (!(s = unescape_delimited_string(&line, 0, 1))) goto brand;
if (!*s) corwin->rmatch[i] = 0;
else {
xregcomp((void *)reg, s, (toys.optflags & FLAG_r)*REG_EXTENDED);
corwin->rmatch[i] = reg-toybuf;
reg += sizeof(regex_t);
}
+ free(s);
} else break;
}
@@ -796,34 +835,57 @@ static void jewel_of_judgement(char **pline, long len)
else if (c == '}') {
if (!TT.nextlen--) break;
} else if (c == 's') {
- char *merlin, *fiona, delim = 0;
+ char *fiona, delim = 0;
// s/pattern/replacement/flags
+ // line continuations use arg1, so we fill out arg2 first (since the
+ // regex part can't be multiple lines) and swap them back later.
+
// get pattern (just record, we parse it later)
- corwin->arg1 = reg - (char *)corwin;
- merlin = line;
- if (-1 == unescape_delimited_string(&line, &delim, 1)) goto brand;
+ corwin->arg2 = reg - (char *)corwin;
+ if (!(TT.remember = unescape_delimited_string(&line, &delim, 1)))
+ goto brand;
+ reg += sizeof(regex_t);
+ corwin->arg1 = reg-(char *)corwin;
+ corwin->hit = delim;
+resume_s:
// get replacement - don't replace escapes because \1 and \& need
// processing later, after we replace \\ with \ we can't tell \\1 from \1
fiona = line;
- while (*line != delim) {
- if (!*line) goto brand;
- if (*line == '\\') {
- if (!line[1]) goto brand;
- line += 2;
- } else line++;
+ while (*fiona != corwin->hit) {
+ if (!*fiona) goto brand;
+ if (*fiona++ == '\\') {
+ if (!*fiona || *fiona == '\n') {
+ fiona[-1] = '\n';
+ break;
+ }
+ fiona++;
+ }
+ }
+
+ reg = extend_string((void *)&corwin, line, reg-(char *)corwin,fiona-line);
+ line = fiona;
+ // line continuation? (note: '\n' can't be a valid delim).
+ if (*line == corwin->hit) corwin->hit = 0;
+ else {
+ if (!*line) continue;
+ reg--;
+ line++;
+ goto resume_s;
}
- corwin->arg2 = corwin->arg1 + sizeof(regex_t);
- reg = extend_string((void *)&corwin, fiona, corwin->arg2, line-fiona)+1;
+ // swap arg1/arg2 so they're back in order arguments occur.
+ i = corwin->arg1;
+ corwin->arg1 = corwin->arg2;
+ corwin->arg2 = i;
// get flags
for (line++; *line; line++) {
long l;
- if (isspace(*line)) continue;
+ if (isspace(*line) && *line != '\n') continue;
if (0 <= (l = stridx("igp", *line))) corwin->sflags |= 1<<l;
else if (!(corwin->sflags>>3) && 0<(l = strtol(line, &line, 10))) {
@@ -834,9 +896,11 @@ static void jewel_of_judgement(char **pline, long len)
// We deferred actually parsing the regex until we had the s///i flag
// allocating the space was done by extend_string() above
- if (!*merlin) corwin->arg1 = 0;
- else xregcomp((void *)(corwin->arg1 + (char *)corwin), merlin,
+ if (!*TT.remember) corwin->arg1 = 0;
+ else xregcomp((void *)(corwin->arg1 + (char *)corwin), TT.remember,
((toys.optflags & FLAG_r)*REG_EXTENDED)|((corwin->sflags&1)*REG_ICASE));
+ free(TT.remember);
+ TT.remember = 0;
if (*line == 'w') {
line++;
goto writenow;
@@ -873,46 +937,65 @@ writenow:
line = cc;
if (delim) line += 2;
} else if (c == 'y') {
- char *s = line, delim = 0;
- int len1, len2;
+ char *s, delim = 0;
+ int len;
- if (-1 == (len1 = unescape_delimited_string(&line, &delim, 0)))
- goto brand;
+ if (!(s = unescape_delimited_string(&line, &delim, 0))) goto brand;
corwin->arg1 = reg-(char *)corwin;
- reg = extend_string((void *)&corwin, s, reg-(char *)corwin, len1);
- s = line;
+ len = strlen(s);
+ reg = extend_string((void *)&corwin, s, reg-(char *)corwin, len);
+ free(s);
corwin->arg2 = reg-(char *)corwin;
- if (-1 == (len2 = unescape_delimited_string(&line, &delim, 0)))
- goto brand;
- if (len1 != len2) goto brand;
- reg = extend_string((void *)&corwin, s, reg-(char*)corwin, len2);
+ if (!(s = unescape_delimited_string(&line, &delim, 0))) goto brand;
+ if (len != strlen(s)) goto brand;
+ reg = extend_string((void *)&corwin, s, reg-(char*)corwin, len);
+ free(s);
} else if (strchr("abcirtTw:", c)) {
- int end, class;
+ int end;
- // Trim whitespace from "b ;" and ": blah " but only first space in "w x "
+ while (isspace(*line) && *line != '\n') line++;
- while (isspace(*line)) line++;
-append:
- class = !strchr("btT:", c);
- end = strcspn(line, class ? "\n" : "; \t\r\n\v\f");
+ // Resume logic differs from 's' case because we don't add a newline
+ // unless it's after something, so we add it on return instead.
+resume_a:
+ corwin->hit = 0;
- if (!end) {
- if (!strchr("btT", c)) break;
- continue;
+ // Trim whitespace from "b ;" and ": blah " but only first space in "w x "
+ if (!(end = strcspn(line, strchr("btT:", c) ? "; \t\r\n\v\f" : "\n"))) {
+ if (strchr("btT", c)) continue;
+ else if (!corwin->arg1) break;
}
// Extend allocation to include new string. We use offsets instead of
- // pointers so realloc() moving stuff doesn't break things. Do it
- // here instead of toybuf so there's no maximum size.
+ // pointers so realloc() moving stuff doesn't break things. Ok to write
+ // \n over NUL terminator because call to extend_string() adds it back.
if (!corwin->arg1) corwin->arg1 = reg - (char*)corwin;
- reg = extend_string((void *)&corwin, line, reg - (char *)corwin, end);
- line += end;
-
- // Line continuation?
- if (class && reg[-1] == '\\') {
- reg[-1] = 0;
- corwin->hit++;
- }
+ else if ((corwin+1) != (void *)reg) *(reg++) = '\n';
+ reg = extend_string((void *)&corwin, line, reg - (char *)corwin, end);
+
+ // Recopy data to remove escape sequences and handle line continuation.
+ if (strchr("aci", c)) {
+ reg -= end+1;
+ for (i = end; i; i--) {
+ if ((*reg++ = *line++)=='\\') {
+
+ // escape at end of line: resume if -e escaped literal newline,
+ // else request callback and resume with next line
+ if (!--i) {
+ *--reg = 0;
+ if (*line) {
+ line++;
+ goto resume_a;
+ }
+ corwin->hit = 256;
+ break;
+ }
+ if (!(reg[-1] = unescape(*line))) reg[-1] = *line;
+ line++;
+ }
+ }
+ *reg = 0;
+ } else line += end;
// Commands that take no arguments
} else if (!strchr("{dDgGhHlnNpPqx=", c)) break;
@@ -920,7 +1003,7 @@ append:
brand:
// Reminisce about chestnut trees.
- error_exit("bad pattern '%s'@%ld (%c)", *pline, line-*pline+1, *line);
+ error_exit("bad pattern '%s'@%ld (%c)", errstart, line-errstart+1L, *line);
}
void sed_main(void)
@@ -949,6 +1032,7 @@ void sed_main(void)
jewel_of_judgement(&dworkin->arg, strlen(dworkin->arg));
for (dworkin = TT.f; dworkin; dworkin = dworkin->next)
do_lines(xopen(dworkin->arg, O_RDONLY), dworkin->arg, jewel_of_judgement);
+ jewel_of_judgement(0, 0);
dlist_terminate(TT.pattern);
if (TT.nextlen) error_exit("no }");
diff --git a/toys/posix/sort.c b/toys/posix/sort.c
index c9f887d..ed7c36c 100644
--- a/toys/posix/sort.c
+++ b/toys/posix/sort.c
@@ -14,9 +14,9 @@ config SORT
Sort all lines of text from input files (or stdin) to stdout.
- -r reverse
- -u unique lines only
- -n numeric order (instead of alphabetical)
+ -r reverse
+ -u unique lines only
+ -n numeric order (instead of alphabetical)
config SORT_BIG
bool "SuSv3 options (Support -ktcsbdfiozM)"
@@ -25,18 +25,18 @@ config SORT_BIG
help
usage: sort [-bcdfiMsz] [-k#[,#[x]] [-t X]] [-o FILE]
- -b ignore leading blanks (or trailing blanks in second part of key)
- -c check whether input is sorted
- -d dictionary order (use alphanumeric and whitespace chars only)
- -f force uppercase (case insensitive sort)
- -i ignore nonprinting characters
- -M month sort (jan, feb, etc).
- -x Hexadecimal numerical sort
- -s skip fallback sort (only sort with keys)
- -z zero (null) terminated input
- -k sort by "key" (see below)
- -t use a key separator other than whitespace
- -o output to FILE instead of stdout
+ -b ignore leading blanks (or trailing blanks in second part of key)
+ -c check whether input is sorted
+ -d dictionary order (use alphanumeric and whitespace chars only)
+ -f force uppercase (case insensitive sort)
+ -i ignore nonprinting characters
+ -M month sort (jan, feb, etc).
+ -x Hexadecimal numerical sort
+ -s skip fallback sort (only sort with keys)
+ -z zero (null) terminated lines
+ -k sort by "key" (see below)
+ -t use a key separator other than whitespace
+ -o output to FILE instead of stdout
Sorting by key looks at a subset of the words on each line. -k2
uses the second word to the end of the line, -k2,2 looks at only
@@ -46,15 +46,13 @@ config SORT_BIG
(such as -2,2n) applies only to sorting that key.
config SORT_FLOAT
- bool "Floating point (-g)"
+ bool
default y
- depends on SORT_BIG
+ depends on SORT_BIG && TOYBOX_FLOAT
help
usage: sort [-g]
-g general numeric sort (double precision with nan and inf)
-
- This version of sort requires floating point.
*/
#define FOR_sort
@@ -386,9 +384,11 @@ void sort_main(void)
// Output result
for (idx = 0; idx<TT.linecount; idx++) {
char *s = TT.lines[idx];
- xwrite(fd, s, strlen(s));
+ unsigned i = strlen(s);
+
+ if (!(toys.optflags&FLAG_z)) s[i] = '\n';
+ xwrite(fd, s, i+1);
if (CFG_TOYBOX_FREE) free(s);
- xwrite(fd, "\n", 1);
}
exit_now:
diff --git a/toys/posix/split.c b/toys/posix/split.c
index d9a556b..aabf931 100644
--- a/toys/posix/split.c
+++ b/toys/posix/split.c
@@ -100,7 +100,7 @@ void split_main(void)
if (!TT.bytes && !TT.lines) TT.lines = 1000;
// Allocate template for output filenames
- TT.outfile = xmprintf("%s% *c", (toys.optc == 2) ? toys.optargs[1] : "x",
+ TT.outfile = xmprintf("%s%*c", (toys.optc == 2) ? toys.optargs[1] : "x",
(int)TT.suflen, ' ');
// We only ever use one input, but this handles '-' or no input for us.
diff --git a/toys/posix/tail.c b/toys/posix/tail.c
index e92c044..80556e2 100644
--- a/toys/posix/tail.c
+++ b/toys/posix/tail.c
@@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/tail.html
-USE_TAIL(NEWTOY(tail, "fc-n-[-cn]", TOYFLAG_BIN))
+USE_TAIL(NEWTOY(tail, "?fc-n-[-cn]", TOYFLAG_USR|TOYFLAG_BIN))
config TAIL
bool "tail"
@@ -17,7 +17,7 @@ config TAIL
-n output the last NUMBER lines (default 10), +X counts from start.
-c output the last NUMBER bytes, +NUMBER counts from start
- -f follow FILE(s), waiting for more data to be appended
+ #-f follow FILE(s), waiting for more data to be appended [TODO]
config TAIL_SEEK
bool "tail seek support"
@@ -213,10 +213,22 @@ static void do_tail(int fd, char *name)
void tail_main(void)
{
- // if nothing specified, default -n to -10
- if (!(toys.optflags&(FLAG_n|FLAG_c))) TT.lines = -10;
+ char **args = toys.optargs;
- loopfiles(toys.optargs, do_tail);
+ if (!(toys.optflags&(FLAG_n|FLAG_c))) {
+ char *arg = *args;
+
+ // handle old "-42" style arguments
+ if (arg && *arg == '-' && arg[1]) {
+ TT.lines = atolx(*(args++));
+ toys.optc--;
+ }
+
+ // if nothing specified, default -n to -10
+ TT.lines = -10;
+ }
+
+ loopfiles(args, do_tail);
// do -f stuff
}
diff --git a/toys/posix/tee.c b/toys/posix/tee.c
index 0388510..d5591b6 100644
--- a/toys/posix/tee.c
+++ b/toys/posix/tee.c
@@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/tee.html
-USE_TEE(NEWTOY(tee, "ia", TOYFLAG_BIN))
+USE_TEE(NEWTOY(tee, "ia", TOYFLAG_USR|TOYFLAG_BIN))
config TEE
bool "tee"
@@ -45,7 +45,7 @@ static void do_tee_open(int fd, char *name)
void tee_main(void)
{
- if (toys.optflags & FLAG_i) signal(SIGINT, SIG_IGN);
+ if (toys.optflags & FLAG_i) xsignal(SIGINT, SIG_IGN);
// Open output files
loopfiles_rw(toys.optargs,
diff --git a/toys/posix/time.c b/toys/posix/time.c
index 0159205..70d2997 100644
--- a/toys/posix/time.c
+++ b/toys/posix/time.c
@@ -9,6 +9,7 @@ USE_TIME(NEWTOY(time, "<1^p", TOYFLAG_USR|TOYFLAG_BIN))
config TIME
bool "time"
default y
+ depends on TOYBOX_FLOAT
help
usage: time [-p] COMMAND [ARGS...]
@@ -27,7 +28,7 @@ void time_main(void)
struct timeval tv, tv2;
gettimeofday(&tv, NULL);
- if (!(pid = xfork())) xexec_optargs(0);
+ if (!(pid = xfork())) xexec(toys.optargs);
else {
int stat;
struct rusage ru;
diff --git a/toys/posix/touch.c b/toys/posix/touch.c
index 71ddc43..052448b 100644
--- a/toys/posix/touch.c
+++ b/toys/posix/touch.c
@@ -2,21 +2,24 @@
*
* Copyright 2012 Choubey Ji <warior.linux@gmail.com>
*
- * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
+ *
+ * TODO: have another go at merging the -t and -d stanzas
-USE_TOUCH(NEWTOY(touch, "acd:mr:t:[!dtr]", TOYFLAG_BIN))
+USE_TOUCH(NEWTOY(touch, "acd:mr:t:h[!dtr]", TOYFLAG_BIN))
config TOUCH
bool "touch"
default y
help
- usage: touch [-amc] [-d DATE] [-t TIME] [-r FILE] FILE...
+ 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
@@ -31,41 +34,20 @@ GLOBALS(
char *date;
)
-// Fetch access and/or modification time of a file
-int fetch(char *file, struct timeval *tv, unsigned flags)
-{
- struct stat st;
-
- if (stat(file, &st)) return 1;
-
- if (flags & FLAG_a) {
- tv[0].tv_sec = st.st_atime;
- tv[0].tv_usec = st.st_atim.tv_nsec/1000;
- }
- if (flags & FLAG_m) {
- tv[1].tv_sec = st.st_mtime;
- tv[1].tv_usec = st.st_mtim.tv_nsec/1000;
- }
-
- return 0;
-}
-
void touch_main(void)
{
- struct timeval tv[2];
+ struct timespec ts[2];
char **ss;
- int flag, fd, i;
-
- // Set time from clock?
-
- gettimeofday(tv, NULL);
+ int fd, i;
+ // use current time if no -t or -d
+ ts[0].tv_nsec = UTIME_NOW;
if (toys.optflags & (FLAG_t|FLAG_d)) {
char *s, *date;
struct tm tm;
- int len;
+ int len = 0;
- localtime_r(&(tv->tv_sec), &tm);
+ localtime_r(&(ts->tv_sec), &tm);
// Set time from -d?
@@ -77,14 +59,13 @@ void touch_main(void)
if (toupper(date[i-1])=='Z') {
date[i-1] = 0;
setenv("TZ", "UTC0", 1);
- localtime_r(&(tv->tv_sec), &tm);
+ localtime_r(&(ts->tv_sec), &tm);
}
s = strptime(date, "%Y-%m-%dT%T", &tm);
- if (s && *s=='.') {
- sscanf(s, ".%d%n", &i, &len);
- s += len;
- tv->tv_usec = i;
- }
+ ts->tv_nsec = 0;
+ if (s && *s=='.' && isdigit(s[1]))
+ sscanf(s, ".%lu%n", &ts->tv_nsec, &len);
+ else len = 0;
} else s = 0;
// Set time from -t?
@@ -92,40 +73,54 @@ void touch_main(void)
} else {
strcpy(toybuf, "%Y%m%d%H%M");
date = TT.time;
+ i = ((s = strchr(date, '.'))) ? s-date : strlen(date);
+ if (i < 8 || i%2) error_exit("bad '%s'", date);
for (i=0;i<3;i++) {
s = strptime(date, toybuf+(i&2), &tm);
if (s) break;
toybuf[1]='y';
}
- if (s && *s=='.') {
- int count = sscanf(s, ".%2d%u%n", &(tm.tm_sec), &i, &len);
-
- if (count==2) tv->tv_usec = i;
- s += len;
- }
+ ts->tv_nsec = 0;
+ if (s && *s=='.' && sscanf(s, ".%2u%n", &(tm.tm_sec), &len) == 1) {
+ sscanf(s += len, "%lu%n", &ts->tv_nsec, &len);
+ len++;
+ } else len = 0;
+ }
+ if (len) {
+ s += len;
+ if (ts->tv_nsec > 999999999) s = 0;
+ else while (len++ < 10) ts->tv_nsec *= 10;
}
errno = 0;
- tv->tv_sec = mktime(&tm);
+ ts->tv_sec = mktime(&tm);
if (!s || *s || errno == EOVERFLOW) perror_exit("bad '%s'", date);
}
- tv[1]=tv[0];
+ ts[1]=ts[0];
// Set time from -r?
- if (TT.file && fetch(TT.file, tv, FLAG_a|FLAG_m))
- perror_exit("-r '%s'", TT.file);
+ if (TT.file) {
+ struct stat st;
- // Ok, we've got a time. Flip -am flags so now it's the ones we _keep_.
+ xstat(TT.file, &st);
+ ts[0] = st.st_atim;
+ ts[1] = st.st_mtim;
+ }
- flag = (~toys.optflags) & (FLAG_m|FLAG_a);
+ // 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;) {
- if ((flag == (FLAG_m|FLAG_a) || !fetch(*ss, tv, flag)) && !utimes(*ss, tv))
- ss++;
+ for (ss = toys.optargs; *ss;) {
+
+ // cheat: FLAG_h is rightmost flag, so its value is 1
+ if (!utimensat(AT_FDCWD, *ss, ts,
+ (toys.optflags & FLAG_h)*AT_SYMLINK_NOFOLLOW)) ss++;
else if (toys.optflags & FLAG_c) ss++;
- else if (-1 != (fd = open(*ss, O_CREAT, 0666))) close(fd);
+ else if (access(*ss, F_OK) && (-1!=(fd = open(*ss, O_CREAT, 0666))))
+ close(fd);
else perror_msg("'%s'", *ss++);
}
}
diff --git a/toys/posix/true.c b/toys/posix/true.c
index b22b7ac..0fbb178 100644
--- a/toys/posix/true.c
+++ b/toys/posix/true.c
@@ -5,7 +5,7 @@
* See http://opengroup.org/onlinepubs/9699919799/utilities/true.html
USE_TRUE(NEWTOY(true, NULL, TOYFLAG_BIN))
-USE_TRUE(OLDTOY(:, true, 0, TOYFLAG_NOFORK))
+USE_TRUE(OLDTOY(:, true, TOYFLAG_NOFORK))
config TRUE
bool "true"
diff --git a/toys/posix/uniq.c b/toys/posix/uniq.c
index 3cfdb94..c127cfe 100644
--- a/toys/posix/uniq.c
+++ b/toys/posix/uniq.c
@@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/uniq.html
-USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_BIN))
+USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_USR|TOYFLAG_BIN))
config UNIQ
bool "uniq"
diff --git a/toys/posix/uuencode.c b/toys/posix/uuencode.c
index 2323c98..34ca701 100644
--- a/toys/posix/uuencode.c
+++ b/toys/posix/uuencode.c
@@ -22,22 +22,13 @@ config UUENCODE
void uuencode_main(void)
{
- char *p, *name = toys.optargs[toys.optc-1], buf[(76/4)*3];
+ char *name = toys.optargs[toys.optc-1], buf[(76/4)*3];
int i, m = toys.optflags & FLAG_m, fd = 0;
if (toys.optc > 1) fd = xopen(toys.optargs[0], O_RDONLY);
- // base64 table
-
- p = toybuf;
- for (i = 'A'; i != ':'; i++) {
- if (i == 'Z'+1) i = 'a';
- if (i == 'z'+1) i = '0';
- *(p++) = i;
- }
- *(p++) = '+';
- *(p++) = '/';
+ base64_init(toybuf);
xprintf("begin%s 744 %s\n", m ? "-base64" : "", name);
for (;;) {
diff --git a/toys/posix/who.c b/toys/posix/who.c
index 876a562..414cdfc 100644
--- a/toys/posix/who.c
+++ b/toys/posix/who.c
@@ -9,7 +9,7 @@
* Posix says to support many options (-abdHlmpqrstTu) but this
* isn't aimed at minicomputers with modem pools.
-USE_WHO(NEWTOY(who, "a", TOYFLAG_BIN))
+USE_WHO(NEWTOY(who, "a", TOYFLAG_USR|TOYFLAG_BIN))
config WHO
bool "who"
diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c
index cde71f6..8178bf0 100644
--- a/toys/posix/xargs.c
+++ b/toys/posix/xargs.c
@@ -3,6 +3,8 @@
* Copyright 2011 Rob Landley <rob@landley.net>
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/xargs.html
+ *
+ * TODO: Rich's whitespace objection, env size isn't fixed anymore.
USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN))