diff options
Diffstat (limited to 'toolbox')
62 files changed, 9470 insertions, 0 deletions
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); + +} |