summaryrefslogtreecommitdiffstats
path: root/cmds/dumpstate
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-01-09 17:51:23 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-01-09 17:51:23 -0800
commitb798689749c64baba81f02e10cf2157c747d6b46 (patch)
treeda394a395ddb1a6cf69193314846b03fe47a397e /cmds/dumpstate
parentf013e1afd1e68af5e3b868c26a653bbfb39538f8 (diff)
downloadframeworks_base-b798689749c64baba81f02e10cf2157c747d6b46.zip
frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.tar.gz
frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.tar.bz2
auto import from //branches/cupcake/...@125939
Diffstat (limited to 'cmds/dumpstate')
-rw-r--r--cmds/dumpstate/Android.mk17
-rwxr-xr-xcmds/dumpstate/dumpstate114
-rw-r--r--cmds/dumpstate/dumpstate.c291
-rw-r--r--cmds/dumpstate/dumpstate.h120
-rw-r--r--cmds/dumpstate/utils.c230
5 files changed, 654 insertions, 118 deletions
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index e101aeb..f61d7ec 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,11 +1,18 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-ALL_PREBUILT += $(TARGET_OUT)/bin/dumpstate
-$(TARGET_OUT)/bin/dumpstate : $(LOCAL_PATH)/dumpstate | $(ACP)
- $(transform-prebuilt-to-target)
+LOCAL_SRC_FILES:= dumpstate.c utils.c
+
+LOCAL_MODULE:= dumpstate
+
+LOCAL_SHARED_LIBRARIES := libcutils
-SYMLINKS := $(TARGET_OUT_EXECUTABLES)/dumpcrash
+include $(BUILD_EXECUTABLE)
+
+COMMANDS = dumpcrash bugreport
+SYMLINKS := $(addprefix $(TARGET_OUT_EXECUTABLES)/,$(COMMANDS))
$(SYMLINKS): DUMPSTATE_BINARY := dumpstate
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
@echo "Symlink: $@ -> $(DUMPSTATE_BINARY)"
@@ -14,3 +21,5 @@ $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
$(hide) ln -sf $(DUMPSTATE_BINARY) $@
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+endif
diff --git a/cmds/dumpstate/dumpstate b/cmds/dumpstate/dumpstate
deleted file mode 100755
index 8dd7d14..0000000
--- a/cmds/dumpstate/dumpstate
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/system/bin/sh
-
-# Dumps to /tmp/state by default.
-# Specify "-" to dump to stdout, or any file name to dump to that file.
-
-crashmode=0
-case $0 in
- dumpcrash|*/dumpcrash) crashmode=1 ;;
-esac
-
-case $1 in
- "") echo "DUMPING STATE TO /tmp/state"; exec 1>/tmp/state ;;
- -) ;;
- *) echo "DUMPING STATE TO $1"; exec 1>$1 ;;
-esac
-
-case $crashmode in 0)
- echo "========================================================"
- echo "== dumpstate"
- echo "========================================================"
- echo "------ SYSTEM LOG ------"
- logcat -v time -d '*:v'
- echo "------ VM TRACES ------"
- cat /data/anr/traces.txt
- echo "------ EVENT LOG TAGS ------"
- cat /etc/event-log-tags
- echo "------ EVENT LOG ------"
- logcat -b events -v time -d '*:v'
- echo "------ RADIO LOG ------"
- logcat -b radio -v time -d '*:v'
- echo "------ NETWORK STATE ------"
- echo "Interfaces:"
- netcfg
- echo ""
- echo "Routes:"
- cat /proc/net/route
- echo "------ SYSTEM PROPERTIES ------"
- getprop
- echo "------ KERNEL LOG ------"
- dmesg
- echo "------ KERNEL WAKELOCKS ------"
- cat /proc/wakelocks
- echo ""
- echo "------ PROCESSES ------"
- ps
- echo "------ PROCESSES AND THREADS ------"
- ps -t -p
- echo "------ MEMORY INFO ------"
- cat /proc/meminfo
- echo "------ PSS INFO ------"
- top -n 1 -d 0 -m 15 -s pss
- echo "------ PROCRANK ------"
- procrank
- echo "------ LIBRANK ------"
- librank
- echo "------ VIRTUAL MEMORY STATS ------"
- cat /proc/vmstat
- echo "------ SLAB INFO ------"
- cat /proc/slabinfo
- echo "------ ZONEINFO ------"
- cat /proc/zoneinfo
- echo "------ BINDER FAILED TRANSACTION LOG ------"
- cat /proc/binder/failed_transaction_log
- echo ""
- echo "------ BINDER TRANSACTION LOG ------"
- cat /proc/binder/transaction_log
- echo ""
- echo "------ BINDER TRANSACTIONS ------"
- cat /proc/binder/transactions
- echo ""
- echo "------ BINDER STATS ------"
- cat /proc/binder/stats
- echo ""
- for i in `ls /proc/binder/proc`; do
- echo "------ BINDER PROCESS STATE: $i ------"
- cat /proc/binder/proc/$i
- echo ""
- done
- echo "------ FILESYSTEMS ------"
- df
- echo "------ PACKAGE SETTINGS ------"
- cat /data/system/packages.xml
- echo "------ PACKAGE UID ERRORS ------"
- cat /data/system/uiderrors.txt
- echo "------ LAST KERNEL LOG ------"
- cat /proc/last_kmsg
-;; esac
-
-echo "========================================================"
-echo "== build.prop"
-echo "========================================================"
-
-# the crash server parses key-value pairs between the VERSION INFO and
-# END lines so we can aggregate crash reports based on this data.
-echo "------ VERSION INFO ------"
-echo "currenttime=`date`"
-echo "kernel.version=`cat /proc/version`"
-echo "kernel.cmdline=`cat /proc/cmdline`"
-cat /system/build.prop
-echo "gsm.version.ril-impl=`getprop gsm.version.ril-impl`"
-echo "gsm.version.baseband=`getprop gsm.version.baseband`"
-echo "gsm.imei=`getprop gsm.imei`"
-echo "gsm.sim.operator.numeric=`getprop gsm.sim.operator.numeric`"
-echo "gsm.operator.alpha=`getprop gsm.operator.alpha`"
-echo "------ END ------"
-
-case $crashmode in 0)
- echo "========================================================"
- echo "== dumpsys"
- echo "========================================================"
- dumpsys
-;; esac
-
-exit 0
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
new file mode 100644
index 0000000..dea269d
--- /dev/null
+++ b/cmds/dumpstate/dumpstate.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "private/android_filesystem_config.h"
+
+#include "dumpstate.h"
+
+static char* const gzip_args[] = { "gzip", "-6", 0 };
+static int start_pattern[] = { 150, 0 };
+static int end_pattern[] = { 75, 50, 75, 50, 75, 0 };
+
+static struct tm now;
+
+/* dumps the current system state to stdout */
+static void dumpstate(int full) {
+ if (full) {
+ PRINT("========================================================");
+ PRINT("== dumpstate");
+ PRINT("========================================================");
+ PRINT("------ SYSTEM LOG ------");
+ EXEC4("logcat", "-v", "time", "-d", "*:v");
+ PRINT("------ VM TRACES ------");
+ DUMP("/data/anr/traces.txt");
+ PRINT("------ EVENT LOG TAGS ------");
+ DUMP("/etc/event-log-tags");
+ PRINT("------ EVENT LOG ------");
+ EXEC6("logcat", "-b", "events", "-v", "time", "-d", "*:v");
+ PRINT("------ RADIO LOG ------");
+ EXEC6("logcat", "-b", "radio", "-v", "time", "-d", "*:v");
+ PRINT("------ NETWORK STATE ------");
+ PRINT("Interfaces:");
+ EXEC("netcfg");
+ PRINT("");
+ PRINT("Routes:");
+ DUMP("/proc/net/route");
+ PRINT("------ SYSTEM PROPERTIES ------");
+ print_properties();
+ PRINT("------ KERNEL LOG ------");
+ EXEC("dmesg");
+ PRINT("------ KERNEL WAKELOCKS ------");
+ DUMP("/proc/wakelocks");
+ PRINT("");
+ PRINT("------ PROCESSES ------");
+ EXEC("ps");
+ PRINT("------ PROCESSES AND THREADS ------");
+ EXEC2("ps", "-t", "-p");
+ PRINT("------ MEMORY INFO ------");
+ DUMP("/proc/meminfo");
+ PRINT("------ PSS INFO ------");
+ EXEC8("top", "-n", "1", "-d", "0", "-m", "15", "-s", "pss");
+ PRINT("------ PROCRANK ------");
+ EXEC("procrank");
+ PRINT("------ LIBRANK ------");
+ EXEC("librank");
+ PRINT("------ VIRTUAL MEMORY STATS ------");
+ DUMP("/proc/vmstat");
+ PRINT("------ SLAB INFO ------");
+ DUMP("/proc/slabinfo");
+ PRINT("------ ZONEINFO ------");
+ DUMP("/proc/zoneinfo");
+ PRINT("------ BINDER FAILED TRANSACTION LOG ------");
+ DUMP("/proc/binder/failed_transaction_log");
+ PRINT("");
+ PRINT("------ BINDER TRANSACTION LOG ------");
+ DUMP("/proc/binder/transaction_log");
+ PRINT("");
+ PRINT("------ BINDER TRANSACTIONS ------");
+ DUMP("/proc/binder/transactions");
+ PRINT("");
+ PRINT("------ BINDER STATS ------");
+ DUMP("/proc/binder/stats");
+ PRINT("");
+ PRINT("------ BINDER PROCESS STATE: $i ------");
+ DUMP_FILES("/proc/binder/proc");
+ PRINT("------ FILESYSTEMS ------");
+ EXEC("df");
+ PRINT("------ PACKAGE SETTINGS ------");
+ DUMP("/data/system/packages.xml");
+ PRINT("------ PACKAGE UID ERRORS ------");
+ DUMP("/data/system/uiderrors.txt");
+ PRINT("------ LAST KERNEL LOG ------");
+ DUMP("/proc/last_kmsg");
+ }
+ PRINT("========================================================");
+ PRINT("== build.prop");
+ PRINT("========================================================");
+
+ /* the crash server parses key-value pairs between the VERSION INFO and
+ * END lines so we can aggregate crash reports based on this data.
+ */
+ PRINT("------ VERSION INFO ------");
+ print_date("currenttime=", &now);
+ DUMP_PROMPT("kernel.version=", "/proc/version");
+ DUMP_PROMPT("kernel.cmdline=", "/proc/cmdline");
+ DUMP("/system/build.prop");
+ PROPERTY("gsm.version.ril-impl");
+ PROPERTY("gsm.version.baseband");
+ PROPERTY("gsm.imei");
+ PROPERTY("gsm.sim.operator.numeric");
+ PROPERTY("gsm.operator.alpha");
+ PRINT("------ END ------");
+
+ if (full) {
+ PRINT("========================================================");
+ PRINT("== dumpsys");
+ PRINT("========================================================");
+ EXEC("dumpsys");
+ }
+}
+
+/* used to check the file name passed via argv[0] */
+static int check_command_name(const char* name, const char* test) {
+ int name_length, test_length;
+
+ if (!strcmp(name, test))
+ return 1;
+
+ name_length = strlen(name);
+ test_length = strlen(test);
+
+ if (name_length > test_length + 2) {
+ name += (name_length - test_length);
+ if (name[-1] != '/')
+ return 0;
+ if (!strcmp(name, test))
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int dumpcrash = check_command_name(argv[0], "dumpcrash");
+ int bugreport = check_command_name(argv[0], "bugreport");
+ int add_date = 0;
+ char* outfile = 0;
+ int vibrate = 0;
+ int compress = 0;
+ int c, fd, vibrate_fd, fds[2];
+ char path[PATH_MAX];
+ pid_t pid;
+
+ /* set as high priority, and protect from OOM killer */
+ setpriority(PRIO_PROCESS, 0, -20);
+ protect_from_oom_killer();
+
+ get_time(&now);
+
+ if (bugreport) {
+ do {
+ c = getopt(argc, argv, "do:vz");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'd':
+ add_date = 1;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'v':
+ vibrate = 1;
+ break;
+ case 'z':
+ compress = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
+ }
+
+ /* open vibrator before switching user */
+ if (vibrate) {
+ vibrate_fd = open("/sys/class/timed_output/vibrator/enable", O_WRONLY);
+ if (vibrate_fd > 0)
+ fcntl(vibrate_fd, F_SETFD, FD_CLOEXEC);
+ } else
+ vibrate_fd = -1;
+
+ /* switch to non-root user and group */
+ setgid(AID_LOG);
+ setuid(AID_SHELL);
+
+ /* make it safe to use both printf and STDOUT_FILENO */
+ setvbuf(stdout, 0, _IONBF, 0);
+
+ if (outfile) {
+ if (strlen(outfile) > sizeof(path) - 100)
+ exit(1);
+
+ strcpy(path, outfile);
+ if (add_date) {
+ char date[260];
+ strftime(date, sizeof(date),
+ "-%Y-%m-%d-%H-%M-%S",
+ &now);
+ strcat(path, date);
+ }
+ if (compress)
+ strcat(path, ".gz");
+
+ /* ensure that all directories in the path exist */
+ create_directories(path);
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ return fd;
+
+ if (compress) {
+ pipe(fds);
+
+ /* redirect our stdout to the pipe */
+ dup2(fds[1], STDOUT_FILENO);
+ close(fds[1]);
+
+ if ((pid = fork()) < 0)
+ {
+ fprintf(stderr, "fork error\n");
+ exit(1);
+ }
+
+ if (pid) {
+ /* parent case */
+
+ /* close our copy of the input to gzip */
+ close(fds[0]);
+ /* close our copy of the output file */
+ close(fd);
+ } else {
+ /* child case */
+
+ /* redirect our input pipe to stdin */
+ dup2(fds[0], STDIN_FILENO);
+ close(fds[0]);
+
+ /* redirect stdout to the output file */
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+
+ /* run gzip to postprocess our output */
+ execv("/system/bin/gzip", gzip_args);
+ fprintf(stderr, "execv returned\n");
+ }
+ } else {
+ /* redirect stdout to the output file */
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+ }
+ }
+ /* else everything will print to stdout */
+
+ if (vibrate) {
+ vibrate_pattern(vibrate_fd, start_pattern);
+ }
+ dumpstate(!dumpcrash);
+ if (vibrate) {
+ vibrate_pattern(vibrate_fd, end_pattern);
+ close(vibrate_fd);
+ }
+
+ /* so gzip will terminate */
+ close(STDOUT_FILENO);
+
+ return 0;
+}
+
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
new file mode 100644
index 0000000..b956f99
--- /dev/null
+++ b/cmds/dumpstate/dumpstate.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DUMPSTATE_H_
+#define _DUMPSTATE_H_
+
+#include <time.h>
+
+// Commands time out after 15 seconds
+#define TIMEOUT 15
+
+#define PRINT(s) printf("%s\n", s)
+
+#define DUMP(file) dump_file(file)
+
+#define DUMP_FILES(path) dump_files(path)
+
+#define DUMP_PROMPT(prompt, file) \
+{ \
+ printf(prompt); \
+ dump_file(file); \
+}
+
+#define EXEC(cmd) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC2(cmd, a1, a2) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC4(cmd, a1, a2, a3, a4) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, a3, a4, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC6(cmd, a1, a2, a3, a4, a5, a6) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, a3, a4, a5, a6, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define EXEC8(cmd, a1, a2, a3, a4, a5, a6, a7, a8) \
+{ \
+ static struct Command c = { \
+ "/system/bin/" cmd, \
+ { cmd, a1, a2, a3, a4, a5, a6, a7, a8, 0 } \
+ }; \
+ run_command(&c, TIMEOUT); \
+}
+
+#define PROPERTY(name) print_property(name)
+
+struct Command {
+ const char* path;
+ char* const args[];
+};
+typedef struct Command Command;
+
+/* prints the contents of a file */
+int dump_file(const char* path);
+
+/* prints the contents of all files in a directory */
+void dump_files(const char* path);
+
+/* forks a command and waits for it to finish */
+int run_command(struct Command* cmd, int timeout);
+
+/* reads the current time into tm */
+void get_time(struct tm *tm);
+
+/* prints the date in tm */
+void print_date(const char* prompt, struct tm *tm);
+
+/* prints the name and value of a system property */
+int print_property(const char* name);
+
+/* prints all the system properties */
+void print_properties();
+
+/* creates directories as needed for the given path */
+void create_directories(char *path);
+
+/* runs the vibrator using the given pattern */
+void vibrate_pattern(int fd, int* pattern);
+
+/* prevents the OOM killer from killing us */
+void protect_from_oom_killer();
+
+#endif /* _DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
new file mode 100644
index 0000000..60d845f
--- /dev/null
+++ b/cmds/dumpstate/utils.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <cutils/properties.h>
+#include <sys/system_properties.h>
+
+#include "dumpstate.h"
+
+
+/* prints the contents of a file */
+int dump_file(const char* path) {
+ char buffer[32768];
+ int fd, amount_read;
+ int ret = 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ do {
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret > 0)
+ ret = write(STDOUT_FILENO, buffer, ret);
+ } while (ret > 0);
+
+ buffer[0] = '\n';
+ write(STDOUT_FILENO, buffer, 1);
+
+ close(fd);
+ return ret;
+}
+
+/* prints the contents of all files in a directory */
+void dump_files(const char* path) {
+ DIR* dir;
+ struct dirent* entry;
+ char buffer[PATH_MAX];
+
+ dir = opendir(path);
+ if (!dir) {
+ fprintf(stderr, "could not open directory %s\n", path);
+ return;
+ }
+
+ while ((entry = readdir(dir))) {
+ if (entry->d_type == DT_REG) {
+ snprintf(buffer, sizeof(buffer), "%s/%s", path, entry->d_name);
+ dump_file(path);
+ printf("\n");
+ }
+ }
+
+ closedir(dir);
+}
+
+/* prints the name and value of a system property */
+int print_property(const char* name) {
+ char value[PROP_VALUE_MAX];
+
+ __system_property_get(name, value);
+ printf("%s=%s\n", name, value);
+ return 0;
+}
+
+static pid_t alarm_pid = 0;
+static int timed_out = 0;
+static void sig_alarm(int sig)
+{
+ if (alarm_pid) {
+ kill(alarm_pid, SIGKILL);
+ timed_out = 1;
+ alarm_pid = 0;
+ }
+}
+
+/* forks a command and waits for it to finish */
+int run_command(struct Command* cmd, int timeout) {
+ struct sigaction sa;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ /* handle error case */
+ if (pid < 0)
+ return pid;
+
+ /* handle child case */
+ if (pid == 0) {
+ int ret = execv(cmd->path, cmd->args);
+ if (ret)
+ fprintf(stderr, "execv %s returned %d\n", cmd->path, ret);
+ exit(ret);
+ }
+
+ /* handle parent case */
+ timed_out = 0;
+ if (timeout) {
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_RESETHAND;
+ sa.sa_handler = sig_alarm;
+ sigaction(SIGALRM, &sa, NULL);
+
+ /* set an alarm so we don't hang forever */
+ alarm_pid = pid;
+ alarm(timeout);
+ }
+
+ waitpid(pid, &status, 0);
+
+ if (timed_out)
+ printf("ERROR: command %s timed out\n", cmd->path);
+
+ return status;
+}
+
+/* reads the current time into tm */
+void get_time(struct tm *tm) {
+ time_t t;
+
+ tzset();
+ time(&t);
+ localtime_r(&t, tm);
+}
+
+/* prints the date in tm */
+void print_date(const char* prompt, struct tm *tm) {
+ char strbuf[260];
+
+ strftime(strbuf, sizeof(strbuf),
+ "%a %b %e %H:%M:%S %Z %Y",
+ tm);
+ printf("%s%s\n", prompt, strbuf);
+}
+
+
+static void print_prop(const char *key, const char *name,
+ void *user __attribute__((unused)))
+{
+ printf("[%s]: [%s]\n", key, name);
+}
+
+/* prints all the system properties */
+void print_properties() {
+ property_list(print_prop, NULL);
+}
+
+/* creates directories as needed for the given path */
+void create_directories(char *path)
+{
+ char *chp = path;
+
+ /* skip initial slash */
+ if (chp[0] == '/')
+ chp++;
+
+ while (chp && chp[0]) {
+ chp = strchr(chp, '/');
+ if (chp) {
+ *chp = 0;
+ mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ *chp = '/';
+ chp++;
+ }
+ }
+}
+
+/* runs the vibrator using the given pattern */
+void vibrate_pattern(int fd, int* pattern)
+{
+ struct timespec tm;
+ char buffer[10];
+
+ while (*pattern) {
+ /* read vibrate on time */
+ int on_time = *pattern++;
+ snprintf(buffer, sizeof(buffer), "%d", on_time);
+ write(fd, buffer, strlen(buffer));
+
+ /* read vibrate off time */
+ int delay = *pattern++;
+ if (delay) {
+ delay += on_time;
+
+ tm.tv_sec = delay / 1000;
+ tm.tv_nsec = (delay % 1000) * 1000000;
+ nanosleep(&tm, NULL);
+ } else
+ break;
+ }
+}
+
+/* prevents the OOM killer from killing us */
+void protect_from_oom_killer()
+{
+ int fd;
+
+ fd = open("/proc/self/oom_adj", O_WRONLY);
+ if (fd >= 0) {
+ // -17 should make us immune to OOM
+ const char* text = "-17";
+ write(fd, text, strlen(text));
+ close(fd);
+ }
+}