auto import from //depot/cupcake/@135843
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
new file mode 100644
index 0000000..5a8dc0b
--- /dev/null
+++ b/toolbox/Android.mk
@@ -0,0 +1,91 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+TOOLS := \
+ ls \
+ mount \
+ cat \
+ ps \
+ kill \
+ ln \
+ insmod \
+ rmmod \
+ lsmod \
+ ifconfig \
+ setconsole \
+ rm \
+ mkdir \
+ rmdir \
+ reboot \
+ getevent \
+ sendevent \
+ date \
+ wipe \
+ sync \
+ umount \
+ start \
+ stop \
+ notify \
+ cmp \
+ dmesg \
+ route \
+ hd \
+ dd \
+ df \
+ getprop \
+ setprop \
+ watchprops \
+ log \
+ sleep \
+ renice \
+ printenv \
+ smd \
+ chmod \
+ chown \
+ mkdosfs \
+ netstat \
+ ioctl \
+ mv \
+ schedtop \
+ top \
+ iftop \
+ id \
+ vmstat
+
+LOCAL_SRC_FILES:= \
+ toolbox.c \
+ $(patsubst %,%.c,$(TOOLS))
+
+LOCAL_SHARED_LIBRARIES := libcutils libc
+
+LOCAL_MODULE:= toolbox
+
+# Including this will define $(intermediates).
+#
+include $(BUILD_EXECUTABLE)
+
+$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
+
+TOOLS_H := $(intermediates)/tools.h
+$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS)
+$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
+$(TOOLS_H): $(LOCAL_PATH)/Android.mk
+$(TOOLS_H):
+ $(transform-generated-source)
+
+# Make #!/system/bin/toolbox launchers for each tool.
+#
+SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS))
+$(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+ @echo "Symlink: $@ -> $(TOOLBOX_BINARY)"
+ @mkdir -p $(dir $@)
+ @rm -rf $@
+ $(hide) ln -sf $(TOOLBOX_BINARY) $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+# We need this so that the installed files could be picked up based on the
+# local module name
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+ $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/toolbox/MODULE_LICENSE_BSD
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
new file mode 100644
index 0000000..12f28b9
--- /dev/null
+++ b/toolbox/NOTICE
@@ -0,0 +1,131 @@
+
+Copyright (c) 2008, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of The Android Open Source Project nor the names
+ of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 1998 Robert Nordier
+Copyright (c) 1989, 1993
+ The Regents of the University of California. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Kevin Fall.
+This code is derived from software contributed to Berkeley by
+Keith Muller of the University of California, San Diego and Lance
+Visser of Convex Computer Corporation.
+This code is derived from software contributed to Berkeley by
+Mike Muuss.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+ Copyright (c) 1989, 1993
+ The Regents of the University of California. All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Kevin Fall.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+
+ Copyright (c) 1991, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Keith Muller of the University of California, San Diego and Lance
+ Visser of Convex Computer Corporation.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
diff --git a/toolbox/alarm.c b/toolbox/alarm.c
new file mode 100644
index 0000000..9bd58aa
--- /dev/null
+++ b/toolbox/alarm.c
@@ -0,0 +1,190 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <asm/ioctl.h>
+//#include <linux/rtc.h>
+#include <linux/android_alarm.h>
+
+int alarm_main(int argc, char *argv[])
+{
+ int c;
+ int res;
+ struct tm tm;
+ time_t t;
+ struct timespec ts;
+// struct rtc_time rtc_time;
+ char strbuf[26];
+ int afd;
+ int nfd;
+// struct timeval timeout = { 0, 0 };
+ int wait = 0;
+ fd_set rfds;
+ const char wake_lock_id[] = "alarm_test";
+ int waitalarmmask = 0;
+
+ int useutc = 0;
+ android_alarm_type_t alarmtype_low = ANDROID_ALARM_RTC_WAKEUP;
+ android_alarm_type_t alarmtype_high = ANDROID_ALARM_RTC_WAKEUP;
+ android_alarm_type_t alarmtype = 0;
+
+ do {
+ //c = getopt(argc, argv, "uw:");
+ c = getopt(argc, argv, "uwat:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'u':
+ useutc = 1;
+ break;
+ case 't':
+ alarmtype_low = alarmtype_high = strtol(optarg, NULL, 0);
+ break;
+ case 'a':
+ alarmtype_low = ANDROID_ALARM_RTC_WAKEUP;
+ alarmtype_high = ANDROID_ALARM_TYPE_COUNT - 1;
+ break;
+ case 'w':
+ //timeout.tv_sec = strtol(optarg, NULL, 0);
+ wait = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+ if(optind + 2 < argc) {
+ fprintf(stderr,"%s [-uwa] [-t type] [seconds]\n", argv[0]);
+ return 1;
+ }
+
+ afd = open("/dev/alarm", O_RDWR);
+ if(afd < 0) {
+ fprintf(stderr, "Unable to open rtc: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if(optind == argc) {
+ for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) {
+ waitalarmmask |= 1U << alarmtype;
+ }
+#if 0
+ res = ioctl(fd, RTC_ALM_READ, &tm);
+ if(res < 0) {
+ fprintf(stderr, "Unable to read alarm: %s\n", strerror(errno));
+ return 1;
+ }
+#endif
+#if 0
+ t = timegm(&tm);
+ if(useutc)
+ gmtime_r(&t, &tm);
+ else
+ localtime_r(&t, &tm);
+#endif
+#if 0
+ asctime_r(&tm, strbuf);
+ printf("%s", strbuf);
+#endif
+ }
+ else if(optind + 1 == argc) {
+#if 0
+ res = ioctl(fd, RTC_RD_TIME, &tm);
+ if(res < 0) {
+ fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+ return 1;
+ }
+ asctime_r(&tm, strbuf);
+ printf("Now: %s", strbuf);
+ time(&tv.tv_sec);
+#endif
+#if 0
+ time(&ts.tv_sec);
+ ts.tv_nsec = 0;
+
+ //strptime(argv[optind], NULL, &tm);
+ //tv.tv_sec = mktime(&tm);
+ //tv.tv_usec = 0;
+#endif
+ for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) {
+ waitalarmmask |= 1U << alarmtype;
+ res = ioctl(afd, ANDROID_ALARM_GET_TIME(alarmtype), &ts);
+ if(res < 0) {
+ fprintf(stderr, "Unable to get current time: %s\n", strerror(errno));
+ return 1;
+ }
+ ts.tv_sec += strtol(argv[optind], NULL, 0);
+ //strtotimeval(argv[optind], &tv);
+ gmtime_r(&ts.tv_sec, &tm);
+ printf("time %s -> %ld.%09ld\n", argv[optind], ts.tv_sec, ts.tv_nsec);
+ asctime_r(&tm, strbuf);
+ printf("Requested %s", strbuf);
+
+ res = ioctl(afd, ANDROID_ALARM_SET(alarmtype), &ts);
+ if(res < 0) {
+ fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+#if 0
+ res = ioctl(fd, RTC_ALM_SET, &tm);
+ if(res < 0) {
+ fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+ return 1;
+ }
+ res = ioctl(fd, RTC_AIE_ON);
+ if(res < 0) {
+ fprintf(stderr, "Unable to enable alarm: %s\n", strerror(errno));
+ return 1;
+ }
+#endif
+ }
+ else {
+ fprintf(stderr,"%s [-u] [date]\n", argv[0]);
+ return 1;
+ }
+
+ if(wait) {
+ while(waitalarmmask) {
+ printf("wait for alarm %x\n", waitalarmmask);
+ res = ioctl(afd, ANDROID_ALARM_WAIT);
+ if(res < 0) {
+ fprintf(stderr, "alarm wait failed\n");
+ }
+ printf("got alarm %x\n", res);
+ waitalarmmask &= ~res;
+ nfd = open("/sys/android_power/acquire_full_wake_lock", O_RDWR);
+ write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1);
+ close(nfd);
+ //sleep(5);
+ nfd = open("/sys/android_power/release_wake_lock", O_RDWR);
+ write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1);
+ close(nfd);
+ }
+ printf("done\n");
+ }
+#if 0
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ res = select(fd + 1, &rfds, NULL, NULL, &timeout);
+ if(res < 0) {
+ fprintf(stderr, "select failed: %s\n", strerror(errno));
+ return 1;
+ }
+ if(res > 0) {
+ int event;
+ read(fd, &event, sizeof(event));
+ fprintf(stderr, "got %x\n", event);
+ }
+ else {
+ fprintf(stderr, "timeout waiting for alarm\n");
+ }
+#endif
+
+ close(afd);
+
+ return 0;
+}
diff --git a/toolbox/cat.c b/toolbox/cat.c
new file mode 100644
index 0000000..6ac31f8
--- /dev/null
+++ b/toolbox/cat.c
@@ -0,0 +1,291 @@
+/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define CAT_BUFSIZ (4096)
+
+static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag;
+static int rval;
+static const char *filename;
+
+static void
+cook_buf(FILE *fp)
+{
+ int ch, gobble, line, prev;
+ int stdout_err = 0;
+
+ line = gobble = 0;
+ for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
+ if (prev == '\n') {
+ if (ch == '\n') {
+ if (sflag) {
+ if (!gobble && putchar(ch) == EOF)
+ break;
+ gobble = 1;
+ continue;
+ }
+ if (nflag) {
+ if (!bflag) {
+ if (fprintf(stdout,
+ "%6d\t", ++line) < 0) {
+ stdout_err++;
+ break;
+ }
+ } else if (eflag) {
+ if (fprintf(stdout,
+ "%6s\t", "") < 0) {
+ stdout_err++;
+ break;
+ }
+ }
+ }
+ } else if (nflag) {
+ if (fprintf(stdout, "%6d\t", ++line) < 0) {
+ stdout_err++;
+ break;
+ }
+ }
+ }
+ gobble = 0;
+ if (ch == '\n') {
+ if (eflag)
+ if (putchar('$') == EOF)
+ break;
+ } else if (ch == '\t') {
+ if (tflag) {
+ if (putchar('^') == EOF || putchar('I') == EOF)
+ break;
+ continue;
+ }
+ } else if (vflag) {
+ if (!isascii(ch)) {
+ if (putchar('M') == EOF || putchar('-') == EOF)
+ break;
+ ch = (ch) & 0x7f;
+ }
+ if (iscntrl(ch)) {
+ if (putchar('^') == EOF ||
+ putchar(ch == '\177' ? '?' :
+ ch | 0100) == EOF)
+ break;
+ continue;
+ }
+ }
+ if (putchar(ch) == EOF)
+ break;
+ }
+ if (stdout_err) {
+ perror(filename);
+ rval = 1;
+ }
+}
+
+static void
+cook_args(char **argv)
+{
+ FILE *fp;
+
+ fp = stdin;
+ filename = "stdin";
+ do {
+ if (*argv) {
+ if (!strcmp(*argv, "-"))
+ fp = stdin;
+ else if ((fp = fopen(*argv,
+ fflag ? "rf" : "r")) == NULL) {
+ perror("fopen");
+ rval = 1;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+ cook_buf(fp);
+ if (fp != stdin)
+ fclose(fp);
+ } while (*argv);
+}
+
+static void
+raw_cat(int rfd)
+{
+ static char *buf;
+ static char fb_buf[CAT_BUFSIZ];
+ static size_t bsize;
+
+ struct stat sbuf;
+ ssize_t nr, nw, off;
+ int wfd;
+
+ wfd = fileno(stdout);
+ if (buf == NULL) {
+ if (fstat(wfd, &sbuf) == 0) {
+ bsize = sbuf.st_blksize > CAT_BUFSIZ ?
+ sbuf.st_blksize : CAT_BUFSIZ;
+ buf = malloc(bsize);
+ }
+ if (buf == NULL) {
+ buf = fb_buf;
+ bsize = CAT_BUFSIZ;
+ }
+ }
+ while ((nr = read(rfd, buf, bsize)) > 0)
+ for (off = 0; nr; nr -= nw, off += nw)
+ if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
+ {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ if (nr < 0) {
+ fprintf(stderr,"%s: invalid length\n", filename);
+ rval = 1;
+ }
+}
+
+static void
+raw_args(char **argv)
+{
+ int fd;
+
+ fd = fileno(stdin);
+ filename = "stdin";
+ do {
+ if (*argv) {
+ if (!strcmp(*argv, "-"))
+ fd = fileno(stdin);
+ else if (fflag) {
+ struct stat st;
+ fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
+ if (fd < 0)
+ goto skip;
+
+ if (fstat(fd, &st) == -1) {
+ close(fd);
+ goto skip;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ close(fd);
+ errno = EINVAL;
+ goto skipnomsg;
+ }
+ }
+ else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
+skip:
+ perror(*argv);
+skipnomsg:
+ rval = 1;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+ raw_cat(fd);
+ if (fd != fileno(stdin))
+ close(fd);
+ } while (*argv);
+}
+
+int
+cat_main(int argc, char *argv[])
+{
+ int ch;
+ struct flock stdout_lock;
+
+ while ((ch = getopt(argc, argv, "beflnstv")) != -1)
+ switch (ch) {
+ case 'b':
+ bflag = nflag = 1; /* -b implies -n */
+ break;
+ case 'e':
+ eflag = vflag = 1; /* -e implies -v */
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 't':
+ tflag = vflag = 1; /* -t implies -v */
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ case '?':
+ fprintf(stderr,
+ "usage: cat [-beflnstv] [-] [file ...]\n");
+ exit(EXIT_FAILURE);
+ }
+ argv += optind;
+
+ if (lflag) {
+ stdout_lock.l_len = 0;
+ stdout_lock.l_start = 0;
+ stdout_lock.l_type = F_WRLCK;
+ stdout_lock.l_whence = SEEK_SET;
+ if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1)
+ {
+ perror("fcntl");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (bflag || eflag || nflag || sflag || tflag || vflag)
+ cook_args(argv);
+ else
+ raw_args(argv);
+ if (fclose(stdout))
+ {
+ perror("fclose");
+ exit(EXIT_FAILURE);
+ }
+ exit(rval);
+}
diff --git a/toolbox/chmod.c b/toolbox/chmod.c
new file mode 100644
index 0000000..31a53bf
--- /dev/null
+++ b/toolbox/chmod.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <time.h>
+
+int chmod_main(int argc, char **argv)
+{
+ int i;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: chmod <MODE> <FILE>\n");
+ return 10;
+ }
+
+ int mode = 0;
+ const char* s = argv[1];
+ while (*s) {
+ if (*s >= '0' && *s <= '7') {
+ mode = (mode<<3) | (*s-'0');
+ }
+ else {
+ fprintf(stderr, "Bad mode\n");
+ return 10;
+ }
+ s++;
+ }
+ for (i = 2; i < argc; i++) {
+ if (chmod(argv[i], mode) < 0) {
+ fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno));
+ return 10;
+ }
+ }
+ return 0;
+}
+
diff --git a/toolbox/chown.c b/toolbox/chown.c
new file mode 100644
index 0000000..13617db
--- /dev/null
+++ b/toolbox/chown.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <unistd.h>
+#include <time.h>
+
+int chown_main(int argc, char **argv)
+{
+ int i;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: chown <USER>[.GROUP] <FILE1> [FILE2] ...\n");
+ return 10;
+ }
+
+ // Copy argv[1] to 'user' so we can truncate it at the period
+ // if a group id specified.
+ char user[32];
+ char *group = NULL;
+ strncpy(user, argv[1], sizeof(user));
+ if ((group = strchr(user, '.')) != NULL) {
+ *group++ = '\0';
+ }
+
+ // Lookup uid (and gid if specified)
+ struct passwd *pw;
+ struct group *grp = NULL;
+ uid_t uid;
+ gid_t gid = -1; // passing -1 to chown preserves current group
+
+ pw = getpwnam(user);
+ if (pw == NULL) {
+ fprintf(stderr, "No such user '%s'\n", user);
+ return 10;
+ }
+ uid = pw->pw_uid;
+
+ if (group != NULL) {
+ grp = getgrnam(group);
+ if (grp == NULL) {
+ fprintf(stderr, "No such group '%s'\n", group);
+ return 10;
+ }
+ gid = grp->gr_gid;
+ }
+
+ for (i = 2; i < argc; i++) {
+ if (chown(argv[i], uid, gid) < 0) {
+ fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno));
+ return 10;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/toolbox/cmp.c b/toolbox/cmp.c
new file mode 100644
index 0000000..9bd2e19
--- /dev/null
+++ b/toolbox/cmp.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+int cmp_main(int argc, char *argv[])
+{
+ int c;
+ int fd1, fd2;
+ char buf1[4096], buf2[4096];
+ int res, res1, res2;
+ int rv = 0;
+ int i;
+ int filepos = 0;
+
+ int show_byte = 0;
+ int show_all = 0;
+ int limit = 0;
+
+ do {
+ c = getopt(argc, argv, "bln:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'b':
+ show_byte = 1;
+ break;
+ case 'l':
+ show_all = 1;
+ break;
+ case 'n':
+ limit = atoi(optarg);
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if (optind + 2 != argc) {
+ fprintf(stderr, "Usage: %s [-b] [-l] [-n count] file1 file2\n", argv[0]);
+ exit(1);
+ }
+
+ fd1 = open(argv[optind], O_RDONLY);
+ if(fd1 < 0) {
+ fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+ return 1;
+ }
+
+ fd2 = open(argv[optind+1], O_RDONLY);
+ if(fd2 < 0) {
+ fprintf(stderr, "could not open %s, %s\n", argv[optind+1], strerror(errno));
+ return 1;
+ }
+
+ while(1) {
+ res1 = read(fd1, &buf1, sizeof(buf1));
+ res2 = read(fd2, &buf2, sizeof(buf2));
+ res = res1 < res2 ? res1 : res2;
+ if(res1 == 0 && res2 == 0) {
+ return rv;
+ }
+ for(i = 0; i < res; i++) {
+ if(buf1[i] != buf2[i]) {
+ printf("%s %s differ byte %d", argv[optind], argv[optind+1], filepos + i);
+ if(show_byte)
+ printf(" 0x%02x 0x%02x", buf1[i], buf2[i]);
+ printf("\n");
+ if(!show_all)
+ return 1;
+ rv = 1;
+ }
+ if(limit) {
+ limit--;
+ if(limit == 0)
+ return rv;
+ }
+ }
+ if(res1 != res2 || res < 0) {
+ printf("%s on %s\n", res < 0 ? "Read error" : "EOF", res1 < res2 ? argv[optind] : argv[optind+1]);
+ return 1;
+ }
+ filepos += res;
+ }
+}
diff --git a/toolbox/date.c b/toolbox/date.c
new file mode 100644
index 0000000..13b5210
--- /dev/null
+++ b/toolbox/date.c
@@ -0,0 +1,132 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <linux/android_alarm.h>
+
+static void settime(char *s) {
+ struct tm tm;
+ int day = atoi(s);
+ int hour;
+ time_t t;
+ int fd;
+ struct timespec ts;
+
+ while (*s && *s != '.')
+ s++;
+
+ if (*s)
+ s++;
+
+ hour = atoi(s);
+
+ tm.tm_year = day / 10000 - 1900;
+ tm.tm_mon = (day % 10000) / 100 - 1;
+ tm.tm_mday = (day % 100);
+ tm.tm_hour = hour / 10000;
+ tm.tm_min = (hour % 10000) / 100;
+ tm.tm_sec = (hour % 100);
+ tm.tm_isdst = -1;
+
+ t = mktime(&tm);
+
+ fd = open("/dev/alarm", O_RDWR);
+ ts.tv_sec = t;
+ ts.tv_nsec = 0;
+ ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+}
+
+int date_main(int argc, char *argv[])
+{
+ int c;
+ int res;
+ struct tm tm;
+ time_t t;
+ struct timeval tv;
+ struct timespec ts;
+ char strbuf[260];
+ int fd;
+
+ int useutc = 0;
+
+ tzset();
+
+ do {
+ c = getopt(argc, argv, "us:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'u':
+ useutc = 1;
+ break;
+ case 's':
+ settime(optarg);
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+ if(optind + 2 < argc) {
+ fprintf(stderr,"%s [-u] [date]\n", argv[0]);
+ return 1;
+ }
+
+ int hasfmt = argc == optind + 1 && argv[optind][0] == '+';
+ if(optind == argc || hasfmt) {
+ char buf[2000];
+ time(&t);
+ if (useutc) {
+ gmtime_r(&t, &tm);
+ strftime(strbuf, sizeof(strbuf),
+ (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S GMT %Y"),
+ &tm);
+ } else {
+ localtime_r(&t, &tm);
+ strftime(strbuf, sizeof(strbuf),
+ (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S %Z %Y"),
+ &tm);
+ }
+ printf("%s\n", strbuf);
+ }
+ else if(optind + 1 == argc) {
+#if 0
+ struct tm *tmptr;
+ tmptr = getdate(argv[optind]);
+ if(tmptr == NULL) {
+ fprintf(stderr,"getdate_r failed\n");
+ return 1;
+ }
+ tm = *tmptr;
+#if 0
+ if(getdate_r(argv[optind], &tm) < 0) {
+ fprintf(stderr,"getdate_r failed %s\n", strerror(errno));
+ return 1;
+ }
+#endif
+#endif
+ //strptime(argv[optind], NULL, &tm);
+ //tv.tv_sec = mktime(&tm);
+ //tv.tv_usec = 0;
+ strtotimeval(argv[optind], &tv);
+ printf("time %s -> %d.%d\n", argv[optind], tv.tv_sec, tv.tv_usec);
+ fd = open("/dev/alarm", O_RDWR);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+ //res = settimeofday(&tv, NULL);
+ if(res < 0) {
+ fprintf(stderr,"settimeofday failed %s\n", strerror(errno));
+ return 1;
+ }
+ }
+ else {
+ fprintf(stderr,"%s [-s 20070325.123456] [-u] [date]\n", argv[0]);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/toolbox/dd.c b/toolbox/dd.c
new file mode 100644
index 0000000..c6af3ea
--- /dev/null
+++ b/toolbox/dd.c
@@ -0,0 +1,1358 @@
+/* $NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dd.h"
+
+#define NO_CONV
+
+//#include "extern.h"
+void block(void);
+void block_close(void);
+void dd_out(int);
+void def(void);
+void def_close(void);
+void jcl(char **);
+void pos_in(void);
+void pos_out(void);
+void summary(void);
+void summaryx(int);
+void terminate(int);
+void unblock(void);
+void unblock_close(void);
+ssize_t bwrite(int, const void *, size_t);
+
+extern IO in, out;
+extern STAT st;
+extern void (*cfunc)(void);
+extern uint64_t cpy_cnt;
+extern uint64_t cbsz;
+extern u_int ddflags;
+extern u_int files_cnt;
+extern int progress;
+extern const u_char *ctab;
+extern const u_char a2e_32V[], a2e_POSIX[];
+extern const u_char e2a_32V[], e2a_POSIX[];
+extern const u_char a2ibm_32V[], a2ibm_POSIX[];
+extern u_char casetab[];
+
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+static void dd_close(void);
+static void dd_in(void);
+static void getfdtype(IO *);
+static int redup_clean_fd(int);
+static void setup(void);
+
+
+IO in, out; /* input/output state */
+STAT st; /* statistics */
+void (*cfunc)(void); /* conversion function */
+uint64_t cpy_cnt; /* # of blocks to copy */
+static off_t pending = 0; /* pending seek if sparse */
+u_int ddflags; /* conversion options */
+uint64_t cbsz; /* conversion block size */
+u_int files_cnt = 1; /* # of files to copy */
+int progress = 0; /* display sign of life */
+const u_char *ctab; /* conversion table */
+sigset_t infoset; /* a set blocking SIGINFO */
+
+int
+dd_main(int argc, char *argv[])
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch (ch) {
+ default:
+ fprintf(stderr, "usage: dd [operand ...]\n");
+ exit(1);
+ /* NOTREACHED */
+ }
+ }
+ argc -= (optind - 1);
+ argv += (optind - 1);
+
+ jcl(argv);
+ setup();
+
+// (void)signal(SIGINFO, summaryx);
+ (void)signal(SIGINT, terminate);
+ (void)sigemptyset(&infoset);
+// (void)sigaddset(&infoset, SIGINFO);
+
+ (void)atexit(summary);
+
+ while (files_cnt--)
+ dd_in();
+
+ dd_close();
+ exit(0);
+ /* NOTREACHED */
+}
+
+static void
+setup(void)
+{
+
+ if (in.name == NULL) {
+ in.name = "stdin";
+ in.fd = STDIN_FILENO;
+ } else {
+ in.fd = open(in.name, O_RDONLY, 0);
+ if (in.fd < 0) {
+ fprintf(stderr, "%s: cannot open for read: %s\n",
+ in.name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ /* Ensure in.fd is outside the stdio descriptor range */
+ in.fd = redup_clean_fd(in.fd);
+ }
+
+ getfdtype(&in);
+
+ if (files_cnt > 1 && !(in.flags & ISTAPE)) {
+ fprintf(stderr,
+ "files is not supported for non-tape devices\n");
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ if (out.name == NULL) {
+ /* No way to check for read access here. */
+ out.fd = STDOUT_FILENO;
+ out.name = "stdout";
+ } else {
+#define OFLAGS \
+ (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
+ out.fd = open(out.name, O_RDWR | OFLAGS /*, DEFFILEMODE */);
+ /*
+ * May not have read access, so try again with write only.
+ * Without read we may have a problem if output also does
+ * not support seeks.
+ */
+ if (out.fd < 0) {
+ out.fd = open(out.name, O_WRONLY | OFLAGS /*, DEFFILEMODE */);
+ out.flags |= NOREAD;
+ }
+ if (out.fd < 0) {
+ fprintf(stderr, "%s: cannot open for write: %s\n",
+ out.name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ /* Ensure out.fd is outside the stdio descriptor range */
+ out.fd = redup_clean_fd(out.fd);
+ }
+
+ getfdtype(&out);
+
+ /*
+ * Allocate space for the input and output buffers. If not doing
+ * record oriented I/O, only need a single buffer.
+ */
+ if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
+ if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) {
+ exit(1);
+ /* NOTREACHED */
+ }
+ out.db = in.db;
+ } else if ((in.db =
+ malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
+ (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) {
+ exit(1);
+ /* NOTREACHED */
+ }
+ in.dbp = in.db;
+ out.dbp = out.db;
+
+ /* Position the input/output streams. */
+ if (in.offset)
+ pos_in();
+ if (out.offset)
+ pos_out();
+
+ /*
+ * Truncate the output file; ignore errors because it fails on some
+ * kinds of output files, tapes, for example.
+ */
+ if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
+ (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
+
+ /*
+ * If converting case at the same time as another conversion, build a
+ * table that does both at once. If just converting case, use the
+ * built-in tables.
+ */
+ if (ddflags & (C_LCASE|C_UCASE)) {
+#ifdef NO_CONV
+ /* Should not get here, but just in case... */
+ fprintf(stderr, "case conv and -DNO_CONV\n");
+ exit(1);
+ /* NOTREACHED */
+#else /* NO_CONV */
+ u_int cnt;
+
+ if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
+ if (ddflags & C_LCASE) {
+ for (cnt = 0; cnt < 0377; ++cnt)
+ casetab[cnt] = tolower(ctab[cnt]);
+ } else {
+ for (cnt = 0; cnt < 0377; ++cnt)
+ casetab[cnt] = toupper(ctab[cnt]);
+ }
+ } else {
+ if (ddflags & C_LCASE) {
+ for (cnt = 0; cnt < 0377; ++cnt)
+ casetab[cnt] = tolower(cnt);
+ } else {
+ for (cnt = 0; cnt < 0377; ++cnt)
+ casetab[cnt] = toupper(cnt);
+ }
+ }
+
+ ctab = casetab;
+#endif /* NO_CONV */
+ }
+
+ (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */
+}
+
+static void
+getfdtype(IO *io)
+{
+// struct mtget mt;
+ struct stat sb;
+
+ if (fstat(io->fd, &sb)) {
+ fprintf(stderr, "%s: cannot fstat: %s\n",
+ io->name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+ if (S_ISCHR(sb.st_mode))
+ io->flags |= /*ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; */ ISCHR;
+ else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
+ io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */
+}
+
+/*
+ * Move the parameter file descriptor to a descriptor that is outside the
+ * stdio descriptor range, if necessary. This is required to avoid
+ * accidentally outputting completion or error messages into the
+ * output file that were intended for the tty.
+ */
+static int
+redup_clean_fd(int fd)
+{
+ int newfd;
+
+ if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
+ fd != STDERR_FILENO)
+ /* File descriptor is ok, return immediately. */
+ return fd;
+
+ /*
+ * 3 is the first descriptor greater than STD*_FILENO. Any
+ * free descriptor valued 3 or above is acceptable...
+ */
+ newfd = fcntl(fd, F_DUPFD, 3);
+ if (newfd < 0) {
+ fprintf(stderr, "dupfd IO: %s\n", strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ close(fd);
+
+ return newfd;
+}
+
+static void
+dd_in(void)
+{
+ int flags;
+ int64_t n;
+
+ for (flags = ddflags;;) {
+ if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
+ return;
+
+ /*
+ * Clear the buffer first if doing "sync" on input.
+ * If doing block operations use spaces. This will
+ * affect not only the C_NOERROR case, but also the
+ * last partial input block which should be padded
+ * with zero and not garbage.
+ */
+ if (flags & C_SYNC) {
+ if (flags & (C_BLOCK|C_UNBLOCK))
+ (void)memset(in.dbp, ' ', in.dbsz);
+ else
+ (void)memset(in.dbp, 0, in.dbsz);
+ }
+
+ n = read(in.fd, in.dbp, in.dbsz);
+ if (n == 0) {
+ in.dbrcnt = 0;
+ return;
+ }
+
+ /* Read error. */
+ if (n < 0) {
+
+ /*
+ * If noerror not specified, die. POSIX requires that
+ * the warning message be followed by an I/O display.
+ */
+ fprintf(stderr, "%s: read error: %s\n",
+ in.name, strerror(errno));
+ if (!(flags & C_NOERROR)) {
+ exit(1);
+ /* NOTREACHED */
+ }
+ summary();
+
+ /*
+ * If it's not a tape drive or a pipe, seek past the
+ * error. If your OS doesn't do the right thing for
+ * raw disks this section should be modified to re-read
+ * in sector size chunks.
+ */
+ if (!(in.flags & (ISPIPE|ISTAPE)) &&
+ lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
+ fprintf(stderr, "%s: seek error: %s\n",
+ in.name, strerror(errno));
+
+ /* If sync not specified, omit block and continue. */
+ if (!(ddflags & C_SYNC))
+ continue;
+
+ /* Read errors count as full blocks. */
+ in.dbcnt += in.dbrcnt = in.dbsz;
+ ++st.in_full;
+
+ /* Handle full input blocks. */
+ } else if (n == in.dbsz) {
+ in.dbcnt += in.dbrcnt = n;
+ ++st.in_full;
+
+ /* Handle partial input blocks. */
+ } else {
+ /* If sync, use the entire block. */
+ if (ddflags & C_SYNC)
+ in.dbcnt += in.dbrcnt = in.dbsz;
+ else
+ in.dbcnt += in.dbrcnt = n;
+ ++st.in_part;
+ }
+
+ /*
+ * POSIX states that if bs is set and no other conversions
+ * than noerror, notrunc or sync are specified, the block
+ * is output without buffering as it is read.
+ */
+ if (ddflags & C_BS) {
+ out.dbcnt = in.dbcnt;
+ dd_out(1);
+ in.dbcnt = 0;
+ continue;
+ }
+
+/* if (ddflags & C_SWAB) {
+ if ((n = in.dbrcnt) & 1) {
+ ++st.swab;
+ --n;
+ }
+ swab(in.dbp, in.dbp, n);
+ }
+*/
+ in.dbp += in.dbrcnt;
+ (*cfunc)();
+ }
+}
+
+/*
+ * Cleanup any remaining I/O and flush output. If necesssary, output file
+ * is truncated.
+ */
+static void
+dd_close(void)
+{
+
+ if (cfunc == def)
+ def_close();
+ else if (cfunc == block)
+ block_close();
+ else if (cfunc == unblock)
+ unblock_close();
+ if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
+ (void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
+ out.dbcnt = out.dbsz;
+ }
+ /* If there are pending sparse blocks, make sure
+ * to write out the final block un-sparse
+ */
+ if ((out.dbcnt == 0) && pending) {
+ memset(out.db, 0, out.dbsz);
+ out.dbcnt = out.dbsz;
+ out.dbp = out.db + out.dbcnt;
+ pending -= out.dbsz;
+ }
+ if (out.dbcnt)
+ dd_out(1);
+
+ /*
+ * Reporting nfs write error may be defered until next
+ * write(2) or close(2) system call. So, we need to do an
+ * extra check. If an output is stdout, the file structure
+ * may be shared among with other processes and close(2) just
+ * decreases the reference count.
+ */
+ if (out.fd == STDOUT_FILENO && fsync(out.fd) == -1 && errno != EINVAL) {
+ fprintf(stderr, "fsync stdout: %s\n", strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+ if (close(out.fd) == -1) {
+ fprintf(stderr, "close: %s\n", strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+}
+
+void
+dd_out(int force)
+{
+ static int warned;
+ int64_t cnt, n, nw;
+ u_char *outp;
+
+ /*
+ * Write one or more blocks out. The common case is writing a full
+ * output block in a single write; increment the full block stats.
+ * Otherwise, we're into partial block writes. If a partial write,
+ * and it's a character device, just warn. If a tape device, quit.
+ *
+ * The partial writes represent two cases. 1: Where the input block
+ * was less than expected so the output block was less than expected.
+ * 2: Where the input block was the right size but we were forced to
+ * write the block in multiple chunks. The original versions of dd(1)
+ * never wrote a block in more than a single write, so the latter case
+ * never happened.
+ *
+ * One special case is if we're forced to do the write -- in that case
+ * we play games with the buffer size, and it's usually a partial write.
+ */
+ outp = out.db;
+ for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
+ for (cnt = n;; cnt -= nw) {
+
+ if (!force && ddflags & C_SPARSE) {
+ int sparse, i;
+ sparse = 1; /* Is buffer sparse? */
+ for (i = 0; i < cnt; i++)
+ if (outp[i] != 0) {
+ sparse = 0;
+ break;
+ }
+ if (sparse) {
+ pending += cnt;
+ outp += cnt;
+ nw = 0;
+ break;
+ }
+ }
+ if (pending != 0) {
+ if (lseek(out.fd, pending, SEEK_CUR) ==
+ -1) {
+ fprintf(stderr,
+ "%s: seek error creating "
+ "sparse file: %s\n",
+ out.name, strerror(errno));
+ exit(1);
+ }
+ }
+ nw = bwrite(out.fd, outp, cnt);
+ if (nw <= 0) {
+ if (nw == 0) {
+ fprintf(stderr, "%s: end of device\n",
+ out.name);
+ exit(1);
+ /* NOTREACHED */
+ }
+ if (errno != EINTR) {
+ fprintf(stderr, "%s: write error: %s\n",
+ out.name, strerror(errno));
+ /* NOTREACHED */
+ exit(1);
+ }
+ nw = 0;
+ }
+ if (pending) {
+ st.bytes += pending;
+ st.sparse += pending/out.dbsz;
+ st.out_full += pending/out.dbsz;
+ pending = 0;
+ }
+ outp += nw;
+ st.bytes += nw;
+ if (nw == n) {
+ if (n != out.dbsz)
+ ++st.out_part;
+ else
+ ++st.out_full;
+ break;
+ }
+ ++st.out_part;
+ if (nw == cnt)
+ break;
+ if (out.flags & ISCHR && !warned) {
+ warned = 1;
+ fprintf(stderr, "%s: short write on character "
+ "device\n", out.name);
+ }
+ if (out.flags & ISTAPE) {
+ fprintf(stderr,
+ "%s: short write on tape device",
+ out.name);
+ exit(1);
+ /* NOTREACHED */
+ }
+ }
+ if ((out.dbcnt -= n) < out.dbsz)
+ break;
+ }
+
+ /* Reassemble the output block. */
+ if (out.dbcnt)
+ (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
+ out.dbp = out.db + out.dbcnt;
+
+ if (progress)
+ (void)write(STDERR_FILENO, ".", 1);
+}
+
+/*
+ * A protected against SIGINFO write
+ */
+ssize_t
+bwrite(int fd, const void *buf, size_t len)
+{
+ sigset_t oset;
+ ssize_t rv;
+ int oerrno;
+
+ (void)sigprocmask(SIG_BLOCK, &infoset, &oset);
+ rv = write(fd, buf, len);
+ oerrno = errno;
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ errno = oerrno;
+ return (rv);
+}
+
+/*
+ * Position input/output data streams before starting the copy. Device type
+ * dependent. Seekable devices use lseek, and the rest position by reading.
+ * Seeking past the end of file can cause null blocks to be written to the
+ * output.
+ */
+void
+pos_in(void)
+{
+ int bcnt, cnt, nr, warned;
+
+ /* If not a pipe or tape device, try to seek on it. */
+ if (!(in.flags & (ISPIPE|ISTAPE))) {
+ if (lseek(in.fd,
+ (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
+ fprintf(stderr, "%s: seek error: %s",
+ in.name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+ return;
+ /* NOTREACHED */
+ }
+
+ /*
+ * Read the data. If a pipe, read until satisfy the number of bytes
+ * being skipped. No differentiation for reading complete and partial
+ * blocks for other devices.
+ */
+ for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
+ if ((nr = read(in.fd, in.db, bcnt)) > 0) {
+ if (in.flags & ISPIPE) {
+ if (!(bcnt -= nr)) {
+ bcnt = in.dbsz;
+ --cnt;
+ }
+ } else
+ --cnt;
+ continue;
+ }
+
+ if (nr == 0) {
+ if (files_cnt > 1) {
+ --files_cnt;
+ continue;
+ }
+ fprintf(stderr, "skip reached end of input\n");
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * Input error -- either EOF with no more files, or I/O error.
+ * If noerror not set die. POSIX requires that the warning
+ * message be followed by an I/O display.
+ */
+ if (ddflags & C_NOERROR) {
+ if (!warned) {
+
+ fprintf(stderr, "%s: error occurred\n",
+ in.name);
+ warned = 1;
+ summary();
+ }
+ continue;
+ }
+ fprintf(stderr, "%s: read error: %s", in.name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+}
+
+void
+pos_out(void)
+{
+// struct mtop t_op;
+ int cnt, n;
+
+ /*
+ * If not a tape, try seeking on the file. Seeking on a pipe is
+ * going to fail, but don't protect the user -- they shouldn't
+ * have specified the seek operand.
+ */
+ if (!(out.flags & ISTAPE)) {
+ if (lseek(out.fd,
+ (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) {
+ fprintf(stderr, "%s: seek error: %s\n",
+ out.name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+ return;
+ }
+
+ /* If no read access, try using mtio. */
+ if (out.flags & NOREAD) {
+/* t_op.mt_op = MTFSR;
+ t_op.mt_count = out.offset;
+
+ if (ioctl(out.fd, MTIOCTOP, &t_op) < 0)*/
+ fprintf(stderr, "%s: cannot read", out.name);
+ exit(1);
+ /* NOTREACHED */
+ return;
+ }
+
+ /* Read it. */
+ for (cnt = 0; cnt < out.offset; ++cnt) {
+ if ((n = read(out.fd, out.db, out.dbsz)) > 0)
+ continue;
+
+ if (n < 0) {
+ fprintf(stderr, "%s: cannot position by reading: %s\n",
+ out.name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ /*
+ * If reach EOF, fill with NUL characters; first, back up over
+ * the EOF mark. Note, cnt has not yet been incremented, so
+ * the EOF read does not count as a seek'd block.
+ */
+/* t_op.mt_op = MTBSR;
+ t_op.mt_count = 1;
+ if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) */ {
+ fprintf(stderr, "%s: cannot position\n", out.name);
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ while (cnt++ < out.offset)
+ if ((n = bwrite(out.fd, out.db, out.dbsz)) != out.dbsz) {
+ fprintf(stderr, "%s: cannot position "
+ "by writing: %s\n",
+ out.name, strerror(errno));
+ exit(1);
+ /* NOTREACHED */
+ }
+ break;
+ }
+}
+
+/*
+ * def --
+ * Copy input to output. Input is buffered until reaches obs, and then
+ * output until less than obs remains. Only a single buffer is used.
+ * Worst case buffer calculation is (ibs + obs - 1).
+ */
+void
+def(void)
+{
+ uint64_t cnt;
+ u_char *inp;
+ const u_char *t;
+
+ if ((t = ctab) != NULL)
+ for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
+ *inp = t[*inp];
+
+ /* Make the output buffer look right. */
+ out.dbp = in.dbp;
+ out.dbcnt = in.dbcnt;
+
+ if (in.dbcnt >= out.dbsz) {
+ /* If the output buffer is full, write it. */
+ dd_out(0);
+
+ /*
+ * Ddout copies the leftover output to the beginning of
+ * the buffer and resets the output buffer. Reset the
+ * input buffer to match it.
+ */
+ in.dbp = out.dbp;
+ in.dbcnt = out.dbcnt;
+ }
+}
+
+void
+def_close(void)
+{
+
+ /* Just update the count, everything is already in the buffer. */
+ if (in.dbcnt)
+ out.dbcnt = in.dbcnt;
+}
+
+#ifdef NO_CONV
+/* Build a smaller version (i.e. for a miniroot) */
+/* These can not be called, but just in case... */
+static const char no_block[] = "unblock and -DNO_CONV?\n";
+void block(void) { fprintf(stderr, "%s", no_block + 2); exit(1); }
+void block_close(void) { fprintf(stderr, "%s", no_block + 2); exit(1); }
+void unblock(void) { fprintf(stderr, "%s", no_block); exit(1); }
+void unblock_close(void) { fprintf(stderr, "%s", no_block); exit(1); }
+#else /* NO_CONV */
+
+/*
+ * Copy variable length newline terminated records with a max size cbsz
+ * bytes to output. Records less than cbs are padded with spaces.
+ *
+ * max in buffer: MAX(ibs, cbsz)
+ * max out buffer: obs + cbsz
+ */
+void
+block(void)
+{
+ static int intrunc;
+ int ch = 0; /* pacify gcc */
+ uint64_t cnt, maxlen;
+ u_char *inp, *outp;
+ const u_char *t;
+
+ /*
+ * Record truncation can cross block boundaries. If currently in a
+ * truncation state, keep tossing characters until reach a newline.
+ * Start at the beginning of the buffer, as the input buffer is always
+ * left empty.
+ */
+ if (intrunc) {
+ for (inp = in.db, cnt = in.dbrcnt;
+ cnt && *inp++ != '\n'; --cnt);
+ if (!cnt) {
+ in.dbcnt = 0;
+ in.dbp = in.db;
+ return;
+ }
+ intrunc = 0;
+ /* Adjust the input buffer numbers. */
+ in.dbcnt = cnt - 1;
+ in.dbp = inp + cnt - 1;
+ }
+
+ /*
+ * Copy records (max cbsz size chunks) into the output buffer. The
+ * translation is done as we copy into the output buffer.
+ */
+ for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
+ maxlen = MIN(cbsz, in.dbcnt);
+ if ((t = ctab) != NULL)
+ for (cnt = 0;
+ cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+ *outp++ = t[ch];
+ else
+ for (cnt = 0;
+ cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+ *outp++ = ch;
+ /*
+ * Check for short record without a newline. Reassemble the
+ * input block.
+ */
+ if (ch != '\n' && in.dbcnt < cbsz) {
+ (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+ break;
+ }
+
+ /* Adjust the input buffer numbers. */
+ in.dbcnt -= cnt;
+ if (ch == '\n')
+ --in.dbcnt;
+
+ /* Pad short records with spaces. */
+ if (cnt < cbsz)
+ (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
+ else {
+ /*
+ * If the next character wouldn't have ended the
+ * block, it's a truncation.
+ */
+ if (!in.dbcnt || *inp != '\n')
+ ++st.trunc;
+
+ /* Toss characters to a newline. */
+ for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
+ if (!in.dbcnt)
+ intrunc = 1;
+ else
+ --in.dbcnt;
+ }
+
+ /* Adjust output buffer numbers. */
+ out.dbp += cbsz;
+ if ((out.dbcnt += cbsz) >= out.dbsz)
+ dd_out(0);
+ outp = out.dbp;
+ }
+ in.dbp = in.db + in.dbcnt;
+}
+
+void
+block_close(void)
+{
+
+ /*
+ * Copy any remaining data into the output buffer and pad to a record.
+ * Don't worry about truncation or translation, the input buffer is
+ * always empty when truncating, and no characters have been added for
+ * translation. The bottom line is that anything left in the input
+ * buffer is a truncated record. Anything left in the output buffer
+ * just wasn't big enough.
+ */
+ if (in.dbcnt) {
+ ++st.trunc;
+ (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
+ (void)memset(out.dbp + in.dbcnt,
+ ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
+ out.dbcnt += cbsz;
+ }
+}
+
+/*
+ * Convert fixed length (cbsz) records to variable length. Deletes any
+ * trailing blanks and appends a newline.
+ *
+ * max in buffer: MAX(ibs, cbsz) + cbsz
+ * max out buffer: obs + cbsz
+ */
+void
+unblock(void)
+{
+ uint64_t cnt;
+ u_char *inp;
+ const u_char *t;
+
+ /* Translation and case conversion. */
+ if ((t = ctab) != NULL)
+ for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--)
+ *inp = t[*inp];
+ /*
+ * Copy records (max cbsz size chunks) into the output buffer. The
+ * translation has to already be done or we might not recognize the
+ * spaces.
+ */
+ for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
+ for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
+ if (t >= inp) {
+ cnt = t - inp + 1;
+ (void)memmove(out.dbp, inp, cnt);
+ out.dbp += cnt;
+ out.dbcnt += cnt;
+ }
+ ++out.dbcnt;
+ *out.dbp++ = '\n';
+ if (out.dbcnt >= out.dbsz)
+ dd_out(0);
+ }
+ if (in.dbcnt)
+ (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+ in.dbp = in.db + in.dbcnt;
+}
+
+void
+unblock_close(void)
+{
+ uint64_t cnt;
+ u_char *t;
+
+ if (in.dbcnt) {
+ warnx("%s: short input record", in.name);
+ for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
+ if (t >= in.db) {
+ cnt = t - in.db + 1;
+ (void)memmove(out.dbp, in.db, cnt);
+ out.dbp += cnt;
+ out.dbcnt += cnt;
+ }
+ ++out.dbcnt;
+ *out.dbp++ = '\n';
+ }
+}
+
+#endif /* NO_CONV */
+
+#define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
+
+void
+summary(void)
+{
+ char buf[100];
+ int64_t mS;
+ struct timeval tv;
+
+ if (progress)
+ (void)write(STDERR_FILENO, "\n", 1);
+
+ (void)gettimeofday(&tv, NULL);
+ mS = tv2mS(tv) - tv2mS(st.start);
+ if (mS == 0)
+ mS = 1;
+ /* Use snprintf(3) so that we don't reenter stdio(3). */
+ (void)snprintf(buf, sizeof(buf),
+ "%llu+%llu records in\n%llu+%llu records out\n",
+ (unsigned long long)st.in_full, (unsigned long long)st.in_part,
+ (unsigned long long)st.out_full, (unsigned long long)st.out_part);
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ if (st.swab) {
+ (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
+ (unsigned long long)st.swab,
+ (st.swab == 1) ? "block" : "blocks");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ if (st.trunc) {
+ (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
+ (unsigned long long)st.trunc,
+ (st.trunc == 1) ? "block" : "blocks");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ if (st.sparse) {
+ (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
+ (unsigned long long)st.sparse,
+ (st.sparse == 1) ? "block" : "blocks");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ (void)snprintf(buf, sizeof(buf),
+ "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n",
+ (unsigned long long) st.bytes,
+ (long) (mS / 1000),
+ (int) (mS % 1000),
+ (unsigned long long) (st.bytes * 1000LL / mS));
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+}
+
+void
+terminate(int notused)
+{
+
+ exit(0);
+ /* NOTREACHED */
+}
+
+static int c_arg(const void *, const void *);
+#ifndef NO_CONV
+static int c_conv(const void *, const void *);
+#endif
+static void f_bs(char *);
+static void f_cbs(char *);
+static void f_conv(char *);
+static void f_count(char *);
+static void f_files(char *);
+static void f_ibs(char *);
+static void f_if(char *);
+static void f_obs(char *);
+static void f_of(char *);
+static void f_seek(char *);
+static void f_skip(char *);
+static void f_progress(char *);
+
+static const struct arg {
+ const char *name;
+ void (*f)(char *);
+ u_int set, noset;
+} args[] = {
+ /* the array needs to be sorted by the first column so
+ bsearch() can be used to find commands quickly */
+ { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC },
+ { "cbs", f_cbs, C_CBS, C_CBS },
+ { "conv", f_conv, 0, 0 },
+ { "count", f_count, C_COUNT, C_COUNT },
+ { "files", f_files, C_FILES, C_FILES },
+ { "ibs", f_ibs, C_IBS, C_BS|C_IBS },
+ { "if", f_if, C_IF, C_IF },
+ { "obs", f_obs, C_OBS, C_BS|C_OBS },
+ { "of", f_of, C_OF, C_OF },
+ { "progress", f_progress, 0, 0 },
+ { "seek", f_seek, C_SEEK, C_SEEK },
+ { "skip", f_skip, C_SKIP, C_SKIP },
+};
+
+/*
+ * args -- parse JCL syntax of dd.
+ */
+void
+jcl(char **argv)
+{
+ struct arg *ap, tmp;
+ char *oper, *arg;
+
+ in.dbsz = out.dbsz = 512;
+
+ while ((oper = *++argv) != NULL) {
+ if ((arg = strchr(oper, '=')) == NULL) {
+ fprintf(stderr, "unknown operand %s\n", oper);
+ exit(1);
+ /* NOTREACHED */
+ }
+ *arg++ = '\0';
+ if (!*arg) {
+ fprintf(stderr, "no value specified for %s\n", oper);
+ exit(1);
+ /* NOTREACHED */
+ }
+ tmp.name = oper;
+ if (!(ap = (struct arg *)bsearch(&tmp, args,
+ sizeof(args)/sizeof(struct arg), sizeof(struct arg),
+ c_arg))) {
+ fprintf(stderr, "unknown operand %s\n", tmp.name);
+ exit(1);
+ /* NOTREACHED */
+ }
+ if (ddflags & ap->noset) {
+ fprintf(stderr,
+ "%s: illegal argument combination or already set\n",
+ tmp.name);
+ exit(1);
+ /* NOTREACHED */
+ }
+ ddflags |= ap->set;
+ ap->f(arg);
+ }
+
+ /* Final sanity checks. */
+
+ if (ddflags & C_BS) {
+ /*
+ * Bs is turned off by any conversion -- we assume the user
+ * just wanted to set both the input and output block sizes
+ * and didn't want the bs semantics, so we don't warn.
+ */
+ if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
+ C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) {
+ ddflags &= ~C_BS;
+ ddflags |= C_IBS|C_OBS;
+ }
+
+ /* Bs supersedes ibs and obs. */
+ if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
+ fprintf(stderr, "bs supersedes ibs and obs\n");
+ }
+
+ /*
+ * Ascii/ebcdic and cbs implies block/unblock.
+ * Block/unblock requires cbs and vice-versa.
+ */
+ if (ddflags & (C_BLOCK|C_UNBLOCK)) {
+ if (!(ddflags & C_CBS)) {
+ fprintf(stderr, "record operations require cbs\n");
+ exit(1);
+ /* NOTREACHED */
+ }
+ cfunc = ddflags & C_BLOCK ? block : unblock;
+ } else if (ddflags & C_CBS) {
+ if (ddflags & (C_ASCII|C_EBCDIC)) {
+ if (ddflags & C_ASCII) {
+ ddflags |= C_UNBLOCK;
+ cfunc = unblock;
+ } else {
+ ddflags |= C_BLOCK;
+ cfunc = block;
+ }
+ } else {
+ fprintf(stderr,
+ "cbs meaningless if not doing record operations\n");
+ exit(1);
+ /* NOTREACHED */
+ }
+ } else
+ cfunc = def;
+
+ /* Read, write and seek calls take off_t as arguments.
+ *
+ * The following check is not done because an off_t is a quad
+ * for current NetBSD implementations.
+ *
+ * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
+ * errx(1, "seek offsets cannot be larger than %d", INT_MAX);
+ */
+}
+
+static int
+c_arg(const void *a, const void *b)
+{
+
+ return (strcmp(((const struct arg *)a)->name,
+ ((const struct arg *)b)->name));
+}
+
+static long long strsuftoll(const char* name, const char* arg, int def, unsigned int max)
+{
+ long long result;
+
+ if (sscanf(arg, "%lld", &result) == 0)
+ result = def;
+ return result;
+}
+
+static void
+f_bs(char *arg)
+{
+
+ in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_cbs(char *arg)
+{
+
+ cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);
+}
+
+static void
+f_count(char *arg)
+{
+
+ cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
+ if (!cpy_cnt)
+ terminate(0);
+}
+
+static void
+f_files(char *arg)
+{
+
+ files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX);
+ if (!files_cnt)
+ terminate(0);
+}
+
+static void
+f_ibs(char *arg)
+{
+
+ if (!(ddflags & C_BS))
+ in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_if(char *arg)
+{
+
+ in.name = arg;
+}
+
+static void
+f_obs(char *arg)
+{
+
+ if (!(ddflags & C_BS))
+ out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_of(char *arg)
+{
+
+ out.name = arg;
+}
+
+static void
+f_seek(char *arg)
+{
+
+ out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_skip(char *arg)
+{
+
+ in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_progress(char *arg)
+{
+
+ if (*arg != '0')
+ progress = 1;
+}
+
+#ifdef NO_CONV
+/* Build a small version (i.e. for a ramdisk root) */
+static void
+f_conv(char *arg)
+{
+
+ fprintf(stderr, "conv option disabled\n");
+ exit(1);
+ /* NOTREACHED */
+}
+#else /* NO_CONV */
+
+static const struct conv {
+ const char *name;
+ u_int set, noset;
+ const u_char *ctab;
+} clist[] = {
+ { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX },
+ { "block", C_BLOCK, C_UNBLOCK, NULL },
+ { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX },
+ { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX },
+ { "lcase", C_LCASE, C_UCASE, NULL },
+ { "noerror", C_NOERROR, 0, NULL },
+ { "notrunc", C_NOTRUNC, 0, NULL },
+ { "oldascii", C_ASCII, C_EBCDIC, e2a_32V },
+ { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V },
+ { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V },
+ { "osync", C_OSYNC, C_BS, NULL },
+ { "sparse", C_SPARSE, 0, NULL },
+ { "swab", C_SWAB, 0, NULL },
+ { "sync", C_SYNC, 0, NULL },
+ { "ucase", C_UCASE, C_LCASE, NULL },
+ { "unblock", C_UNBLOCK, C_BLOCK, NULL },
+ /* If you add items to this table, be sure to add the
+ * conversions to the C_BS check in the jcl routine above.
+ */
+};
+
+static void
+f_conv(char *arg)
+{
+ struct conv *cp, tmp;
+
+ while (arg != NULL) {
+ tmp.name = strsep(&arg, ",");
+ if (!(cp = (struct conv *)bsearch(&tmp, clist,
+ sizeof(clist)/sizeof(struct conv), sizeof(struct conv),
+ c_conv))) {
+ errx(EXIT_FAILURE, "unknown conversion %s", tmp.name);
+ /* NOTREACHED */
+ }
+ if (ddflags & cp->noset) {
+ errx(EXIT_FAILURE, "%s: illegal conversion combination", tmp.name);
+ /* NOTREACHED */
+ }
+ ddflags |= cp->set;
+ if (cp->ctab)
+ ctab = cp->ctab;
+ }
+}
+
+static int
+c_conv(const void *a, const void *b)
+{
+
+ return (strcmp(((const struct conv *)a)->name,
+ ((const struct conv *)b)->name));
+}
+
+#endif /* NO_CONV */
+
+
diff --git a/toolbox/dd.h b/toolbox/dd.h
new file mode 100644
index 0000000..794a464
--- /dev/null
+++ b/toolbox/dd.h
@@ -0,0 +1,91 @@
+/* $NetBSD: dd.h,v 1.12 2004/01/17 20:48:57 dbj Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dd.h 8.3 (Berkeley) 4/2/94
+ */
+
+/* Input/output stream state. */
+typedef struct {
+ u_char *db; /* buffer address */
+ u_char *dbp; /* current buffer I/O address */
+ uint64_t dbcnt; /* current buffer byte count */
+ int64_t dbrcnt; /* last read byte count */
+ uint64_t dbsz; /* buffer size */
+
+#define ISCHR 0x01 /* character device (warn on short) */
+#define ISPIPE 0x02 /* pipe (not truncatable) */
+#define ISTAPE 0x04 /* tape (not seekable) */
+#define NOREAD 0x08 /* not readable */
+ u_int flags;
+
+ const char *name; /* name */
+ int fd; /* file descriptor */
+ uint64_t offset; /* # of blocks to skip */
+} IO;
+
+typedef struct {
+ uint64_t in_full; /* # of full input blocks */
+ uint64_t in_part; /* # of partial input blocks */
+ uint64_t out_full; /* # of full output blocks */
+ uint64_t out_part; /* # of partial output blocks */
+ uint64_t trunc; /* # of truncated records */
+ uint64_t swab; /* # of odd-length swab blocks */
+ uint64_t sparse; /* # of sparse output blocks */
+ uint64_t bytes; /* # of bytes written */
+ struct timeval start; /* start time of dd */
+} STAT;
+
+/* Flags (in ddflags). */
+#define C_ASCII 0x00001
+#define C_BLOCK 0x00002
+#define C_BS 0x00004
+#define C_CBS 0x00008
+#define C_COUNT 0x00010
+#define C_EBCDIC 0x00020
+#define C_FILES 0x00040
+#define C_IBS 0x00080
+#define C_IF 0x00100
+#define C_LCASE 0x00200
+#define C_NOERROR 0x00400
+#define C_NOTRUNC 0x00800
+#define C_OBS 0x01000
+#define C_OF 0x02000
+#define C_SEEK 0x04000
+#define C_SKIP 0x08000
+#define C_SWAB 0x10000
+#define C_SYNC 0x20000
+#define C_UCASE 0x40000
+#define C_UNBLOCK 0x80000
+#define C_OSYNC 0x100000
+#define C_SPARSE 0x200000
diff --git a/toolbox/df.c b/toolbox/df.c
new file mode 100644
index 0000000..90476bd
--- /dev/null
+++ b/toolbox/df.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/statfs.h>
+
+static int ok = EXIT_SUCCESS;
+
+static void df(char *s, int always) {
+ struct statfs st;
+
+ if (statfs(s, &st) < 0) {
+ fprintf(stderr, "%s: %s\n", s, strerror(errno));
+ ok = EXIT_FAILURE;
+ } else {
+ if (st.f_blocks == 0 && !always)
+ return;
+
+ printf("%s: %lldK total, %lldK used, %lldK available (block size %d)\n",
+ s,
+ ((long long)st.f_blocks * (long long)st.f_bsize) / 1024,
+ ((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize) / 1024,
+ ((long long)st.f_bfree * (long long)st.f_bsize) / 1024,
+ (int) st.f_bsize);
+ }
+}
+
+int df_main(int argc, char *argv[]) {
+ if (argc == 1) {
+ char s[2000];
+ FILE *f = fopen("/proc/mounts", "r");
+
+ while (fgets(s, 2000, f)) {
+ char *c, *e = s;
+
+ for (c = s; *c; c++) {
+ if (*c == ' ') {
+ e = c + 1;
+ break;
+ }
+ }
+
+ for (c = e; *c; c++) {
+ if (*c == ' ') {
+ *c = '\0';
+ break;
+ }
+ }
+
+ df(e, 0);
+ }
+
+ fclose(f);
+ } else {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ df(argv[i], 1);
+ }
+ }
+
+ exit(ok);
+}
diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c
new file mode 100644
index 0000000..e57f607
--- /dev/null
+++ b/toolbox/dmesg.c
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/klog.h>
+#include <string.h>
+
+#define KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT from our kernel */
+#define KLOG_BUF_LEN (1 << KLOG_BUF_SHIFT)
+
+int dmesg_main(int argc, char **argv)
+{
+ char buffer[KLOG_BUF_LEN + 1];
+ char *p = buffer;
+ ssize_t ret;
+ int n, op;
+
+ if((argc == 2) && (!strcmp(argv[1],"-c"))) {
+ op = KLOG_READ_CLEAR;
+ } else {
+ op = KLOG_READ_ALL;
+ }
+
+ n = klogctl(op, buffer, KLOG_BUF_LEN);
+ if (n < 0) {
+ perror("klogctl");
+ return EXIT_FAILURE;
+ }
+ buffer[n] = '\0';
+
+ while((ret = write(STDOUT_FILENO, p, n))) {
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ perror("write");
+ return EXIT_FAILURE;
+ }
+ p += ret;
+ n -= ret;
+ }
+
+ return 0;
+}
diff --git a/toolbox/exists.c b/toolbox/exists.c
new file mode 100644
index 0000000..e348668
--- /dev/null
+++ b/toolbox/exists.c
@@ -0,0 +1,16 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int exists_main(int argc, char *argv[])
+{
+ struct stat s;
+
+ if(argc < 2) return 1;
+
+ if(stat(argv[1], &s)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
new file mode 100644
index 0000000..14372cb
--- /dev/null
+++ b/toolbox/getevent.c
@@ -0,0 +1,427 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <sys/limits.h>
+#include <sys/poll.h>
+#include <linux/input.h> // this does not compile
+#include <errno.h>
+
+static struct pollfd *ufds;
+static char **device_names;
+static int nfds;
+
+enum {
+ PRINT_DEVICE_ERRORS = 1U << 0,
+ PRINT_DEVICE = 1U << 1,
+ PRINT_DEVICE_NAME = 1U << 2,
+ PRINT_DEVICE_INFO = 1U << 3,
+ PRINT_VERSION = 1U << 4,
+ PRINT_POSSIBLE_EVENTS = 1U << 5,
+};
+
+static int print_possible_events(int fd)
+{
+ uint8_t *bits = NULL;
+ ssize_t bits_size = 0;
+ int i, j, k;
+ int res, res2;
+
+ printf(" events:\n");
+ for(i = 0; i <= EV_MAX; i++) {
+ int count = 0;
+ while(1) {
+ res = ioctl(fd, EVIOCGBIT(i, bits_size), bits);
+ if(res < bits_size)
+ break;
+ bits_size = res + 16;
+ bits = realloc(bits, bits_size * 2);
+ if(bits == NULL) {
+ fprintf(stderr, "failed to allocate buffer of size %d\n", bits_size);
+ return 1;
+ }
+ }
+ switch(i) {
+ case EV_KEY:
+ res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);
+ break;
+ case EV_LED:
+ res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);
+ break;
+ case EV_SND:
+ res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);
+ break;
+ case EV_SW:
+ res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);
+ break;
+ default:
+ res2 = 0;
+ }
+ for(j = 0; j < res; j++) {
+ for(k = 0; k < 8; k++)
+ if(bits[j] & 1 << k) {
+ char down;
+ if(j < res2 && (bits[j + bits_size] & 1 << k))
+ down = '*';
+ else
+ down = ' ';
+ if(count == 0)
+ printf(" type %04x:", i);
+ else if((count & 0x7) == 0 || i == EV_ABS)
+ printf("\n ");
+ printf(" %04x%c", j * 8 + k, down);
+ if(i == EV_ABS) {
+ struct input_absinfo abs;
+ if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) {
+ printf(" value %d, min %d, max %d, fuzz %d flat %d", abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat);
+ }
+ }
+ count++;
+ }
+ }
+ if(count)
+ printf("\n");
+ }
+ free(bits);
+ return 0;
+}
+
+static int open_device(const char *device, int print_flags)
+{
+ int version;
+ int fd;
+ struct pollfd *new_ufds;
+ char **new_device_names;
+ char name[80];
+ char location[80];
+ char idstr[80];
+ struct input_id id;
+
+ fd = open(device, O_RDWR);
+ if(fd < 0) {
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
+ return -1;
+ }
+
+ if(ioctl(fd, EVIOCGVERSION, &version)) {
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));
+ return -1;
+ }
+ if(ioctl(fd, EVIOCGID, &id)) {
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));
+ return -1;
+ }
+ name[sizeof(name) - 1] = '\0';
+ location[sizeof(location) - 1] = '\0';
+ idstr[sizeof(idstr) - 1] = '\0';
+ if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
+ //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
+ name[0] = '\0';
+ }
+ if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
+ //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
+ location[0] = '\0';
+ }
+ if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
+ //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
+ idstr[0] = '\0';
+ }
+
+ new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
+ if(new_ufds == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+ ufds = new_ufds;
+ new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
+ if(new_device_names == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+ device_names = new_device_names;
+
+ if(print_flags & PRINT_DEVICE)
+ printf("add device %d: %s\n", nfds, device);
+ if(print_flags & PRINT_DEVICE_INFO)
+ printf(" bus: %04x\n"
+ " vendor %04x\n"
+ " product %04x\n"
+ " version %04x\n",
+ id.bustype, id.vendor, id.product, id.version);
+ if(print_flags & PRINT_DEVICE_NAME)
+ printf(" name: \"%s\"\n", name);
+ if(print_flags & PRINT_DEVICE_INFO)
+ printf(" location: \"%s\"\n"
+ " id: \"%s\"\n", location, idstr);
+ if(print_flags & PRINT_VERSION)
+ printf(" version: %d.%d.%d\n",
+ version >> 16, (version >> 8) & 0xff, version & 0xff);
+
+ if(print_flags & PRINT_POSSIBLE_EVENTS) {
+ print_possible_events(fd);
+ }
+
+ ufds[nfds].fd = fd;
+ ufds[nfds].events = POLLIN;
+ device_names[nfds] = strdup(device);
+ nfds++;
+
+ return 0;
+}
+
+int close_device(const char *device, int print_flags)
+{
+ int i;
+ for(i = 1; i < nfds; i++) {
+ if(strcmp(device_names[i], device) == 0) {
+ int count = nfds - i - 1;
+ if(print_flags & PRINT_DEVICE)
+ printf("remove device %d: %s\n", i, device);
+ free(device_names[i]);
+ memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
+ memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
+ nfds--;
+ return 0;
+ }
+ }
+ if(print_flags & PRINT_DEVICE_ERRORS)
+ fprintf(stderr, "remote device: %s not found\n", device);
+ return -1;
+}
+
+static int read_notify(const char *dirname, int nfd, int print_flags)
+{
+ int res;
+ char devname[PATH_MAX];
+ char *filename;
+ char event_buf[512];
+ int event_size;
+ int event_pos = 0;
+ struct inotify_event *event;
+
+ res = read(nfd, event_buf, sizeof(event_buf));
+ if(res < (int)sizeof(*event)) {
+ if(errno == EINTR)
+ return 0;
+ fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ return 1;
+ }
+ //printf("got %d bytes of event information\n", res);
+
+ strcpy(devname, dirname);
+ filename = devname + strlen(devname);
+ *filename++ = '/';
+
+ while(res >= (int)sizeof(*event)) {
+ event = (struct inotify_event *)(event_buf + event_pos);
+ //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+ if(event->len) {
+ strcpy(filename, event->name);
+ if(event->mask & IN_CREATE) {
+ open_device(devname, print_flags);
+ }
+ else {
+ close_device(devname, print_flags);
+ }
+ }
+ event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+ return 0;
+}
+
+static int scan_dir(const char *dirname, int print_flags)
+{
+ char devname[PATH_MAX];
+ char *filename;
+ DIR *dir;
+ struct dirent *de;
+ dir = opendir(dirname);
+ if(dir == NULL)
+ return -1;
+ strcpy(devname, dirname);
+ filename = devname + strlen(devname);
+ *filename++ = '/';
+ while((de = readdir(dir))) {
+ if(de->d_name[0] == '.' &&
+ (de->d_name[1] == '\0' ||
+ (de->d_name[1] == '.' && de->d_name[2] == '\0')))
+ continue;
+ strcpy(filename, de->d_name);
+ open_device(devname, print_flags);
+ }
+ closedir(dir);
+ return 0;
+}
+
+static void usage(int argc, char *argv[])
+{
+ fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-q] [-c count] [-r] [device]\n", argv[0]);
+}
+
+int getevent_main(int argc, char *argv[])
+{
+ int c;
+ int i;
+ int res;
+ int pollres;
+ int get_time = 0;
+ int print_device = 0;
+ char *newline = "\n";
+ uint16_t get_switch = 0;
+ struct input_event event;
+ int version;
+ int print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
+ int print_flags_set = 0;
+ int dont_block = -1;
+ int event_count = 0;
+ int sync_rate = 0;
+ int64_t last_sync_time = 0;
+ const char *device = NULL;
+ const char *device_path = "/dev/input";
+
+ opterr = 0;
+ do {
+ c = getopt(argc, argv, "tns:Sv::qc:rh");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 't':
+ get_time = 1;
+ break;
+ case 'n':
+ newline = "";
+ break;
+ case 's':
+ get_switch = strtoul(optarg, NULL, 0);
+ if(dont_block == -1)
+ dont_block = 1;
+ break;
+ case 'S':
+ get_switch = ~0;
+ if(dont_block == -1)
+ dont_block = 1;
+ break;
+ case 'v':
+ if(optarg)
+ print_flags = strtoul(optarg, NULL, 0);
+ else
+ print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
+ print_flags_set = 1;
+ break;
+ case 'q':
+ print_flags = 0;
+ print_flags_set = 1;
+ break;
+ case 'c':
+ event_count = atoi(optarg);
+ dont_block = 0;
+ break;
+ case 'r':
+ sync_rate = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ case 'h':
+ usage(argc, argv);
+ exit(1);
+ }
+ } while (1);
+ if(dont_block == -1)
+ dont_block = 0;
+
+ if (optind + 1 == argc) {
+ device = argv[optind];
+ optind++;
+ }
+ if (optind != argc) {
+ usage(argc, argv);
+ exit(1);
+ }
+ nfds = 1;
+ ufds = calloc(1, sizeof(ufds[0]));
+ ufds[0].fd = inotify_init();
+ ufds[0].events = POLLIN;
+ if(device) {
+ if(!print_flags_set)
+ print_flags = PRINT_DEVICE_ERRORS;
+ res = open_device(device, print_flags);
+ if(res < 0) {
+ return 1;
+ }
+ }
+ else {
+ print_device = 1;
+ res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
+ if(res < 0) {
+ fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
+ return 1;
+ }
+ res = scan_dir(device_path, print_flags);
+ if(res < 0) {
+ fprintf(stderr, "scan dir failed for %s\n", device_path);
+ return 1;
+ }
+ }
+
+ if(get_switch) {
+ for(i = 1; i < nfds; i++) {
+ uint16_t sw;
+ res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);
+ if(res < 0) {
+ fprintf(stderr, "could not get switch state, %s\n", strerror(errno));
+ return 1;
+ }
+ sw &= get_switch;
+ printf("%04x%s", sw, newline);
+ }
+ }
+
+ if(dont_block)
+ return 0;
+
+ while(1) {
+ pollres = poll(ufds, nfds, -1);
+ //printf("poll %d, returned %d\n", nfds, pollres);
+ if(ufds[0].revents & POLLIN) {
+ read_notify(device_path, ufds[0].fd, print_flags);
+ }
+ for(i = 1; i < nfds; i++) {
+ if(ufds[i].revents) {
+ if(ufds[i].revents & POLLIN) {
+ res = read(ufds[i].fd, &event, sizeof(event));
+ if(res < (int)sizeof(event)) {
+ fprintf(stderr, "could not get event\n");
+ return 1;
+ }
+ if(get_time) {
+ printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec);
+ }
+ if(print_device)
+ printf("%s: ", device_names[i]);
+ printf("%04x %04x %08x", event.type, event.code, event.value);
+ if(sync_rate && event.type == 0 && event.code == 0) {
+ int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
+ if(last_sync_time)
+ printf(" rate %lld", 1000000LL / (now - last_sync_time));
+ last_sync_time = now;
+ }
+ printf("%s", newline);
+ if(event_count && --event_count == 0)
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/toolbox/getprop.c b/toolbox/getprop.c
new file mode 100644
index 0000000..fc80a4d
--- /dev/null
+++ b/toolbox/getprop.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+
+#include <cutils/properties.h>
+
+#include <sys/system_properties.h>
+
+static void proplist(const char *key, const char *name,
+ void *user __attribute__((unused)))
+{
+ printf("[%s]: [%s]\n", key, name);
+}
+
+int __system_property_wait(prop_info *pi);
+
+int getprop_main(int argc, char *argv[])
+{
+ int n = 0;
+
+ if (argc == 1) {
+ (void)property_list(proplist, NULL);
+ } else {
+ char value[PROPERTY_VALUE_MAX];
+ char *default_value;
+ if(argc > 2) {
+ default_value = argv[2];
+ } else {
+ default_value = "";
+ }
+
+ property_get(argv[1], value, default_value);
+ printf("%s\n", value);
+ }
+ return 0;
+}
diff --git a/toolbox/hd.c b/toolbox/hd.c
new file mode 100644
index 0000000..1f7d179
--- /dev/null
+++ b/toolbox/hd.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+int hd_main(int argc, char *argv[])
+{
+ int c;
+ int fd;
+ unsigned char buf[4096];
+ int res;
+ int read_len;
+ int rv = 0;
+ int i;
+ int filepos = 0;
+ int sum;
+ int lsum;
+
+ int base = -1;
+ int count = 0;
+ int repeat = 0;
+
+ do {
+ c = getopt(argc, argv, "b:c:r:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'b':
+ base = strtol(optarg, NULL, 0);
+ break;
+ case 'c':
+ count = strtol(optarg, NULL, 0);
+ break;
+ case 'r':
+ repeat = strtol(optarg, NULL, 0);
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if (optind + 1 != argc) {
+ fprintf(stderr, "Usage: %s [-b base] [-c count] [-r delay] file\n", argv[0]);
+ exit(1);
+ }
+
+ fd = open(argv[optind], O_RDONLY);
+ if(fd < 0) {
+ fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+ return 1;
+ }
+
+ do {
+ if(base >= 0) {
+ lseek(fd, base, SEEK_SET);
+ filepos = base;
+ }
+ sum = 0;
+ lsum = 0;
+ while(1) {
+ read_len = sizeof(buf);
+ if(count > 0 && base + count - filepos < read_len)
+ read_len = base + count - filepos;
+ res = read(fd, &buf, read_len);
+ for(i = 0; i < res; i++) {
+ if((i & 15) == 0) {
+ printf("%08x: ", filepos + i);
+ }
+ lsum += buf[i];
+ sum += buf[i];
+ printf("%02x ", buf[i]);
+ if(((i & 15) == 15) || (i == res - 1)) {
+ printf("s %x\n", lsum);
+ lsum = 0;
+ }
+ }
+ if(res <= 0) {
+ printf("Read error on %s, offset %d len %d, %s\n", argv[optind], filepos, read_len, strerror(errno));
+ return 1;
+ }
+ filepos += res;
+ if(filepos == base + count)
+ break;
+ }
+ printf("sum %x\n", sum);
+ if(repeat)
+ sleep(repeat);
+ } while(repeat);
+ return 0;
+}
diff --git a/toolbox/id.c b/toolbox/id.c
new file mode 100644
index 0000000..bb03cad
--- /dev/null
+++ b/toolbox/id.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+static void print_uid(uid_t uid)
+{
+ struct passwd *pw = getpwuid(uid);
+
+ if (pw) {
+ printf("%d(%s)", uid, pw->pw_name);
+ } else {
+ printf("%d",uid);
+ }
+}
+
+static void print_gid(gid_t gid)
+{
+ struct group *gr = getgrgid(gid);
+ if (gr) {
+ printf("%d(%s)", gid, gr->gr_name);
+ } else {
+ printf("%d",gid);
+ }
+}
+
+int id_main(int argc, char **argv)
+{
+ gid_t list[64];
+ int n, max;
+
+ max = getgroups(64, list);
+ if (max < 0) max = 0;
+
+ printf("uid=");
+ print_uid(getuid());
+ printf(" gid=");
+ print_gid(getgid());
+ if (max) {
+ printf(" groups=");
+ print_gid(list[0]);
+ for(n = 1; n < max; n++) {
+ printf(",");
+ print_gid(list[n]);
+ }
+ }
+ printf("\n");
+ return 0;
+}
diff --git a/toolbox/ifconfig.c b/toolbox/ifconfig.c
new file mode 100644
index 0000000..e83cd8b
--- /dev/null
+++ b/toolbox/ifconfig.c
@@ -0,0 +1,139 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+
+static void die(const char *s)
+{
+ fprintf(stderr,"error: %s (%s)\n", s, strerror(errno));
+ exit(-1);
+}
+
+static void setflags(int s, struct ifreq *ifr, int set, int clr)
+{
+ if(ioctl(s, SIOCGIFFLAGS, ifr) < 0) die("SIOCGIFFLAGS");
+ ifr->ifr_flags = (ifr->ifr_flags & (~clr)) | set;
+ if(ioctl(s, SIOCSIFFLAGS, ifr) < 0) die("SIOCSIFFLAGS");
+}
+
+static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr)
+{
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = inet_addr(addr);
+}
+
+static void setnetmask(int s, struct ifreq *ifr, const char *addr)
+{
+ init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_netmask, addr);
+ if(ioctl(s, SIOCSIFNETMASK, ifr) < 0) die("SIOCSIFNETMASK");
+}
+
+static void setaddr(int s, struct ifreq *ifr, const char *addr)
+{
+ init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_addr, addr);
+ if(ioctl(s, SIOCSIFADDR, ifr) < 0) die("SIOCSIFADDR");
+}
+
+int ifconfig_main(int argc, char *argv[])
+{
+ struct ifreq ifr;
+ int s;
+ unsigned int addr, mask, flags;
+ char astring[20];
+ char mstring[20];
+ char *updown, *brdcst, *loopbk, *ppp, *running, *multi;
+
+ argc--;
+ argv++;
+
+ if(argc == 0) return 0;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+ argc--, argv++;
+
+ if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ die("cannot open control socket\n");
+ }
+
+ if (argc == 0) {
+ if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+ perror(ifr.ifr_name);
+ return -1;
+ } else
+ addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+
+ if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0) {
+ perror(ifr.ifr_name);
+ return -1;
+ } else
+ mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+ perror(ifr.ifr_name);
+ return -1;
+ } else
+ flags = ifr.ifr_flags;
+
+ sprintf(astring, "%d.%d.%d.%d",
+ addr & 0xff,
+ ((addr >> 8) & 0xff),
+ ((addr >> 16) & 0xff),
+ ((addr >> 24) & 0xff));
+ sprintf(mstring, "%d.%d.%d.%d",
+ mask & 0xff,
+ ((mask >> 8) & 0xff),
+ ((mask >> 16) & 0xff),
+ ((mask >> 24) & 0xff));
+ printf("%s: ip %s mask %s flags [", ifr.ifr_name,
+ astring,
+ mstring
+ );
+
+ updown = (flags & IFF_UP) ? "up" : "down";
+ brdcst = (flags & IFF_BROADCAST) ? " broadcast" : "";
+ loopbk = (flags & IFF_LOOPBACK) ? " loopback" : "";
+ ppp = (flags & IFF_POINTOPOINT) ? " point-to-point" : "";
+ running = (flags & IFF_RUNNING) ? " running" : "";
+ multi = (flags & IFF_MULTICAST) ? " multicast" : "";
+ printf("%s%s%s%s%s%s]\n", updown, brdcst, loopbk, ppp, running, multi);
+
+
+
+/* char *updown, *brdcst, *loopbk, *ppp, *running, *multi; */
+
+ return 0;
+ }
+
+ while(argc > 0){
+ if(!strcmp(argv[0], "up")) {
+ setflags(s, &ifr, IFF_UP, 0);
+ } else if(!strcmp(argv[0], "down")) {
+ setflags(s, &ifr, 0, IFF_UP);
+ } else if(!strcmp(argv[0], "netmask")) {
+ argc--, argv++;
+ if (0 == argc) {
+ errno = EINVAL;
+ die("expecting an IP address for parameter \"netmask\"");
+ }
+ setnetmask(s, &ifr, argv[0]);
+ } else if(isdigit(argv[0][0])){
+ setaddr(s, &ifr, argv[0]);
+ }
+ argc--, argv++;
+ }
+
+ return 0;
+}
diff --git a/toolbox/iftop.c b/toolbox/iftop.c
new file mode 100644
index 0000000..800c0f0
--- /dev/null
+++ b/toolbox/iftop.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#define PROC_NET_DEV "/proc/net/dev"
+
+#define MAX_IF 8 /* max interfaces we can handle */
+
+#ifndef PAGE_SIZE
+# define PAGE_SIZE 4096
+#endif
+
+#define _STR(s) #s
+#define STR(s) _STR(s)
+
+struct if_stats {
+ char name[IFNAMSIZ];
+
+ unsigned int mtu;
+
+ unsigned int rx_bytes;
+ unsigned int rx_packets;
+ unsigned int rx_errors;
+ unsigned int rx_dropped;
+
+ unsigned int tx_bytes;
+ unsigned int tx_packets;
+ unsigned int tx_errors;
+ unsigned int tx_dropped;
+};
+
+static int get_mtu(const char *if_name)
+{
+ struct ifreq ifr;
+ int s, ret;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ ifr.ifr_addr.sa_family = AF_INET;
+ strcpy(ifr.ifr_name, if_name);
+
+ ret = ioctl(s, SIOCGIFMTU, &ifr);
+ if (ret < 0) {
+ perror("ioctl");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = close(s);
+ if (ret < 0) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+
+ return ifr.ifr_mtu;
+}
+
+static int get_interfaces(struct if_stats *ifs)
+{
+ char buf[PAGE_SIZE];
+ char *p;
+ int ret, nr, fd;
+
+ fd = open(PROC_NET_DEV, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ if (ret < 0) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ } else if (!ret) {
+ fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n");
+ exit(EXIT_FAILURE);
+ }
+ buf[ret] = '\0';
+
+ /* skip down to the third line */
+ p = strchr(buf, '\n');
+ if (!p) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+ p = strchr(p + 1, '\n');
+ if (!p) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+ p += 1;
+
+ /*
+ * Key:
+ * if: (Rx) bytes packets errs drop fifo frame compressed multicast \
+ * (Tx) bytes packets errs drop fifo colls carrier compressed
+ */
+ for (nr = 0; nr < MAX_IF; nr++) {
+ char *c;
+
+ ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name);
+ if (ret != 1) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * This works around a bug in the proc file where large interface names
+ * or Rx byte counts eat the delimiter, breaking sscanf.
+ */
+ c = strchr(ifs->name, ':');
+ if (c)
+ *c = '\0';
+
+ p = strchr(p, ':') + 1;
+
+ ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u "
+ "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets,
+ &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes,
+ &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped);
+ if (ret != 8) {
+ fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ifs->mtu = get_mtu(ifs->name);
+
+ p = strchr(p, '\n') + 1;
+ if (*p == '\0') {
+ nr++;
+ break;
+ }
+
+ ifs++;
+ }
+
+ ret = close(fd);
+ if (ret) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+
+ return nr;
+}
+
+static void print_header(void)
+{
+ printf(" Rx Tx\n");
+ printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n",
+ "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes",
+ "packets", "errs", "drpd");
+}
+
+static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr)
+{
+ int i = 0;
+
+ while (nr--) {
+ if (old->rx_packets || old->tx_packets) {
+ printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n",
+ new->name, new->mtu,
+ new->rx_bytes - old->rx_bytes,
+ new->rx_packets - old->rx_packets,
+ new->rx_errors - old->rx_errors,
+ new->rx_dropped - old->rx_dropped,
+ new->tx_bytes - old->tx_bytes,
+ new->tx_packets - old->tx_packets,
+ new->tx_errors - old->tx_errors,
+ new->tx_dropped - old->tx_dropped);
+ i++;
+ }
+ old++;
+ new++;
+ }
+
+ return i;
+}
+
+static void usage(const char *cmd)
+{
+ fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd);
+}
+
+int iftop_main(int argc, char *argv[])
+{
+ struct if_stats ifs[2][MAX_IF];
+ int count = 0, header_interval = 22, delay = 1, i;
+ unsigned int toggle = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-d")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Option -d requires an argument.\n");
+ exit(EXIT_FAILURE);
+ }
+ delay = atoi(argv[i++]);
+ if (!delay)
+ delay = 1;
+ continue;
+ }
+ if (!strcmp(argv[i], "-r")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Option -r requires an argument.\n");
+ exit(EXIT_FAILURE);
+ }
+ header_interval = atoi(argv[i++]);
+ if (header_interval < MAX_IF)
+ header_interval = MAX_IF;
+ continue;
+ }
+ if (!strcmp(argv[i], "-h")) {
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ get_interfaces(ifs[!toggle]);
+ if (header_interval)
+ print_header();
+ while (1) {
+ int nr;
+
+ sleep(delay);
+ nr = get_interfaces(ifs[toggle]);
+ if (header_interval && count + nr > header_interval) {
+ print_header();
+ count = 0;
+ }
+ count += print_interfaces(ifs[!toggle], ifs[toggle], nr);
+ toggle = !toggle;
+ }
+
+ return 0;
+}
diff --git a/toolbox/insmod.c b/toolbox/insmod.c
new file mode 100644
index 0000000..44b9847
--- /dev/null
+++ b/toolbox/insmod.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+extern int init_module(void *, unsigned long, const char *);
+
+static void *read_file(const char *filename, ssize_t *_size)
+{
+ int ret, fd;
+ struct stat sb;
+ ssize_t size;
+ void *buffer = NULL;
+
+ /* open the file */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ /* find out how big it is */
+ if (fstat(fd, &sb) < 0)
+ goto bail;
+ size = sb.st_size;
+
+ /* allocate memory for it to be read into */
+ buffer = malloc(size);
+ if (!buffer)
+ goto bail;
+
+ /* slurp it into our buffer */
+ ret = read(fd, buffer, size);
+ if (ret != size)
+ goto bail;
+
+ /* let the caller know how big it is */
+ *_size = size;
+
+bail:
+ close(fd);
+ return buffer;
+}
+
+#define min(x,y) ((x) < (y) ? (x) : (y))
+int insmod_main(int argc, char **argv)
+{
+ void *file;
+ ssize_t size = 0;
+ char opts[1024];
+ int ret;
+
+ /* make sure we've got an argument */
+ if (argc < 2) {
+ fprintf(stderr, "usage: insmod <module.o>\n");
+ return -1;
+ }
+
+ /* read the file into memory */
+ file = read_file(argv[1], &size);
+ if (!file) {
+ fprintf(stderr, "insmod: can't open '%s'\n", argv[1]);
+ return -1;
+ }
+
+ opts[0] = '\0';
+ if (argc > 2) {
+ int i, len;
+ char *end = opts + sizeof(opts) - 1;
+ char *ptr = opts;
+
+ for (i = 2; (i < argc) && (ptr < end); i++) {
+ len = min(strlen(argv[i]), end - ptr);
+ memcpy(ptr, argv[i], len);
+ ptr += len;
+ *ptr++ = ' ';
+ *ptr++ = '\0';
+ }
+ *(ptr - 1) = '\0';
+ }
+
+ /* pass it to the kernel */
+ ret = init_module(file, size, opts);
+ if (ret != 0) {
+ fprintf(stderr,
+ "insmod: init_module '%s' failed (%s)\n",
+ argv[1], strerror(errno));
+ }
+
+ /* free the file buffer */
+ free(file);
+
+ return ret;
+}
+
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
new file mode 100644
index 0000000..e28f2a4
--- /dev/null
+++ b/toolbox/ioctl.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+#include <pthread.h>
+
+int ioctl_main(int argc, char *argv[])
+{
+ int c;
+ int fd;
+ int res;
+
+ int read_only = 0;
+ int length = -1;
+ int arg_size = 4;
+ int direct_arg = 0;
+ uint32_t ioctl_nr;
+ void *ioctl_args;
+ uint8_t *ioctl_argp;
+ uint8_t *ioctl_argp_save;
+ int rem;
+
+ do {
+ c = getopt(argc, argv, "rdl:a:h");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'r':
+ read_only = 1;
+ break;
+ case 'd':
+ direct_arg = 1;
+ break;
+ case 'l':
+ length = strtol(optarg, NULL, 0);
+ break;
+ case 'a':
+ arg_size = strtol(optarg, NULL, 0);
+ break;
+ case 'h':
+ fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
+ " -l <lenght> Length of io buffer\n"
+ " -a <argsize> Size of each argument (1-8)\n"
+ " -r Open device in read only mode\n"
+ " -d Direct argument (no iobuffer)\n"
+ " -h Print help\n", argv[0]);
+ return -1;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if(optind + 2 > argc) {
+ fprintf(stderr, "%s: too few arguments\n", argv[0]);
+ exit(1);
+ }
+
+ fd = open(argv[optind], O_RDWR | O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s\n", argv[optind]);
+ return 1;
+ }
+ optind++;
+
+ ioctl_nr = strtol(argv[optind], NULL, 0);
+ optind++;
+
+ if(direct_arg) {
+ arg_size = 4;
+ length = 4;
+ }
+
+ if(length < 0) {
+ length = (argc - optind) * arg_size;
+ }
+ if(length) {
+ ioctl_args = calloc(1, length);
+
+ ioctl_argp_save = ioctl_argp = ioctl_args;
+ rem = length;
+ while(optind < argc) {
+ uint64_t tmp = strtoull(argv[optind], NULL, 0);
+ if(rem < arg_size) {
+ fprintf(stderr, "%s: too many arguments\n", argv[0]);
+ exit(1);
+ }
+ memcpy(ioctl_argp, &tmp, arg_size);
+ ioctl_argp += arg_size;
+ rem -= arg_size;
+ optind++;
+ }
+ }
+ printf("sending ioctl 0x%x", ioctl_nr);
+ rem = length;
+ while(rem--) {
+ printf(" 0x%02x", *ioctl_argp_save++);
+ }
+ printf("\n");
+
+ if(direct_arg)
+ res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
+ else if(length)
+ res = ioctl(fd, ioctl_nr, ioctl_args);
+ else
+ res = ioctl(fd, ioctl_nr, 0);
+ if (res < 0) {
+ fprintf(stderr, "ioctl 0x%x failed, %d\n", ioctl_nr, res);
+ return 1;
+ }
+ if(length) {
+ printf("return buf:");
+ ioctl_argp = ioctl_args;
+ rem = length;
+ while(rem--) {
+ printf(" %02x", *ioctl_argp++);
+ }
+ printf("\n");
+ }
+ return 0;
+}
diff --git a/toolbox/kill.c b/toolbox/kill.c
new file mode 100644
index 0000000..4d0e479
--- /dev/null
+++ b/toolbox/kill.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <signal.h>
+
+int kill_main(int argc, char **argv)
+{
+ int sig = SIGTERM;
+ int result = 0;
+
+ argc--;
+ argv++;
+
+ if(argc >= 2 && argv[0][0] == '-'){
+ sig = atoi(argv[0] + 1);
+ argc--;
+ argv++;
+ }
+
+ while(argc > 0){
+ int pid = atoi(argv[0]);
+ int err = kill(pid, sig);
+ if (err < 0) {
+ result = err;
+ fprintf(stderr, "could not kill pid %d: %s\n", pid, strerror(errno));
+ }
+
+ argc--;
+ argv++;
+ }
+
+ return result;
+}
diff --git a/toolbox/ln.c b/toolbox/ln.c
new file mode 100644
index 0000000..dcd5e3a
--- /dev/null
+++ b/toolbox/ln.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+ fprintf(stderr,"ln [-s] <target> <name>\n");
+ return -1;
+}
+
+int ln_main(int argc, char *argv[])
+{
+ int symbolic = 0;
+ int ret;
+ if(argc < 2) return usage();
+
+ if(!strcmp(argv[1],"-s")) {
+ symbolic = 1;
+ argc--;
+ argv++;
+ }
+
+ if(argc < 3) return usage();
+
+ if(symbolic) {
+ ret = symlink(argv[1], argv[2]);
+ } else {
+ ret = link(argv[1], argv[2]);
+ }
+ if(ret < 0)
+ fprintf(stderr, "link failed %s\n", strerror(errno));
+ return ret;
+}
diff --git a/toolbox/log.c b/toolbox/log.c
new file mode 100644
index 0000000..f30e6a7
--- /dev/null
+++ b/toolbox/log.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <cutils/logd.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <cutils/sockets.h>
+#include <unistd.h>
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri (char c)
+{
+ android_LogPriority pri;
+
+ c = tolower(c);
+
+ if (c >= '0' && c <= '9') {
+ if (c >= ('0'+ANDROID_LOG_SILENT)) {
+ pri = ANDROID_LOG_VERBOSE;
+ } else {
+ pri = (android_LogPriority)(c - '0');
+ }
+ } else if (c == 'v') {
+ pri = ANDROID_LOG_VERBOSE;
+ } else if (c == 'd') {
+ pri = ANDROID_LOG_DEBUG;
+ } else if (c == 'i') {
+ pri = ANDROID_LOG_INFO;
+ } else if (c == 'w') {
+ pri = ANDROID_LOG_WARN;
+ } else if (c == 'e') {
+ pri = ANDROID_LOG_ERROR;
+ } else if (c == 'f') {
+ pri = ANDROID_LOG_FATAL;
+ } else if (c == 's') {
+ pri = ANDROID_LOG_SILENT;
+ } else if (c == '*') {
+ pri = ANDROID_LOG_DEFAULT;
+ } else {
+ pri = ANDROID_LOG_UNKNOWN;
+ }
+
+ return pri;
+}
+
+static int usage(const char *s)
+{
+ fprintf(stderr, "USAGE: %s [-p priorityChar] [-t tag] message\n", s);
+
+ fprintf(stderr, "\tpriorityChar should be one of:\n"
+ "\t\tv,d,i,w,e\n");
+ exit(-1);
+}
+
+
+int log_main(int argc, char *argv[])
+{
+ android_LogPriority priority;
+ const char *tag = "log";
+ char buffer[4096];
+ int i;
+
+ priority = ANDROID_LOG_INFO;
+
+ for (;;) {
+ int ret;
+
+ ret = getopt(argc, argv, "t:p:h");
+
+ if (ret < 0) {
+ break;
+ }
+
+ switch(ret) {
+ case 't':
+ tag = optarg;
+ break;
+
+ case 'p':
+ priority = filterCharToPri(optarg[0]);
+ if (priority == ANDROID_LOG_UNKNOWN) {
+ usage(argv[0]);
+ }
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ usage(argv[0]);
+ }
+
+ buffer[0] = '\0';
+
+ for (i = optind ; i < argc ; i++) {
+ strncat(buffer, argv[i], sizeof(buffer)-1);
+ strncat(buffer, " ", sizeof(buffer)-1);
+ }
+
+ if(buffer[0] == 0) {
+ usage(argv[0]);
+ }
+
+ __android_log_print(priority, tag, "%s", buffer);
+
+ return 0;
+}
+
diff --git a/toolbox/ls.c b/toolbox/ls.c
new file mode 100644
index 0000000..f609df2
--- /dev/null
+++ b/toolbox/ls.c
@@ -0,0 +1,285 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <pwd.h>
+#include <grp.h>
+
+#include <linux/kdev_t.h>
+
+// bits for flags argument
+#define LIST_LONG (1 << 0)
+#define LIST_ALL (1 << 1)
+#define LIST_RECURSIVE (1 << 2)
+
+// fwd
+static int listpath(const char *name, int flags);
+
+static char mode2kind(unsigned mode)
+{
+ switch(mode & S_IFMT){
+ case S_IFSOCK: return 's';
+ case S_IFLNK: return 'l';
+ case S_IFREG: return '-';
+ case S_IFDIR: return 'd';
+ case S_IFBLK: return 'b';
+ case S_IFCHR: return 'c';
+ case S_IFIFO: return 'p';
+ default: return '?';
+ }
+}
+
+static void mode2str(unsigned mode, char *out)
+{
+ *out++ = mode2kind(mode);
+
+ *out++ = (mode & 0400) ? 'r' : '-';
+ *out++ = (mode & 0200) ? 'w' : '-';
+ if(mode & 04000) {
+ *out++ = (mode & 0100) ? 's' : 'S';
+ } else {
+ *out++ = (mode & 0100) ? 'x' : '-';
+ }
+ *out++ = (mode & 040) ? 'r' : '-';
+ *out++ = (mode & 020) ? 'w' : '-';
+ if(mode & 02000) {
+ *out++ = (mode & 010) ? 's' : 'S';
+ } else {
+ *out++ = (mode & 010) ? 'x' : '-';
+ }
+ *out++ = (mode & 04) ? 'r' : '-';
+ *out++ = (mode & 02) ? 'w' : '-';
+ if(mode & 01000) {
+ *out++ = (mode & 01) ? 't' : 'T';
+ } else {
+ *out++ = (mode & 01) ? 'x' : '-';
+ }
+ *out = 0;
+}
+
+static void user2str(unsigned uid, char *out)
+{
+ struct passwd *pw = getpwuid(uid);
+ if(pw) {
+ strcpy(out, pw->pw_name);
+ } else {
+ sprintf(out, "%d", uid);
+ }
+}
+
+static void group2str(unsigned gid, char *out)
+{
+ struct group *gr = getgrgid(gid);
+ if(gr) {
+ strcpy(out, gr->gr_name);
+ } else {
+ sprintf(out, "%d", gid);
+ }
+}
+
+static int listfile(const char *path, int flags)
+{
+ struct stat s;
+ char date[32];
+ char mode[16];
+ char user[16];
+ char group[16];
+ const char *name;
+
+ /* name is anything after the final '/', or the whole path if none*/
+ name = strrchr(path, '/');
+ if(name == 0) {
+ name = path;
+ } else {
+ name++;
+ }
+
+ if(lstat(path, &s) < 0) {
+ return -1;
+ }
+
+ mode2str(s.st_mode, mode);
+ user2str(s.st_uid, user);
+ group2str(s.st_gid, group);
+
+ strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
+ date[31] = 0;
+
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
+// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
+
+ switch(s.st_mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+ printf("%s %-8s %-8s %3d, %3d %s %s\n",
+ mode, user, group,
+ (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
+ date, name);
+ break;
+ case S_IFREG:
+ printf("%s %-8s %-8s %8d %s %s\n",
+ mode, user, group, (int) s.st_size, date, name);
+ break;
+ case S_IFLNK: {
+ char linkto[256];
+ int len;
+
+ len = readlink(path, linkto, 256);
+ if(len < 0) return -1;
+
+ if(len > 255) {
+ linkto[252] = '.';
+ linkto[253] = '.';
+ linkto[254] = '.';
+ linkto[255] = 0;
+ } else {
+ linkto[len] = 0;
+ }
+
+ printf("%s %-8s %-8s %s %s -> %s\n",
+ mode, user, group, date, name, linkto);
+ break;
+ }
+ default:
+ printf("%s %-8s %-8s %s %s\n",
+ mode, user, group, date, name);
+
+ }
+ return 0;
+}
+
+static int listdir(const char *name, int flags)
+{
+ char tmp[4096];
+ DIR *d;
+ struct dirent *de;
+
+ d = opendir(name);
+ if(d == 0) {
+ fprintf(stderr, "opendir failed, %s\n", strerror(errno));
+ return -1;
+ }
+
+ while((de = readdir(d)) != 0){
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
+ if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
+ if ((flags & LIST_LONG) != 0) {
+ sprintf(tmp, "%s/%s", name, de->d_name);
+ listfile(tmp, flags);
+ } else {
+ printf("%s\n", de->d_name);
+ }
+ }
+
+ if (flags & LIST_RECURSIVE) {
+ rewinddir(d);
+
+ while ((de = readdir(d)) != 0) {
+ struct stat s;
+ int err;
+
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+ if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
+ continue;
+
+ if (!strcmp(name, "/")) sprintf(tmp, "/%s", de->d_name);
+ else sprintf(tmp, "%s/%s", name, de->d_name);
+
+ /*
+ * If the name ends in a '/', use stat() so we treat it like a
+ * directory even if it's a symlink.
+ */
+ if (tmp[strlen(tmp)-1] == '/')
+ err = stat(tmp, &s);
+ else
+ err = lstat(tmp, &s);
+
+ if (err < 0) {
+ perror(tmp);
+ closedir(d);
+ return -1;
+ }
+
+ if (S_ISDIR(s.st_mode)) {
+ printf("\n%s:\n", tmp);
+ listdir(tmp, flags);
+ }
+ }
+ }
+
+ closedir(d);
+ return 0;
+}
+
+static int listpath(const char *name, int flags)
+{
+ struct stat s;
+ int err;
+
+ /*
+ * If the name ends in a '/', use stat() so we treat it like a
+ * directory even if it's a symlink.
+ */
+ if (name[strlen(name)-1] == '/')
+ err = stat(name, &s);
+ else
+ err = lstat(name, &s);
+
+ if (err < 0) {
+ perror(name);
+ return -1;
+ }
+
+ if (S_ISDIR(s.st_mode)) {
+ if (flags & LIST_RECURSIVE)
+ printf("\n%s:\n", name);
+ return listdir(name, flags);
+ } else {
+ if ((flags & LIST_LONG) != 0) {
+ /* yeah this calls stat() again*/
+ return listfile(name, flags);
+ } else {
+ printf("%s\n", name);
+ return 0;
+ }
+ }
+}
+
+int ls_main(int argc, char **argv)
+{
+ int flags = 0;
+ int listed = 0;
+
+ if(argc > 1) {
+ int i;
+ int err = 0;
+
+ for (i = 1; i < argc; i++) {
+ if(!strcmp(argv[i], "-l")) {
+ flags |= LIST_LONG;
+ } else if (!strcmp(argv[i], "-a")) {
+ flags |= LIST_ALL;
+ } else if (!strcmp(argv[i], "-R")) {
+ flags |= LIST_RECURSIVE;
+ } else {
+ listed++;
+ if(listpath(argv[i], flags) != 0) {
+ err = EXIT_FAILURE;
+ }
+ }
+ }
+
+ if (listed > 0) return err;
+ }
+
+ // list working directory if no files or directories were specified
+ return listpath(".", flags);
+}
diff --git a/toolbox/lsmod.c b/toolbox/lsmod.c
new file mode 100644
index 0000000..8b55ee6
--- /dev/null
+++ b/toolbox/lsmod.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+extern int cat_main(int argc, char **argv);
+
+int lsmod_main(int argc, char **argv)
+{
+ char *cat_argv[] = { "cat", "/proc/modules", NULL };
+ return cat_main(2, cat_argv);
+}
+
diff --git a/toolbox/mkdir.c b/toolbox/mkdir.c
new file mode 100644
index 0000000..121adab
--- /dev/null
+++ b/toolbox/mkdir.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+ fprintf(stderr,"mkdir <target>\n");
+ return -1;
+}
+
+int mkdir_main(int argc, char *argv[])
+{
+ int symbolic = 0;
+ int ret;
+ if(argc < 2) return usage();
+
+ while(argc > 1) {
+ argc--;
+ argv++;
+ ret = mkdir(argv[0], 0777);
+ if(ret < 0) {
+ fprintf(stderr, "mkdir failed for %s, %s\n", argv[0], strerror(errno));
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/toolbox/mkdosfs.c b/toolbox/mkdosfs.c
new file mode 100644
index 0000000..744aad1
--- /dev/null
+++ b/toolbox/mkdosfs.c
@@ -0,0 +1,848 @@
+/* $NetBSD: newfs_msdos.c,v 1.18.2.1 2005/05/01 18:44:02 tron Exp $ */
+
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define __USE_FILE_OFFSET64
+
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef __FreeBSD__
+#include <sys/diskslice.h>
+#endif
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __NetBSD__
+#include <disktab.h>
+#include <util.h>
+#endif
+
+#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
+#define BPN 4 /* bits per nibble */
+#define NPB 2 /* nibbles per byte */
+
+#define DOSMAGIC 0xaa55 /* DOS magic number */
+#define MINBPS 128 /* minimum bytes per sector */
+#define MAXSPC 128 /* maximum sectors per cluster */
+#define MAXNFT 16 /* maximum number of FATs */
+#define DEFBLK 4096 /* default block size */
+#define DEFBLK16 2048 /* default block size FAT16 */
+#define DEFRDE 512 /* default root directory entries */
+#define RESFTE 2 /* reserved FAT entries */
+#define MINCLS12 1 /* minimum FAT12 clusters */
+#define MINCLS16 0x1000 /* minimum FAT16 clusters */
+#define MINCLS32 2 /* minimum FAT32 clusters */
+#define MAXCLS12 0xfed /* maximum FAT12 clusters */
+#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */
+#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */
+
+#define mincls(fat) ((fat) == 12 ? MINCLS12 : \
+ (fat) == 16 ? MINCLS16 : \
+ MINCLS32)
+
+#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \
+ (fat) == 16 ? MAXCLS16 : \
+ MAXCLS32)
+
+#define mk1(p, x) \
+ (p) = (u_int8_t)(x)
+
+#define mk2(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010), \
+ (p)[2] = (u_int8_t)((x) >> 020), \
+ (p)[3] = (u_int8_t)((x) >> 030)
+
+#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg)
+#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg)
+#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg)
+#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg)
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+static int powerof2(int x) {
+ int i;
+ for (i = 0; i < 32; i++) {
+ if (x & 1) {
+ x >>= 1;
+ // if x is zero, then original x was a power of two
+ return (x == 0);
+ }
+ x >>= 1;
+ }
+
+ return 0;
+}
+
+#ifndef howmany
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+
+#pragma pack(push, 1)
+struct bs {
+ u_int8_t jmp[3]; /* bootstrap entry point */
+ u_int8_t oem[8]; /* OEM name and version */
+};
+#define BS_SIZE 11
+
+struct bsbpb {
+ u_int8_t bps[2]; /* bytes per sector */
+ u_int8_t spc; /* sectors per cluster */
+ u_int8_t res[2]; /* reserved sectors */
+ u_int8_t nft; /* number of FATs */
+ u_int8_t rde[2]; /* root directory entries */
+ u_int8_t sec[2]; /* total sectors */
+ u_int8_t mid; /* media descriptor */
+ u_int8_t spf[2]; /* sectors per FAT */
+ u_int8_t spt[2]; /* sectors per track */
+ u_int8_t hds[2]; /* drive heads */
+ u_int8_t hid[4]; /* hidden sectors */
+ u_int8_t bsec[4]; /* big total sectors */
+};
+#define BSBPB_SIZE 25
+
+struct bsxbpb {
+ u_int8_t bspf[4]; /* big sectors per FAT */
+ u_int8_t xflg[2]; /* FAT control flags */
+ u_int8_t vers[2]; /* file system version */
+ u_int8_t rdcl[4]; /* root directory start cluster */
+ u_int8_t infs[2]; /* file system info sector */
+ u_int8_t bkbs[2]; /* backup boot sector */
+ u_int8_t rsvd[12]; /* reserved */
+};
+#define BSXBPB_SIZE 28
+
+struct bsx {
+ u_int8_t drv; /* drive number */
+ u_int8_t rsvd; /* reserved */
+ u_int8_t sig; /* extended boot signature */
+ u_int8_t volid[4]; /* volume ID number */
+ u_int8_t label[11]; /* volume label */
+ u_int8_t type[8]; /* file system type */
+};
+#define BSX_SIZE 26
+
+struct de {
+ u_int8_t namext[11]; /* name and extension */
+ u_int8_t attr; /* attributes */
+ u_int8_t rsvd[10]; /* reserved */
+ u_int8_t time[2]; /* creation time */
+ u_int8_t date[2]; /* creation date */
+ u_int8_t clus[2]; /* starting cluster */
+ u_int8_t size[4]; /* size */
+#define DE_SIZE 32
+};
+#pragma pack(pop)
+
+struct bpb {
+ u_int bps; /* bytes per sector */
+ u_int spc; /* sectors per cluster */
+ u_int res; /* reserved sectors */
+ u_int nft; /* number of FATs */
+ u_int rde; /* root directory entries */
+ u_int sec; /* total sectors */
+ u_int mid; /* media descriptor */
+ u_int spf; /* sectors per FAT */
+ u_int spt; /* sectors per track */
+ u_int hds; /* drive heads */
+ u_int hid; /* hidden sectors */
+ u_int bsec; /* big total sectors */
+ u_int bspf; /* big sectors per FAT */
+ u_int rdcl; /* root directory start cluster */
+ u_int infs; /* file system info sector */
+ u_int bkbs; /* backup boot sector */
+};
+
+static u_int8_t bootcode[] = {
+ 0xfa, /* cli */
+ 0x31, 0xc0, /* xor ax,ax */
+ 0x8e, 0xd0, /* mov ss,ax */
+ 0xbc, 0x00, 0x7c, /* mov sp,7c00h */
+ 0xfb, /* sti */
+ 0x8e, 0xd8, /* mov ds,ax */
+ 0xe8, 0x00, 0x00, /* call $ + 3 */
+ 0x5e, /* pop si */
+ 0x83, 0xc6, 0x19, /* add si,+19h */
+ 0xbb, 0x07, 0x00, /* mov bx,0007h */
+ 0xfc, /* cld */
+ 0xac, /* lodsb */
+ 0x84, 0xc0, /* test al,al */
+ 0x74, 0x06, /* jz $ + 8 */
+ 0xb4, 0x0e, /* mov ah,0eh */
+ 0xcd, 0x10, /* int 10h */
+ 0xeb, 0xf5, /* jmp $ - 9 */
+ 0x30, 0xe4, /* xor ah,ah */
+ 0xcd, 0x16, /* int 16h */
+ 0xcd, 0x19, /* int 19h */
+ 0x0d, 0x0a,
+ 'N', 'o', 'n', '-', 's', 'y', 's', 't',
+ 'e', 'm', ' ', 'd', 'i', 's', 'k',
+ 0x0d, 0x0a,
+ 'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+ 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+ ' ', 'r', 'e', 'b', 'o', 'o', 't',
+ 0x0d, 0x0a,
+ 0
+};
+
+static void print_bpb(struct bpb *);
+static u_int ckgeom(const char *, u_int, const char *);
+static u_int argtou(const char *, u_int, u_int, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void usage(char* progname);
+
+/*
+ * Construct a FAT12, FAT16, or FAT32 file system.
+ */
+int
+mkdosfs_main(int argc, char *argv[])
+{
+ static char opts[] = "NB:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
+ static const char *opt_B, *opt_L, *opt_O;
+ static u_int opt_F, opt_I, opt_S, opt_a, opt_b, opt_c, opt_e;
+ static u_int opt_h, opt_i, opt_k, opt_m, opt_n, opt_o, opt_r;
+ static u_int opt_s, opt_u;
+ static int opt_N;
+ static int Iflag, mflag, oflag;
+ char buf[MAXPATHLEN];
+ struct stat sb;
+ struct timeval tv;
+ struct bpb bpb;
+ struct tm *tm;
+ struct bs *bs;
+ struct bsbpb *bsbpb;
+ struct bsxbpb *bsxbpb;
+ struct bsx *bsx;
+ struct de *de;
+ u_int8_t *img;
+ const char *fname, *dtype, *bname;
+ ssize_t n;
+ time_t now;
+ u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
+ int ch, fd, fd1;
+ char* progname = argv[0];
+
+ while ((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case 'N':
+ opt_N = 1;
+ break;
+ case 'B':
+ opt_B = optarg;
+ break;
+ case 'F':
+ if (strcmp(optarg, "12") &&
+ strcmp(optarg, "16") &&
+ strcmp(optarg, "32"))
+ fprintf(stderr, "%s: bad FAT type\n", optarg);
+ opt_F = atoi(optarg);
+ break;
+ case 'I':
+ opt_I = argto4(optarg, 0, "volume ID");
+ Iflag = 1;
+ break;
+ case 'L':
+ if (!oklabel(optarg))
+ fprintf(stderr, "%s: bad volume label\n", optarg);
+ opt_L = optarg;
+ break;
+ case 'O':
+ if (strlen(optarg) > 8)
+ fprintf(stderr, "%s: bad OEM string\n", optarg);
+ opt_O = optarg;
+ break;
+ case 'S':
+ opt_S = argto2(optarg, 1, "bytes/sector");
+ break;
+ case 'a':
+ opt_a = argto4(optarg, 1, "sectors/FAT");
+ break;
+ case 'b':
+ opt_b = argtox(optarg, 1, "block size");
+ opt_c = 0;
+ break;
+ case 'c':
+ opt_c = argto1(optarg, 1, "sectors/cluster");
+ opt_b = 0;
+ break;
+ case 'e':
+ opt_e = argto2(optarg, 1, "directory entries");
+ break;
+ case 'h':
+ opt_h = argto2(optarg, 1, "drive heads");
+ break;
+ case 'i':
+ opt_i = argto2(optarg, 1, "info sector");
+ break;
+ case 'k':
+ opt_k = argto2(optarg, 1, "backup sector");
+ break;
+ case 'm':
+ opt_m = argto1(optarg, 0, "media descriptor");
+ mflag = 1;
+ break;
+ case 'n':
+ opt_n = argto1(optarg, 1, "number of FATs");
+ break;
+ case 'o':
+ opt_o = argto4(optarg, 0, "hidden sectors");
+ oflag = 1;
+ break;
+ case 'r':
+ opt_r = argto2(optarg, 1, "reserved sectors");
+ break;
+ case 's':
+ opt_s = argto4(optarg, 1, "file system size");
+ break;
+ case 'u':
+ opt_u = argto2(optarg, 1, "sectors/track");
+ break;
+ default:
+ usage(progname);
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1 || argc > 2)
+ usage(progname);
+ fname = *argv++;
+ if (!strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname);
+ if (!(fname = strdup(buf)))
+ fprintf(stderr, NULL);
+ }
+ dtype = *argv;
+ if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1 ||
+ fstat(fd, &sb))
+ fprintf(stderr, "%s\n", fname);
+ memset(&bpb, 0, sizeof(bpb));
+
+ if (opt_h)
+ bpb.hds = opt_h;
+ if (opt_u)
+ bpb.spt = opt_u;
+ if (opt_S)
+ bpb.bps = opt_S;
+ if (opt_s)
+ bpb.bsec = opt_s;
+ if (oflag)
+ bpb.hid = opt_o;
+
+ bpb.bps = 512; // 512 bytes/sector
+ bpb.spc = 8; // 4K clusters
+
+
+ fprintf(stderr, "opening %s\n", fname);
+ if ((fd1 = open(fname, O_RDONLY)) == -1) {
+ fprintf(stderr, "failed to open %s\n", fname);
+ exit(1);
+ }
+
+ lseek64(fd1, 0, SEEK_SET);
+ loff_t length = lseek64(fd1, 0, SEEK_END);
+ if (length > 0) {
+ bpb.bsec = length / bpb.bps;
+ bpb.spt = bpb.bsec;
+ // use FAT32 for 2 gig or greater
+ if (length >= 2 *1024 *1024 *1024) {
+ fat = 32;
+ } else {
+ fat = 16;
+ }
+ }
+ close(fd1);
+ fd1 = -1;
+
+ if (!powerof2(bpb.bps))
+ fprintf(stderr, "bytes/sector (%u) is not a power of 2\n", bpb.bps);
+ if (bpb.bps < MINBPS)
+ fprintf(stderr, "bytes/sector (%u) is too small; minimum is %u\n",
+ bpb.bps, MINBPS);
+
+ if (!(fat = opt_F)) {
+ if (!opt_e && (opt_i || opt_k))
+ fat = 32;
+ }
+
+ if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
+ fprintf(stderr, "-%c is not a legal FAT%s option\n",
+ fat == 32 ? 'e' : opt_i ? 'i' : 'k',
+ fat == 32 ? "32" : "12/16");
+ if (fat == 32)
+ bpb.rde = 0;
+ if (opt_b) {
+ if (!powerof2(opt_b))
+ fprintf(stderr, "block size (%u) is not a power of 2\n", opt_b);
+ if (opt_b < bpb.bps)
+ fprintf(stderr, "block size (%u) is too small; minimum is %u\n",
+ opt_b, bpb.bps);
+ if (opt_b > bpb.bps * MAXSPC)
+ fprintf(stderr, "block size (%u) is too large; maximum is %u\n",
+ opt_b, bpb.bps * MAXSPC);
+ bpb.spc = opt_b / bpb.bps;
+ }
+ if (opt_c) {
+ if (!powerof2(opt_c))
+ fprintf(stderr, "sectors/cluster (%u) is not a power of 2\n", opt_c);
+ bpb.spc = opt_c;
+ }
+ if (opt_r)
+ bpb.res = opt_r;
+ if (opt_n) {
+ if (opt_n > MAXNFT)
+ fprintf(stderr, "number of FATs (%u) is too large; maximum is %u\n",
+ opt_n, MAXNFT);
+ bpb.nft = opt_n;
+ }
+ if (opt_e)
+ bpb.rde = opt_e;
+ if (mflag) {
+ if (opt_m < 0xf0)
+ fprintf(stderr, "illegal media descriptor (%#x)\n", opt_m);
+ bpb.mid = opt_m;
+ }
+ if (opt_a)
+ bpb.bspf = opt_a;
+ if (opt_i)
+ bpb.infs = opt_i;
+ if (opt_k)
+ bpb.bkbs = opt_k;
+ bss = 1;
+ bname = NULL;
+ fd1 = -1;
+ if (opt_B) {
+ bname = opt_B;
+ if (!strchr(bname, '/')) {
+ snprintf(buf, sizeof(buf), "/boot/%s", bname);
+ if (!(bname = strdup(buf)))
+ fprintf(stderr, NULL);
+ }
+ if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
+ fprintf(stderr, "%s", bname);
+ if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+ sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
+ fprintf(stderr, "%s: inappropriate file type or format\n", bname);
+ bss = sb.st_size / bpb.bps;
+ }
+ if (!bpb.nft)
+ bpb.nft = 2;
+ if (!fat) {
+ if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+ ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+ bpb.nft +
+ howmany(bpb.rde ? bpb.rde : DEFRDE,
+ bpb.bps / DE_SIZE) +
+ (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+ (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+ fat = 12;
+ else if (bpb.rde || bpb.bsec <
+ (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+ howmany(DEFRDE, bpb.bps / DE_SIZE) +
+ (MAXCLS16 + 1) *
+ (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+ fat = 16;
+ else
+ fat = 32;
+ }
+ x = bss;
+ if (fat == 32) {
+ if (!bpb.infs) {
+ if (x == MAXU16 || x == bpb.bkbs)
+ fprintf(stderr, "no room for info sector\n");
+ bpb.infs = x;
+ }
+ if (bpb.infs != MAXU16 && x <= bpb.infs)
+ x = bpb.infs + 1;
+ if (!bpb.bkbs) {
+ if (x == MAXU16)
+ fprintf(stderr, "no room for backup sector\n");
+ bpb.bkbs = x;
+ } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
+ fprintf(stderr, "backup sector would overwrite info sector\n");
+ if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+ x = bpb.bkbs + 1;
+ }
+ if (!bpb.res)
+ bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
+ else if (bpb.res < x)
+ fprintf(stderr, "too few reserved sectors (need %d have %d)\n", x, bpb.res);
+ if (fat != 32 && !bpb.rde)
+ bpb.rde = DEFRDE;
+ rds = howmany(bpb.rde, bpb.bps / DE_SIZE);
+ if (!bpb.spc)
+ for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+ bpb.spc < MAXSPC &&
+ bpb.res +
+ howmany((RESFTE + maxcls(fat)) * (fat / BPN),
+ bpb.bps * NPB) * bpb.nft +
+ rds +
+ (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
+ bpb.spc <<= 1);
+ if (fat != 32 && bpb.bspf > MAXU16)
+ fprintf(stderr, "too many sectors/FAT for FAT12/16\n");
+ x1 = bpb.res + rds;
+ x = bpb.bspf ? bpb.bspf : 1;
+ if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
+ fprintf(stderr, "meta data exceeds file system size\n");
+ x1 += x * bpb.nft;
+ x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+ (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
+ x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
+ bpb.bps * NPB);
+ if (!bpb.bspf) {
+ bpb.bspf = x2;
+ x1 += (bpb.bspf - 1) * bpb.nft;
+ }
+ cls = (bpb.bsec - x1) / bpb.spc;
+ x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
+ if (cls > x)
+ cls = x;
+ if (bpb.bspf < x2)
+ fprintf(stderr, "warning: sectors/FAT limits file system to %u clusters\n",
+ cls);
+ if (cls < mincls(fat))
+ fprintf(stderr, "%u clusters too few clusters for FAT%u, need %u\n", cls, fat,
+ mincls(fat));
+ if (cls > maxcls(fat)) {
+ cls = maxcls(fat);
+ bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+ fprintf(stderr, "warning: FAT type limits file system to %u sectors\n",
+ bpb.bsec);
+ }
+ printf("%s: %u sector%s in %u FAT%u cluster%s "
+ "(%u bytes/cluster)\n", fname, cls * bpb.spc,
+ cls * bpb.spc == 1 ? "" : "s", cls, fat,
+ cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+ if (!bpb.mid)
+ bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+ if (fat == 32)
+ bpb.rdcl = RESFTE;
+ if (bpb.hid + bpb.bsec <= MAXU16) {
+ bpb.sec = bpb.bsec;
+ bpb.bsec = 0;
+ }
+ if (fat != 32) {
+ bpb.spf = bpb.bspf;
+ bpb.bspf = 0;
+ }
+ ch = 0;
+ if (fat == 12)
+ ch = 1; /* 001 Primary DOS with 12 bit FAT */
+ else if (fat == 16) {
+ if (bpb.bsec == 0)
+ ch = 4; /* 004 Primary DOS with 16 bit FAT <32M */
+ else
+ ch = 6; /* 006 Primary 'big' DOS, 16-bit FAT (> 32MB) */
+ /*
+ * XXX: what about:
+ * 014 DOS (16-bit FAT) - LBA
+ * ?
+ */
+ } else if (fat == 32) {
+ ch = 11; /* 011 Primary DOS with 32 bit FAT */
+ /*
+ * XXX: what about:
+ * 012 Primary DOS with 32 bit FAT - LBA
+ * ?
+ */
+ }
+ if (ch != 0)
+ printf("MBR type: %d\n", ch);
+ print_bpb(&bpb);
+ if (!opt_N) {
+ gettimeofday(&tv, NULL);
+ now = tv.tv_sec;
+ tm = localtime(&now);
+ if (!(img = malloc(bpb.bps)))
+ fprintf(stderr, NULL);
+ dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+
+ for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
+ x = lsn;
+ if (opt_B &&
+ fat == 32 && bpb.bkbs != MAXU16 &&
+ bss <= bpb.bkbs && x >= bpb.bkbs) {
+ x -= bpb.bkbs;
+ if (!x && lseek64(fd1, 0, SEEK_SET))
+ fprintf(stderr, "lseek64 failed for %s\n", bname);
+ }
+ if (opt_B && x < bss) {
+ if ((n = read(fd1, img, bpb.bps)) == -1)
+ fprintf(stderr, "%s\n", bname);
+ if (n != bpb.bps)
+ fprintf(stderr, "%s: can't read sector %u\n", bname, x);
+ } else
+ memset(img, 0, bpb.bps);
+ if (!lsn ||
+ (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+ x1 = BS_SIZE;
+ bsbpb = (struct bsbpb *)(img + x1);
+ mk2(bsbpb->bps, bpb.bps);
+ mk1(bsbpb->spc, bpb.spc);
+ mk2(bsbpb->res, bpb.res);
+ mk1(bsbpb->nft, bpb.nft);
+ mk2(bsbpb->rde, bpb.rde);
+ mk2(bsbpb->sec, bpb.sec);
+ mk1(bsbpb->mid, bpb.mid);
+ mk2(bsbpb->spf, bpb.spf);
+ mk2(bsbpb->spt, bpb.spt);
+ mk2(bsbpb->hds, bpb.hds);
+ mk4(bsbpb->hid, bpb.hid);
+ mk4(bsbpb->bsec, bpb.bsec);
+ x1 += BSBPB_SIZE;
+ if (fat == 32) {
+ bsxbpb = (struct bsxbpb *)(img + x1);
+ mk4(bsxbpb->bspf, bpb.bspf);
+ mk2(bsxbpb->xflg, 0);
+ mk2(bsxbpb->vers, 0);
+ mk4(bsxbpb->rdcl, bpb.rdcl);
+ mk2(bsxbpb->infs, bpb.infs);
+ mk2(bsxbpb->bkbs, bpb.bkbs);
+ x1 += BSXBPB_SIZE;
+ }
+ bsx = (struct bsx *)(img + x1);
+ mk1(bsx->sig, 0x29);
+ if (Iflag)
+ x = opt_I;
+ else
+ x = (((u_int)(1 + tm->tm_mon) << 8 |
+ (u_int)tm->tm_mday) +
+ ((u_int)tm->tm_sec << 8 |
+ (u_int)(tv.tv_usec / 10))) << 16 |
+ ((u_int)(1900 + tm->tm_year) +
+ ((u_int)tm->tm_hour << 8 |
+ (u_int)tm->tm_min));
+ mk4(bsx->volid, x);
+ mklabel(bsx->label, opt_L ? opt_L : "NO_NAME");
+ snprintf(buf, sizeof(buf), "FAT%u", fat);
+ setstr(bsx->type, buf, sizeof(bsx->type));
+ if (!opt_B) {
+ x1 += BSX_SIZE;
+ bs = (struct bs *)img;
+ mk1(bs->jmp[0], 0xeb);
+ mk1(bs->jmp[1], x1 - 2);
+ mk1(bs->jmp[2], 0x90);
+ setstr(bs->oem, opt_O ? opt_O : "NetBSD",
+ sizeof(bs->oem));
+ memcpy(img + x1, bootcode, sizeof(bootcode));
+ mk2(img + bpb.bps - 2, DOSMAGIC);
+ }
+ } else if (fat == 32 && bpb.infs != MAXU16 &&
+ (lsn == bpb.infs ||
+ (bpb.bkbs != MAXU16 &&
+ lsn == bpb.bkbs + bpb.infs))) {
+ mk4(img, 0x41615252);
+ mk4(img + bpb.bps - 28, 0x61417272);
+ mk4(img + bpb.bps - 24, 0xffffffff);
+ mk4(img + bpb.bps - 20, bpb.rdcl);
+ mk2(img + bpb.bps - 2, DOSMAGIC);
+ } else if (lsn >= bpb.res && lsn < dir &&
+ !((lsn - bpb.res) %
+ (bpb.spf ? bpb.spf : bpb.bspf))) {
+ mk1(img[0], bpb.mid);
+ for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
+ mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
+ } else if (lsn == dir && opt_L) {
+ de = (struct de *)img;
+ mklabel(de->namext, opt_L);
+ mk1(de->attr, 050);
+ x = (u_int)tm->tm_hour << 11 |
+ (u_int)tm->tm_min << 5 |
+ (u_int)tm->tm_sec >> 1;
+ mk2(de->time, x);
+ x = (u_int)(tm->tm_year - 80) << 9 |
+ (u_int)(tm->tm_mon + 1) << 5 |
+ (u_int)tm->tm_mday;
+ mk2(de->date, x);
+ }
+ if ((n = write(fd, img, bpb.bps)) == -1)
+ fprintf(stderr, "%s\n", fname);
+ if (n != bpb.bps)
+ fprintf(stderr, "%s: can't write sector %u\n", fname, lsn);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Print out BPB values.
+ */
+static void
+print_bpb(struct bpb *bpb)
+{
+ printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+ bpb->nft);
+ if (bpb->rde)
+ printf(" rde=%u", bpb->rde);
+ if (bpb->sec)
+ printf(" sec=%u", bpb->sec);
+ printf(" mid=%#x", bpb->mid);
+ if (bpb->spf)
+ printf(" spf=%u", bpb->spf);
+ printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+ if (bpb->bsec)
+ printf(" bsec=%u", bpb->bsec);
+ if (!bpb->spf) {
+ printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+ printf(" infs=");
+ printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+ printf(" bkbs=");
+ printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+ }
+ printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static u_int
+ckgeom(const char *fname, u_int val, const char *msg)
+{
+ if (!val)
+ fprintf(stderr, "%s: no default %s\n", fname, msg);
+ if (val > MAXU16)
+ fprintf(stderr, "%s: illegal %s\n", fname, msg);
+ return val;
+}
+
+/*
+ * Convert and check a numeric option argument.
+ */
+static u_int
+argtou(const char *arg, u_int lo, u_int hi, const char *msg)
+{
+ char *s;
+ u_long x;
+
+ errno = 0;
+ x = strtoul(arg, &s, 0);
+ if (errno || !*arg || *s || x < lo || x > hi)
+ fprintf(stderr, "%s: bad %s\n", arg, msg);
+ return x;
+}
+
+/*
+ * Check a volume label.
+ */
+static int
+oklabel(const char *src)
+{
+ int c, i;
+
+ for (i = 0; i <= 11; i++) {
+ c = (u_char)*src++;
+ if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+ break;
+ }
+ return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void
+mklabel(u_int8_t *dest, const char *src)
+{
+ int c, i;
+
+ for (i = 0; i < 11; i++) {
+ c = *src ? toupper((unsigned char)*src++) : ' ';
+ *dest++ = !i && c == '\xe5' ? 5 : c;
+ }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void
+setstr(u_int8_t *dest, const char *src, size_t len)
+{
+ while (len--)
+ *dest++ = *src ? *src++ : ' ';
+}
+
+/*
+ * Print usage message.
+ */
+static void
+usage(char* progname)
+{
+ fprintf(stderr,
+ "usage: %s [ -options ] special [disktype]\n", progname);
+ fprintf(stderr, "where the options are:\n");
+ fprintf(stderr, "\t-N don't create file system: "
+ "just print out parameters\n");
+ fprintf(stderr, "\t-B get bootstrap from file\n");
+ fprintf(stderr, "\t-F FAT type (12, 16, or 32)\n");
+ fprintf(stderr, "\t-I volume ID\n");
+ fprintf(stderr, "\t-L volume label\n");
+ fprintf(stderr, "\t-O OEM string\n");
+ fprintf(stderr, "\t-S bytes/sector\n");
+ fprintf(stderr, "\t-a sectors/FAT\n");
+ fprintf(stderr, "\t-b block size\n");
+ fprintf(stderr, "\t-c sectors/cluster\n");
+ fprintf(stderr, "\t-e root directory entries\n");
+ fprintf(stderr, "\t-h drive heads\n");
+ fprintf(stderr, "\t-i file system info sector\n");
+ fprintf(stderr, "\t-k backup boot sector\n");
+ fprintf(stderr, "\t-m media descriptor\n");
+ fprintf(stderr, "\t-n number of FATs\n");
+ fprintf(stderr, "\t-o hidden sectors\n");
+ fprintf(stderr, "\t-r reserved sectors\n");
+ fprintf(stderr, "\t-s file system size (sectors)\n");
+ fprintf(stderr, "\t-u sectors/track\n");
+ exit(1);
+}
+
+
diff --git a/toolbox/mount.c b/toolbox/mount.c
new file mode 100644
index 0000000..ef13e1f
--- /dev/null
+++ b/toolbox/mount.c
@@ -0,0 +1,273 @@
+/*
+ * mount.c, by rmk
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/loop.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+// FIXME - only one loop mount is supported at a time
+#define LOOP_DEVICE "/dev/block/loop0"
+
+struct mount_opts {
+ const char str[8];
+ unsigned long rwmask;
+ unsigned long rwset;
+ unsigned long rwnoset;
+};
+
+struct extra_opts {
+ char *str;
+ char *end;
+ int used_size;
+ int alloc_size;
+};
+
+/*
+ * These options define the function of "mount(2)".
+ */
+#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE)
+
+
+static const struct mount_opts options[] = {
+ /* name mask set noset */
+ { "async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS },
+ { "atime", MS_NOATIME, 0, MS_NOATIME },
+ { "bind", MS_TYPE, MS_BIND, 0, },
+ { "dev", MS_NODEV, 0, MS_NODEV },
+ { "diratime", MS_NODIRATIME, 0, MS_NODIRATIME },
+ { "dirsync", MS_DIRSYNC, MS_DIRSYNC, 0 },
+ { "exec", MS_NOEXEC, 0, MS_NOEXEC },
+ { "move", MS_TYPE, MS_MOVE, 0 },
+ { "recurse", MS_REC, MS_REC, 0 },
+ { "remount", MS_TYPE, MS_REMOUNT, 0 },
+ { "ro", MS_RDONLY, MS_RDONLY, 0 },
+ { "rw", MS_RDONLY, 0, MS_RDONLY },
+ { "suid", MS_NOSUID, 0, MS_NOSUID },
+ { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0 },
+ { "verbose", MS_VERBOSE, MS_VERBOSE, 0 },
+};
+
+static void add_extra_option(struct extra_opts *extra, char *s)
+{
+ int len = strlen(s);
+ int newlen = extra->used_size + len;
+
+ if (extra->str)
+ len++; /* +1 for ',' */
+
+ if (newlen >= extra->alloc_size) {
+ char *new;
+
+ new = realloc(extra->str, newlen + 1); /* +1 for NUL */
+ if (!new)
+ return;
+
+ extra->str = new;
+ extra->end = extra->str + extra->used_size;
+ extra->alloc_size = newlen;
+ }
+
+ if (extra->used_size) {
+ *extra->end = ',';
+ extra->end++;
+ }
+ strcpy(extra->end, s);
+ extra->used_size += len;
+
+}
+
+static unsigned long
+parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop)
+{
+ char *s;
+
+ *loop = 0;
+ while ((s = strsep(&arg, ",")) != NULL) {
+ char *opt = s;
+ unsigned int i;
+ int res, no = s[0] == 'n' && s[1] == 'o';
+
+ if (no)
+ s += 2;
+
+ if (strcmp(s, "loop") == 0) {
+ *loop = 1;
+ continue;
+ }
+ for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
+ res = strcmp(s, options[i].str);
+
+ if (res == 0) {
+ rwflag &= ~options[i].rwmask;
+ if (no)
+ rwflag |= options[i].rwnoset;
+ else
+ rwflag |= options[i].rwset;
+ }
+ if (res <= 0)
+ break;
+ }
+
+ if (res != 0 && s[0])
+ add_extra_option(extra, opt);
+ }
+
+ return rwflag;
+}
+
+static char *progname;
+
+static struct extra_opts extra;
+static unsigned long rwflag;
+
+static int
+do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop)
+{
+ char *s;
+ int error = 0;
+
+ if (loop) {
+ int file_fd, device_fd;
+
+ // FIXME - only one loop mount supported at a time
+ file_fd = open(dev, O_RDWR);
+ if (file_fd < -1) {
+ perror("open backing file failed");
+ return 1;
+ }
+ device_fd = open(LOOP_DEVICE, O_RDWR);
+ if (device_fd < -1) {
+ perror("open loop device failed");
+ close(file_fd);
+ return 1;
+ }
+ if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
+ perror("ioctl LOOP_SET_FD failed");
+ close(file_fd);
+ close(device_fd);
+ return 1;
+ }
+
+ close(file_fd);
+ close(device_fd);
+ dev = LOOP_DEVICE;
+ }
+
+ while ((s = strsep(&type, ",")) != NULL) {
+retry:
+ if (mount(dev, dir, s, rwflag, data) == -1) {
+ error = errno;
+ /*
+ * If the filesystem is not found, or the
+ * superblock is invalid, try the next.
+ */
+ if (error == ENODEV || error == EINVAL)
+ continue;
+
+ /*
+ * If we get EACCESS, and we're trying to
+ * mount readwrite and this isn't a remount,
+ * try read only.
+ */
+ if (error == EACCES &&
+ (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
+ rwflag |= MS_RDONLY;
+ goto retry;
+ }
+ break;
+ }
+ }
+
+ if (error) {
+ errno = error;
+ perror("mount");
+ return 255;
+ }
+
+ return 0;
+}
+
+static int print_mounts()
+{
+ FILE* f;
+ int length;
+ char buffer[100];
+
+ f = fopen("/proc/mounts", "r");
+ if (!f) {
+ fprintf(stdout, "could not open /proc/mounts\n");
+ return -1;
+ }
+
+ do {
+ length = fread(buffer, 1, 100, f);
+ if (length > 0)
+ fwrite(buffer, 1, length, stdout);
+ } while (length > 0);
+
+ fclose(f);
+ return 0;
+}
+
+int mount_main(int argc, char *argv[])
+{
+ char *type = NULL;
+ int c;
+ int loop;
+
+ progname = argv[0];
+ rwflag = MS_VERBOSE;
+
+ // mount with no arguments is equivalent to "cat /proc/mounts"
+ if (argc == 1) return print_mounts();
+
+ do {
+ c = getopt(argc, argv, "o:rt:w");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'o':
+ rwflag = parse_mount_options(optarg, rwflag, &extra, &loop);
+ break;
+ case 'r':
+ rwflag |= MS_RDONLY;
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case 'w':
+ rwflag &= ~MS_RDONLY;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ progname, optopt);
+ exit(1);
+ }
+ } while (1);
+
+ /*
+ * If remount, bind or move was specified, then we don't
+ * have a "type" as such. Use the dummy "none" type.
+ */
+ if (rwflag & MS_TYPE)
+ type = "none";
+
+ if (optind + 2 != argc || type == NULL) {
+ fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] "
+ "device directory\n", progname);
+ exit(1);
+ }
+
+ return do_mount(argv[optind], argv[optind + 1], type, rwflag,
+ extra.str, loop);
+}
diff --git a/toolbox/mv.c b/toolbox/mv.c
new file mode 100644
index 0000000..a5bc225
--- /dev/null
+++ b/toolbox/mv.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+int mv_main(int argc, char *argv[])
+{
+ const char* dest;
+ struct stat st;
+ int i;
+
+ if (argc < 3) {
+ fprintf(stderr,"USAGE: %s <source...> <destination>\n", argv[0]);
+ return -1;
+ }
+
+ /* check if destination exists */
+ dest = argv[argc - 1];
+ if (stat(dest, &st)) {
+ /* an error, unless the destination was missing */
+ if (errno != ENOENT) {
+ fprintf(stderr, "failed on %s - %s\n", dest, strerror(errno));
+ return -1;
+ }
+ st.st_mode = 0;
+ }
+
+ for (i = 1; i < argc - 1; i++) {
+ const char *source = argv[i];
+ char fullDest[PATH_MAX + 1 + PATH_MAX + 1];
+ /* assume we build "dest/source", and let rename() fail on pathsize */
+ if (strlen(dest) + 1 + strlen(source) + 1 > sizeof(fullDest)) {
+ fprintf(stderr, "path too long\n");
+ return -1;
+ }
+ strcpy(fullDest, dest);
+
+ /* if destination is a directory, concat the source file name */
+ if (S_ISDIR(st.st_mode)) {
+ const char *fileName = strrchr(source, '/');
+ if (fullDest[strlen(fullDest)-1] != '/') {
+ strcat(fullDest, "/");
+ }
+ strcat(fullDest, fileName ? fileName + 1 : source);
+ }
+
+ /* attempt to move it */
+ if (rename(source, fullDest)) {
+ fprintf(stderr, "failed on '%s' - %s\n", source, strerror(errno));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/toolbox/netstat.c b/toolbox/netstat.c
new file mode 100644
index 0000000..6404309
--- /dev/null
+++ b/toolbox/netstat.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef union iaddr iaddr;
+
+union iaddr {
+ unsigned u;
+ unsigned char b[4];
+};
+
+static const char *state2str(unsigned state)
+{
+ switch(state){
+ case 0x1: return "ESTABLISHED";
+ case 0x2: return "SYN_SENT";
+ case 0x3: return "SYN_RECV";
+ case 0x4: return "FIN_WAIT1";
+ case 0x5: return "FIN_WAIT2";
+ case 0x6: return "TIME_WAIT";
+ case 0x7: return "CLOSE";
+ case 0x8: return "CLOSE_WAIT";
+ case 0x9: return "LAST_ACK";
+ case 0xA: return "LISTEN";
+ case 0xB: return "CLOSING";
+ default: return "UNKNOWN";
+ }
+}
+
+void addr2str(iaddr addr, unsigned port, char *buf)
+{
+ if(port) {
+ snprintf(buf, 64, "%d.%d.%d.%d:%d",
+ addr.b[0], addr.b[1], addr.b[2], addr.b[3], port);
+ } else {
+ snprintf(buf, 64, "%d.%d.%d.%d:*",
+ addr.b[0], addr.b[1], addr.b[2], addr.b[3]);
+ }
+}
+
+int netstat_main(int argc, char *argv[])
+{
+ char buf[512];
+ char lip[64];
+ char rip[64];
+ iaddr laddr, raddr;
+ unsigned lport, rport, state, txq, rxq, num;
+ int n;
+ FILE *fp;
+
+ printf("Proto Recv-Q Send-Q Local Address Foreign Address State\n");
+
+ fp = fopen("/proc/net/tcp", "r");
+ if(fp != 0) {
+ fgets(buf, 512, fp);
+ while(fgets(buf, 512, fp)){
+ n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x",
+ &num, &laddr.u, &lport, &raddr.u, &rport,
+ &state, &txq, &rxq);
+ if(n == 8) {
+ addr2str(laddr, lport, lip);
+ addr2str(raddr, rport, rip);
+
+ printf("tcp %6d %6d %-22s %-22s %s\n",
+ txq, rxq, lip, rip,
+ state2str(state));
+ }
+ }
+ fclose(fp);
+ }
+ fp = fopen("/proc/net/udp", "r");
+ if(fp != 0) {
+ fgets(buf, 512, fp);
+ while(fgets(buf, 512, fp)){
+ n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x",
+ &num, &laddr.u, &lport, &raddr.u, &rport,
+ &state, &txq, &rxq);
+ if(n == 8) {
+ addr2str(laddr, lport, lip);
+ addr2str(raddr, rport, rip);
+
+ printf("udp %6d %6d %-22s %-22s\n",
+ txq, rxq, lip, rip);
+ }
+ }
+ fclose(fp);
+ }
+
+ return 0;
+}
diff --git a/toolbox/notify.c b/toolbox/notify.c
new file mode 100644
index 0000000..b1761d2
--- /dev/null
+++ b/toolbox/notify.c
@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <errno.h>
+
+int notify_main(int argc, char *argv[])
+{
+ int c;
+ int nfd, ffd;
+ int res;
+ char event_buf[512];
+ struct inotify_event *event;
+ int event_mask = IN_ALL_EVENTS;
+ int event_count = 1;
+ int print_files = 0;
+ int verbose = 2;
+ int width = 80;
+ char **file_names;
+ int file_count;
+ int id_offset = 0;
+ int i;
+ char *buf;
+
+ do {
+ c = getopt(argc, argv, "m:c:pv:w:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'm':
+ event_mask = strtol(optarg, NULL, 0);
+ break;
+ case 'c':
+ event_count = atoi(optarg);
+ break;
+ case 'p':
+ print_files = 1;
+ break;
+ case 'v':
+ verbose = atoi(optarg);
+ break;
+ case 'w':
+ width = atoi(optarg);
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if (argc <= optind) {
+ fprintf(stderr, "Usage: %s [-m eventmask] [-c count] [-p] [-v verbosity] path [path ...]\n", argv[0]);
+ return 1;
+ }
+
+ nfd = inotify_init();
+ if(nfd < 0) {
+ fprintf(stderr, "inotify_init failed, %s\n", strerror(errno));
+ return 1;
+ }
+ file_names = argv + optind;
+ file_count = argc - optind;
+ for(i = 0; i < file_count; i++) {
+ res = inotify_add_watch(nfd, file_names[i], event_mask);
+ if(res < 0) {
+ fprintf(stderr, "inotify_add_watch failed for %s, %s\n", file_names[i], strerror(errno));
+ return 1;
+ }
+ if(i == 0)
+ id_offset = -res;
+ if(res + id_offset != i) {
+ fprintf(stderr, "%s got unexpected id %d instead of %d\n", file_names[i], res, i);
+ return 1;
+ }
+ }
+
+ buf = malloc(width + 2);
+
+ while(1) {
+ int event_pos = 0;
+ res = read(nfd, event_buf, sizeof(event_buf));
+ if(res < (int)sizeof(*event)) {
+ if(errno == EINTR)
+ continue;
+ fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ return 1;
+ }
+ //printf("got %d bytes of event information\n", res);
+ while(res >= (int)sizeof(*event)) {
+ int event_size;
+ event = (struct inotify_event *)(event_buf + event_pos);
+ if(verbose >= 2)
+ printf("%s: %08x %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->cookie, event->len ? event->name : "");
+ else if(verbose >= 2)
+ printf("%s: %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->len ? event->name : "");
+ else if(verbose >= 1)
+ printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+ if(print_files && (event->mask & IN_MODIFY)) {
+ char filename[512];
+ ssize_t read_len;
+ char *display_name;
+ int buflen;
+ strcpy(filename, file_names[event->wd + id_offset]);
+ if(event->len) {
+ strcat(filename, "/");
+ strcat(filename, event->name);
+ }
+ ffd = open(filename, O_RDONLY);
+ display_name = (verbose >= 2 || event->len == 0) ? filename : event->name;
+ buflen = width - strlen(display_name);
+ read_len = read(ffd, buf, buflen);
+ if(read_len > 0) {
+ if(read_len < buflen && buf[read_len-1] != '\n') {
+ buf[read_len] = '\n';
+ read_len++;
+ }
+ if(read_len == buflen) {
+ buf[--read_len] = '\0';
+ buf[--read_len] = '\n';
+ buf[--read_len] = '.';
+ buf[--read_len] = '.';
+ buf[--read_len] = '.';
+ }
+ else {
+ buf[read_len] = '\0';
+ }
+ printf("%s: %s", display_name, buf);
+ }
+ close(ffd);
+ }
+ if(event_count && --event_count == 0)
+ return 0;
+ event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+ }
+
+ return 0;
+}
diff --git a/toolbox/powerd.c b/toolbox/powerd.c
new file mode 100644
index 0000000..1f29a8b
--- /dev/null
+++ b/toolbox/powerd.c
@@ -0,0 +1,441 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/select.h>
+#include <sys/inotify.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+//#include <linux/input.h> // this does not compile
+
+// from <linux/input.h>
+
+struct input_event {
+ struct timeval time;
+ __u16 type;
+ __u16 code;
+ __s32 value;
+};
+
+#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
+#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */
+#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */
+#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */
+
+#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
+#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
+#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
+
+#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */
+#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */
+#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */
+#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */
+
+#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */
+#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */
+#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */
+
+#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */
+#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */
+#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
+
+#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
+
+/*
+ * Event types
+ */
+
+#define EV_SYN 0x00
+#define EV_KEY 0x01
+#define EV_REL 0x02
+#define EV_ABS 0x03
+#define EV_MSC 0x04
+#define EV_SW 0x05
+#define EV_LED 0x11
+#define EV_SND 0x12
+#define EV_REP 0x14
+#define EV_FF 0x15
+#define EV_PWR 0x16
+#define EV_FF_STATUS 0x17
+#define EV_MAX 0x1f
+
+#define KEY_POWER 116
+#define KEY_SLEEP 142
+#define SW_0 0x00
+
+// end <linux/input.h>
+
+struct notify_entry {
+ int id;
+ int (*handler)(struct notify_entry *entry, struct inotify_event *event);
+ const char *filename;
+};
+
+int charging_state_notify_handler(struct notify_entry *entry, struct inotify_event *event)
+{
+ static int state = -1;
+ int last_state;
+ char buf[40];
+ int read_len;
+ int fd;
+
+ last_state = state;
+ fd = open(entry->filename, O_RDONLY);
+ read_len = read(fd, buf, sizeof(buf));
+ if(read_len > 0) {
+ //printf("charging_state_notify_handler: \"%s\"\n", buf);
+ state = !(strncmp(buf, "Unknown", 7) == 0
+ || strncmp(buf, "Discharging", 11) == 0);
+ }
+ close(fd);
+ //printf("charging_state_notify_handler: %d -> %d\n", last_state, state);
+ return state > last_state;
+}
+
+struct notify_entry watched_files[] = {
+ {
+ .filename = "/sys/android_power/charging_state",
+ .handler = charging_state_notify_handler
+ }
+};
+
+int call_notify_handler(struct inotify_event *event)
+{
+ unsigned int start, i;
+ start = event->wd - watched_files[0].id;
+ if(start >= ARRAY_SIZE(watched_files))
+ start = 0;
+ //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+ for(i = start; i < ARRAY_SIZE(watched_files); i++) {
+ if(event->wd == watched_files[i].id) {
+ if(watched_files[i].handler) {
+ return watched_files[i].handler(&watched_files[i], event);
+ }
+ return 1;
+ }
+ }
+ for(i = 0; i < start; i++) {
+ if(event->wd == watched_files[i].id) {
+ if(watched_files[i].handler) {
+ return watched_files[i].handler(&watched_files[i], event);
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int handle_inotify_event(int nfd)
+{
+ int res;
+ int wake_up = 0;
+ struct inotify_event *event;
+ char event_buf[512];
+ int event_pos = 0;
+
+ res = read(nfd, event_buf, sizeof(event_buf));
+ if(res < (int)sizeof(*event)) {
+ if(errno == EINTR)
+ return 0;
+ fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ return 0;
+ }
+ printf("got %d bytes of event information\n", res);
+ while(res >= (int)sizeof(*event)) {
+ int event_size;
+ event = (struct inotify_event *)(event_buf + event_pos);
+ wake_up |= call_notify_handler(event);
+ event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+ return wake_up;
+}
+
+int powerd_main(int argc, char *argv[])
+{
+ int c;
+ unsigned int i;
+ int res;
+ struct timeval tv;
+ int eventfd;
+ int notifyfd;
+ int powerfd;
+ int powerfd_is_sleep;
+ int user_activity_fd;
+ int acquire_partial_wake_lock_fd;
+ int acquire_full_wake_lock_fd;
+ int release_wake_lock_fd;
+ char *eventdev = "/dev/input/event0";
+ const char *android_sleepdev = "/sys/android_power/request_sleep";
+ const char *android_autooff_dev = "/sys/android_power/auto_off_timeout";
+ const char *android_user_activity_dev = "/sys/android_power/last_user_activity";
+ const char *android_acquire_partial_wake_lock_dev = "/sys/android_power/acquire_partial_wake_lock";
+ const char *android_acquire_full_wake_lock_dev = "/sys/android_power/acquire_full_wake_lock";
+ const char *android_release_wake_lock_dev = "/sys/android_power/release_wake_lock";
+ const char *powerdev = "/sys/power/state";
+ const char suspendstring[] = "standby";
+ const char wakelockstring[] = "powerd";
+ fd_set rfds;
+ struct input_event event;
+ struct input_event light_event;
+ struct input_event light_event2;
+ int gotkey = 1;
+ time_t idle_time = 5;
+ const char *idle_time_string = "5";
+ time_t lcd_light_time = 0;
+ time_t key_light_time = 0;
+ int verbose = 1;
+ int event_sleep = 0;
+ int got_power_key_down = 0;
+ struct timeval power_key_down_time = { 0, 0 };
+
+ light_event.type = EV_LED;
+ light_event.code = 4; // bright lcd backlight
+ light_event.value = 0; // light off -- sleep after timeout
+
+ light_event2.type = EV_LED;
+ light_event2.code = 8; // keyboard backlight
+ light_event2.value = 0; // light off -- sleep after timeout
+
+ do {
+ c = getopt(argc, argv, "e:ni:vql:k:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'e':
+ eventdev = optarg;
+ break;
+ case 'n':
+ gotkey = 0;
+ break;
+ case 'i':
+ idle_time = atoi(optarg);
+ idle_time_string = optarg;
+ break;
+ case 'v':
+ verbose = 2;
+ break;
+ case 'q':
+ verbose = 0;
+ break;
+ case 'l':
+ lcd_light_time = atoi(optarg);
+ break;
+ case 'k':
+ key_light_time = atoi(optarg);
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+ if(optind != argc) {
+ fprintf(stderr,"%s [-e eventdev]\n", argv[0]);
+ return 1;
+ }
+
+ eventfd = open(eventdev, O_RDWR | O_NONBLOCK);
+ if(eventfd < 0) {
+ fprintf(stderr, "could not open %s, %s\n", eventdev, strerror(errno));
+ return 1;
+ }
+ if(key_light_time >= lcd_light_time) {
+ lcd_light_time = key_light_time + 1;
+ fprintf(stderr,"lcd bright backlight time must be longer than keyboard backlight time.\n"
+ "Setting lcd bright backlight time to %ld seconds\n", lcd_light_time);
+ }
+
+ user_activity_fd = open(android_user_activity_dev, O_RDWR);
+ if(user_activity_fd >= 0) {
+ int auto_off_fd = open(android_autooff_dev, O_RDWR);
+ write(auto_off_fd, idle_time_string, strlen(idle_time_string));
+ close(auto_off_fd);
+ }
+
+ powerfd = open(android_sleepdev, O_RDWR);
+ if(powerfd >= 0) {
+ powerfd_is_sleep = 1;
+ if(verbose > 0)
+ printf("Using android sleep dev: %s\n", android_sleepdev);
+ }
+ else {
+ powerfd_is_sleep = 0;
+ powerfd = open(powerdev, O_RDWR);
+ if(powerfd >= 0) {
+ if(verbose > 0)
+ printf("Using linux power dev: %s\n", powerdev);
+ }
+ }
+ if(powerfd < 0) {
+ fprintf(stderr, "could not open %s, %s\n", powerdev, strerror(errno));
+ return 1;
+ }
+
+ notifyfd = inotify_init();
+ if(notifyfd < 0) {
+ fprintf(stderr, "inotify_init failed, %s\n", strerror(errno));
+ return 1;
+ }
+ fcntl(notifyfd, F_SETFL, O_NONBLOCK | fcntl(notifyfd, F_GETFL));
+ for(i = 0; i < ARRAY_SIZE(watched_files); i++) {
+ watched_files[i].id = inotify_add_watch(notifyfd, watched_files[i].filename, IN_MODIFY);
+ printf("Watching %s, id %d\n", watched_files[i].filename, watched_files[i].id);
+ }
+
+ acquire_partial_wake_lock_fd = open(android_acquire_partial_wake_lock_dev, O_RDWR);
+ acquire_full_wake_lock_fd = open(android_acquire_full_wake_lock_dev, O_RDWR);
+ release_wake_lock_fd = open(android_release_wake_lock_dev, O_RDWR);
+
+ if(user_activity_fd >= 0) {
+ idle_time = 60*60*24; // driver handles real timeout
+ }
+ if(gotkey) {
+ tv.tv_sec = idle_time;
+ tv.tv_usec = 0;
+ }
+ else {
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ }
+
+ while(1) {
+ FD_ZERO(&rfds);
+ //FD_SET(0, &rfds);
+ FD_SET(eventfd, &rfds);
+ FD_SET(notifyfd, &rfds);
+ res = select(((notifyfd > eventfd) ? notifyfd : eventfd) + 1, &rfds, NULL, NULL, &tv);
+ if(res < 0) {
+ fprintf(stderr, "select failed, %s\n", strerror(errno));
+ return 1;
+ }
+ if(res == 0) {
+ if(light_event2.value == 1)
+ goto light2_off;
+ if(light_event.value == 1)
+ goto light_off;
+ if(user_activity_fd < 0) {
+ if(gotkey && verbose > 0)
+ printf("Idle - sleep\n");
+ if(!gotkey && verbose > 1)
+ printf("Reenter sleep\n");
+ goto sleep;
+ }
+ else {
+ tv.tv_sec = 60*60*24;
+ tv.tv_usec = 0;
+ }
+ }
+ if(res > 0) {
+ //if(FD_ISSET(0, &rfds)) {
+ // printf("goto data on stdin quit\n");
+ // return 0;
+ //}
+ if(FD_ISSET(notifyfd, &rfds)) {
+ write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ if(handle_inotify_event(notifyfd) > 0) {
+ write(acquire_full_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ }
+ write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ }
+ if(FD_ISSET(eventfd, &rfds)) {
+ write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ res = read(eventfd, &event, sizeof(event));
+ if(res < (int)sizeof(event)) {
+ fprintf(stderr, "could not get event\n");
+ write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ return 1;
+ }
+ if(event.type == EV_PWR && event.code == KEY_SLEEP) {
+ event_sleep = event.value;
+ }
+ if(event.type == EV_KEY || (event.type == EV_SW && event.code == SW_0 && event.value == 1)) {
+ gotkey = 1;
+ if(user_activity_fd >= 0) {
+ char buf[32];
+ int len;
+ len = sprintf(buf, "%ld%06lu000", event.time.tv_sec, event.time.tv_usec);
+ write(user_activity_fd, buf, len);
+ }
+ if(lcd_light_time | key_light_time) {
+ tv.tv_sec = key_light_time;
+ light_event.value = 1;
+ write(eventfd, &light_event, sizeof(light_event));
+ light_event2.value = 1;
+ write(eventfd, &light_event2, sizeof(light_event2));
+ }
+ else {
+ tv.tv_sec = idle_time;
+ }
+ tv.tv_usec = 0;
+ if(verbose > 1)
+ printf("got %s %s %d%s\n", event.type == EV_KEY ? "key" : "switch", event.value ? "down" : "up", event.code, event_sleep ? " from sleep" : "");
+ if(event.code == KEY_POWER) {
+ if(event.value == 0) {
+ int tmp_got_power_key_down = got_power_key_down;
+ got_power_key_down = 0;
+ if(tmp_got_power_key_down) {
+ // power key released
+ if(verbose > 0)
+ printf("Power key released - sleep\n");
+ write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ goto sleep;
+ }
+ }
+ else if(event_sleep == 0) {
+ got_power_key_down = 1;
+ power_key_down_time = event.time;
+ }
+ }
+ }
+ if(event.type == EV_SW && event.code == SW_0 && event.value == 0) {
+ if(verbose > 0)
+ printf("Flip closed - sleep\n");
+ power_key_down_time = event.time;
+ write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ goto sleep;
+ }
+ write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+ }
+ }
+ if(0) {
+light_off:
+ light_event.value = 0;
+ write(eventfd, &light_event, sizeof(light_event));
+ tv.tv_sec = idle_time - lcd_light_time;
+ }
+ if(0) {
+light2_off:
+ light_event2.value = 0;
+ write(eventfd, &light_event2, sizeof(light_event2));
+ tv.tv_sec = lcd_light_time - key_light_time;
+ }
+ if(0) {
+sleep:
+ if(light_event.value == 1) {
+ light_event.value = 0;
+ write(eventfd, &light_event, sizeof(light_event));
+ light_event2.value = 0;
+ write(eventfd, &light_event2, sizeof(light_event2));
+ tv.tv_sec = idle_time - lcd_light_time;
+ }
+ if(powerfd_is_sleep) {
+ char buf[32];
+ int len;
+ len = sprintf(buf, "%ld%06lu000", power_key_down_time.tv_sec, power_key_down_time.tv_usec);
+ write(powerfd, buf, len);
+ }
+ else
+ write(powerfd, suspendstring, sizeof(suspendstring) - 1);
+ gotkey = 0;
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ }
+ }
+
+ return 0;
+}
diff --git a/toolbox/printenv.c b/toolbox/printenv.c
new file mode 100644
index 0000000..d5ea531
--- /dev/null
+++ b/toolbox/printenv.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+extern char** environ;
+
+int printenv_main (int argc, char **argv)
+{
+ char** e;
+ char* v;
+ int i;
+
+ if (argc == 1) {
+ e = environ;
+ while (*e) {
+ printf("%s\n", *e);
+ e++;
+ }
+ } else {
+ for (i=1; i<argc; i++) {
+ v = getenv(argv[i]);
+ if (v) {
+ printf("%s\n", v);
+ }
+ }
+ }
+
+ return 0;
+}
+
diff --git a/toolbox/ps.c b/toolbox/ps.c
new file mode 100644
index 0000000..3b86fa2
--- /dev/null
+++ b/toolbox/ps.c
@@ -0,0 +1,214 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <pwd.h>
+
+
+static char *nexttoksep(char **strp, char *sep)
+{
+ char *p = strsep(strp,sep);
+ return (p == 0) ? "" : p;
+}
+static char *nexttok(char **strp)
+{
+ return nexttoksep(strp, " ");
+}
+
+#define SHOW_PRIO 1
+#define SHOW_TIME 2
+
+static int display_flags = 0;
+
+static int ps_line(int pid, int tid, char *namefilter)
+{
+ char statline[1024];
+ char cmdline[1024];
+ char user[32];
+ struct stat stats;
+ int fd, r;
+ char *ptr, *name, *state;
+ int ppid, tty;
+ unsigned wchan, rss, vss, eip;
+ unsigned utime, stime;
+ int prio, nice, rtprio, sched;
+ struct passwd *pw;
+
+ sprintf(statline, "/proc/%d", pid);
+ stat(statline, &stats);
+
+ if(tid) {
+ sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+ cmdline[0] = 0;
+ } else {
+ sprintf(statline, "/proc/%d/stat", pid);
+ sprintf(cmdline, "/proc/%d/cmdline", pid);
+ fd = open(cmdline, O_RDONLY);
+ if(fd == 0) {
+ r = 0;
+ } else {
+ r = read(fd, cmdline, 1023);
+ close(fd);
+ if(r < 0) r = 0;
+ }
+ cmdline[r] = 0;
+ }
+
+ fd = open(statline, O_RDONLY);
+ if(fd == 0) return -1;
+ r = read(fd, statline, 1023);
+ close(fd);
+ if(r < 0) return -1;
+ statline[r] = 0;
+
+ ptr = statline;
+ nexttok(&ptr); // skip pid
+ ptr++; // skip "("
+
+ name = ptr;
+ ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')',
+ *ptr++ = '\0'; // and null-terminate name.
+
+ ptr++; // skip " "
+ state = nexttok(&ptr);
+ ppid = atoi(nexttok(&ptr));
+ nexttok(&ptr); // pgrp
+ nexttok(&ptr); // sid
+ tty = atoi(nexttok(&ptr));
+
+ nexttok(&ptr); // tpgid
+ nexttok(&ptr); // flags
+ nexttok(&ptr); // minflt
+ nexttok(&ptr); // cminflt
+ nexttok(&ptr); // majflt
+ nexttok(&ptr); // cmajflt
+#if 1
+ utime = atoi(nexttok(&ptr));
+ stime = atoi(nexttok(&ptr));
+#else
+ nexttok(&ptr); // utime
+ nexttok(&ptr); // stime
+#endif
+ nexttok(&ptr); // cutime
+ nexttok(&ptr); // cstime
+ prio = atoi(nexttok(&ptr));
+ nice = atoi(nexttok(&ptr));
+ nexttok(&ptr); // threads
+ nexttok(&ptr); // itrealvalue
+ nexttok(&ptr); // starttime
+ vss = strtoul(nexttok(&ptr), 0, 10); // vsize
+ rss = strtoul(nexttok(&ptr), 0, 10); // rss
+ nexttok(&ptr); // rlim
+ nexttok(&ptr); // startcode
+ nexttok(&ptr); // endcode
+ nexttok(&ptr); // startstack
+ nexttok(&ptr); // kstkesp
+ eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip
+ nexttok(&ptr); // signal
+ nexttok(&ptr); // blocked
+ nexttok(&ptr); // sigignore
+ nexttok(&ptr); // sigcatch
+ wchan = strtoul(nexttok(&ptr), 0, 10); // wchan
+ nexttok(&ptr); // nswap
+ nexttok(&ptr); // cnswap
+ nexttok(&ptr); // exit signal
+ nexttok(&ptr); // processor
+ rtprio = atoi(nexttok(&ptr)); // rt_priority
+ sched = atoi(nexttok(&ptr)); // scheduling policy
+
+ tty = atoi(nexttok(&ptr));
+
+ if(tid != 0) {
+ ppid = pid;
+ pid = tid;
+ }
+
+ pw = getpwuid(stats.st_uid);
+ if(pw == 0) {
+ sprintf(user,"%d",(int)stats.st_uid);
+ } else {
+ strcpy(user,pw->pw_name);
+ }
+
+ if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) {
+ printf("%-8s %-5d %-5d %-5d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+ if(display_flags&SHOW_PRIO)
+ printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+ printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name);
+ if(display_flags&SHOW_TIME)
+ printf(" (u:%d, s:%d)", utime, stime);
+ printf("\n");
+ }
+ return 0;
+}
+
+
+void ps_threads(int pid, char *namefilter)
+{
+ char tmp[128];
+ DIR *d;
+ struct dirent *de;
+
+ sprintf(tmp,"/proc/%d/task",pid);
+ d = opendir(tmp);
+ if(d == 0) return;
+
+ while((de = readdir(d)) != 0){
+ if(isdigit(de->d_name[0])){
+ int tid = atoi(de->d_name);
+ if(tid == pid) continue;
+ ps_line(pid, tid, namefilter);
+ }
+ }
+ closedir(d);
+}
+
+int ps_main(int argc, char **argv)
+{
+ DIR *d;
+ struct dirent *de;
+ char *namefilter = 0;
+ int pidfilter = 0;
+ int threads = 0;
+
+ d = opendir("/proc");
+ if(d == 0) return -1;
+
+ while(argc > 1){
+ if(!strcmp(argv[1],"-t")) {
+ threads = 1;
+ } else if(!strcmp(argv[1],"-x")) {
+ display_flags |= SHOW_TIME;
+ } else if(!strcmp(argv[1],"-p")) {
+ display_flags |= SHOW_PRIO;
+ } else if(isdigit(argv[1][0])){
+ pidfilter = atoi(argv[1]);
+ } else {
+ namefilter = argv[1];
+ }
+ argc--;
+ argv++;
+ }
+
+ printf("USER PID PPID VSIZE RSS %sWCHAN PC NAME\n",
+ (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"");
+ while((de = readdir(d)) != 0){
+ if(isdigit(de->d_name[0])){
+ int pid = atoi(de->d_name);
+ if(!pidfilter || (pidfilter == pid)) {
+ ps_line(pid, 0, namefilter);
+ if(threads) ps_threads(pid, namefilter);
+ }
+ }
+ }
+ closedir(d);
+ return 0;
+}
+
diff --git a/toolbox/r.c b/toolbox/r.c
new file mode 100644
index 0000000..5a82e20
--- /dev/null
+++ b/toolbox/r.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+
+static int usage()
+{
+ fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
+ return -1;
+}
+
+int r_main(int argc, char *argv[])
+{
+ int width = 4, set = 0, fd;
+ unsigned addr, value;
+ void *page;
+
+ if(argc < 2) return usage();
+
+ if(!strcmp(argv[1], "-b")) {
+ width = 1;
+ argc--;
+ argv++;
+ } else if(!strcmp(argv[1], "-s")) {
+ width = 2;
+ argc--;
+ argv++;
+ }
+
+ if(argc < 2) return usage();
+ addr = strtoul(argv[1], 0, 16);
+
+ if(argc > 2) {
+ set = 1;
+ value = strtoul(argv[2], 0, 16);
+ }
+
+ fd = open("/dev/mem", O_RDWR | O_SYNC);
+ if(fd < 0) {
+ fprintf(stderr,"cannot open /dev/mem\n");
+ return -1;
+ }
+
+ page = mmap(0, 8192, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, addr & (~4095));
+
+ if(page == MAP_FAILED){
+ fprintf(stderr,"cannot mmap region\n");
+ return -1;
+ }
+
+ switch(width){
+ case 4: {
+ unsigned *x = (unsigned*) (((unsigned) page) + (addr & 4095));
+ if(set) *x = value;
+ fprintf(stderr,"%08x: %08x\n", addr, *x);
+ break;
+ }
+ case 2: {
+ unsigned short *x = (unsigned short*) (((unsigned) page) + (addr & 4095));
+ if(set) *x = value;
+ fprintf(stderr,"%08x: %04x\n", addr, *x);
+ break;
+ }
+ case 1: {
+ unsigned char *x = (unsigned char*) (((unsigned) page) + (addr & 4095));
+ if(set) *x = value;
+ fprintf(stderr,"%08x: %02x\n", addr, *x);
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/toolbox/readtty.c b/toolbox/readtty.c
new file mode 100644
index 0000000..2b27548
--- /dev/null
+++ b/toolbox/readtty.c
@@ -0,0 +1,183 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+struct {
+ char key;
+ char *chars;
+} map[] = {
+ { '1', "_ -1?!,.:;\"'<=>()_" },
+ { '2', "Cabc2ABC" },
+ { '3', "Fdef3DEF" },
+ { '4', "Ighi4GHI" },
+ { '5', "Ljkl5JKL" },
+ { '6', "Omno6MNO" },
+ { '7', "Spqrs7PQRS" },
+ { '8', "Vtuv8TUV" },
+ { '9', "Zwxyz9WXYZ" },
+ { '0', "*+&0@/#*" },
+};
+
+char next_char(char key, char current)
+{
+ int i;
+ char *next;
+ for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+ if(key == map[i].key) {
+ next = strchr(map[i].chars, current);
+ if(next && next[1])
+ return next[1];
+ return map[i].chars[1];
+ }
+ }
+ return key;
+}
+
+char prev_char(char key, char current)
+{
+ int i;
+ char *next;
+ for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+ if(key == map[i].key) {
+ next = strchr(map[i].chars+1, current);
+ if(next && next[-1])
+ return next[-1];
+ return map[i].chars[1];
+ }
+ }
+ return key;
+}
+
+int readtty_main(int argc, char *argv[])
+{
+ int c;
+ //int flags;
+ char buf[1];
+ int res;
+ struct termios ttyarg;
+ struct termios savedttyarg;
+ int nonblock = 0;
+ int timeout = 0;
+ int flush = 0;
+ int phone = 0;
+ char *accept = NULL;
+ char *rejectstring = NULL;
+ char last_char_in = 0;
+ char current_char = 0;
+ char *exit_string = NULL;
+ int exit_match = 0;
+
+ do {
+ c = getopt(argc, argv, "nt:fa:r:pe:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 't':
+ timeout = atoi(optarg);
+ break;
+ case 'n':
+ nonblock = 1;
+ break;
+ case 'f':
+ flush = 1;
+ break;
+ case 'a':
+ accept = optarg;
+ break;
+ case 'r':
+ rejectstring = optarg;
+ break;
+ case 'p':
+ phone = 1;
+ break;
+ case 'e':
+ exit_string = optarg;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if(flush)
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ ioctl(STDIN_FILENO, TCGETS , &savedttyarg) ; /* set changed tty arguments */
+ ttyarg = savedttyarg;
+ ttyarg.c_cc[VMIN] = (timeout > 0 || nonblock) ? 0 : 1; /* minimum of 0 chars */
+ ttyarg.c_cc[VTIME] = timeout; /* wait max 15/10 sec */
+ ttyarg.c_iflag = BRKINT | ICRNL;
+ ttyarg.c_lflag &= ~(ECHO | ICANON);
+ ioctl(STDIN_FILENO, TCSETS , &ttyarg);
+
+ while (1) {
+ res = read(STDIN_FILENO, buf, 1);
+ if(res <= 0) {
+ if(phone) {
+ if(current_char) {
+ write(STDERR_FILENO, ¤t_char, 1);
+ write(STDOUT_FILENO, ¤t_char, 1);
+ if(exit_string && current_char == exit_string[exit_match]) {
+ exit_match++;
+ if(exit_string[exit_match] == '\0')
+ break;
+ }
+ else
+ exit_match = 0;
+ current_char = 0;
+ }
+ continue;
+ }
+ break;
+ }
+ if(accept && strchr(accept, buf[0]) == NULL) {
+ if(rejectstring) {
+ write(STDOUT_FILENO, rejectstring, strlen(rejectstring));
+ break;
+ }
+ if(flush)
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ continue;
+ }
+ if(phone) {
+ //if(!isprint(buf[0])) {
+ // fprintf(stderr, "got unprintable character 0x%x\n", buf[0]);
+ //}
+ if(buf[0] == '\0') {
+ if(current_char) {
+ current_char = prev_char(last_char_in, current_char);
+ write(STDERR_FILENO, ¤t_char, 1);
+ write(STDERR_FILENO, "\b", 1);
+ }
+ continue;
+ }
+ if(current_char && buf[0] != last_char_in) {
+ write(STDERR_FILENO, ¤t_char, 1);
+ write(STDOUT_FILENO, ¤t_char, 1);
+ if(exit_string && current_char == exit_string[exit_match]) {
+ exit_match++;
+ if(exit_string[exit_match] == '\0')
+ break;
+ }
+ else
+ exit_match = 0;
+ current_char = 0;
+ }
+ last_char_in = buf[0];
+ current_char = next_char(last_char_in, current_char);
+ write(STDERR_FILENO, ¤t_char, 1);
+ write(STDERR_FILENO, "\b", 1);
+ continue;
+ }
+ write(STDOUT_FILENO, buf, 1);
+ break;
+ }
+ ioctl(STDIN_FILENO, TCSETS , &savedttyarg) ; /* set changed tty arguments */
+
+ return 0;
+}
diff --git a/toolbox/reboot.c b/toolbox/reboot.c
new file mode 100644
index 0000000..aebe185
--- /dev/null
+++ b/toolbox/reboot.c
@@ -0,0 +1,56 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/reboot.h>
+#include <unistd.h>
+
+int reboot_main(int argc, char *argv[])
+{
+ int ret;
+ int nosync = 0;
+ int poweroff = 0;
+
+ opterr = 0;
+ do {
+ int c;
+
+ c = getopt(argc, argv, "np");
+
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 'n':
+ nosync = 1;
+ break;
+ case 'p':
+ poweroff = 1;
+ break;
+ case '?':
+ fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ } while (1);
+
+ if(argc > optind + 1) {
+ fprintf(stderr, "%s: too many arguments\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if(!nosync)
+ sync();
+
+ if(poweroff)
+ ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
+ else if(argc > optind)
+ ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, argv[optind]);
+ else
+ ret = reboot(RB_AUTOBOOT);
+ if(ret < 0) {
+ perror("reboot");
+ exit(EXIT_FAILURE);
+ }
+ fprintf(stderr, "reboot returned\n");
+ return 0;
+}
diff --git a/toolbox/renice.c b/toolbox/renice.c
new file mode 100644
index 0000000..978b329
--- /dev/null
+++ b/toolbox/renice.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+static void
+usage(const char *s)
+{
+ fprintf(stderr, "USAGE: %s [[-r] priority pids ...] [-g pid]\n", s);
+ exit(EXIT_FAILURE);
+}
+
+void print_prio(pid_t pid)
+{
+ int sched;
+ struct sched_param sp;
+
+ printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid));
+
+ printf("scheduling class: ");
+ sched = sched_getscheduler(pid);
+ switch (sched) {
+ case SCHED_FIFO:
+ printf("FIFO\n");
+ break;
+ case SCHED_RR:
+ printf("RR\n");
+ break;
+ case SCHED_OTHER:
+ printf("Normal\n");
+ break;
+ case -1:
+ perror("sched_getscheduler");
+ break;
+ default:
+ printf("Unknown\n");
+ }
+
+ sched_getparam(pid, &sp);
+ printf("RT prio: %d (of %d to %d)\n", sp.sched_priority,
+ sched_get_priority_min(sched), sched_get_priority_max(sched));
+}
+
+int renice_main(int argc, char *argv[])
+{
+ int prio;
+ int realtime = 0;
+ char *cmd = argv[0];
+
+ // consume command name
+ argc--;
+ argv++;
+
+ if (argc < 1)
+ usage(cmd);
+
+ if(strcmp("-r", argv[0]) == 0) {
+ // do realtime priority adjustment
+ realtime = 1;
+ argc--;
+ argv++;
+ }
+
+ if(strcmp("-g", argv[0]) == 0) {
+ if (argc < 2)
+ usage(cmd);
+ print_prio(atoi(argv[1]));
+ return 0;
+ }
+
+ if (argc < 1)
+ usage(cmd);
+
+ prio = atoi(argv[0]);
+ argc--;
+ argv++;
+
+ if (argc < 1)
+ usage(cmd);
+
+ while(argc) {
+ pid_t pid;
+
+ pid = atoi(argv[0]);
+ argc--;
+ argv++;
+
+ if (realtime) {
+ struct sched_param sp = { .sched_priority = prio };
+ int ret;
+
+ ret = sched_setscheduler(pid, SCHED_RR, &sp);
+ if (ret) {
+ perror("sched_set_scheduler");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ int ret;
+
+ ret = setpriority(PRIO_PROCESS, pid, prio);
+ if (ret) {
+ perror("setpriority");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
diff --git a/toolbox/rm.c b/toolbox/rm.c
new file mode 100644
index 0000000..bd66311
--- /dev/null
+++ b/toolbox/rm.c
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+static int usage()
+{
+ fprintf(stderr,"rm [-rR] <target>\n");
+ return -1;
+}
+
+/* return -1 on failure, with errno set to the first error */
+static int unlink_recursive(const char* name)
+{
+ struct stat st;
+ DIR *dir;
+ struct dirent *de;
+ int fail = 0;
+
+ /* is it a file or directory? */
+ if (lstat(name, &st) < 0)
+ return -1;
+
+ /* a file, so unlink it */
+ if (!S_ISDIR(st.st_mode))
+ return unlink(name);
+
+ /* a directory, so open handle */
+ dir = opendir(name);
+ if (dir == NULL)
+ return -1;
+
+ /* recurse over components */
+ errno = 0;
+ while ((de = readdir(dir)) != NULL) {
+ char dn[PATH_MAX];
+ if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+ continue;
+ sprintf(dn, "%s/%s", name, de->d_name);
+ if (unlink_recursive(dn) < 0) {
+ fail = 1;
+ break;
+ }
+ errno = 0;
+ }
+ /* in case readdir or unlink_recursive failed */
+ if (fail || errno < 0) {
+ int save = errno;
+ closedir(dir);
+ errno = save;
+ return -1;
+ }
+
+ /* close directory handle */
+ if (closedir(dir) < 0)
+ return -1;
+
+ /* delete target directory */
+ return rmdir(name);
+}
+
+int rm_main(int argc, char *argv[])
+{
+ int ret;
+ int i = 1;
+ int recursive = 0;
+
+ if (argc < 2)
+ return usage();
+
+ /* check if recursive */
+ if (argc >=2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "-R"))) {
+ recursive = 1;
+ i = 2;
+ }
+
+ /* loop over the file/directory args */
+ for (; i < argc; i++) {
+ int ret = recursive ? unlink_recursive(argv[i]) : unlink(argv[i]);
+ if (ret < 0) {
+ fprintf(stderr, "rm failed for %s, %s\n", argv[i], strerror(errno));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/toolbox/rmdir.c b/toolbox/rmdir.c
new file mode 100644
index 0000000..06f3df2
--- /dev/null
+++ b/toolbox/rmdir.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+ fprintf(stderr,"rmdir <directory>\n");
+ return -1;
+}
+
+int rmdir_main(int argc, char *argv[])
+{
+ int symbolic = 0;
+ int ret;
+ if(argc < 2) return usage();
+
+ while(argc > 1) {
+ argc--;
+ argv++;
+ ret = rmdir(argv[0]);
+ if(ret < 0) {
+ fprintf(stderr, "rmdir failed for %s, %s\n", argv[0], strerror(errno));
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/toolbox/rmmod.c b/toolbox/rmmod.c
new file mode 100644
index 0000000..7e10c06
--- /dev/null
+++ b/toolbox/rmmod.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <asm/unistd.h>
+
+extern int delete_module(const char *, unsigned int);
+
+int rmmod_main(int argc, char **argv)
+{
+ int ret;
+ char *modname, *dot;
+
+ /* make sure we've got an argument */
+ if (argc < 2) {
+ fprintf(stderr, "usage: rmmod <module>\n");
+ return -1;
+ }
+
+ /* if given /foo/bar/blah.ko, make a weak attempt
+ * to convert to "blah", just for convenience
+ */
+ modname = strrchr(argv[1], '/');
+ if (!modname)
+ modname = argv[1];
+ dot = strchr(argv[1], '.');
+ if (dot)
+ *dot = '\0';
+
+ /* pass it to the kernel */
+ ret = delete_module(modname, O_NONBLOCK | O_EXCL);
+ if (ret != 0) {
+ fprintf(stderr, "rmmod: delete_module '%s' failed (errno %d)\n",
+ modname, errno);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/toolbox/rotatefb.c b/toolbox/rotatefb.c
new file mode 100644
index 0000000..2ff4127
--- /dev/null
+++ b/toolbox/rotatefb.c
@@ -0,0 +1,71 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/fb.h>
+
+
+int rotatefb_main(int argc, char *argv[])
+{
+ int c;
+ char *fbdev = "/dev/graphics/fb0";
+ int rotation = 0;
+ int fd;
+ int res;
+ struct fb_var_screeninfo fbinfo;
+
+ do {
+ c = getopt(argc, argv, "d:");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'd':
+ fbdev = optarg;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+
+ if(optind + 1 != argc) {
+ fprintf(stderr, "%s: specify rotation\n", argv[0]);
+ exit(1);
+ }
+ rotation = atoi(argv[optind]);
+
+ fd = open(fbdev, O_RDWR);
+ if(fd < 0) {
+ fprintf(stderr, "cannot open %s\n", fbdev);
+ return 1;
+ }
+
+ res = ioctl(fd, FBIOGET_VSCREENINFO, &fbinfo);
+ if(res < 0) {
+ fprintf(stderr, "failed to get fbinfo: %s\n", strerror(errno));
+ return 1;
+ }
+ if((fbinfo.rotate ^ rotation) & 1) {
+ unsigned int xres = fbinfo.yres;
+ fbinfo.yres = fbinfo.xres;
+ fbinfo.xres = xres;
+ fbinfo.xres_virtual = fbinfo.xres;
+ fbinfo.yres_virtual = fbinfo.yres * 2;
+ if(fbinfo.yoffset == xres)
+ fbinfo.yoffset = fbinfo.yres;
+ }
+ fbinfo.rotate = rotation;
+ res = ioctl(fd, FBIOPUT_VSCREENINFO, &fbinfo);
+ if(res < 0) {
+ fprintf(stderr, "failed to set fbinfo: %s\n", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/toolbox/route.c b/toolbox/route.c
new file mode 100644
index 0000000..adf5c69
--- /dev/null
+++ b/toolbox/route.c
@@ -0,0 +1,97 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+#include <linux/route.h>
+
+static void die(const char *s)
+{
+ fprintf(stderr,"error: %s (%s)\n", s, strerror(errno));
+ exit(-1);
+}
+
+static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr)
+{
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = inet_addr(addr);
+}
+
+#define ADVANCE(argc, argv) do { argc--, argv++; } while(0)
+#define EXPECT_NEXT(argc, argv) do { \
+ ADVANCE(argc, argv); \
+ if (0 == argc) { \
+ errno = EINVAL; \
+ die("expecting one more argument"); \
+ } \
+} while(0)
+
+/* current support two kinds of usage */
+/* route add default dev wlan0 */
+/* route add default gw 192.168.20.1 dev wlan0 */
+
+int route_main(int argc, char *argv[])
+{
+ struct ifreq ifr;
+ int s,i;
+ struct rtentry rt;
+ struct sockaddr_in ina;
+
+ if(argc == 0) return 0;
+
+ strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+ ADVANCE(argc, argv);
+
+ if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ die("cannot open control socket\n");
+ }
+
+ while(argc > 0){
+ if(!strcmp(argv[0], "add")) {
+ EXPECT_NEXT(argc, argv);
+ if(!strcmp(argv[0], "default")) {
+ EXPECT_NEXT(argc, argv);
+ memset((char *) &rt, 0, sizeof(struct rtentry));
+ rt.rt_dst.sa_family = AF_INET;
+ if(!strcmp(argv[0], "dev")) {
+ EXPECT_NEXT(argc, argv);
+ rt.rt_flags = RTF_UP | RTF_HOST;
+ rt.rt_dev = argv[0];
+ if (ioctl(s, SIOCADDRT, &rt) < 0) die("SIOCADDRT");
+ }else if(!strcmp(argv[0], "gw")) {
+ EXPECT_NEXT(argc, argv);
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ init_sockaddr_in((struct sockaddr_in *)&(rt.rt_genmask), "0.0.0.0");
+ if(isdigit(argv[0][0])){
+ init_sockaddr_in((struct sockaddr_in *)&(rt.rt_gateway), argv[0]);
+ }else{
+ die("expecting an IP address for parameter \"gw\"");
+ }
+ EXPECT_NEXT(argc, argv);
+ if(!strcmp(argv[0], "dev")) {
+ EXPECT_NEXT(argc, argv);
+ rt.rt_dev = argv[0];
+ if (ioctl(s, SIOCADDRT, &rt) < 0){
+ die("SIOCADDRT");
+ }
+ }
+ }
+ }
+ }
+ ADVANCE(argc, argv);
+ }
+
+ return 0;
+}
+
diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c
new file mode 100644
index 0000000..c0e0141
--- /dev/null
+++ b/toolbox/schedtop.c
@@ -0,0 +1,335 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
+
+#include <pwd.h>
+
+struct thread_info {
+ int pid;
+ int tid;
+ char name[64];
+ uint64_t exec_time;
+ uint64_t delay_time;
+ uint32_t run_count;
+};
+
+struct thread_table {
+ size_t allocated;
+ size_t active;
+ struct thread_info *data;
+};
+
+enum {
+ FLAG_BATCH = 1U << 0,
+ FLAG_HIDE_IDLE = 1U << 1,
+ FLAG_SHOW_THREADS = 1U << 2,
+ FLAG_USE_ALTERNATE_SCREEN = 1U << 3,
+};
+
+static int time_dp = 9;
+static int time_div = 1;
+#define NS_TO_S_D(ns) \
+ (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div)
+
+struct thread_table processes;
+struct thread_table last_processes;
+struct thread_table threads;
+struct thread_table last_threads;
+
+static void grow_table(struct thread_table *table)
+{
+ size_t size = table->allocated;
+ struct thread_info *new_table;
+ if (size < 128)
+ size = 128;
+ else
+ size *= 2;
+
+ new_table = realloc(table->data, size * sizeof(*table->data));
+ if (new_table == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ table->data = new_table;
+ table->allocated = size;
+}
+
+static struct thread_info *get_item(struct thread_table *table)
+{
+ if (table->active >= table->allocated)
+ grow_table(table);
+ return table->data + table->active;
+}
+
+static void commit_item(struct thread_table *table)
+{
+ table->active++;
+}
+
+static int read_line(char *line, size_t line_size)
+{
+ int fd;
+ int len;
+ fd = open(line, O_RDONLY);
+ if(fd == 0)
+ return -1;
+ len = read(fd, line, line_size - 1);
+ close(fd);
+ if (len <= 0)
+ return -1;
+ line[len] = '\0';
+ return 0;
+}
+
+static void add_thread(int pid, int tid, struct thread_info *proc_info)
+{
+ char line[1024];
+ char *name, *name_end;
+ size_t name_len;
+ struct thread_info *info;
+ if(tid == 0)
+ info = get_item(&processes);
+ else
+ info = get_item(&threads);
+ info->pid = pid;
+ info->tid = tid;
+
+ if(tid)
+ sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid);
+ else
+ sprintf(line, "/proc/%d/schedstat", pid);
+ if (read_line(line, sizeof(line)))
+ return;
+ if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3)
+ return;
+ if (proc_info) {
+ proc_info->exec_time += info->exec_time;
+ proc_info->delay_time += info->delay_time;
+ proc_info->run_count += info->run_count;
+ }
+
+ name = NULL;
+ if (!tid) {
+ sprintf(line, "/proc/%d/cmdline", pid);
+ if (read_line(line, sizeof(line)) == 0 && line[0]) {
+ name = line;
+ name_len = strlen(name);
+ }
+ }
+ if (!name) {
+ if (tid)
+ sprintf(line, "/proc/%d/task/%d/stat", pid, tid);
+ else
+ sprintf(line, "/proc/%d/stat", pid);
+ if (read_line(line, sizeof(line)))
+ return;
+ name = strchr(line, '(');
+ if (name == NULL)
+ return;
+ name_end = strchr(name, ')');
+ if (name_end == NULL)
+ return;
+ name++;
+ name_len = name_end - name;
+ }
+ if (name_len >= sizeof(info->name))
+ name_len = sizeof(info->name) - 1;
+ memcpy(info->name, name, name_len);
+ info->name[name_len] = '\0';
+ if(tid == 0)
+ commit_item(&processes);
+ else
+ commit_item(&threads);
+}
+
+static void add_threads(int pid, struct thread_info *proc_info)
+{
+ char path[1024];
+ DIR *d;
+ struct dirent *de;
+ sprintf(path, "/proc/%d/task", pid);
+ d = opendir(path);
+ if(d == 0) return;
+ while((de = readdir(d)) != 0){
+ if(isdigit(de->d_name[0])){
+ int tid = atoi(de->d_name);
+ add_thread(pid, tid, proc_info);
+ }
+ }
+ closedir(d);
+}
+
+static void print_threads(int pid, uint32_t flags)
+{
+ size_t i, j;
+ for (i = 0; i < last_threads.active; i++) {
+ int epid = last_threads.data[i].pid;
+ int tid = last_threads.data[i].tid;
+ if (epid != pid)
+ continue;
+ for (j = 0; j < threads.active; j++)
+ if (tid == threads.data[j].tid)
+ break;
+ if (j == threads.active)
+ printf(" %5u died\n", tid);
+ else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count)
+ printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", tid,
+ NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time),
+ NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time),
+ threads.data[j].run_count - last_threads.data[i].run_count,
+ NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time),
+ threads.data[j].run_count, threads.data[j].name);
+ }
+}
+
+static void update_table(DIR *d, uint32_t flags)
+{
+ size_t i, j;
+ struct dirent *de;
+
+ rewinddir(d);
+ while((de = readdir(d)) != 0){
+ if(isdigit(de->d_name[0])){
+ int pid = atoi(de->d_name);
+ struct thread_info *proc_info;
+ add_thread(pid, 0, NULL);
+ proc_info = &processes.data[processes.active - 1];
+ proc_info->exec_time = 0;
+ proc_info->delay_time = 0;
+ proc_info->run_count = 0;
+ add_threads(pid, proc_info);
+ }
+ }
+ if (!(flags & FLAG_BATCH))
+ printf("\e[H\e[0J");
+ printf("Processes: %d, Threads %d\n", processes.active, threads.active);
+ switch (time_dp) {
+ case 3:
+ printf(" TID --- SINCE LAST ---- ---------- TOTAL ----------\n");
+ printf(" PID EXEC_T DELAY SCHED EXEC_TIME DELAY_TIM SCHED NAME\n");
+ break;
+ case 6:
+ printf(" TID ------ SINCE LAST ------- ------------ TOTAL -----------\n");
+ printf(" PID EXEC_TIME DELAY_TIM SCHED EXEC_TIME DELAY_TIME SCHED NAME\n");
+ break;
+ default:
+ printf(" TID -------- SINCE LAST -------- ------------- TOTAL -------------\n");
+ printf(" PID EXEC_TIME DELAY_TIME SCHED EXEC_TIME DELAY_TIME SCHED NAME\n");
+ break;
+ }
+ for (i = 0; i < last_processes.active; i++) {
+ int pid = last_processes.data[i].pid;
+ int tid = last_processes.data[i].tid;
+ for (j = 0; j < processes.active; j++)
+ if (pid == processes.data[j].pid)
+ break;
+ if (j == processes.active)
+ printf("%5u died\n", pid);
+ else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) {
+ printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid,
+ NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time),
+ NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time),
+ processes.data[j].run_count - last_processes.data[i].run_count,
+ NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time),
+ processes.data[j].run_count, processes.data[j].name);
+ if (flags & FLAG_SHOW_THREADS)
+ print_threads(pid, flags);
+ }
+ }
+
+ {
+ struct thread_table tmp;
+ tmp = last_processes;
+ last_processes = processes;
+ processes = tmp;
+ processes.active = 0;
+ tmp = last_threads;
+ last_threads = threads;
+ threads = tmp;
+ threads.active = 0;
+ }
+}
+
+void
+sig_abort(int signum)
+{
+ printf("\e[?47l");
+ exit(0);
+}
+
+
+int schedtop_main(int argc, char **argv)
+{
+ int c;
+ DIR *d;
+ struct dirent *de;
+ char *namefilter = 0;
+ int pidfilter = 0;
+ uint32_t flags = 0;
+ int delay = 3000000;
+ float delay_f;
+
+ while(1) {
+ c = getopt(argc, argv, "d:ibtamun");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'd':
+ delay_f = atof(optarg);
+ delay = delay_f * 1000000;
+ break;
+ case 'b':
+ flags |= FLAG_BATCH;
+ break;
+ case 'i':
+ flags |= FLAG_HIDE_IDLE;
+ break;
+ case 't':
+ flags |= FLAG_SHOW_THREADS;
+ break;
+ case 'a':
+ flags |= FLAG_USE_ALTERNATE_SCREEN;
+ break;
+ case 'm':
+ time_dp = 3;
+ time_div = 1000000;
+ break;
+ case 'u':
+ time_dp = 6;
+ time_div = 1000;
+ break;
+ case 'n':
+ time_dp = 9;
+ time_div = 1;
+ break;
+ }
+ }
+
+ d = opendir("/proc");
+ if(d == 0) return -1;
+
+ if (!(flags & FLAG_BATCH)) {
+ if(flags & FLAG_USE_ALTERNATE_SCREEN) {
+ signal(SIGINT, sig_abort);
+ signal(SIGPIPE, sig_abort);
+ signal(SIGTERM, sig_abort);
+ printf("\e7\e[?47h");
+ }
+ printf("\e[2J");
+ }
+ while (1) {
+ update_table(d, flags);
+ usleep(delay);
+ }
+ closedir(d);
+ return 0;
+}
+
diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c
new file mode 100644
index 0000000..1608e6c
--- /dev/null
+++ b/toolbox/sendevent.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+//#include <linux/input.h> // this does not compile
+#include <errno.h>
+
+
+// from <linux/input.h>
+
+struct input_event {
+ struct timeval time;
+ __u16 type;
+ __u16 code;
+ __s32 value;
+};
+
+#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
+#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */
+#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */
+#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */
+
+#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
+#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
+#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
+
+#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */
+#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */
+#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */
+#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */
+
+#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */
+#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */
+#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */
+
+#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */
+#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */
+#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
+
+#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
+
+// end <linux/input.h>
+
+
+
+int sendevent_main(int argc, char *argv[])
+{
+ int i;
+ int fd;
+ int ret;
+ int version;
+ struct input_event event;
+
+ if(argc != 5) {
+ fprintf(stderr, "use: %s device type code value\n", argv[0]);
+ return 1;
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if(fd < 0) {
+ fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+ return 1;
+ }
+ if (ioctl(fd, EVIOCGVERSION, &version)) {
+ fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno));
+ return 1;
+ }
+ memset(&event, 0, sizeof(event));
+ event.type = atoi(argv[2]);
+ event.code = atoi(argv[3]);
+ event.value = atoi(argv[4]);
+ ret = write(fd, &event, sizeof(event));
+ if(ret < sizeof(event)) {
+ fprintf(stderr, "write event failed, %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
diff --git a/toolbox/setconsole.c b/toolbox/setconsole.c
new file mode 100644
index 0000000..b0ce13f
--- /dev/null
+++ b/toolbox/setconsole.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+#include <pthread.h>
+
+static int activate_thread_switch_vc;
+static void *activate_thread(void *arg)
+{
+ int res;
+ int fd = (int)arg;
+ while(activate_thread_switch_vc >= 0) {
+ do {
+ res = ioctl(fd, VT_ACTIVATE, (void*)activate_thread_switch_vc);
+ } while(res < 0 && errno == EINTR);
+ if (res < 0) {
+ fprintf(stderr, "ioctl( vcfd, VT_ACTIVATE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), activate_thread_switch_vc);
+ }
+ if(activate_thread_switch_vc >= 0)
+ sleep(1);
+ }
+ return NULL;
+}
+
+
+int setconsole_main(int argc, char *argv[])
+{
+ int c;
+ int fd;
+ int res;
+
+ int mode = -1;
+ int new_vc = 0;
+ int close_vc = 0;
+ int switch_vc = -1;
+ int printvc = 0;
+ char *ttydev = "/dev/tty0";
+
+ do {
+ c = getopt(argc, argv, "d:gtncv:poh");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'd':
+ ttydev = optarg;
+ break;
+ case 'g':
+ if(mode == KD_TEXT) {
+ fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]);
+ exit(1);
+ }
+ mode = KD_GRAPHICS;
+ break;
+ case 't':
+ if(mode == KD_GRAPHICS) {
+ fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]);
+ exit(1);
+ }
+ mode = KD_TEXT;
+ break;
+ case 'n':
+ new_vc = 1;
+ break;
+ case 'c':
+ close_vc = 1;
+ break;
+ case 'v':
+ switch_vc = atoi(optarg);
+ break;
+ case 'p':
+ printvc |= 1;
+ break;
+ case 'o':
+ printvc |= 2;
+ break;
+ case 'h':
+ fprintf(stderr, "%s [-d <dev>] [-v <vc>] [-gtncpoh]\n"
+ " -d <dev> Use <dev> instead of /dev/tty0\n"
+ " -v <vc> Switch to virtual console <vc>\n"
+ " -g Switch to graphics mode\n"
+ " -t Switch to text mode\n"
+ " -n Create and switch to new virtual console\n"
+ " -c Close unused virtual consoles\n"
+ " -p Print new virtual console\n"
+ " -o Print old virtual console\n"
+ " -h Print help\n", argv[0]);
+ return -1;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+ if(mode == -1 && new_vc == 0 && close_vc == 0 && switch_vc == -1 && printvc == 0) {
+ fprintf(stderr,"%s [-d <dev>] [-v <vc>] [-gtncpoh]\n", argv[0]);
+ return -1;
+ }
+
+ fd = open(ttydev, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s\n", ttydev);
+ return -1;
+ }
+
+ if ((printvc && !new_vc) || (printvc & 2)) {
+ struct vt_stat vs;
+
+ res = ioctl(fd, VT_GETSTATE, &vs);
+ if (res < 0) {
+ fprintf(stderr, "ioctl(vcfd, VT_GETSTATE, &vs) failed, %d\n", res);
+ }
+ printf("%d\n", vs.v_active);
+ }
+
+ if (new_vc) {
+ int vtnum;
+ res = ioctl(fd, VT_OPENQRY, &vtnum);
+ if (res < 0 || vtnum == -1) {
+ fprintf(stderr, "ioctl(vcfd, VT_OPENQRY, &vtnum) failed, res %d, vtnum %d\n", res, vtnum);
+ }
+ switch_vc = vtnum;
+ }
+ if (switch_vc != -1) {
+ pthread_t thread;
+ pthread_attr_t attr;
+ activate_thread_switch_vc = switch_vc;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_create(&thread, &attr, activate_thread, (void*)fd);
+
+ do {
+ res = ioctl(fd, VT_WAITACTIVE, (void*)switch_vc);
+ } while(res < 0 && errno == EINTR);
+ activate_thread_switch_vc = -1;
+ if (res < 0) {
+ fprintf(stderr, "ioctl( vcfd, VT_WAITACTIVE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), switch_vc);
+ }
+ if(printvc & 1)
+ printf("%d\n", switch_vc);
+
+ close(fd);
+ fd = open(ttydev, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s\n", ttydev);
+ return -1;
+ }
+ }
+ if (close_vc) {
+ res = ioctl(fd, VT_DISALLOCATE, 0);
+ if (res < 0) {
+ fprintf(stderr, "ioctl(vcfd, VT_DISALLOCATE, 0) failed, %d\n", res);
+ }
+ }
+ if (mode != -1) {
+ if (ioctl(fd, KDSETMODE, (void*)mode) < 0) {
+ fprintf(stderr, "KDSETMODE %d failed\n", mode);
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/toolbox/setkey.c b/toolbox/setkey.c
new file mode 100644
index 0000000..1ff2774
--- /dev/null
+++ b/toolbox/setkey.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+
+static void setkey_usage(char *argv[])
+{
+ fprintf(stderr, "%s [-t <table>] [-k <index>] [-v value] [-r] [-h]\n"
+ " -t <table> Select table\n"
+ " -k <index> Select key\n"
+ " -v <value> Set entry\n"
+ " -r Read current entry\n"
+ " -h Print help\n", argv[0]);
+}
+
+#define TTYDEV "/dev/tty0"
+
+int setkey_main(int argc, char *argv[])
+{
+ int fd;
+ struct kbentry kbe;
+ int did_something = 0;
+
+ kbe.kb_table = 0;
+ kbe.kb_index = -1;
+ kbe.kb_value = 0;
+
+ fd = open(TTYDEV, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "open %s: %s\n", TTYDEV, strerror(errno));
+ return 1;
+ }
+
+ do {
+ int c, ret;
+
+ c = getopt(argc, argv, "t:k:v:hr");
+ if (c == EOF)
+ break;
+
+ switch (c) {
+ case 't':
+ kbe.kb_table = strtol(optarg, NULL, 0);
+ break;
+ case 'k':
+ kbe.kb_index = strtol(optarg, NULL, 0);
+ break;
+ case 'v':
+ kbe.kb_value = strtol(optarg, NULL, 0);
+ ret = ioctl(fd, KDSKBENT, &kbe);
+ if (ret < 0) {
+ fprintf(stderr, "KDSKBENT %d %d %d failed: %s\n",
+ kbe.kb_table, kbe.kb_index, kbe.kb_value,
+ strerror(errno));
+ return 1;
+ }
+ did_something = 1;
+ break;
+ case 'r':
+ ret = ioctl(fd, KDGKBENT, &kbe);
+ if (ret < 0) {
+ fprintf(stderr, "KDGKBENT %d %d failed: %s\n",
+ kbe.kb_table, kbe.kb_index, strerror(errno));
+ return 1;
+ }
+ printf("0x%x 0x%x 0x%x\n",
+ kbe.kb_table, kbe.kb_index, kbe.kb_value);
+ did_something = 1;
+ break;
+ case 'h':
+ setkey_usage(argv);
+ return 1;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ return 1;
+ }
+ } while (1);
+
+ if(optind != argc || !did_something) {
+ setkey_usage(argv);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/toolbox/setprop.c b/toolbox/setprop.c
new file mode 100644
index 0000000..63ad2b4
--- /dev/null
+++ b/toolbox/setprop.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include <cutils/properties.h>
+
+int setprop_main(int argc, char *argv[])
+{
+ if(argc != 3) {
+ fprintf(stderr,"usage: setprop <key> <value>\n");
+ return 1;
+ }
+
+ if(property_set(argv[1], argv[2])){
+ fprintf(stderr,"could not set property\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/toolbox/sleep.c b/toolbox/sleep.c
new file mode 100644
index 0000000..c09ae03
--- /dev/null
+++ b/toolbox/sleep.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static void
+usage(const char *s)
+{
+ fprintf(stderr, "USAGE: %s SECONDS\n", s);
+ exit(-1);
+}
+
+int sleep_main(int argc, char *argv[])
+{
+ unsigned long seconds;
+ char *endptr;
+
+ if (argc != 2) {
+ usage(argv[0]);
+ }
+
+ seconds = strtoul(argv[1], &endptr, 10);
+
+ if (endptr == argv[1]) {
+ usage(argv[0]);
+ }
+
+
+ sleep((unsigned int)seconds);
+
+ return 0;
+}
+
+
diff --git a/toolbox/smd.c b/toolbox/smd.c
new file mode 100644
index 0000000..65ff994
--- /dev/null
+++ b/toolbox/smd.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int smd_main(int argc, char **argv)
+{
+ int fd, len, r, port = 0;
+ char devname[32];
+ argc--;
+ argv++;
+
+ if((argc > 0) && (argv[0][0] == '-')) {
+ port = atoi(argv[0] + 1);
+ argc--;
+ argv++;
+ }
+
+ sprintf(devname,"/dev/smd%d",port);
+ fd = open(devname, O_WRONLY);
+ if(fd < 0) {
+ fprintf(stderr,"failed to open smd0 - %s\n",
+ strerror(errno));
+ return -1;
+ }
+ while(argc > 0) {
+ len = strlen(argv[0]);
+ r = write(fd, argv[0], len);
+ if(r != len) {
+ fprintf(stderr,"failed to write smd0 (%d) %s\n",
+ r, strerror(errno));
+ return -1;
+ }
+ argc--;
+ argv++;
+ write(fd, argc ? " " : "\r", 1);
+ }
+ close(fd);
+ return 0;
+}
diff --git a/toolbox/start.c b/toolbox/start.c
new file mode 100644
index 0000000..3bd9fbb
--- /dev/null
+++ b/toolbox/start.c
@@ -0,0 +1,20 @@
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+int start_main(int argc, char *argv[])
+{
+ char buf[1024];
+ if(argc > 1) {
+ property_set("ctl.start", argv[1]);
+ } else {
+ /* default to "start zygote" "start runtime" */
+ property_set("ctl.start", "zygote");
+ property_set("ctl.start", "runtime");
+ }
+
+ return 0;
+}
diff --git a/toolbox/stop.c b/toolbox/stop.c
new file mode 100644
index 0000000..05baffd
--- /dev/null
+++ b/toolbox/stop.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+
+int stop_main(int argc, char *argv[])
+{
+ char buf[1024];
+
+ if(argc > 1) {
+ property_set("ctl.stop", argv[1]);
+ } else{
+ /* default to "stop runtime" "stop zygote" */
+ property_set("ctl.stop", "runtime");
+ property_set("ctl.stop", "zygote");
+ }
+
+ return 0;
+}
+
diff --git a/toolbox/sync.c b/toolbox/sync.c
new file mode 100644
index 0000000..8284276
--- /dev/null
+++ b/toolbox/sync.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int sync_main(int argc, char **argv)
+{
+ sync();
+ return 0;
+}
diff --git a/toolbox/syren.c b/toolbox/syren.c
new file mode 100644
index 0000000..06e329e
--- /dev/null
+++ b/toolbox/syren.c
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+/* ioctl crap */
+#define SYREN_RD 101
+#define SYREN_WR 102
+#define SYREN_OLD_RD 108
+#define SYREN_OLD_WR 109
+
+struct syren_io_args {
+ unsigned long page;
+ unsigned long addr;
+ unsigned long value;
+};
+
+typedef struct {
+ u_char page;
+ u_char addr;
+ const char *name;
+} syren_reg;
+
+static syren_reg registers[] = {
+ { 0, 0x04, "TOGBR1" },
+ { 0, 0x05, "TOGBR2" },
+ { 0, 0x06, "VBDCTRL" },
+ { 1, 0x07, "VBUCTRL" },
+ { 1, 0x08, "VBCTRL" },
+ { 1, 0x09, "PWDNRG" },
+ { 1, 0x0a, "VBPOP" },
+ { 1, 0x0b, "VBCTRL2" },
+ { 1, 0x0f, "VAUDCTRL" },
+ { 1, 0x10, "VAUSCTRL" },
+ { 1, 0x11, "VAUOCTRL" },
+ { 1, 0x12, "VAUDPLL" },
+ { 1, 0x17, "VRPCSIMR" },
+ { 0, 0, 0 }
+};
+
+static syren_reg *find_reg(const char *name)
+{
+ int i;
+
+ for (i = 0; registers[i].name != 0; i++) {
+ if (!strcasecmp(registers[i].name, name))
+ return ®isters[i];
+ }
+
+ return NULL;
+}
+
+static int usage(void)
+{
+ fprintf(stderr, "usage: syren [r/w] [REGNAME | page:addr] (value)\n");
+ return 1;
+}
+
+int
+syren_main(int argc, char **argv)
+{
+ int cmd = -1;
+ syren_reg *r;
+ struct syren_io_args sio;
+ char name[32];
+ int fd;
+
+ if (argc < 3) {
+ return usage();
+ }
+
+ switch(argv[1][0]) {
+ case 'r':
+ cmd = SYREN_RD;
+ break;
+ case 'w':
+ cmd = SYREN_WR;
+ break;
+ case 'R':
+ cmd = SYREN_OLD_RD;
+ break;
+ case 'W':
+ cmd = SYREN_OLD_WR;
+ break;
+ default:
+ return usage();
+ }
+
+ if (cmd == SYREN_WR || cmd == SYREN_OLD_WR) {
+ if (argc < 4)
+ return usage();
+ sio.value = strtoul(argv[3], 0, 0);
+ }
+
+ fd = open("/dev/eac", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "can't open /dev/eac\n");
+ return 1;
+ }
+
+ if (strcasecmp(argv[2], "all") == 0) {
+ int i;
+ if (cmd != SYREN_RD && cmd != SYREN_OLD_RD) {
+ fprintf(stderr, "can only read all registers\n");
+ return 1;
+ }
+
+ for (i = 0; registers[i].name; i++) {
+ sio.page = registers[i].page;
+ sio.addr = registers[i].addr;
+ if (ioctl(fd, cmd, &sio) < 0) {
+ fprintf(stderr, "%s: error\n", registers[i].name);
+ } else {
+ fprintf(stderr, "%s: %04x\n", registers[i].name, sio.value);
+ }
+ }
+
+ close(fd);
+ return 0;
+ }
+
+ r = find_reg(argv[2]);
+ if (r == NULL) {
+ strcpy(name, argv[2]);
+ char *addr_str = strchr(argv[2], ':');
+ if (addr_str == NULL)
+ return usage();
+ *addr_str++ = 0;
+ sio.page = strtoul(argv[2], 0, 0);
+ sio.addr = strtoul(addr_str, 0, 0);
+ } else {
+ strcpy(name, r->name);
+ sio.page = r->page;
+ sio.addr = r->addr;
+ }
+
+ if (ioctl(fd, cmd, &sio) < 0) {
+ fprintf(stderr, "ioctl(%d) failed\n", cmd);
+ return 1;
+ }
+
+ if (cmd == SYREN_RD || cmd == SYREN_OLD_RD) {
+ printf("%s: %04x\n", name, sio.value);
+ } else {
+ printf("wrote %04x to %s\n", sio.value, name);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c
new file mode 100644
index 0000000..0eac390
--- /dev/null
+++ b/toolbox/toolbox.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int, char **);
+
+static int toolbox_main(int argc, char **argv)
+{
+ // "toolbox foo ..." is equivalent to "foo ..."
+ if (argc > 1) {
+ return main(argc - 1, argv + 1);
+ } else {
+ printf("Toolbox!\n");
+ return 0;
+ }
+}
+
+#define TOOL(name) int name##_main(int, char**);
+#include "tools.h"
+#undef TOOL
+
+static struct
+{
+ const char *name;
+ int (*func)(int, char**);
+} tools[] = {
+ { "toolbox", toolbox_main },
+#define TOOL(name) { #name, name##_main },
+#include "tools.h"
+#undef TOOL
+ { 0, 0 },
+};
+
+int main(int argc, char **argv)
+{
+ int i;
+ char *name = argv[0];
+
+ if((argc > 1) && (argv[1][0] == '@')) {
+ name = argv[1] + 1;
+ argc--;
+ argv++;
+ } else {
+ char *cmd = strrchr(argv[0], '/');
+ if (cmd)
+ name = cmd + 1;
+ }
+
+ for(i = 0; tools[i].name; i++){
+ if(!strcmp(tools[i].name, name)){
+ return tools[i].func(argc, argv);
+ }
+ }
+
+ printf("%s: no such tool\n", argv[0]);
+ return -1;
+}
diff --git a/toolbox/top.c b/toolbox/top.c
new file mode 100644
index 0000000..dcc0843
--- /dev/null
+++ b/toolbox/top.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+struct cpu_info {
+ long unsigned utime, ntime, stime, itime;
+ long unsigned iowtime, irqtime, sirqtime;
+};
+
+#define PROC_NAME_LEN 64
+#define THREAD_NAME_LEN 32
+
+struct proc_info {
+ struct proc_info *next;
+ pid_t pid;
+ pid_t tid;
+ uid_t uid;
+ gid_t gid;
+ char name[PROC_NAME_LEN];
+ char tname[THREAD_NAME_LEN];
+ char state;
+ long unsigned utime;
+ long unsigned stime;
+ long unsigned delta_utime;
+ long unsigned delta_stime;
+ long unsigned delta_time;
+ long vss;
+ long rss;
+ int num_threads;
+};
+
+struct proc_list {
+ struct proc_info **array;
+ int size;
+};
+
+#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
+
+#define INIT_PROCS 50
+#define THREAD_MULT 8
+static struct proc_info **old_procs, **new_procs;
+static int num_old_procs, num_new_procs;
+static struct proc_info *free_procs;
+static int num_used_procs, num_free_procs;
+
+static int max_procs, delay, iterations, threads;
+
+static struct cpu_info old_cpu, new_cpu;
+
+static struct proc_info *alloc_proc(void);
+static void free_proc(struct proc_info *proc);
+static void read_procs(void);
+static int read_stat(char *filename, struct proc_info *proc);
+static void add_proc(int proc_num, struct proc_info *proc);
+static int read_cmdline(char *filename, struct proc_info *proc);
+static int read_status(char *filename, struct proc_info *proc);
+static void print_procs(void);
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid);
+static void free_old_procs(void);
+static int (*proc_cmp)(const void *a, const void *b);
+static int proc_cpu_cmp(const void *a, const void *b);
+static int proc_vss_cmp(const void *a, const void *b);
+static int proc_rss_cmp(const void *a, const void *b);
+static int proc_thr_cmp(const void *a, const void *b);
+static int numcmp(long long a, long long b);
+static void usage(char *cmd);
+
+int top_main(int argc, char *argv[]) {
+ int i;
+
+ num_used_procs = num_free_procs = 0;
+
+ max_procs = 0;
+ delay = 3;
+ iterations = -1;
+ proc_cmp = &proc_cpu_cmp;
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-m")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -m expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ max_procs = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-n")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -n expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ iterations = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-d")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -d expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ delay = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-s")) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "Option -s expects an argument.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ ++i;
+ if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; }
+ if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; }
+ if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; }
+ if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; }
+ fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
+ if (!strcmp(argv[i], "-h")) {
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (threads && proc_cmp == &proc_thr_cmp) {
+ fprintf(stderr, "Sorting by threads per thread makes no sense!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ free_procs = NULL;
+
+ num_new_procs = num_old_procs = 0;
+ new_procs = old_procs = NULL;
+
+ read_procs();
+ while ((iterations == -1) || (iterations-- > 0)) {
+ old_procs = new_procs;
+ num_old_procs = num_new_procs;
+ memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
+ sleep(delay);
+ read_procs();
+ print_procs();
+ free_old_procs();
+ }
+
+ return 0;
+}
+
+static struct proc_info *alloc_proc(void) {
+ struct proc_info *proc;
+
+ if (free_procs) {
+ proc = free_procs;
+ free_procs = free_procs->next;
+ num_free_procs--;
+ } else {
+ proc = malloc(sizeof(*proc));
+ if (!proc) die("Could not allocate struct process_info.\n");
+ }
+
+ num_used_procs++;
+
+ return proc;
+}
+
+static void free_proc(struct proc_info *proc) {
+ proc->next = free_procs;
+ free_procs = proc;
+
+ num_used_procs--;
+ num_free_procs++;
+}
+
+#define MAX_LINE 256
+
+static void read_procs(void) {
+ DIR *proc_dir, *task_dir;
+ struct dirent *pid_dir, *tid_dir;
+ char filename[64];
+ FILE *file;
+ int proc_num;
+ struct proc_info *proc;
+ pid_t pid, tid;
+
+ int i;
+
+ proc_dir = opendir("/proc");
+ if (!proc_dir) die("Could not open /proc.\n");
+
+ new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *));
+ num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1);
+
+ file = fopen("/proc/stat", "r");
+ if (!file) die("Could not open /proc/stat.\n");
+ fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime,
+ &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime);
+ fclose(file);
+
+ proc_num = 0;
+ while ((pid_dir = readdir(proc_dir))) {
+ if (!isdigit(pid_dir->d_name[0]))
+ continue;
+
+ pid = atoi(pid_dir->d_name);
+
+ struct proc_info cur_proc;
+
+ if (!threads) {
+ proc = alloc_proc();
+
+ proc->pid = proc->tid = pid;
+
+ sprintf(filename, "/proc/%d/stat", pid);
+ read_stat(filename, proc);
+
+ sprintf(filename, "/proc/%d/cmdline", pid);
+ read_cmdline(filename, proc);
+
+ sprintf(filename, "/proc/%d/status", pid);
+ read_status(filename, proc);
+
+ proc->num_threads = 0;
+ } else {
+ sprintf(filename, "/proc/%d/cmdline", pid);
+ read_cmdline(filename, &cur_proc);
+
+ sprintf(filename, "/proc/%d/status", pid);
+ read_status(filename, &cur_proc);
+
+ proc = NULL;
+ }
+
+ sprintf(filename, "/proc/%d/task", pid);
+ task_dir = opendir(filename);
+ if (!task_dir) continue;
+
+ while ((tid_dir = readdir(task_dir))) {
+ if (!isdigit(tid_dir->d_name[0]))
+ continue;
+
+ if (threads) {
+ tid = atoi(tid_dir->d_name);
+
+ proc = alloc_proc();
+
+ proc->pid = pid; proc->tid = tid;
+
+ sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+ read_stat(filename, proc);
+
+ strcpy(proc->name, cur_proc.name);
+ proc->uid = cur_proc.uid;
+ proc->gid = cur_proc.gid;
+
+ add_proc(proc_num++, proc);
+ } else {
+ proc->num_threads++;
+ }
+ }
+
+ closedir(task_dir);
+
+ if (!threads)
+ add_proc(proc_num++, proc);
+ }
+
+ for (i = proc_num; i < num_new_procs; i++)
+ new_procs[i] = NULL;
+
+ closedir(proc_dir);
+}
+
+static int read_stat(char *filename, struct proc_info *proc) {
+ FILE *file;
+ char buf[MAX_LINE], *open_paren, *close_paren;
+ int res, idx;
+
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ fgets(buf, MAX_LINE, file);
+ fclose(file);
+
+ /* Split at first '(' and last ')' to get process name. */
+ open_paren = strchr(buf, '(');
+ close_paren = strrchr(buf, ')');
+ if (!open_paren || !close_paren) return 1;
+
+ *open_paren = *close_paren = '\0';
+ strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
+ proc->tname[THREAD_NAME_LEN-1] = 0;
+
+ /* Scan rest of string. */
+ sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+ "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld",
+ &proc->state, &proc->utime, &proc->stime, &proc->vss, &proc->rss);
+
+ return 0;
+}
+
+static void add_proc(int proc_num, struct proc_info *proc) {
+ int i;
+
+ if (proc_num >= num_new_procs) {
+ new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
+ if (!new_procs) die("Could not expand procs array.\n");
+ for (i = num_new_procs; i < 2 * num_new_procs; i++)
+ new_procs[i] = NULL;
+ num_new_procs = 2 * num_new_procs;
+ }
+ new_procs[proc_num] = proc;
+}
+
+static int read_cmdline(char *filename, struct proc_info *proc) {
+ FILE *file;
+ char line[MAX_LINE];
+
+ line[0] = '\0';
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ fgets(line, MAX_LINE, file);
+ fclose(file);
+ if (strlen(line) > 0) {
+ strncpy(proc->name, line, PROC_NAME_LEN);
+ proc->name[PROC_NAME_LEN-1] = 0;
+ } else
+ proc->name[0] = 0;
+ return 0;
+}
+
+static int read_status(char *filename, struct proc_info *proc) {
+ FILE *file;
+ char line[MAX_LINE];
+ unsigned int uid, gid;
+
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ while (fgets(line, MAX_LINE, file)) {
+ sscanf(line, "Uid: %u", &uid);
+ sscanf(line, "Gid: %u", &gid);
+ }
+ fclose(file);
+ proc->uid = uid; proc->gid = gid;
+ return 0;
+}
+
+static void print_procs(void) {
+ int i;
+ struct proc_info *old_proc, *proc;
+ long unsigned total_delta_time;
+ struct passwd *user;
+ struct group *group;
+ char *user_str, user_buf[20];
+ char *group_str, group_buf[20];
+
+ for (i = 0; i < num_new_procs; i++) {
+ if (new_procs[i]) {
+ old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid);
+ if (old_proc) {
+ new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
+ new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
+ } else {
+ new_procs[i]->delta_utime = 0;
+ new_procs[i]->delta_stime = 0;
+ }
+ new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
+ }
+ }
+
+ total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime
+ + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime)
+ - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime
+ + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime);
+
+ qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
+
+ printf("\n\n\n");
+ printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n",
+ ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100 / total_delta_time,
+ ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time,
+ ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time,
+ ((new_cpu.irqtime + new_cpu.sirqtime)
+ - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time);
+ printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n",
+ new_cpu.utime - old_cpu.utime,
+ new_cpu.ntime - old_cpu.ntime,
+ new_cpu.stime - old_cpu.stime,
+ new_cpu.itime - old_cpu.itime,
+ new_cpu.iowtime - old_cpu.iowtime,
+ new_cpu.irqtime - old_cpu.irqtime,
+ new_cpu.sirqtime - old_cpu.sirqtime,
+ total_delta_time);
+ printf("\n");
+ if (!threads)
+ printf("%5s %4s %1s %5s %7s %7s %-8s %s\n", "PID", "CPU%", "S", "#THR", "VSS", "RSS", "UID", "Name");
+ else
+ printf("%5s %5s %4s %1s %7s %7s %-8s %-15s %s\n", "PID", "TID", "CPU%", "S", "VSS", "RSS", "UID", "Thread", "Proc");
+
+ for (i = 0; i < num_new_procs; i++) {
+ proc = new_procs[i];
+
+ if (!proc || (max_procs && (i >= max_procs)))
+ break;
+ user = getpwuid(proc->uid);
+ group = getgrgid(proc->gid);
+ if (user && user->pw_name) {
+ user_str = user->pw_name;
+ } else {
+ snprintf(user_buf, 20, "%d", proc->uid);
+ user_str = user_buf;
+ }
+ if (group && group->gr_name) {
+ group_str = group->gr_name;
+ } else {
+ snprintf(group_buf, 20, "%d", proc->gid);
+ group_str = group_buf;
+ }
+ if (!threads)
+ printf("%5d %3ld%% %c %5d %6ldK %6ldK %-8.8s %s\n", proc->pid, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
+ proc->vss / 1024, proc->rss * getpagesize() / 1024, user_str, proc->name[0] != 0 ? proc->name : proc->tname);
+ else
+ printf("%5d %5d %3ld%% %c %6ldK %6ldK %-8.8s %-15s %s\n", proc->pid, proc->tid, proc->delta_time * 100 / total_delta_time, proc->state,
+ proc->vss / 1024, proc->rss * getpagesize() / 1024, user_str, proc->tname, proc->name);
+ }
+}
+
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid) {
+ int i;
+
+ for (i = 0; i < num_old_procs; i++)
+ if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid))
+ return old_procs[i];
+
+ return NULL;
+}
+
+static void free_old_procs(void) {
+ int i;
+
+ for (i = 0; i < num_old_procs; i++)
+ if (old_procs[i])
+ free_proc(old_procs[i]);
+
+ free(old_procs);
+}
+
+static int proc_cpu_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->delta_time, pb->delta_time);
+}
+
+static int proc_vss_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->vss, pb->vss);
+}
+
+static int proc_rss_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->rss, pb->rss);
+}
+
+static int proc_thr_cmp(const void *a, const void *b) {
+ struct proc_info *pa, *pb;
+
+ pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+ if (!pa && !pb) return 0;
+ if (!pa) return 1;
+ if (!pb) return -1;
+
+ return -numcmp(pa->num_threads, pb->num_threads);
+}
+
+static int numcmp(long long a, long long b) {
+ if (a < b) return -1;
+ if (a > b) return 1;
+ return 0;
+}
+
+static void usage(char *cmd) {
+ fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n"
+ " -m num Maximum number of processes to display.\n"
+ " -n num Updates to show before exiting.\n"
+ " -d num Seconds to wait between updates.\n"
+ " -s col Column to sort by (cpu,vss,rss,thr).\n"
+ " -t Show threads instead of processes.\n"
+ " -h Display this help screen.\n",
+ cmd);
+}
diff --git a/toolbox/umount.c b/toolbox/umount.c
new file mode 100644
index 0000000..92c6076
--- /dev/null
+++ b/toolbox/umount.c
@@ -0,0 +1,74 @@
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/loop.h>
+
+// FIXME - only one loop mount is supported at a time
+#define LOOP_DEVICE "/dev/block/loop0"
+
+static int is_loop_mount(const char* path)
+{
+ FILE* f;
+ int count;
+ char device[256];
+ char mount_path[256];
+ char rest[256];
+ int result = 0;
+ int path_length = strlen(path);
+
+ f = fopen("/proc/mounts", "r");
+ if (!f) {
+ fprintf(stdout, "could not open /proc/mounts\n");
+ return -1;
+ }
+
+ do {
+ count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
+ if (count == 3) {
+ if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0) {
+ result = 1;
+ break;
+ }
+ }
+ } while (count == 3);
+
+ fclose(f);
+ return result;
+}
+
+int umount_main(int argc, char *argv[])
+{
+ int loop, loop_fd;
+
+ if(argc != 2) {
+ fprintf(stderr,"umount <path>\n");
+ return 1;
+ }
+
+ loop = is_loop_mount(argv[1]);
+ if(umount(argv[1])){
+ fprintf(stderr,"failed.\n");
+ return 1;
+ }
+
+ if (loop) {
+ // free the loop device
+ loop_fd = open(LOOP_DEVICE, O_RDONLY);
+ if (loop_fd < -1) {
+ perror("open loop device failed");
+ return 1;
+ }
+ if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+ perror("ioctl LOOP_CLR_FD failed");
+ return 1;
+ }
+
+ close(loop_fd);
+ }
+
+ return 0;
+}
diff --git a/toolbox/vmstat.c b/toolbox/vmstat.c
new file mode 100644
index 0000000..600f136
--- /dev/null
+++ b/toolbox/vmstat.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+struct state {
+ long procs_r;
+ long procs_b;
+
+ long mem_free;
+ long mem_mapped;
+ long mem_anon;
+ long mem_slab;
+
+ long sys_in;
+ long sys_cs;
+ long sys_flt;
+
+ long cpu_us;
+ long cpu_ni;
+ long cpu_sy;
+ long cpu_id;
+ long cpu_wa;
+ long cpu_ir;
+ long cpu_si;
+};
+
+#define MAX_LINE 256
+
+char line[MAX_LINE];
+
+static void read_state(struct state *s);
+static int read_meminfo(struct state *s);
+static int read_stat(struct state *s);
+static int read_vmstat(struct state *s);
+static void print_header(void);
+static void print_line(struct state *old, struct state *new);
+static void usage(char *cmd);
+
+int vmstat_main(int argc, char *argv[]) {
+ struct state s[2];
+ int iterations, delay, header_interval;
+ int toggle, count;
+ int i;
+
+ iterations = 0;
+ delay = 1;
+ header_interval = 20;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-n")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Option -n requires an argument.\n");
+ exit(EXIT_FAILURE);
+ }
+ iterations = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-d")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Option -d requires an argument.\n");
+ exit(EXIT_FAILURE);
+ }
+ delay = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-r")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Option -r requires an argument.\n");
+ exit(EXIT_FAILURE);
+ }
+ header_interval = atoi(argv[++i]);
+ continue;
+ }
+ if (!strcmp(argv[i], "-h")) {
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ toggle = 0;
+ count = 0;
+
+ if (!header_interval)
+ print_header();
+ read_state(&s[1 - toggle]);
+ while ((iterations == 0) || (iterations-- > 0)) {
+ sleep(delay);
+ read_state(&s[toggle]);
+ if (header_interval) {
+ if (count == 0)
+ print_header();
+ count = (count + 1) % header_interval;
+ }
+ print_line(&s[1 - toggle], &s[toggle]);
+ toggle = 1 - toggle;
+ }
+
+ return 0;
+}
+
+static void read_state(struct state *s) {
+ int error;
+
+ error = read_meminfo(s);
+ if (error) {
+ fprintf(stderr, "vmstat: could not read /proc/meminfo: %s\n", strerror(error));
+ exit(EXIT_FAILURE);
+ }
+
+ error = read_stat(s);
+ if (error) {
+ fprintf(stderr, "vmstat: could not read /proc/stat: %s\n", strerror(error));
+ exit(EXIT_FAILURE);
+ }
+
+ error = read_vmstat(s);
+ if (error) {
+ fprintf(stderr, "vmstat: could not read /proc/vmstat: %s\n", strerror(error));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int read_meminfo(struct state *s) {
+ FILE *f;
+
+ f = fopen("/proc/meminfo", "r");
+ if (!f) return errno;
+
+ while (fgets(line, MAX_LINE, f)) {
+ sscanf(line, "MemFree: %ld kB", &s->mem_free);
+ sscanf(line, "AnonPages: %ld kB", &s->mem_anon);
+ sscanf(line, "Mapped: %ld kB", &s->mem_mapped);
+ sscanf(line, "Slab: %ld kB", &s->mem_slab);
+ }
+
+ fclose(f);
+
+ return 0;
+}
+
+static int read_stat(struct state *s) {
+ FILE *f;
+
+ f = fopen("/proc/stat", "r");
+ if (!f) return errno;
+
+ while (fgets(line, MAX_LINE, f)) {
+ if (!strncmp(line, "cpu ", 4)) {
+ sscanf(line, "cpu %ld %ld %ld %ld %ld %ld %ld",
+ &s->cpu_us, &s->cpu_ni, &s->cpu_sy, &s->cpu_id, &s->cpu_wa,
+ &s->cpu_ir, &s->cpu_si);
+ }
+ sscanf(line, "intr %ld", &s->sys_in);
+ sscanf(line, "ctxt %ld", &s->sys_cs);
+ sscanf(line, "procs_running %ld", &s->procs_r);
+ sscanf(line, "procs_blocked %ld", &s->procs_b);
+ }
+
+ fclose(f);
+
+ return 0;
+}
+
+static int read_vmstat(struct state *s) {
+ FILE *f;
+
+ f = fopen("/proc/vmstat", "r");
+ if (!f) return errno;
+
+ while (fgets(line, MAX_LINE, f)) {
+ sscanf(line, "pgmajfault %ld", &s->sys_flt);
+ }
+
+ fclose(f);
+
+ return 0;
+}
+
+static void print_header(void) {
+ printf("%-5s %-27s %-14s %-17s\n", "procs", "memory", "system", "cpu");
+ printf("%2s %2s %6s %6s %6s %6s %4s %4s %4s %2s %2s %2s %2s %2s %2s\n", "r", "b", "free", "mapped", "anon", "slab", "in", "cs", "flt", "us", "ni", "sy", "id", "wa", "ir");
+}
+
+/* Jiffies to percent conversion */
+#define JP(jif) ((jif) * 100 / (HZ))
+#define NORM(var) ((var) = (((var) > 99) ? (99) : (var)))
+
+static void print_line(struct state *old, struct state *new) {
+ int us, ni, sy, id, wa, ir;
+ us = JP(new->cpu_us - old->cpu_us); NORM(us);
+ ni = JP(new->cpu_ni - old->cpu_ni); NORM(ni);
+ sy = JP(new->cpu_sy - old->cpu_sy); NORM(sy);
+ id = JP(new->cpu_id - old->cpu_id); NORM(id);
+ wa = JP(new->cpu_wa - old->cpu_wa); NORM(wa);
+ ir = JP(new->cpu_ir - old->cpu_ir); NORM(ir);
+ printf("%2ld %2ld %6ld %6ld %6ld %6ld %4ld %4ld %4ld %2d %2d %2d %2d %2d %2d\n",
+ new->procs_r ? (new->procs_r - 1) : 0, new->procs_b,
+ new->mem_free, new->mem_mapped, new->mem_anon, new->mem_slab,
+ new->sys_in - old->sys_in, new->sys_cs - old->sys_cs, new->sys_flt - old->sys_flt,
+ us, ni, sy, id, wa, ir);
+}
+
+static void usage(char *cmd) {
+ fprintf(stderr, "Usage: %s [ -h ] [ -n iterations ] [ -d delay ] [ -r header_repeat ]\n"
+ " -n iterations How many rows of data to print.\n"
+ " -d delay How long to sleep between rows.\n"
+ " -r header_repeat How many rows to print before repeating\n"
+ " the header. Zero means never repeat.\n"
+ " -h Displays this help screen.\n",
+ cmd);
+}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
new file mode 100644
index 0000000..d311992
--- /dev/null
+++ b/toolbox/watchprops.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <cutils/properties.h>
+
+#include <sys/atomics.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+
+extern prop_area *__system_property_area__;
+
+typedef struct pwatch pwatch;
+
+struct pwatch
+{
+ const prop_info *pi;
+ unsigned serial;
+};
+
+static pwatch watchlist[1024];
+
+static void announce(const prop_info *pi)
+{
+ char name[PROP_NAME_MAX];
+ char value[PROP_VALUE_MAX];
+ char *x;
+
+ __system_property_read(pi, name, value);
+
+ for(x = value; *x; x++) {
+ if((*x < 32) || (*x > 127)) *x = '.';
+ }
+
+ fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
+}
+
+int watchprops_main(int argc, char *argv[])
+{
+ prop_area *pa = __system_property_area__;
+ unsigned serial = pa->serial;
+ unsigned count = pa->count;
+ unsigned n;
+
+ if(count >= 1024) exit(1);
+
+ for(n = 0; n < count; n++) {
+ watchlist[n].pi = __system_property_find_nth(n);
+ watchlist[n].serial = watchlist[n].pi->serial;
+ }
+
+ for(;;) {
+ do {
+ __futex_wait(&pa->serial, serial, 0);
+ } while(pa->serial == serial);
+
+ while(count < pa->count){
+ watchlist[count].pi = __system_property_find_nth(count);
+ watchlist[count].serial = watchlist[n].pi->serial;
+ announce(watchlist[count].pi);
+ count++;
+ if(count == 1024) exit(1);
+ }
+
+ for(n = 0; n < count; n++){
+ unsigned tmp = watchlist[n].pi->serial;
+ if(watchlist[n].serial != tmp) {
+ announce(watchlist[n].pi);
+ watchlist[n].serial = tmp;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/toolbox/wipe.c b/toolbox/wipe.c
new file mode 100644
index 0000000..7e263fd
--- /dev/null
+++ b/toolbox/wipe.c
@@ -0,0 +1,176 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+
+/* Directories created by init defined in system/rootdir/init.rc */
+static char *INIT_DIRS[] = {
+ "/system/etc/ppp",
+ "/data/misc",
+ "/data/local",
+ "/data/local/tmp",
+ "/data/data",
+ "/data/app_private",
+ "/data/app",
+ NULL
+};
+
+static void wipe (const char *path);
+
+static int usage()
+{
+ fprintf(stderr, "wipe <system|data|all>\n\n"
+ "system means '/system'\n"
+ "data means '/data'\n");
+
+ return -1;
+}
+
+int wipe_main (int argc, char *argv[])
+{
+ char *whatToWipe;
+
+ if (argc != 2) return usage();
+
+ whatToWipe = argv[1];
+
+ if (0 == strcmp (whatToWipe, "system")) {
+ fprintf(stdout, "Wiping /system\n");
+ wipe ("/system");
+ fprintf(stdout, "Done wiping /android\n");
+ } else if (0 == strcmp (whatToWipe, "data")) {
+ fprintf(stdout, "Wiping /data\n");
+ wipe ("/data");
+ fprintf(stdout, "Done wiping /data\n");
+ } else if (0 == strcmp (whatToWipe, "all")) {
+ fprintf(stdout, "Wiping /system and /data\n");
+ wipe ("/system");
+ wipe ("/data");
+ fprintf(stdout, "Done wiping /system and /data\n");
+ } else if (0 == strcmp(whatToWipe, "nuke")) {
+ int ret;
+ fprintf(stdout, "Nuking the device...\n");
+ wipe ("/system");
+ wipe ("/data");
+ fprintf(stdout, "Device nuked! Rebooting...\n");
+ ret = reboot(RB_AUTOBOOT);
+ if (ret < 0) {
+ fprintf(stderr, "Reboot failed, %s\n", strerror(errno));
+ return 1;
+ }
+ } else {
+ return usage();
+ }
+
+ return 0;
+}
+
+static char nameBuffer[PATH_MAX];
+static struct stat statBuffer;
+
+static void wipe (const char *path)
+{
+ DIR *dir;
+ struct dirent *de;
+ int ret;
+
+ dir = opendir(path);
+
+ if (dir == NULL) {
+ fprintf (stderr, "Error opendir'ing %s '%s'\n",
+ path, strerror(errno));
+ return;
+ }
+
+ char *filenameOffset;
+
+ strcpy(nameBuffer, path);
+ strcat(nameBuffer, "/");
+
+ filenameOffset = nameBuffer + strlen(nameBuffer);
+
+ for (;;) {
+ de = readdir(dir);
+
+ if (de == NULL) {
+ break;
+ }
+
+ if (0 == strcmp(de->d_name, ".")
+ || 0 == strcmp(de->d_name, "..")
+ || 0 == strcmp(de->d_name, "lost+found")
+ ) {
+ continue;
+ }
+
+ strcpy(filenameOffset, de->d_name);
+
+ ret = lstat (nameBuffer, &statBuffer);
+
+ if (ret != 0) {
+ fprintf(stderr, "stat() error on '%s' '%s'\n",
+ nameBuffer, strerror(errno));
+ }
+
+ if(S_ISDIR(statBuffer.st_mode)) {
+ int i;
+ char *newpath;
+
+#if 0
+ closedir(dir);
+#endif
+
+ newpath = strdup(nameBuffer);
+ wipe(newpath);
+
+ /* Leave directories created by init, they have special permissions. */
+ for (i = 0; INIT_DIRS[i]; i++) {
+ if (strcmp(INIT_DIRS[i], newpath) == 0) {
+ break;
+ }
+ }
+ if (INIT_DIRS[i] == NULL) {
+ ret = rmdir(newpath);
+ if (ret != 0) {
+ fprintf(stderr, "rmdir() error on '%s' '%s'\n",
+ newpath, strerror(errno));
+ }
+ }
+
+ free(newpath);
+
+#if 0
+ dir = opendir(path);
+ if (dir == NULL) {
+ fprintf (stderr, "Error opendir'ing %s '%s'\n",
+ path, strerror(errno));
+ return;
+ }
+#endif
+
+ strcpy(nameBuffer, path);
+ strcat(nameBuffer, "/");
+
+ } else {
+ ret = unlink(nameBuffer);
+
+ if (ret != 0) {
+ fprintf(stderr, "unlink() error on '%s' '%s'\n",
+ nameBuffer, strerror(errno));
+ }
+ }
+ }
+
+ closedir(dir);
+
+}