Merge "healthd: logd: add timestamp to kernel logged battery messages"
diff --git a/.gitignore b/.gitignore
index b25c15b..2f836aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
*~
+*.pyc
diff --git a/adb/Android.mk b/adb/Android.mk
index 355bb7f..e2d0bb1 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -11,6 +11,9 @@
adb_host_clang := true
endif
+adb_host_sanitize :=
+adb_target_sanitize :=
+
adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
ADB_COMMON_CFLAGS := \
@@ -19,6 +22,15 @@
-Wno-missing-field-initializers \
-DADB_REVISION='"$(adb_version)"' \
+# Define windows.h and tchar.h Unicode preprocessor symbols so that
+# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
+# build if you accidentally pass char*. Fix by calling like:
+# CreateFileW(widen(utf8).c_str()).
+ADB_COMMON_windows_CFLAGS := \
+ -DUNICODE=1 -D_UNICODE=1 \
+
+ADB_COMMON_CFLAGS += $(ADB_COMMON_$(HOST_OS)_CFLAGS)
+
# libadb
# =========================================================
@@ -64,7 +76,6 @@
usb_linux.cpp \
LIBADB_windows_SRC_FILES := \
- get_my_path_windows.cpp \
sysdeps_win32.cpp \
usb_windows.cpp \
@@ -80,6 +91,7 @@
qemu_tracing.cpp \
usb_linux_client.cpp \
+LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_SHARED_LIBRARIES := libbase
# Even though we're building a static library (and thus there's no link step for
@@ -97,6 +109,7 @@
$(LIBADB_$(HOST_OS)_SRC_FILES) \
adb_auth_host.cpp \
+LOCAL_SANITIZE := $(adb_host_sanitize)
LOCAL_SHARED_LIBRARIES := libbase
# Even though we're building a static library (and thus there's no link step for
@@ -114,16 +127,20 @@
LOCAL_MODULE := adbd_test
LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
+LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_STATIC_LIBRARIES := libadbd
LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
include $(BUILD_NATIVE_TEST)
-ifneq ($(HOST_OS),windows)
+# adb_test
+# =========================================================
+
include $(CLEAR_VARS)
LOCAL_CLANG := $(adb_host_clang)
LOCAL_MODULE := adb_test
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
+LOCAL_SANITIZE := $(adb_host_sanitize)
LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_STATIC_LIBRARIES := \
libadb \
@@ -138,9 +155,13 @@
LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
endif
-include $(BUILD_HOST_NATIVE_TEST)
+ifeq ($(HOST_OS),windows)
+ LOCAL_LDLIBS += -lws2_32 -luserenv
+ LOCAL_STATIC_LIBRARIES += AdbWinApi
endif
+include $(BUILD_HOST_NATIVE_TEST)
+
# adb device tracker (used by ddms) test tool
# =========================================================
@@ -150,6 +171,7 @@
LOCAL_MODULE := adb_device_tracker_test
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := test_track_devices.cpp
+LOCAL_SANITIZE := $(adb_host_sanitize)
LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils
LOCAL_LDLIBS += -lrt -ldl -lpthread
@@ -171,6 +193,8 @@
endif
ifeq ($(HOST_OS),windows)
+ # Use wmain instead of main
+ LOCAL_LDFLAGS += -municode
LOCAL_LDLIBS += -lws2_32 -lgdi32
EXTRA_STATIC_LIBS := AdbWinApi
endif
@@ -193,6 +217,7 @@
LOCAL_MODULE := adb
LOCAL_MODULE_TAGS := debug
+LOCAL_SANITIZE := $(adb_host_sanitize)
LOCAL_STATIC_LIBRARIES := \
libadb \
libbase \
@@ -257,6 +282,7 @@
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_C_INCLUDES += system/extras/ext4_utils
+LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_STATIC_LIBRARIES := \
libadbd \
libbase \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 97ce125..fc7320c 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -35,6 +35,7 @@
#include <unordered_map>
#include <base/logging.h>
+#include <base/macros.h>
#include <base/stringprintf.h>
#include <base/strings.h>
@@ -191,16 +192,13 @@
}
void adb_trace_init(char** argv) {
+#if !ADB_HOST
// Don't open log file if no tracing, since this will block
// the crypto unmount of /data
- const std::string trace_setting = get_trace_setting();
- if (trace_setting.empty()) {
- return;
- }
-
-#if !ADB_HOST
- if (isatty(STDOUT_FILENO) == 0) {
- start_device_log();
+ if (!get_trace_setting().empty()) {
+ if (isatty(STDOUT_FILENO) == 0) {
+ start_device_log();
+ }
}
#endif
@@ -557,9 +555,9 @@
HANDLE pipe_read, pipe_write;
HANDLE stdout_handle, stderr_handle;
SECURITY_ATTRIBUTES sa;
- STARTUPINFO startup;
+ STARTUPINFOW startup;
PROCESS_INFORMATION pinfo;
- char program_path[ MAX_PATH ];
+ WCHAR program_path[ MAX_PATH ];
int ret;
sa.nLength = sizeof(sa);
@@ -579,8 +577,8 @@
FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (nul_read == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "CreateFileW(nul, GENERIC_READ) failure, error %ld\n",
- GetLastError());
+ fprintf(stderr, "CreateFileW(nul, GENERIC_READ) failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -588,8 +586,8 @@
FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (nul_write == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "CreateFileW(nul, GENERIC_WRITE) failure, error %ld\n",
- GetLastError());
+ fprintf(stderr, "CreateFileW(nul, GENERIC_WRITE) failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
CloseHandle(nul_read);
return -1;
}
@@ -597,7 +595,8 @@
/* create pipe, and ensure its read handle isn't inheritable */
ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
if (!ret) {
- fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+ fprintf(stderr, "CreatePipe() failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
CloseHandle(nul_read);
CloseHandle(nul_write);
return -1;
@@ -628,17 +627,39 @@
ZeroMemory( &startup, sizeof(startup) );
startup.cb = sizeof(startup);
startup.hStdInput = nul_read;
- startup.hStdOutput = pipe_write;
+ startup.hStdOutput = nul_write;
startup.hStdError = nul_write;
startup.dwFlags = STARTF_USESTDHANDLES;
ZeroMemory( &pinfo, sizeof(pinfo) );
/* get path of current program */
- GetModuleFileName( NULL, program_path, sizeof(program_path) );
- char args[64];
- snprintf(args, sizeof(args), "adb -P %d fork-server server", server_port);
- ret = CreateProcess(
+ DWORD module_result = GetModuleFileNameW(NULL, program_path,
+ arraysize(program_path));
+ if ((module_result == arraysize(program_path)) || (module_result == 0)) {
+ // String truncation or some other error.
+ fprintf(stderr, "GetModuleFileNameW() failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ return -1;
+ }
+
+ // Verify that the pipe_write handle value can be passed on the command line
+ // as %d and that the rest of adb code can pass it around in an int.
+ const int pipe_write_as_int = cast_handle_to_int(pipe_write);
+ if (cast_int_to_handle(pipe_write_as_int) != pipe_write) {
+ // If this fires, either handle values are larger than 32-bits or else
+ // there is a bug in our casting.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
+ fprintf(stderr, "CreatePipe handle value too large: 0x%p\n",
+ pipe_write);
+ return -1;
+ }
+
+ WCHAR args[64];
+ snwprintf(args, arraysize(args),
+ L"adb -P %d fork-server server --reply-fd %d", server_port,
+ pipe_write_as_int);
+ ret = CreateProcessW(
program_path, /* program path */
args,
/* the fork-server argument will set the
@@ -657,7 +678,8 @@
CloseHandle( pipe_write );
if (!ret) {
- fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
+ fprintf(stderr, "CreateProcess failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
CloseHandle( pipe_read );
return -1;
}
@@ -673,7 +695,8 @@
ret = ReadFile( pipe_read, temp, 3, &count, NULL );
CloseHandle( pipe_read );
if ( !ret ) {
- fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
+ fprintf(stderr, "could not read ok from ADB Server, error: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
@@ -686,7 +709,7 @@
int fd[2];
// set up a pipe so the child can tell us when it is ready.
- // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
+ // fd[0] will be parent's end, and the child will write on fd[1]
if (pipe(fd)) {
fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
return -1;
@@ -698,16 +721,14 @@
if (pid == 0) {
// child side of the fork
- // redirect stderr to the pipe
- // we use stderr instead of stdout due to stdout's buffering behavior.
adb_close(fd[0]);
- dup2(fd[1], STDERR_FILENO);
- adb_close(fd[1]);
char str_port[30];
- snprintf(str_port, sizeof(str_port), "%d", server_port);
+ snprintf(str_port, sizeof(str_port), "%d", server_port);
+ char reply_fd[30];
+ snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
// child process
- int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL);
+ int result = execl(path, "adb", "-P", str_port, "fork-server", "server", "--reply-fd", reply_fd, NULL);
// this should not return
fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
} else {
@@ -770,12 +791,12 @@
if (android::base::StartsWith(service, "killforward:")) {
kill_forward = true;
service += 12;
+ } else {
+ service += 8; // skip past "forward:"
if (android::base::StartsWith(service, "norebind:")) {
no_rebind = true;
service += 9;
}
- } else {
- service += 8;
}
std::vector<std::string> pieces = android::base::Split(service, ";");
@@ -801,11 +822,13 @@
return 1;
}
+ std::string error;
InstallStatus r;
if (kill_forward) {
r = remove_listener(pieces[0].c_str(), transport);
} else {
- r = install_listener(pieces[0], pieces[1].c_str(), transport, no_rebind);
+ r = install_listener(pieces[0], pieces[1].c_str(), transport,
+ no_rebind, &error);
}
if (r == INSTALL_STATUS_OK) {
#if ADB_HOST
@@ -821,10 +844,11 @@
case INSTALL_STATUS_OK: message = "success (!)"; break;
case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
case INSTALL_STATUS_CANNOT_BIND:
- message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
+ message = android::base::StringPrintf("cannot bind to socket: %s",
+ error.c_str());
break;
case INSTALL_STATUS_CANNOT_REBIND:
- message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
+ message = android::base::StringPrintf("cannot rebind existing socket");
break;
case INSTALL_STATUS_LISTENER_NOT_FOUND:
message = android::base::StringPrintf("listener '%s' not found", service);
diff --git a/adb/adb.h b/adb/adb.h
index 309b0e9..b0e53f0 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -299,7 +299,7 @@
void get_my_path(char *s, size_t maxLen);
int launch_server(int server_port);
-int adb_main(int is_daemon, int server_port);
+int adb_main(int is_daemon, int server_port, int ack_reply_fd);
/* initialize a transport object's func pointers and state */
#if ADB_HOST
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index b6bb00c..e7f82a9 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -301,11 +301,18 @@
char android_dir[PATH_MAX];
struct stat buf;
#ifdef _WIN32
- char path[PATH_MAX];
+ std::string home_str;
home = getenv("ANDROID_SDK_HOME");
if (!home) {
- SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
- home = path;
+ WCHAR path[MAX_PATH];
+ const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
+ if (FAILED(hr)) {
+ D("SHGetFolderPathW failed: %s\n",
+ SystemErrorCodeToString(hr).c_str());
+ return -1;
+ }
+ home_str = narrow(path);
+ home = home_str.c_str();
}
format = "%s\\%s";
#else
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index 418662c..afff2ef 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -153,8 +153,8 @@
}
int fd;
+ std::string reason;
if (__adb_server_name) {
- std::string reason;
fd = network_connect(__adb_server_name, __adb_server_port, SOCK_STREAM, 0, &reason);
if (fd == -1) {
*error = android::base::StringPrintf("can't connect to %s:%d: %s",
@@ -163,9 +163,10 @@
return -2;
}
} else {
- fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
+ fd = network_loopback_client(__adb_server_port, SOCK_STREAM, &reason);
if (fd == -1) {
- *error = perror_str("cannot connect to daemon");
+ *error = android::base::StringPrintf("cannot connect to daemon: %s",
+ reason.c_str());
return -2;
}
}
@@ -228,8 +229,9 @@
}
} else {
// if fd is -1, then check for "unknown host service",
- // which would indicate a version of adb that does not support the version command
- if (*error == "unknown host service") {
+ // which would indicate a version of adb that does not support the
+ // version command, in which case we should fall-through to kill it.
+ if (*error != "unknown host service") {
return fd;
}
}
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 8fd5cbf..f637073 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -28,30 +28,13 @@
#include <string>
#include "base/file.h"
+#include "base/test_utils.h"
-class TemporaryFile {
- public:
- TemporaryFile() {
- init("/data/local/tmp");
- if (fd == -1) {
- init("/tmp");
- }
- }
-
- ~TemporaryFile() {
- close(fd);
- unlink(filename);
- }
-
- int fd;
- char filename[1024];
-
- private:
- void init(const char* tmp_dir) {
- snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
- fd = mkstemp(filename);
- }
-};
+// All of these tests fail on Windows because they use the C Runtime open(),
+// but the adb_io APIs expect file descriptors from adb_open(). Also, the
+// android::base file APIs use the C Runtime which uses CR/LF translation by
+// default (changeable with _setmode()), but the adb_io APIs use adb_read()
+// and adb_write() which do no translation.
TEST(io, ReadFdExactly_whole) {
const char expected[] = "Foobar";
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index bb45022..1e7ce5d 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -110,27 +110,30 @@
free_listener(reinterpret_cast<alistener*>(listener));
}
-static int local_name_to_fd(const char* name) {
+static int local_name_to_fd(const char* name, std::string* error) {
if (!strncmp("tcp:", name, 4)) {
int port = atoi(name + 4);
if (gListenAll > 0) {
- return socket_inaddr_any_server(port, SOCK_STREAM);
+ return network_inaddr_any_server(port, SOCK_STREAM, error);
} else {
- return socket_loopback_server(port, SOCK_STREAM);
+ return network_loopback_server(port, SOCK_STREAM, error);
}
}
#if !defined(_WIN32) // No Unix-domain sockets on Windows.
// It's nonsensical to support the "reserved" space on the adb host side
if (!strncmp(name, "local:", 6)) {
- return socket_local_server(name + 6, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ return network_local_server(name + 6,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
} else if (!strncmp(name, "localabstract:", 14)) {
- return socket_local_server(name + 14, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ return network_local_server(name + 14,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
} else if (!strncmp(name, "localfilesystem:", 16)) {
- return socket_local_server(name + 16, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+ return network_local_server(name + 16,
+ ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM, error);
}
#endif
- printf("unknown local portname '%s'\n", name);
+ *error = android::base::StringPrintf("unknown local portname '%s'", name);
return -1;
}
@@ -178,7 +181,8 @@
InstallStatus install_listener(const std::string& local_name,
const char *connect_to,
atransport* transport,
- int no_rebind)
+ int no_rebind,
+ std::string* error)
{
for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
if (local_name == l->local_name) {
@@ -226,9 +230,9 @@
goto nomem;
}
- listener->fd = local_name_to_fd(listener->local_name);
+ listener->fd = local_name_to_fd(listener->local_name, error);
if (listener->fd < 0) {
- printf("cannot bind '%s': %s\n", listener->local_name, strerror(errno));
+ printf("cannot bind '%s': %s\n", listener->local_name, error->c_str());
free(listener->local_name);
free(listener->connect_to);
free(listener);
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 67deb21..fa98eed 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -33,7 +33,8 @@
InstallStatus install_listener(const std::string& local_name,
const char* connect_to,
atransport* transport,
- int no_rebind);
+ int no_rebind,
+ std::string* error);
std::string format_listeners();
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 6fa6c2e..ca843bd 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -18,6 +18,7 @@
#include "adb_utils.h"
+#include <libgen.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -28,16 +29,11 @@
#include <base/logging.h>
#include <base/stringprintf.h>
#include <base/strings.h>
-#include <cutils/sockets.h>
#include "adb_trace.h"
#include "sysdeps.h"
-#if defined(_WIN32)
-#include <ws2tcpip.h>
-#else
-#include <netdb.h>
-#endif
+ADB_MUTEX_DEFINE(dirname_lock);
bool getcwd(std::string* s) {
char* cwd = getcwd(nullptr, 0);
@@ -72,24 +68,87 @@
return result;
}
-int mkdirs(const std::string& path) {
- // TODO: rewrite this function and merge it with the *other* mkdirs in adb.
- std::unique_ptr<char> path_rw(strdup(path.c_str()));
- int ret;
- char* x = path_rw.get() + 1;
+std::string adb_basename(const std::string& path) {
+ size_t base = path.find_last_of(OS_PATH_SEPARATORS);
+ return (base != std::string::npos) ? path.substr(base + 1) : path;
+}
- for(;;) {
- x = const_cast<char*>(adb_dirstart(x));
- if(x == 0) return 0;
- *x = 0;
- ret = adb_mkdir(path_rw.get(), 0775);
- *x = OS_PATH_SEPARATOR;
- if((ret < 0) && (errno != EEXIST)) {
- return ret;
- }
- x++;
+std::string adb_dirname(const std::string& path) {
+ // Copy path because dirname may modify the string passed in.
+ std::string parent_storage(path);
+
+ // Use lock because dirname() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to dirname in the process also grab this same lock.
+ adb_mutex_lock(&dirname_lock);
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* parent = dirname(&parent_storage[0]);
+
+ // In case dirname returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ const std::string result(parent);
+
+ adb_mutex_unlock(&dirname_lock);
+
+ return result;
+}
+
+// Given a relative or absolute filepath, create the parent directory hierarchy
+// as needed. Returns true if the hierarchy is/was setup.
+bool mkdirs(const std::string& path) {
+ // TODO: all the callers do unlink && mkdirs && adb_creat ---
+ // that's probably the operation we should expose.
+
+ // Implementation Notes:
+ //
+ // Pros:
+ // - Uses dirname, so does not need to deal with OS_PATH_SEPARATOR.
+ // - On Windows, uses mingw dirname which accepts '/' and '\\', drive letters
+ // (C:\foo), UNC paths (\\server\share\dir\dir\file), and Unicode (when
+ // combined with our adb_mkdir() which takes UTF-8).
+ // - Is optimistic wrt thinking that a deep directory hierarchy will exist.
+ // So it does as few stat()s as possible before doing mkdir()s.
+ // Cons:
+ // - Recursive, so it uses stack space relative to number of directory
+ // components.
+
+ const std::string parent(adb_dirname(path));
+
+ if (directory_exists(parent)) {
+ return true;
+ }
+
+ // If dirname returned the same path as what we passed in, don't go recursive.
+ // This can happen on Windows when walking up the directory hierarchy and not
+ // finding anything that already exists (unlike POSIX that will eventually
+ // find . or /).
+ if (parent == path) {
+ errno = ENOENT;
+ return false;
+ }
+
+ // Recursively make parent directories of 'parent'.
+ if (!mkdirs(parent)) {
+ return false;
+ }
+
+ // Now that the parent directory hierarchy of 'parent' has been ensured,
+ // create parent itself.
+ if (adb_mkdir(parent, 0775) == -1) {
+ // Can't just check for errno == EEXIST because it might be a file that
+ // exists.
+ const int saved_errno = errno;
+ if (directory_exists(parent)) {
+ return true;
}
- return 0;
+ errno = saved_errno;
+ return false;
+ }
+
+ return true;
}
void dump_hex(const void* data, size_t byte_count) {
@@ -166,18 +225,3 @@
<< " (" << *canonical_address << ")";
return true;
}
-
-int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
- int getaddrinfo_error = 0;
- int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
- if (fd != -1) {
- return fd;
- }
- if (getaddrinfo_error != 0) {
- // TODO: gai_strerror is not thread safe on Win32.
- *error = gai_strerror(getaddrinfo_error);
- } else {
- *error = strerror(errno);
- }
- return -1;
-}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 673aaac..739efcc 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -22,7 +22,12 @@
bool getcwd(std::string* cwd);
bool directory_exists(const std::string& path);
-int mkdirs(const std::string& path);
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string adb_basename(const std::string& path);
+std::string adb_dirname(const std::string& path);
+
+bool mkdirs(const std::string& path);
std::string escape_arg(const std::string& s);
@@ -39,6 +44,4 @@
std::string* host, int* port,
std::string* error);
-int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
-
#endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 7aa610a..17c8d0a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -16,12 +16,60 @@
#include "adb_utils.h"
+#ifdef _WIN32
+#include <windows.h>
+#include <userenv.h>
+#endif
+
+#include <string>
+
#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sysdeps.h"
+
+#include <base/macros.h>
+#include <base/test_utils.h>
+
+#ifdef _WIN32
+static std::string subdir(const char* parent, const char* child) {
+ std::string str(parent);
+ str += OS_PATH_SEPARATOR;
+ str += child;
+ return str;
+}
+#endif
+
TEST(adb_utils, directory_exists) {
+#ifdef _WIN32
+ char profiles_dir[MAX_PATH];
+ DWORD cch = arraysize(profiles_dir);
+
+ // On typical Windows 7, returns C:\Users
+ ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
+
+ ASSERT_TRUE(directory_exists(profiles_dir));
+
+ // On modern (English?) Windows, this is a directory symbolic link to
+ // C:\ProgramData. Symbolic links are rare on Windows and the user requires
+ // a special permission (by default granted to Administrative users) to
+ // create symbolic links.
+ ASSERT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
+
+ // On modern (English?) Windows, this is a directory junction to
+ // C:\Users\Default. Junctions are used throughout user profile directories
+ // for backwards compatibility and they don't require any special permissions
+ // to create.
+ ASSERT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
+
+ ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
+#else
ASSERT_TRUE(directory_exists("/proc"));
ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
+#endif
}
TEST(adb_utils, escape_arg) {
@@ -51,6 +99,11 @@
ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
}
+TEST(adb_utils, adb_basename) {
+ EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
+ EXPECT_EQ("sh", adb_basename("sh"));
+}
+
TEST(adb_utils, parse_host_and_port) {
std::string canonical_address;
std::string host;
@@ -132,3 +185,20 @@
EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error));
EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
}
+
+void test_mkdirs(const std::string basepath) {
+ EXPECT_TRUE(mkdirs(basepath));
+ EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
+ EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+}
+
+TEST(adb_utils, mkdirs) {
+ TemporaryDir td;
+
+ // Absolute paths.
+ test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+
+ // Relative paths.
+ ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
+ test_mkdirs(std::string("relative/subrel/file"));
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index c018b8a..73acbb0 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -82,21 +82,22 @@
static std::string GetLogFilePath() {
const char log_name[] = "adb.log";
- char temp_path[MAX_PATH - sizeof(log_name) + 1];
+ WCHAR temp_path[MAX_PATH];
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
- DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
- CHECK_LE(nchars, sizeof(temp_path));
- if (nchars == 0) {
- // TODO(danalbert): Log the error message from FormatError().
- // Windows unfortunately has two errnos, errno and GetLastError(), so
- // I'm not sure what to do about PLOG here. Probably better to just
- // ignore it and add a simplified version of FormatError() for use in
- // log messages.
+ DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
+ if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
+ // If string truncation or some other error.
+ // TODO(danalbert): Log the error message from
+ // FormatMessage(GetLastError()). Pure Windows APIs only touch
+ // GetLastError(), C Runtime APIs touch errno, so maybe there should be
+ // WPLOG or PLOGW (which would read GetLastError() instead of errno),
+ // in addition to PLOG, or maybe better to just ignore it and add a
+ // simplified version of FormatMessage() for use in log messages.
LOG(ERROR) << "Error creating log file";
}
- return std::string(temp_path) + log_name;
+ return narrow(temp_path) + log_name;
}
#else
static const char kNullFileName[] = "/dev/null";
@@ -131,7 +132,7 @@
fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
}
-int adb_main(int is_daemon, int server_port) {
+int adb_main(int is_daemon, int server_port, int ack_reply_fd) {
HOST = 1;
#if defined(_WIN32)
@@ -150,34 +151,34 @@
local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
adb_auth_init();
+ std::string error;
std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
- if (install_listener(local_name, "*smartsocket*", nullptr, 0)) {
- LOG(FATAL) << "Could not install *smartsocket* listener";
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0, &error)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener: " << error;
}
+ // Inform our parent that we are up and running.
if (is_daemon) {
- // Inform our parent that we are up and running.
+#if defined(_WIN32)
+ const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+ const CHAR ack[] = "OK\n";
+ const DWORD bytes_to_write = arraysize(ack) - 1;
+ DWORD written = 0;
+ if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+ fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+ SystemErrorCodeToString(GetLastError()).c_str());
+ }
+ if (written != bytes_to_write) {
+ fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
+ bytes_to_write, written);
+ }
+ CloseHandle(ack_reply_handle);
+#else
// TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
// "OKAY".
- // TODO(danalbert): Why do we use stdout for Windows? There is a
- // comment in launch_server() that suggests that non-Windows uses
- // stderr because it is non-buffered. So perhaps the history is that
- // stdout was preferred for all platforms, but it was discovered that
- // non-Windows needed a non-buffered fd, so stderr was used there.
- // Note that using stderr on unix means that if you do
- // `ADB_TRACE=all adb start-server`, it will say "ADB server didn't ACK"
- // and "* failed to start daemon *" because the adb server will write
- // logging to stderr, obscuring the OK\n output that is sent to stderr.
-#if defined(_WIN32)
- int reply_fd = STDOUT_FILENO;
- // Change stdout mode to binary so \n => \r\n translation does not
- // occur. In a moment stdout will be reopened to the daemon log file
- // anyway.
- _setmode(reply_fd, _O_BINARY);
-#else
- int reply_fd = STDERR_FILENO;
+ android::base::WriteStringToFd("OK\n", ack_reply_fd);
+ unix_close(ack_reply_fd);
#endif
- android::base::WriteStringToFd("OK\n", reply_fd);
close_stdin();
setup_daemon_logging();
}
@@ -188,9 +189,34 @@
return 0;
}
+#ifdef _WIN32
+static bool _argv_is_utf8 = false;
+#endif
+
int main(int argc, char** argv) {
+#ifdef _WIN32
+ if (!_argv_is_utf8) {
+ fatal("_argv_is_utf8 is not set, suggesting that wmain was not "
+ "called. Did you forget to link with -municode?");
+ }
+#endif
+
adb_sysdeps_init();
adb_trace_init(argv);
- D("Handling commandline()\n");
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}
+
+#ifdef _WIN32
+
+extern "C"
+int wmain(int argc, wchar_t **argv) {
+ // Set diagnostic flag to try to detect if the build system was not
+ // configured to call wmain.
+ _argv_is_utf8 = true;
+
+ // Convert args from UTF-16 to UTF-8 and pass that to main().
+ NarrowArgs narrow_args(argc, argv);
+ return main(argc, narrow_args.data());
+}
+
+#endif
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index d54faec..1e1690e 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -847,25 +847,25 @@
* Given <hint>, try to construct an absolute path to the
* ANDROID_PRODUCT_OUT dir.
*/
-static std::string find_product_out_path(const char* hint) {
- if (hint == NULL || hint[0] == '\0') {
+static std::string find_product_out_path(const std::string& hint) {
+ if (hint.empty()) {
return "";
}
// If it's already absolute, don't bother doing any work.
- if (adb_is_absolute_host_path(hint)) {
+ if (adb_is_absolute_host_path(hint.c_str())) {
return hint;
}
// If there are any slashes in it, assume it's a relative path;
// make it absolute.
- if (adb_dirstart(hint) != nullptr) {
+ if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {
std::string cwd;
if (!getcwd(&cwd)) {
fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
return "";
}
- return android::base::StringPrintf("%s%s%s", cwd.c_str(), OS_PATH_SEPARATOR_STR, hint);
+ return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
}
// It's a string without any slashes. Try to do something with it.
@@ -889,7 +889,7 @@
path += hint;
if (!directory_exists(path)) {
fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
- "\"%s\" doesn't exist\n", hint, path.c_str());
+ "\"%s\" doesn't exist\n", hint.c_str(), path.c_str());
return "";
}
return path;
@@ -953,6 +953,7 @@
int is_server = 0;
int r;
TransportType transport_type = kTransportAny;
+ int ack_reply_fd = -1;
// If defined, this should be an absolute path to
// the directory containing all of the various system images
@@ -971,7 +972,7 @@
const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
int server_port = DEFAULT_ADB_PORT;
if (server_port_str && strlen(server_port_str) > 0) {
- server_port = (int) strtol(server_port_str, NULL, 0);
+ server_port = strtol(server_port_str, nullptr, 0);
if (server_port <= 0 || server_port > 65535) {
fprintf(stderr,
"adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65535. Got \"%s\"\n",
@@ -989,8 +990,25 @@
} else if (!strcmp(argv[0], "fork-server")) {
/* this is a special flag used only when the ADB client launches the ADB Server */
is_daemon = 1;
+ } else if (!strcmp(argv[0], "--reply-fd")) {
+ if (argc < 2) return usage();
+ const char* reply_fd_str = argv[1];
+ argc--;
+ argv++;
+ ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
+#ifdef _WIN32
+ const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+ if ((GetStdHandle(STD_INPUT_HANDLE) == ack_reply_handle) ||
+ (GetStdHandle(STD_OUTPUT_HANDLE) == ack_reply_handle) ||
+ (GetStdHandle(STD_ERROR_HANDLE) == ack_reply_handle)) {
+#else
+ if (ack_reply_fd <= 2) { // Disallow stdin, stdout, and stderr.
+#endif
+ fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
+ return usage();
+ }
} else if (!strncmp(argv[0], "-p", 2)) {
- const char *product = NULL;
+ const char* product = nullptr;
if (argv[0][2] == '\0') {
if (argc < 2) return usage();
product = argv[1];
@@ -1066,7 +1084,11 @@
if (is_server) {
if (no_daemon || is_daemon) {
- r = adb_main(is_daemon, server_port);
+ if (is_daemon && (ack_reply_fd == -1)) {
+ fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
+ return usage();
+ }
+ r = adb_main(is_daemon, server_port, ack_reply_fd);
} else {
r = launch_server(server_port);
}
@@ -1462,22 +1484,11 @@
return pm_command(transport, serial, argc, argv);
}
-static int delete_file(TransportType transport, const char* serial, char* filename) {
+static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
std::string cmd = "shell:rm -f " + escape_arg(filename);
return send_shell_command(transport, serial, cmd);
}
-static const char* get_basename(const char* filename)
-{
- const char* basename = adb_dirstop(filename);
- if (basename) {
- basename++;
- return basename;
- } else {
- return filename;
- }
-}
-
static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
static const char *const DATA_DEST = "/data/local/tmp/%s";
static const char *const SD_DEST = "/sdcard/tmp/%s";
@@ -1514,13 +1525,12 @@
}
const char* apk_file = argv[last_apk];
- char apk_dest[PATH_MAX];
- snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
- int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);
+ std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
+ int err = do_sync_push(apk_file, apk_dest.c_str(), 0 /* no show progress */);
if (err) {
goto cleanup_apk;
} else {
- argv[last_apk] = apk_dest; /* destination name, not source location */
+ argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
}
err = pm_command(transport, serial, argc, argv);
@@ -1604,7 +1614,7 @@
std::string cmd = android::base::StringPrintf(
"exec:pm install-write -S %" PRIu64 " %d %d_%s -",
- static_cast<uint64_t>(sb.st_size), session_id, i, get_basename(file));
+ static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
diff --git a/adb/console.cpp b/adb/console.cpp
index b7f5345..ba5a72b 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -71,9 +71,11 @@
return -1;
}
- int fd = socket_loopback_client(port, SOCK_STREAM);
+ std::string error;
+ int fd = network_loopback_client(port, SOCK_STREAM, &error);
if (fd == -1) {
- fprintf(stderr, "error: could not connect to TCP port %d\n", port);
+ fprintf(stderr, "error: could not connect to TCP port %d: %s\n", port,
+ error.c_str());
return -1;
}
return fd;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 157c97b..dc89639 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -68,13 +68,6 @@
#if defined(ALLOW_ADBD_ROOT)
char value[PROPERTY_VALUE_MAX];
- // The emulator is never secure, so don't drop privileges there.
- // TODO: this seems like a bug --- shouldn't the emulator behave like a device?
- property_get("ro.kernel.qemu", value, "");
- if (strcmp(value, "1") == 0) {
- return false;
- }
-
// The properties that affect `adb root` and `adb unroot` are ro.secure and
// ro.debuggable. In this context the names don't make the expected behavior
// particularly obvious.
@@ -177,10 +170,13 @@
LOG(FATAL) << "Could not set selinux context";
}
}
+ std::string error;
std::string local_name =
android::base::StringPrintf("tcp:%d", server_port);
- if (install_listener(local_name, "*smartsocket*", nullptr, 0)) {
- LOG(FATAL) << "Could not install *smartsocket* listener";
+ if (install_listener(local_name, "*smartsocket*", nullptr, 0,
+ &error)) {
+ LOG(FATAL) << "Could not install *smartsocket* listener: "
+ << error;
}
}
diff --git a/adb/device.py b/adb/device.py
index 57f17fc..a15675b 100644
--- a/adb/device.py
+++ b/adb/device.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+import logging
import os
import re
import subprocess
@@ -146,10 +147,12 @@
return result, out
def _simple_call(self, cmd):
+ logging.info(' '.join(self.adb_cmd + cmd))
return subprocess.check_output(
self.adb_cmd + cmd, stderr=subprocess.STDOUT)
def shell(self, cmd):
+ logging.info(' '.join(self.adb_cmd + ['shell'] + cmd))
cmd = self._make_shell_cmd(cmd)
out = subprocess.check_output(cmd)
rc, out = self._parse_shell_output(out)
@@ -161,13 +164,18 @@
def shell_nocheck(self, cmd):
cmd = self._make_shell_cmd(cmd)
+ logging.info(' '.join(cmd))
p = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out, _ = p.communicate()
return self._parse_shell_output(out)
- def install(self, filename):
- return self._simple_call(['install', filename])
+ def install(self, filename, replace=False):
+ cmd = ['install']
+ if replace:
+ cmd.append('-r')
+ cmd.append(filename)
+ return self._simple_call(cmd)
def push(self, local, remote):
return self._simple_call(['push', local, remote])
@@ -190,6 +198,9 @@
def usb(self):
return self._simple_call(['usb'])
+ def reboot(self):
+ return self._simple_call(['reboot'])
+
def root(self):
return self._simple_call(['root'])
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 5cd4988..a8abade 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -234,7 +234,7 @@
#else /* USE_SELECT */
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
#include <winsock2.h>
#else
#include <sys/select.h>
@@ -617,7 +617,7 @@
fde->func = func;
fde->arg = arg;
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
fcntl(fd, F_SETFL, O_NONBLOCK);
#endif
fdevent_register(fde);
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 49d42a3..da80013 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -35,6 +35,8 @@
#include "adb_utils.h"
#include "file_sync_service.h"
+#include <base/stringprintf.h>
+
static unsigned long long total_bytes;
static long long start_time;
@@ -92,36 +94,30 @@
typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
static int sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
+ int len = strlen(path);
+ if (len > 1024) goto fail;
+
syncmsg msg;
- char buf[257];
- int len;
-
- len = strlen(path);
- if(len > 1024) goto fail;
-
msg.req.id = ID_LIST;
msg.req.namelen = htoll(len);
- if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
- !WriteFdExactly(fd, path, len)) {
+ if (!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) || !WriteFdExactly(fd, path, len)) {
goto fail;
}
- for(;;) {
- if(!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) break;
- if(msg.dent.id == ID_DONE) return 0;
- if(msg.dent.id != ID_DENT) break;
+ for (;;) {
+ if (!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) break;
+ if (msg.dent.id == ID_DONE) return 0;
+ if (msg.dent.id != ID_DENT) break;
len = ltohl(msg.dent.namelen);
- if(len > 256) break;
+ if (len > 256) break;
- if(!ReadFdExactly(fd, buf, len)) break;
+ char buf[257];
+ if (!ReadFdExactly(fd, buf, len)) break;
buf[len] = 0;
- func(ltohl(msg.dent.mode),
- ltohl(msg.dent.size),
- ltohl(msg.dent.time),
- buf, cookie);
+ func(ltohl(msg.dent.mode), ltohl(msg.dent.size), ltohl(msg.dent.time), buf, cookie);
}
fail:
@@ -220,7 +216,7 @@
return 0;
}
-static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show_progress)
+static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, bool show_progress)
{
int lfd, err = 0;
unsigned long long size = 0;
@@ -274,7 +270,7 @@
}
static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf,
- int show_progress)
+ bool show_progress)
{
int err = 0;
int total = 0;
@@ -308,10 +304,8 @@
#else
static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
{
- int len, ret;
-
- len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
- if(len < 0) {
+ int len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
+ if (len < 0) {
fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
return -1;
}
@@ -320,9 +314,9 @@
sbuf->size = htoll(len + 1);
sbuf->id = ID_DATA;
- ret = !WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
- if(ret)
+ if (!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + len + 1)) {
return -1;
+ }
total_bytes += len + 1;
@@ -331,7 +325,7 @@
#endif
static int sync_send(int fd, const char *lpath, const char *rpath,
- unsigned mtime, mode_t mode, int show_progress)
+ unsigned mtime, mode_t mode, bool show_progress)
{
syncmsg msg;
int len, r;
@@ -396,7 +390,7 @@
return -1;
}
-static int sync_recv(int fd, const char* rpath, const char* lpath, int show_progress) {
+static int sync_recv(int fd, const char* rpath, const char* lpath, bool show_progress) {
syncmsg msg;
int len;
int lfd = -1;
@@ -523,12 +517,12 @@
return 1;
}
- if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
+ if (sync_ls(fd, path, do_sync_ls_cb, 0)) {
return 1;
- } else {
- sync_quit(fd);
- return 0;
}
+
+ sync_quit(fd);
+ return 0;
}
struct copyinfo
@@ -712,11 +706,7 @@
}
-int do_sync_push(const char *lpath, const char *rpath, int show_progress)
-{
- struct stat st;
- unsigned mode;
-
+int do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
std::string error;
int fd = adb_connect("sync:", &error);
if (fd < 0) {
@@ -724,51 +714,38 @@
return 1;
}
- if(stat(lpath, &st)) {
+ struct stat st;
+ if (stat(lpath, &st)) {
fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
sync_quit(fd);
return 1;
}
- if(S_ISDIR(st.st_mode)) {
+ if (S_ISDIR(st.st_mode)) {
BEGIN();
- if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
+ if (copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
return 1;
- } else {
- END();
- sync_quit(fd);
}
} else {
- if(sync_readmode(fd, rpath, &mode)) {
+ unsigned mode;
+ if (sync_readmode(fd, rpath, &mode)) {
return 1;
}
- if((mode != 0) && S_ISDIR(mode)) {
- /* if we're copying a local file to a remote directory,
- ** we *really* want to copy to remotedir + "/" + localfilename
- */
- const char *name = adb_dirstop(lpath);
- if(name == 0) {
- name = lpath;
- } else {
- name++;
- }
- int tmplen = strlen(name) + strlen(rpath) + 2;
- char *tmp = reinterpret_cast<char*>(
- malloc(strlen(name) + strlen(rpath) + 2));
- if(tmp == 0) return 1;
- snprintf(tmp, tmplen, "%s/%s", rpath, name);
- rpath = tmp;
+ std::string path_holder;
+ if ((mode != 0) && S_ISDIR(mode)) {
+ // If we're copying a local file to a remote directory,
+ // we really want to copy to remote_dir + "/" + local_filename.
+ path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
+ rpath = path_holder.c_str();
}
BEGIN();
- if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) {
+ if (sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) {
return 1;
- } else {
- END();
- sync_quit(fd);
- return 0;
}
}
+ END();
+ sync_quit(fd);
return 0;
}
@@ -934,11 +911,7 @@
return ret;
}
-int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int copy_attrs)
-{
- unsigned mode, time;
- struct stat st;
-
+int do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) {
std::string error;
int fd = adb_connect("sync:", &error);
if (fd < 0) {
@@ -946,56 +919,46 @@
return 1;
}
- if(sync_readtime(fd, rpath, &time, &mode)) {
+ unsigned mode, time;
+ if (sync_readtime(fd, rpath, &time, &mode)) {
return 1;
}
- if(mode == 0) {
+ if (mode == 0) {
fprintf(stderr,"remote object '%s' does not exist\n", rpath);
return 1;
}
- if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
- if(stat(lpath, &st) == 0) {
- if(S_ISDIR(st.st_mode)) {
- /* if we're copying a remote file to a local directory,
- ** we *really* want to copy to localdir + "/" + remotefilename
- */
- const char *name = adb_dirstop(rpath);
- if(name == 0) {
- name = rpath;
- } else {
- name++;
- }
- int tmplen = strlen(name) + strlen(lpath) + 2;
- char *tmp = reinterpret_cast<char*>(malloc(tmplen));
- if(tmp == 0) return 1;
- snprintf(tmp, tmplen, "%s/%s", lpath, name);
- lpath = tmp;
+ if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+ std::string path_holder;
+ struct stat st;
+ if (stat(lpath, &st) == 0) {
+ if (S_ISDIR(st.st_mode)) {
+ // If we're copying a remote file to a local directory,
+ // we really want to copy to local_dir + "/" + basename(remote).
+ path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
+ lpath = path_holder.c_str();
}
}
BEGIN();
if (sync_recv(fd, rpath, lpath, show_progress)) {
return 1;
} else {
- if (copy_attrs && set_time_and_mode(lpath, time, mode))
+ if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
return 1;
- END();
- sync_quit(fd);
- return 0;
+ }
}
} else if(S_ISDIR(mode)) {
BEGIN();
if (copy_remote_dir_local(fd, rpath, lpath, copy_attrs)) {
return 1;
- } else {
- END();
- sync_quit(fd);
- return 0;
}
} else {
fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
return 1;
}
+ END();
+ sync_quit(fd);
+ return 0;
}
int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only)
@@ -1012,9 +975,8 @@
BEGIN();
if (copy_local_dir_remote(fd, lpath.c_str(), rpath.c_str(), 1, list_only)) {
return 1;
- } else {
- END();
- sync_quit(fd);
- return 0;
}
+ END();
+ sync_quit(fd);
+ return 0;
}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 2067836..ea019f4 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -34,47 +34,46 @@
#include "adb_io.h"
#include "private/android_filesystem_config.h"
-static bool should_use_fs_config(const char* path) {
+#include <base/strings.h>
+
+static bool should_use_fs_config(const std::string& path) {
// TODO: use fs_config to configure permissions on /data.
- return strncmp("/system/", path, strlen("/system/")) == 0 ||
- strncmp("/vendor/", path, strlen("/vendor/")) == 0 ||
- strncmp("/oem/", path, strlen("/oem/")) == 0;
+ return android::base::StartsWith(path, "/system/") ||
+ android::base::StartsWith(path, "/vendor/") ||
+ android::base::StartsWith(path, "/oem/");
}
-static int mkdirs(char *name)
-{
- int ret;
- char *x = name + 1;
+static bool secure_mkdirs(const std::string& path) {
uid_t uid = -1;
gid_t gid = -1;
unsigned int mode = 0775;
uint64_t cap = 0;
- if(name[0] != '/') return -1;
+ if (path[0] != '/') return false;
- for(;;) {
- x = const_cast<char*>(adb_dirstart(x));
- if(x == 0) return 0;
- *x = 0;
- if (should_use_fs_config(name)) {
- fs_config(name, 1, &uid, &gid, &mode, &cap);
+ std::vector<std::string> path_components = android::base::Split(path, "/");
+ path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
+
+ std::string partial_path;
+ for (auto& path_component : path_components) {
+ if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
+ partial_path += path_component;
+
+ if (should_use_fs_config(partial_path)) {
+ fs_config(partial_path.c_str(), 1, &uid, &gid, &mode, &cap);
}
- ret = adb_mkdir(name, mode);
- if((ret < 0) && (errno != EEXIST)) {
- D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
- *x = '/';
- return ret;
- } else if(ret == 0) {
- ret = chown(name, uid, gid);
- if (ret < 0) {
- *x = '/';
- return ret;
+ if (adb_mkdir(partial_path.c_str(), mode) == -1) {
+ if (errno != EEXIST) {
+ return false;
}
- selinux_android_restorecon(name, 0);
+ } else {
+ if (chown(partial_path.c_str(), uid, gid) == -1) {
+ return false;
+ }
+ selinux_android_restorecon(partial_path.c_str(), 0);
}
- *x++ = '/';
}
- return 0;
+ return true;
}
static int do_stat(int s, const char *path)
@@ -99,26 +98,24 @@
static int do_list(int s, const char *path)
{
- DIR *d;
struct dirent *de;
struct stat st;
- syncmsg msg;
- int len;
char tmp[1024 + 256 + 1];
char *fname;
- len = strlen(path);
+ size_t len = strlen(path);
memcpy(tmp, path, len);
tmp[len] = '/';
fname = tmp + len + 1;
+ syncmsg msg;
msg.dent.id = ID_DENT;
- d = opendir(path);
- if(d == 0) goto done;
+ std::unique_ptr<DIR, int(*)(DIR*)> d(opendir(path), closedir);
+ if (!d) goto done;
- while((de = readdir(d))) {
+ while ((de = readdir(d.get()))) {
int len = strlen(de->d_name);
/* not supposed to be possible, but
@@ -134,14 +131,11 @@
if(!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
!WriteFdExactly(s, de->d_name, len)) {
- closedir(d);
return -1;
}
}
}
- closedir(d);
-
done:
msg.dent.id = ID_DONE;
msg.dent.mode = 0;
@@ -182,7 +176,7 @@
fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
if(fd < 0 && errno == ENOENT) {
- if(mkdirs(path) != 0) {
+ if (!secure_mkdirs(path)) {
if(fail_errno(s))
return -1;
fd = -1;
@@ -294,7 +288,7 @@
ret = symlink(buffer, path);
if(ret && errno == ENOENT) {
- if(mkdirs(path) != 0) {
+ if (!secure_mkdirs(path)) {
fail_errno(s);
return -1;
}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 344eb98..1d3e3bd 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -68,9 +68,9 @@
void file_sync_service(int fd, void *cookie);
int do_sync_ls(const char *path);
-int do_sync_push(const char *lpath, const char *rpath, int show_progress);
+int do_sync_push(const char *lpath, const char *rpath, bool show_progress);
int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
+int do_sync_pull(const char *rpath, const char *lpath, bool show_progress, int pullTime);
#define SYNC_DATA_MAX (64*1024)
diff --git a/adb/get_my_path_windows.cpp b/adb/get_my_path_windows.cpp
deleted file mode 100644
index 9d23e1c..0000000
--- a/adb/get_my_path_windows.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007 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 <assert.h>
-#include <limits.h>
-#include <windows.h>
-
-#include "adb.h"
-
-void get_my_path(char *exe, size_t maxLen)
-{
- char *r;
-
- /* XXX: should be GetModuleFileNameA */
- if (GetModuleFileName(NULL, exe, maxLen) > 0) {
- r = strrchr(exe, '\\');
- if (r != NULL)
- *r = '\0';
- } else {
- exe[0] = '\0';
- }
-}
-
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
index ff72751..9003361 100644
--- a/adb/mutex_list.h
+++ b/adb/mutex_list.h
@@ -6,6 +6,7 @@
#ifndef ADB_MUTEX
#error ADB_MUTEX not defined when including this file
#endif
+ADB_MUTEX(dirname_lock)
ADB_MUTEX(socket_list_lock)
ADB_MUTEX(transport_lock)
#if ADB_HOST
diff --git a/adb/services.cpp b/adb/services.cpp
index 82efb1c..63a0a76 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -432,7 +432,8 @@
int port = atoi(name + 4);
name = strchr(name + 4, ':');
if(name == 0) {
- ret = socket_loopback_client(port, SOCK_STREAM);
+ std::string error;
+ ret = network_loopback_client(port, SOCK_STREAM, &error);
if (ret >= 0)
disable_tcp_nagle(ret);
} else {
@@ -443,7 +444,7 @@
return -1;
#endif
}
-#ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */
+#if !defined(_WIN32) /* winsock doesn't implement unix domain sockets */
} else if(!strncmp(name, "local:", 6)) {
ret = socket_local_client(name + 6,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 729bbcb..6f3c443 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -26,6 +26,8 @@
#include <errno.h>
+#include <string>
+
/*
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
* <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
@@ -41,21 +43,39 @@
_rc; })
#endif
+// Some printf-like functions are implemented in terms of
+// android::base::StringAppendV, so they should use the same attribute for
+// compile-time format string checking. On Windows, if the mingw version of
+// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
+// and PRIu64 (and related) to be recognized by the compile-time checking.
+#define ADB_FORMAT_ARCHETYPE __printf__
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+#undef ADB_FORMAT_ARCHETYPE
+#define ADB_FORMAT_ARCHETYPE gnu_printf
+#endif
+#endif
+
#ifdef _WIN32
#include <ctype.h>
#include <direct.h>
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
#include <sys/stat.h>
+#include <utime.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
+#include <string> // Prototypes for narrow() and widen() use std::(w)string.
+
#include "fdevent.h"
+#define OS_PATH_SEPARATORS "\\/"
#define OS_PATH_SEPARATOR '\\'
#define OS_PATH_SEPARATOR_STR "\\"
#define ENV_PATH_SEPARATOR_STR ";"
@@ -104,26 +124,11 @@
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
-static __inline__ int adb_unlink(const char* path)
-{
- int rc = unlink(path);
-
- if (rc == -1 && errno == EACCES) {
- /* unlink returns EACCES when the file is read-only, so we first */
- /* try to make it writable, then unlink again... */
- rc = chmod(path, _S_IREAD|_S_IWRITE );
- if (rc == 0)
- rc = unlink(path);
- }
- return rc;
-}
+extern int adb_unlink(const char* path);
#undef unlink
#define unlink ___xxx_unlink
-static __inline__ int adb_mkdir(const char* path, int mode)
-{
- return _mkdir(path);
-}
+extern int adb_mkdir(const std::string& path, int mode);
#undef mkdir
#define mkdir ___xxx_mkdir
@@ -165,22 +170,7 @@
}
// See the comments for the !defined(_WIN32) version of unix_open().
-static __inline__ int unix_open(const char* path, int options,...)
-{
- if ((options & O_CREAT) == 0)
- {
- return open(path, options);
- }
- else
- {
- int mode;
- va_list args;
- va_start( args, options );
- mode = va_arg( args, int );
- va_end( args );
- return open(path, options, mode);
- }
-}
+extern int unix_open(const char* path, int options, ...);
#define open ___xxx_unix_open
@@ -210,6 +200,12 @@
Sleep( mseconds );
}
+int network_loopback_client(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error);
+int network_inaddr_any_server(int port, int type, std::string* error);
+int network_connect(const std::string& host, int port, int type, int timeout,
+ std::string* error);
+
extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
#undef accept
@@ -234,41 +230,136 @@
extern int adb_socketpair( int sv[2] );
-static __inline__ char* adb_dirstart( const char* path )
-{
- char* p = strchr(path, '/');
- char* p2 = strchr(path, '\\');
-
- if ( !p )
- p = p2;
- else if ( p2 && p2 > p )
- p = p2;
-
- return p;
-}
-
-static __inline__ const char* adb_dirstop( const char* path )
-{
- const char* p = strrchr(path, '/');
- const char* p2 = strrchr(path, '\\');
-
- if ( !p )
- p = p2;
- else if ( p2 && p2 > p )
- p = p2;
-
- return p;
-}
-
-static __inline__ int adb_is_absolute_host_path( const char* path )
-{
+static __inline__ int adb_is_absolute_host_path(const char* path) {
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
+// Like strerror(), but for Win32 error codes.
+std::string SystemErrorCodeToString(DWORD error_code);
+
+// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
+// struct stat s;
+// stat(filename, &s);
+// To turn into the following:
+// struct adb_stat s;
+// adb_stat(filename, &s);
+// To get this to work, we need to make 'struct adb_stat' the same as
+// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
+// *current* macro definition of stat, so it may actually be inheriting from
+// struct _stat32i64 (or some other remapping).
+struct adb_stat : public stat {};
+
+static_assert(sizeof(struct adb_stat) == sizeof(struct stat),
+ "structures should be the same");
+
+extern int adb_stat(const char* f, struct adb_stat* s);
+
+// stat is already a macro, undefine it so we can redefine it.
+#undef stat
+#define stat adb_stat
+
+// UTF-8 versions of POSIX APIs.
+extern DIR* adb_opendir(const char* dirname);
+extern struct dirent* adb_readdir(DIR* dir);
+extern int adb_closedir(DIR* dir);
+
+extern int adb_utime(const char *, struct utimbuf *);
+extern int adb_chmod(const char *, int);
+
+extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
+ __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
+extern int adb_fprintf(FILE *stream, const char *format, ...)
+ __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
+extern int adb_printf(const char *format, ...)
+ __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+
+extern int adb_fputs(const char* buf, FILE* stream);
+extern int adb_fputc(int ch, FILE* stream);
+extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
+ FILE* stream);
+
+extern FILE* adb_fopen(const char* f, const char* m);
+
+extern char* adb_getenv(const char* name);
+
+extern char* adb_getcwd(char* buf, int size);
+
+// Remap calls to POSIX APIs to our UTF-8 versions.
+#define opendir adb_opendir
+#define readdir adb_readdir
+#define closedir adb_closedir
+#define rewinddir rewinddir_utf8_not_yet_implemented
+#define telldir telldir_utf8_not_yet_implemented
+#define seekdir seekdir_utf8_not_yet_implemented
+
+#define utime adb_utime
+#define chmod adb_chmod
+
+#define vfprintf adb_vfprintf
+#define fprintf adb_fprintf
+#define printf adb_printf
+#define fputs adb_fputs
+#define fputc adb_fputc
+#define fwrite adb_fwrite
+
+#define fopen adb_fopen
+
+#define getenv adb_getenv
+#define putenv putenv_utf8_not_yet_implemented
+#define setenv setenv_utf8_not_yet_implemented
+#define unsetenv unsetenv_utf8_not_yet_implemented
+
+#define getcwd adb_getcwd
+
+// Convert from UTF-8 to UTF-16, typically used to convert char strings into
+// wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs
+// on Windows.
+extern std::wstring widen(const std::string& utf8);
+extern std::wstring widen(const char* utf8);
+
+// Convert from UTF-16 to UTF-8, typically used to convert strings from OS and
+// C Runtime APIs that return wchar_t, to a format for our char-based data
+// structures.
+extern std::string narrow(const std::wstring& utf16);
+extern std::string narrow(const wchar_t* utf16);
+
+// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
+// passed to main().
+class NarrowArgs {
+public:
+ NarrowArgs(int argc, wchar_t** argv);
+ ~NarrowArgs();
+
+ inline char** data() {
+ return narrow_args;
+ }
+
+private:
+ char** narrow_args;
+};
+
+// Windows HANDLE values only use 32-bits of the type, even on 64-bit machines,
+// so they can fit in an int. To convert back, we just need to sign-extend.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
+// Note that this does not make a HANDLE value work with APIs like open(), nor
+// does this make a value from open() passable to APIs taking a HANDLE. This
+// just lets you take a HANDLE, pass it around as an int, and then use it again
+// as a HANDLE.
+inline int cast_handle_to_int(const HANDLE h) {
+ // truncate
+ return static_cast<int>(reinterpret_cast<INT_PTR>(h));
+}
+
+inline HANDLE cast_int_to_handle(const int fd) {
+ // sign-extend
+ return reinterpret_cast<HANDLE>(static_cast<INT_PTR>(fd));
+}
+
#else /* !_WIN32 a.k.a. Unix */
#include "fdevent.h"
#include <cutils/misc.h>
+#include <cutils/sockets.h>
#include <cutils/threads.h>
#include <signal.h>
#include <sys/wait.h>
@@ -279,11 +370,15 @@
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <unistd.h>
+#include <string>
+
+#define OS_PATH_SEPARATORS "/"
#define OS_PATH_SEPARATOR '/'
#define OS_PATH_SEPARATOR_STR "/"
#define ENV_PATH_SEPARATOR_STR ":"
@@ -426,6 +521,48 @@
#undef creat
#define creat ___xxx_creat
+// Helper for network_* functions.
+inline int _fd_set_error_str(int fd, std::string* error) {
+ if (fd == -1) {
+ *error = strerror(errno);
+ }
+ return fd;
+}
+
+inline int network_loopback_client(int port, int type, std::string* error) {
+ return _fd_set_error_str(socket_loopback_client(port, type), error);
+}
+
+inline int network_loopback_server(int port, int type, std::string* error) {
+ return _fd_set_error_str(socket_loopback_server(port, type), error);
+}
+
+inline int network_inaddr_any_server(int port, int type, std::string* error) {
+ return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
+}
+
+inline int network_local_server(const char *name, int namespace_id, int type,
+ std::string* error) {
+ return _fd_set_error_str(socket_local_server(name, namespace_id, type),
+ error);
+}
+
+inline int network_connect(const std::string& host, int port, int type,
+ int timeout, std::string* error) {
+ int getaddrinfo_error = 0;
+ int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
+ &getaddrinfo_error);
+ if (fd != -1) {
+ return fd;
+ }
+ if (getaddrinfo_error != 0) {
+ *error = gai_strerror(getaddrinfo_error);
+ } else {
+ *error = strerror(errno);
+ }
+ return -1;
+}
+
static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
{
int fd;
@@ -510,10 +647,11 @@
usleep( mseconds*1000 );
}
-static __inline__ int adb_mkdir(const char* path, int mode)
+static __inline__ int adb_mkdir(const std::string& path, int mode)
{
- return mkdir(path, mode);
+ return mkdir(path.c_str(), mode);
}
+
#undef mkdir
#define mkdir ___xxx_mkdir
@@ -521,18 +659,7 @@
{
}
-static __inline__ const char* adb_dirstart(const char* path)
-{
- return strchr(path, '/');
-}
-
-static __inline__ const char* adb_dirstop(const char* path)
-{
- return strrchr(path, '/');
-}
-
-static __inline__ int adb_is_absolute_host_path( const char* path )
-{
+static __inline__ int adb_is_absolute_host_path(const char* path) {
return path[0] == '/';
}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index a274892..db552a2 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -25,8 +25,16 @@
#include <stdio.h>
#include <stdlib.h>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
#include <cutils/sockets.h>
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
#include "adb.h"
extern void fatal(const char *fmt, ...);
@@ -80,6 +88,30 @@
#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+std::string SystemErrorCodeToString(const DWORD error_code) {
+ const int kErrorMessageBufferSize = 256;
+ WCHAR msgbuf[kErrorMessageBufferSize];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
+ arraysize(msgbuf), nullptr);
+ if (len == 0) {
+ return android::base::StringPrintf(
+ "Error (%lu) while retrieving error. (%lu)", GetLastError(),
+ error_code);
+ }
+
+ // Convert UTF-16 to UTF-8.
+ std::string msg(narrow(msgbuf));
+ // Messages returned by the system end with line breaks.
+ msg = android::base::Trim(msg);
+ // There are many Windows error messages compared to POSIX, so include the
+ // numeric error code for easier, quicker, accurate identification. Use
+ // decimal instead of hex because there are decimal ranges like 10000-11999
+ // for Winsock.
+ android::base::StringAppendF(&msg, " (%lu)", error_code);
+ return msg;
+}
+
/**************************************************************************/
/**************************************************************************/
/***** *****/
@@ -94,13 +126,13 @@
char *data;
DWORD file_size;
- file = CreateFile( fn,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL );
+ file = CreateFileW( widen(fn).c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
if (file == INVALID_HANDLE_VALUE)
return NULL;
@@ -171,7 +203,7 @@
static adb_mutex_t _win32_lock;
static FHRec _win32_fhs[ WIN32_MAX_FHS ];
-static int _win32_fh_count;
+static int _win32_fh_next; // where to start search for free FHRec
static FH
_fh_from_int( int fd, const char* func )
@@ -180,7 +212,7 @@
fd -= WIN32_FH_BASE;
- if (fd < 0 || fd >= _win32_fh_count) {
+ if (fd < 0 || fd >= WIN32_MAX_FHS) {
D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
func );
errno = EBADF;
@@ -212,28 +244,32 @@
static FH
_fh_alloc( FHClass clazz )
{
- int nn;
FH f = NULL;
adb_mutex_lock( &_win32_lock );
- if (_win32_fh_count < WIN32_MAX_FHS) {
- f = &_win32_fhs[ _win32_fh_count++ ];
- goto Exit;
- }
-
- for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
- if ( _win32_fhs[nn].clazz == NULL) {
- f = &_win32_fhs[nn];
+ // Search entire array, starting from _win32_fh_next.
+ for (int nn = 0; nn < WIN32_MAX_FHS; nn++) {
+ // Keep incrementing _win32_fh_next to avoid giving out an index that
+ // was recently closed, to try to avoid use-after-free.
+ const int index = _win32_fh_next++;
+ // Handle wrap-around of _win32_fh_next.
+ if (_win32_fh_next == WIN32_MAX_FHS) {
+ _win32_fh_next = 0;
+ }
+ if (_win32_fhs[index].clazz == NULL) {
+ f = &_win32_fhs[index];
goto Exit;
}
}
D( "_fh_alloc: no more free file descriptors\n" );
+ errno = EMFILE; // Too many open files
Exit:
if (f) {
- f->clazz = clazz;
- f->used = 1;
- f->eof = 0;
+ f->clazz = clazz;
+ f->used = 1;
+ f->eof = 0;
+ f->name[0] = '\0';
clazz->_fh_init(f);
}
adb_mutex_unlock( &_win32_lock );
@@ -244,15 +280,37 @@
static int
_fh_close( FH f )
{
- if ( f->used ) {
+ // Use lock so that closing only happens once and so that _fh_alloc can't
+ // allocate a FH that we're in the middle of closing.
+ adb_mutex_lock(&_win32_lock);
+ if (f->used) {
f->clazz->_fh_close( f );
- f->used = 0;
- f->eof = 0;
- f->clazz = NULL;
+ f->name[0] = '\0';
+ f->eof = 0;
+ f->used = 0;
+ f->clazz = NULL;
}
+ adb_mutex_unlock(&_win32_lock);
return 0;
}
+// Deleter for unique_fh.
+class fh_deleter {
+ public:
+ void operator()(struct FHRec_* fh) {
+ // We're called from a destructor and destructors should not overwrite
+ // errno because callers may do:
+ // errno = EBLAH;
+ // return -1; // calls destructor, which should not overwrite errno
+ const int saved_errno = errno;
+ _fh_close(fh);
+ errno = saved_errno;
+ }
+};
+
+// Like std::unique_ptr, but calls _fh_close() instead of operator delete().
+typedef std::unique_ptr<struct FHRec_, fh_deleter> unique_fh;
+
/**************************************************************************/
/**************************************************************************/
/***** *****/
@@ -355,12 +413,11 @@
f = _fh_alloc( &_fh_file_class );
if ( !f ) {
- errno = ENOMEM;
return -1;
}
- f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
- 0, NULL );
+ f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode,
+ NULL, OPEN_EXISTING, 0, NULL );
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
const DWORD err = GetLastError();
@@ -378,7 +435,8 @@
return -1;
default:
- D( "unknown error: %ld\n", err );
+ D( "unknown error: %s\n",
+ SystemErrorCodeToString( err ).c_str() );
errno = ENOENT;
return -1;
}
@@ -396,13 +454,13 @@
f = _fh_alloc( &_fh_file_class );
if ( !f ) {
- errno = ENOMEM;
return -1;
}
- f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
- NULL );
+ f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ NULL );
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
const DWORD err = GetLastError();
@@ -420,7 +478,8 @@
return -1;
default:
- D( "unknown error: %ld\n", err );
+ D( "unknown error: %s\n",
+ SystemErrorCodeToString( err ).c_str() );
errno = ENOENT;
return -1;
}
@@ -467,21 +526,6 @@
}
-int adb_shutdown(int fd)
-{
- FH f = _fh_from_int(fd, __func__);
-
- if (!f || f->clazz != &_fh_socket_class) {
- D("adb_shutdown: invalid fd %d\n", fd);
- return -1;
- }
-
- D( "adb_shutdown: %s\n", f->name);
- shutdown( f->fh_socket, SD_BOTH );
- return 0;
-}
-
-
int adb_close(int fd)
{
FH f = _fh_from_int(fd, __func__);
@@ -505,29 +549,63 @@
#undef setsockopt
-static void _socket_set_errno( void ) {
- switch (WSAGetLastError()) {
+static void _socket_set_errno( const DWORD err ) {
+ // The Windows C Runtime (MSVCRT.DLL) strerror() does not support a lot of
+ // POSIX and socket error codes, so this can only meaningfully map so much.
+ switch ( err ) {
case 0: errno = 0; break;
case WSAEWOULDBLOCK: errno = EAGAIN; break;
case WSAEINTR: errno = EINTR; break;
+ case WSAEFAULT: errno = EFAULT; break;
+ case WSAEINVAL: errno = EINVAL; break;
+ case WSAEMFILE: errno = EMFILE; break;
default:
- D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
errno = EINVAL;
+ D( "_socket_set_errno: mapping Windows error code %lu to errno %d\n",
+ err, errno );
}
}
static void _fh_socket_init( FH f ) {
f->fh_socket = INVALID_SOCKET;
f->event = WSACreateEvent();
+ if (f->event == WSA_INVALID_EVENT) {
+ D("WSACreateEvent failed: %s\n",
+ SystemErrorCodeToString(WSAGetLastError()).c_str());
+
+ // _event_socket_start assumes that this field is INVALID_HANDLE_VALUE
+ // on failure, instead of NULL which is what Windows really returns on
+ // error. It might be better to change all the other code to look for
+ // NULL, but that is a much riskier change.
+ f->event = INVALID_HANDLE_VALUE;
+ }
f->mask = 0;
}
static int _fh_socket_close( FH f ) {
- /* gently tell any peer that we're closing the socket */
- shutdown( f->fh_socket, SD_BOTH );
- closesocket( f->fh_socket );
- f->fh_socket = INVALID_SOCKET;
- CloseHandle( f->event );
+ if (f->fh_socket != INVALID_SOCKET) {
+ /* gently tell any peer that we're closing the socket */
+ if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+ // If the socket is not connected, this returns an error. We want to
+ // minimize logging spam, so don't log these errors for now.
+#if 0
+ D("socket shutdown failed: %s\n",
+ SystemErrorCodeToString(WSAGetLastError()).c_str());
+#endif
+ }
+ if (closesocket(f->fh_socket) == SOCKET_ERROR) {
+ D("closesocket failed: %s\n",
+ SystemErrorCodeToString(WSAGetLastError()).c_str());
+ }
+ f->fh_socket = INVALID_SOCKET;
+ }
+ if (f->event != NULL) {
+ if (!CloseHandle(f->event)) {
+ D("CloseHandle failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ }
+ f->event = NULL;
+ }
f->mask = 0;
return 0;
}
@@ -540,7 +618,10 @@
static int _fh_socket_read(FH f, void* buf, int len) {
int result = recv(f->fh_socket, reinterpret_cast<char*>(buf), len, 0);
if (result == SOCKET_ERROR) {
- _socket_set_errno();
+ const DWORD err = WSAGetLastError();
+ D("recv fd %d failed: %s\n", _fh_to_int(f),
+ SystemErrorCodeToString(err).c_str());
+ _socket_set_errno(err);
result = -1;
}
return result;
@@ -549,7 +630,10 @@
static int _fh_socket_write(FH f, const void* buf, int len) {
int result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
if (result == SOCKET_ERROR) {
- _socket_set_errno();
+ const DWORD err = WSAGetLastError();
+ D("send fd %d failed: %s\n", _fh_to_int(f),
+ SystemErrorCodeToString(err).c_str());
+ _socket_set_errno(err);
result = -1;
}
return result;
@@ -570,31 +654,39 @@
static void
_cleanup_winsock( void )
{
+ // TODO: WSAStartup() might be called multiple times and this won't properly
+ // cleanup the right number of times. Plus, WSACleanup() probably doesn't
+ // make sense since it might interrupt other threads using Winsock (since
+ // our various threads are not explicitly cleanly shutdown at process exit).
WSACleanup();
}
static void
_init_winsock( void )
{
+ // TODO: Multiple threads calling this may potentially cause multiple calls
+ // to WSAStartup() and multiple atexit() calls.
if (!_winsock_init) {
WSADATA wsaData;
int rc = WSAStartup( MAKEWORD(2,2), &wsaData);
if (rc != 0) {
- fatal( "adb: could not initialize Winsock\n" );
+ fatal( "adb: could not initialize Winsock: %s",
+ SystemErrorCodeToString( rc ).c_str());
}
atexit( _cleanup_winsock );
_winsock_init = 1;
}
}
-int socket_loopback_client(int port, int type)
-{
- FH f = _fh_alloc( &_fh_socket_class );
+int network_loopback_client(int port, int type, std::string* error) {
struct sockaddr_in addr;
SOCKET s;
- if (!f)
+ unique_fh f(_fh_alloc(&_fh_socket_class));
+ if (!f) {
+ *error = strerror(errno);
return -1;
+ }
if (!_winsock_init)
_init_winsock();
@@ -606,32 +698,40 @@
s = socket(AF_INET, type, 0);
if(s == INVALID_SOCKET) {
- D("socket_loopback_client: could not create socket\n" );
- _fh_close(f);
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not create socket: %s\n", error->c_str());
+ return -1;
+ }
+ f->fh_socket = s;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not connect to %s:%d: %s\n",
+ type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
return -1;
}
- f->fh_socket = s;
- if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
- _fh_close(f);
- return -1;
- }
- snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
+ const int fd = _fh_to_int(f.get());
+ snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd,
+ type != SOCK_STREAM ? "udp:" : "", port );
+ D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
+ fd );
+ f.release();
+ return fd;
}
#define LISTEN_BACKLOG 4
-int socket_loopback_server(int port, int type)
-{
- FH f = _fh_alloc( &_fh_socket_class );
+// interface_address is INADDR_LOOPBACK or INADDR_ANY.
+static int _network_server(int port, int type, u_long interface_address,
+ std::string* error) {
struct sockaddr_in addr;
SOCKET s;
int n;
+ unique_fh f(_fh_alloc(&_fh_socket_class));
if (!f) {
+ *error = strerror(errno);
return -1;
}
@@ -641,149 +741,159 @@
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_addr.s_addr = htonl(interface_address);
+ // TODO: Consider using dual-stack socket that can simultaneously listen on
+ // IPv4 and IPv6.
s = socket(AF_INET, type, 0);
- if(s == INVALID_SOCKET) return -1;
+ if (s == INVALID_SOCKET) {
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not create socket: %s\n", error->c_str());
+ return -1;
+ }
f->fh_socket = s;
n = 1;
- setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+ if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n,
+ sizeof(n)) == SOCKET_ERROR) {
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("setsockopt level %d optname %d failed: %s\n",
+ SOL_SOCKET, SO_EXCLUSIVEADDRUSE, error->c_str());
+ return -1;
+ }
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- _fh_close(f);
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not bind to %s:%d: %s\n",
+ type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
return -1;
}
if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
- if (ret < 0) {
- _fh_close(f);
+ if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not listen on %s:%d: %s\n",
+ type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
return -1;
}
}
- snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
+ const int fd = _fh_to_int(f.get());
+ snprintf( f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
+ interface_address == INADDR_LOOPBACK ? "lo" : "any",
+ type != SOCK_STREAM ? "udp:" : "", port );
+ D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
+ fd );
+ f.release();
+ return fd;
}
+int network_loopback_server(int port, int type, std::string* error) {
+ return _network_server(port, type, INADDR_LOOPBACK, error);
+}
-int socket_network_client_timeout(const char *host, int port, int type, int timeout,
- int* getaddrinfo_error) {
- FH f = _fh_alloc( &_fh_socket_class );
- if (!f) return -1;
+int network_inaddr_any_server(int port, int type, std::string* error) {
+ return _network_server(port, type, INADDR_ANY, error);
+}
+
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
+ unique_fh f(_fh_alloc(&_fh_socket_class));
+ if (!f) {
+ *error = strerror(errno);
+ return -1;
+ }
if (!_winsock_init) _init_winsock();
- hostent* hp = gethostbyname(host);
- if(hp == 0) {
- _fh_close(f);
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = type;
+
+ char port_str[16];
+ snprintf(port_str, sizeof(port_str), "%d", port);
+
+ struct addrinfo* addrinfo_ptr = nullptr;
+
+#if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
+ // TODO: When the Android SDK tools increases the Windows system
+ // requirements >= WinXP SP2, switch to GetAddrInfoW(widen(host).c_str()).
+#else
+ // Otherwise, keep using getaddrinfo(), or do runtime API detection
+ // with GetProcAddress("GetAddrInfoW").
+#endif
+ if (getaddrinfo(host.c_str(), port_str, &hints, &addrinfo_ptr) != 0) {
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not resolve host '%s' and port %s: %s\n", host.c_str(),
+ port_str, error->c_str());
return -1;
}
+ std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*>
+ addrinfo(addrinfo_ptr, freeaddrinfo);
+ addrinfo_ptr = nullptr;
- sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = hp->h_addrtype;
- addr.sin_port = htons(port);
- memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
-
- SOCKET s = socket(hp->h_addrtype, type, 0);
+ // TODO: Try all the addresses if there's more than one? This just uses
+ // the first. Or, could call WSAConnectByName() (Windows Vista and newer)
+ // which tries all addresses, takes a timeout and more.
+ SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
+ addrinfo->ai_protocol);
if(s == INVALID_SOCKET) {
- _fh_close(f);
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not create socket: %s\n", error->c_str());
return -1;
}
f->fh_socket = s;
- // TODO: implement timeouts for Windows.
-
- if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- _fh_close(f);
+ // TODO: Implement timeouts for Windows. Seems like the default in theory
+ // (according to http://serverfault.com/a/671453) and in practice is 21 sec.
+ if(connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
+ *error = SystemErrorCodeToString(WSAGetLastError());
+ D("could not connect to %s:%s:%s: %s\n",
+ type != SOCK_STREAM ? "udp" : "tcp", host.c_str(), port_str,
+ error->c_str());
return -1;
}
- snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_network_client_timeout: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
-}
-
-
-int socket_inaddr_any_server(int port, int type)
-{
- FH f = _fh_alloc( &_fh_socket_class );
- struct sockaddr_in addr;
- SOCKET s;
- int n;
-
- if (!f)
- return -1;
-
- if (!_winsock_init)
- _init_winsock();
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- s = socket(AF_INET, type, 0);
- if(s == INVALID_SOCKET) {
- _fh_close(f);
- return -1;
- }
-
- f->fh_socket = s;
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
-
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- _fh_close(f);
- return -1;
- }
-
- if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
- if (ret < 0) {
- _fh_close(f);
- return -1;
- }
- }
- snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
- D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
- return _fh_to_int(f);
+ const int fd = _fh_to_int(f.get());
+ snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", fd,
+ type != SOCK_STREAM ? "udp:" : "", port );
+ D( "host '%s' port %d type %s => fd %d\n", host.c_str(), port,
+ type != SOCK_STREAM ? "udp" : "tcp", fd );
+ f.release();
+ return fd;
}
#undef accept
int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
{
FH serverfh = _fh_from_int(serverfd, __func__);
- FH fh;
if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
- D( "adb_socket_accept: invalid fd %d\n", serverfd );
+ D("adb_socket_accept: invalid fd %d\n", serverfd);
+ errno = EBADF;
return -1;
}
- fh = _fh_alloc( &_fh_socket_class );
+ unique_fh fh(_fh_alloc( &_fh_socket_class ));
if (!fh) {
- D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+ PLOG(ERROR) << "adb_socket_accept: failed to allocate accepted socket "
+ "descriptor";
return -1;
}
fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
if (fh->fh_socket == INVALID_SOCKET) {
const DWORD err = WSAGetLastError();
- _fh_close( fh );
- D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, err );
+ LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd <<
+ " failed: " + SystemErrorCodeToString(err);
+ _socket_set_errno( err );
return -1;
}
- snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
- D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
- return _fh_to_int(fh);
+ const int fd = _fh_to_int(fh.get());
+ snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name );
+ D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, fd );
+ fh.release();
+ return fd;
}
@@ -793,10 +903,42 @@
if ( !fh || fh->clazz != &_fh_socket_class ) {
D("adb_setsockopt: invalid fd %d\n", fd);
+ errno = EBADF;
+ return -1;
+ }
+ int result = setsockopt( fh->fh_socket, level, optname,
+ reinterpret_cast<const char*>(optval), optlen );
+ if ( result == SOCKET_ERROR ) {
+ const DWORD err = WSAGetLastError();
+ D( "adb_setsockopt: setsockopt on fd %d level %d optname %d "
+ "failed: %s\n", fd, level, optname,
+ SystemErrorCodeToString(err).c_str() );
+ _socket_set_errno( err );
+ result = -1;
+ }
+ return result;
+}
+
+
+int adb_shutdown(int fd)
+{
+ FH f = _fh_from_int(fd, __func__);
+
+ if (!f || f->clazz != &_fh_socket_class) {
+ D("adb_shutdown: invalid fd %d\n", fd);
+ errno = EBADF;
return -1;
}
- return setsockopt( fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen );
+ D( "adb_shutdown: %s\n", f->name);
+ if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+ const DWORD err = WSAGetLastError();
+ D("socket shutdown fd %d failed: %s\n", fd,
+ SystemErrorCodeToString(err).c_str());
+ _socket_set_errno(err);
+ return -1;
+ }
+ return 0;
}
/**************************************************************************/
@@ -1199,16 +1341,19 @@
int adb_socketpair(int sv[2]) {
SocketPair pair;
- FH fa = _fh_alloc(&_fh_socketpair_class);
- FH fb = _fh_alloc(&_fh_socketpair_class);
-
- if (!fa || !fb)
- goto Fail;
+ unique_fh fa(_fh_alloc(&_fh_socketpair_class));
+ if (!fa) {
+ return -1;
+ }
+ unique_fh fb(_fh_alloc(&_fh_socketpair_class));
+ if (!fb) {
+ return -1;
+ }
pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
if (pair == NULL) {
D("adb_socketpair: not enough memory to allocate pipes\n" );
- goto Fail;
+ return -1;
}
bip_buffer_init( &pair->a2b_bip );
@@ -1217,10 +1362,10 @@
fa->fh_pair = pair;
fb->fh_pair = pair;
pair->used = 2;
- pair->a_fd = fa;
+ pair->a_fd = fa.get();
- sv[0] = _fh_to_int(fa);
- sv[1] = _fh_to_int(fb);
+ sv[0] = _fh_to_int(fa.get());
+ sv[1] = _fh_to_int(fb.get());
pair->a2b_bip.fdin = sv[0];
pair->a2b_bip.fdout = sv[1];
@@ -1230,12 +1375,9 @@
snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+ fa.release();
+ fb.release();
return 0;
-
-Fail:
- _fh_close(fb);
- _fh_close(fa);
- return -1;
}
/**************************************************************************/
@@ -2083,6 +2225,7 @@
hook->check = _event_socket_check;
hook->peek = _event_socket_peek;
+ // TODO: check return value?
_event_socket_start( hook );
}
@@ -2190,7 +2333,7 @@
memset(input_record, 0, sizeof(*input_record));
if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
- "failure, error %ld\n", GetLastError());
+ "failed: %s\n", SystemErrorCodeToString(GetLastError()).c_str());
errno = EIO;
return false;
}
@@ -3004,8 +3147,8 @@
if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
// This really should not fail.
- D("stdin_raw_init: SetConsoleMode() failure, error %ld\n",
- GetLastError());
+ D("stdin_raw_init: SetConsoleMode() failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
}
// Once this is set, it means that stdin has been configured for
@@ -3026,8 +3169,8 @@
if (!SetConsoleMode(in, _old_console_mode)) {
// This really should not fail.
- D("stdin_raw_restore: SetConsoleMode() failure, error %ld\n",
- GetLastError());
+ D("stdin_raw_restore: SetConsoleMode() failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
}
}
}
@@ -3052,3 +3195,624 @@
#pragma pop_macro("read")
}
}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** Unicode support *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+// This implements support for using files with Unicode filenames and for
+// outputting Unicode text to a Win32 console window. This is inspired from
+// http://utf8everywhere.org/.
+//
+// Background
+// ----------
+//
+// On POSIX systems, to deal with files with Unicode filenames, just pass UTF-8
+// filenames to APIs such as open(). This works because filenames are largely
+// opaque 'cookies' (perhaps excluding path separators).
+//
+// On Windows, the native file APIs such as CreateFileW() take 2-byte wchar_t
+// UTF-16 strings. There is an API, CreateFileA() that takes 1-byte char
+// strings, but the strings are in the ANSI codepage and not UTF-8. (The
+// CreateFile() API is really just a macro that adds the W/A based on whether
+// the UNICODE preprocessor symbol is defined).
+//
+// Options
+// -------
+//
+// Thus, to write a portable program, there are a few options:
+//
+// 1. Write the program with wchar_t filenames (wchar_t path[256];).
+// For Windows, just call CreateFileW(). For POSIX, write a wrapper openW()
+// that takes a wchar_t string, converts it to UTF-8 and then calls the real
+// open() API.
+//
+// 2. Write the program with a TCHAR typedef that is 2 bytes on Windows and
+// 1 byte on POSIX. Make T-* wrappers for various OS APIs and call those,
+// potentially touching a lot of code.
+//
+// 3. Write the program with a 1-byte char filenames (char path[256];) that are
+// UTF-8. For POSIX, just call open(). For Windows, write a wrapper that
+// takes a UTF-8 string, converts it to UTF-16 and then calls the real OS
+// or C Runtime API.
+//
+// The Choice
+// ----------
+//
+// The code below chooses option 3, the UTF-8 everywhere strategy. It
+// introduces narrow() which converts UTF-16 to UTF-8. This is used by the
+// NarrowArgs helper class that is used to convert wmain() args into UTF-8
+// args that are passed to main() at the beginning of program startup. We also
+// introduce widen() which converts from UTF-8 to UTF-16. This is used to
+// implement wrappers below that call UTF-16 OS and C Runtime APIs.
+//
+// Unicode console output
+// ----------------------
+//
+// The way to output Unicode to a Win32 console window is to call
+// WriteConsoleW() with UTF-16 text. (The user must also choose a proper font
+// such as Lucida Console or Consolas, and in the case of East Asian languages
+// (such as Chinese, Japanese, Korean), the user must go to the Control Panel
+// and change the "system locale" to Chinese, etc., which allows a Chinese, etc.
+// font to be used in console windows.)
+//
+// The problem is getting the C Runtime to make fprintf and related APIs call
+// WriteConsoleW() under the covers. The C Runtime API, _setmode() sounds
+// promising, but the various modes have issues:
+//
+// 1. _setmode(_O_TEXT) (the default) does not use WriteConsoleW() so UTF-8 and
+// UTF-16 do not display properly.
+// 2. _setmode(_O_BINARY) does not use WriteConsoleW() and the text comes out
+// totally wrong.
+// 3. _setmode(_O_U8TEXT) seems to cause the C Runtime _invalid_parameter
+// handler to be called (upon a later I/O call), aborting the process.
+// 4. _setmode(_O_U16TEXT) and _setmode(_O_WTEXT) cause non-wide printf/fprintf
+// to output nothing.
+//
+// So the only solution is to write our own adb_fprintf() that converts UTF-8
+// to UTF-16 and then calls WriteConsoleW().
+
+
+// Function prototype because attributes cannot be placed on func definitions.
+static void _widen_fatal(const char *fmt, ...)
+ __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+
+// A version of fatal() that does not call adb_(v)fprintf(), so it can be
+// called from those functions.
+static void _widen_fatal(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ // If (v)fprintf are macros that point to adb_(v)fprintf, when random adb
+ // code calls (v)fprintf, it may end up calling adb_(v)fprintf, which then
+ // calls _widen_fatal(). So then how does _widen_fatal() output a error?
+ // By directly calling real C Runtime APIs that don't properly output
+ // Unicode, but will be able to get a comprehendible message out. To do
+ // this, make sure we don't call (v)fprintf macros by undefining them.
+#pragma push_macro("fprintf")
+#pragma push_macro("vfprintf")
+#undef fprintf
+#undef vfprintf
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+#pragma pop_macro("vfprintf")
+#pragma pop_macro("fprintf")
+ va_end(ap);
+ exit(-1);
+}
+
+// TODO: Consider implementing widen() and narrow() out of std::wstring_convert
+// once libcxx is supported on Windows. Or, consider libutils/Unicode.cpp.
+
+// Convert from UTF-8 to UTF-16. A size of -1 specifies a NULL terminated
+// string. Any other size specifies the number of chars to convert, excluding
+// any NULL terminator (if you're passing an explicit size, you probably don't
+// have a NULL terminated string in the first place).
+std::wstring widen(const char* utf8, const int size) {
+ // Note: Do not call SystemErrorCodeToString() from widen() because
+ // SystemErrorCodeToString() calls narrow() which may call fatal() which
+ // calls adb_vfprintf() which calls widen(), potentially causing infinite
+ // recursion.
+ const int chars_to_convert = MultiByteToWideChar(CP_UTF8, 0, utf8, size,
+ NULL, 0);
+ if (chars_to_convert <= 0) {
+ // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
+ _widen_fatal("MultiByteToWideChar failed counting: %d, "
+ "GetLastError: %lu", chars_to_convert, GetLastError());
+ }
+
+ std::wstring utf16;
+ size_t chars_to_allocate = chars_to_convert;
+ if (size == -1) {
+ // chars_to_convert includes a NULL terminator, so subtract space
+ // for that because resize() includes that itself.
+ --chars_to_allocate;
+ }
+ utf16.resize(chars_to_allocate);
+
+ // This uses &string[0] to get write-access to the entire string buffer
+ // which may be assuming that the chars are all contiguous, but it seems
+ // to work and saves us the hassle of using a temporary
+ // std::vector<wchar_t>.
+ const int result = MultiByteToWideChar(CP_UTF8, 0, utf8, size, &utf16[0],
+ chars_to_convert);
+ if (result != chars_to_convert) {
+ // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
+ _widen_fatal("MultiByteToWideChar failed conversion: %d, "
+ "GetLastError: %lu", result, GetLastError());
+ }
+
+ // If a size was passed in (size != -1), then the string is NULL terminated
+ // by a NULL char that was written by std::string::resize(). If size == -1,
+ // then MultiByteToWideChar() read a NULL terminator from the original
+ // string and converted it to a NULL UTF-16 char in the output.
+
+ return utf16;
+}
+
+// Convert a NULL terminated string from UTF-8 to UTF-16.
+std::wstring widen(const char* utf8) {
+ // Pass -1 to let widen() determine the string length.
+ return widen(utf8, -1);
+}
+
+// Convert from UTF-8 to UTF-16.
+std::wstring widen(const std::string& utf8) {
+ return widen(utf8.c_str(), utf8.length());
+}
+
+// Convert from UTF-16 to UTF-8.
+std::string narrow(const std::wstring& utf16) {
+ return narrow(utf16.c_str());
+}
+
+// Convert from UTF-16 to UTF-8.
+std::string narrow(const wchar_t* utf16) {
+ // Note: Do not call SystemErrorCodeToString() from narrow() because
+ // SystemErrorCodeToString() calls narrow() and we don't want potential
+ // infinite recursion.
+ const int chars_required = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL,
+ 0, NULL, NULL);
+ if (chars_required <= 0) {
+ // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
+ fatal("WideCharToMultiByte failed counting: %d, GetLastError: %lu",
+ chars_required, GetLastError());
+ }
+
+ std::string utf8;
+ // Subtract space for the NULL terminator because resize() includes
+ // that itself. Note that this could potentially throw a std::bad_alloc
+ // exception.
+ utf8.resize(chars_required - 1);
+
+ // This uses &string[0] to get write-access to the entire string buffer
+ // which may be assuming that the chars are all contiguous, but it seems
+ // to work and saves us the hassle of using a temporary
+ // std::vector<char>.
+ const int result = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, &utf8[0],
+ chars_required, NULL, NULL);
+ if (result != chars_required) {
+ // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
+ fatal("WideCharToMultiByte failed conversion: %d, GetLastError: %lu",
+ result, GetLastError());
+ }
+
+ return utf8;
+}
+
+// Constructor for helper class to convert wmain() UTF-16 args to UTF-8 to
+// be passed to main().
+NarrowArgs::NarrowArgs(const int argc, wchar_t** const argv) {
+ narrow_args = new char*[argc + 1];
+
+ for (int i = 0; i < argc; ++i) {
+ narrow_args[i] = strdup(narrow(argv[i]).c_str());
+ }
+ narrow_args[argc] = nullptr; // terminate
+}
+
+NarrowArgs::~NarrowArgs() {
+ if (narrow_args != nullptr) {
+ for (char** argp = narrow_args; *argp != nullptr; ++argp) {
+ free(*argp);
+ }
+ delete[] narrow_args;
+ narrow_args = nullptr;
+ }
+}
+
+int unix_open(const char* path, int options, ...) {
+ if ((options & O_CREAT) == 0) {
+ return _wopen(widen(path).c_str(), options);
+ } else {
+ int mode;
+ va_list args;
+ va_start(args, options);
+ mode = va_arg(args, int);
+ va_end(args);
+ return _wopen(widen(path).c_str(), options, mode);
+ }
+}
+
+// Version of stat() that takes a UTF-8 path.
+int adb_stat(const char* f, struct adb_stat* s) {
+#pragma push_macro("wstat")
+// This definition of wstat seems to be missing from <sys/stat.h>.
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#ifdef _USE_32BIT_TIME_T
+#define wstat _wstat32i64
+#else
+#define wstat _wstat64
+#endif
+#else
+// <sys/stat.h> has a function prototype for wstat() that should be available.
+#endif
+
+ return wstat(widen(f).c_str(), s);
+
+#pragma pop_macro("wstat")
+}
+
+// Version of opendir() that takes a UTF-8 path.
+DIR* adb_opendir(const char* name) {
+ // Just cast _WDIR* to DIR*. This doesn't work if the caller reads any of
+ // the fields, but right now all the callers treat the structure as
+ // opaque.
+ return reinterpret_cast<DIR*>(_wopendir(widen(name).c_str()));
+}
+
+// Version of readdir() that returns UTF-8 paths.
+struct dirent* adb_readdir(DIR* dir) {
+ _WDIR* const wdir = reinterpret_cast<_WDIR*>(dir);
+ struct _wdirent* const went = _wreaddir(wdir);
+ if (went == nullptr) {
+ return nullptr;
+ }
+ // Convert from UTF-16 to UTF-8.
+ const std::string name_utf8(narrow(went->d_name));
+
+ // Cast the _wdirent* to dirent* and overwrite the d_name field (which has
+ // space for UTF-16 wchar_t's) with UTF-8 char's.
+ struct dirent* ent = reinterpret_cast<struct dirent*>(went);
+
+ if (name_utf8.length() + 1 > sizeof(went->d_name)) {
+ // Name too big to fit in existing buffer.
+ errno = ENOMEM;
+ return nullptr;
+ }
+
+ // Note that sizeof(_wdirent::d_name) is bigger than sizeof(dirent::d_name)
+ // because _wdirent contains wchar_t instead of char. So even if name_utf8
+ // can fit in _wdirent::d_name, the resulting dirent::d_name field may be
+ // bigger than the caller expects because they expect a dirent structure
+ // which has a smaller d_name field. Ignore this since the caller should be
+ // resilient.
+
+ // Rewrite the UTF-16 d_name field to UTF-8.
+ strcpy(ent->d_name, name_utf8.c_str());
+
+ return ent;
+}
+
+// Version of closedir() to go with our version of adb_opendir().
+int adb_closedir(DIR* dir) {
+ return _wclosedir(reinterpret_cast<_WDIR*>(dir));
+}
+
+// Version of unlink() that takes a UTF-8 path.
+int adb_unlink(const char* path) {
+ const std::wstring wpath(widen(path));
+
+ int rc = _wunlink(wpath.c_str());
+
+ if (rc == -1 && errno == EACCES) {
+ /* unlink returns EACCES when the file is read-only, so we first */
+ /* try to make it writable, then unlink again... */
+ rc = _wchmod(wpath.c_str(), _S_IREAD | _S_IWRITE);
+ if (rc == 0)
+ rc = _wunlink(wpath.c_str());
+ }
+ return rc;
+}
+
+// Version of mkdir() that takes a UTF-8 path.
+int adb_mkdir(const std::string& path, int mode) {
+ return _wmkdir(widen(path.c_str()).c_str());
+}
+
+// Version of utime() that takes a UTF-8 path.
+int adb_utime(const char* path, struct utimbuf* u) {
+ static_assert(sizeof(struct utimbuf) == sizeof(struct _utimbuf),
+ "utimbuf and _utimbuf should be the same size because they both "
+ "contain the same types, namely time_t");
+ return _wutime(widen(path).c_str(), reinterpret_cast<struct _utimbuf*>(u));
+}
+
+// Version of chmod() that takes a UTF-8 path.
+int adb_chmod(const char* path, int mode) {
+ return _wchmod(widen(path).c_str(), mode);
+}
+
+// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
+static HANDLE _get_console_handle(FILE* const stream) {
+ // Get a C Runtime file descriptor number from the FILE* structure.
+ const int fd = fileno(stream);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ // If it is not a "character device", it is probably a file and not a
+ // console. Do this check early because it is probably cheap. Still do more
+ // checks after this since there are devices that pass this test, but are
+ // not a console, such as NUL, the Windows /dev/null equivalent (I think).
+ if (!isatty(fd)) {
+ return NULL;
+ }
+
+ // Given a C Runtime file descriptor number, get the underlying OS
+ // file handle.
+ const intptr_t osfh = _get_osfhandle(fd);
+ if (osfh == -1) {
+ return NULL;
+ }
+
+ const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
+
+ DWORD old_mode = 0;
+ if (!GetConsoleMode(h, &old_mode)) {
+ return NULL;
+ }
+
+ // If GetConsoleMode() was successful, assume this is a console.
+ return h;
+}
+
+// Internal helper function to write UTF-8 bytes to a console. Returns -1
+// on error.
+static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
+ HANDLE console) {
+ // Convert from UTF-8 to UTF-16.
+ // This could throw std::bad_alloc.
+ const std::wstring output(widen(buf, size));
+
+ // Note that this does not do \n => \r\n translation because that
+ // doesn't seem necessary for the Windows console. For the Windows
+ // console \r moves to the beginning of the line and \n moves to a new
+ // line.
+
+ // Flush any stream buffering so that our output is afterwards which
+ // makes sense because our call is afterwards.
+ (void)fflush(stream);
+
+ // Write UTF-16 to the console.
+ DWORD written = 0;
+ if (!WriteConsoleW(console, output.c_str(), output.length(), &written,
+ NULL)) {
+ errno = EIO;
+ return -1;
+ }
+
+ // This is the number of UTF-16 chars written, which might be different
+ // than the number of UTF-8 chars passed in. It doesn't seem practical to
+ // get this count correct.
+ return written;
+}
+
+// Function prototype because attributes cannot be placed on func definitions.
+static int _console_vfprintf(const HANDLE console, FILE* stream,
+ const char *format, va_list ap)
+ __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
+
+// Internal function to format a UTF-8 string and write it to a Win32 console.
+// Returns -1 on error.
+static int _console_vfprintf(const HANDLE console, FILE* stream,
+ const char *format, va_list ap) {
+ std::string output_utf8;
+
+ // Format the string.
+ // This could throw std::bad_alloc.
+ android::base::StringAppendV(&output_utf8, format, ap);
+
+ return _console_write_utf8(output_utf8.c_str(), output_utf8.length(),
+ stream, console);
+}
+
+// Version of vfprintf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_vfprintf(FILE *stream, const char *format, va_list ap) {
+ const HANDLE console = _get_console_handle(stream);
+
+ // If there is an associated Win32 console, write to it specially,
+ // otherwise defer to the regular C Runtime, passing it UTF-8.
+ if (console != NULL) {
+ return _console_vfprintf(console, stream, format, ap);
+ } else {
+ // If vfprintf is a macro, undefine it, so we can call the real
+ // C Runtime API.
+#pragma push_macro("vfprintf")
+#undef vfprintf
+ return vfprintf(stream, format, ap);
+#pragma pop_macro("vfprintf")
+ }
+}
+
+// Version of fprintf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fprintf(FILE *stream, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ const int result = adb_vfprintf(stream, format, ap);
+ va_end(ap);
+
+ return result;
+}
+
+// Version of printf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_printf(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ const int result = adb_vfprintf(stdout, format, ap);
+ va_end(ap);
+
+ return result;
+}
+
+// Version of fputs() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fputs(const char* buf, FILE* stream) {
+ // adb_fprintf returns -1 on error, which is conveniently the same as EOF
+ // which fputs (and hence adb_fputs) should return on error.
+ return adb_fprintf(stream, "%s", buf);
+}
+
+// Version of fputc() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fputc(int ch, FILE* stream) {
+ const int result = adb_fprintf(stream, "%c", ch);
+ if (result <= 0) {
+ // If there was an error, or if nothing was printed (which should be an
+ // error), return an error, which fprintf signifies with EOF.
+ return EOF;
+ }
+ // For success, fputc returns the char, cast to unsigned char, then to int.
+ return static_cast<unsigned char>(ch);
+}
+
+// Internal function to write UTF-8 to a Win32 console. Returns the number of
+// items (of length size) written. On error, returns a short item count or 0.
+static size_t _console_fwrite(const void* ptr, size_t size, size_t nmemb,
+ FILE* stream, HANDLE console) {
+ // TODO: Note that a Unicode character could be several UTF-8 bytes. But
+ // if we're passed only some of the bytes of a character (for example, from
+ // the network socket for adb shell), we won't be able to convert the char
+ // to a complete UTF-16 char (or surrogate pair), so the output won't look
+ // right.
+ //
+ // To fix this, see libutils/Unicode.cpp for hints on decoding UTF-8.
+ //
+ // For now we ignore this problem because the alternative is that we'd have
+ // to parse UTF-8 and buffer things up (doable). At least this is better
+ // than what we had before -- always incorrect multi-byte UTF-8 output.
+ int result = _console_write_utf8(reinterpret_cast<const char*>(ptr),
+ size * nmemb, stream, console);
+ if (result == -1) {
+ return 0;
+ }
+ return result / size;
+}
+
+// Version of fwrite() that takes UTF-8 and can write Unicode to a
+// Windows console.
+size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) {
+ const HANDLE console = _get_console_handle(stream);
+
+ // If there is an associated Win32 console, write to it specially,
+ // otherwise defer to the regular C Runtime, passing it UTF-8.
+ if (console != NULL) {
+ return _console_fwrite(ptr, size, nmemb, stream, console);
+ } else {
+ // If fwrite is a macro, undefine it, so we can call the real
+ // C Runtime API.
+#pragma push_macro("fwrite")
+#undef fwrite
+ return fwrite(ptr, size, nmemb, stream);
+#pragma pop_macro("fwrite")
+ }
+}
+
+// Version of fopen() that takes a UTF-8 filename and can access a file with
+// a Unicode filename.
+FILE* adb_fopen(const char* f, const char* m) {
+ return _wfopen(widen(f).c_str(), widen(m).c_str());
+}
+
+// Shadow UTF-8 environment variable name/value pairs that are created from
+// _wenviron the first time that adb_getenv() is called. Note that this is not
+// currently updated if putenv, setenv, unsetenv are called. Note that no
+// thread synchronization is done, but we're called early enough in
+// single-threaded startup that things work ok.
+static std::unordered_map<std::string, char*> g_environ_utf8;
+
+// Make sure that shadow UTF-8 environment variables are setup.
+static void _ensure_env_setup() {
+ // If some name/value pairs exist, then we've already done the setup below.
+ if (g_environ_utf8.size() != 0) {
+ return;
+ }
+
+ // Read name/value pairs from UTF-16 _wenviron and write new name/value
+ // pairs to UTF-8 g_environ_utf8. Note that it probably does not make sense
+ // to use the D() macro here because that tracing only works if the
+ // ADB_TRACE environment variable is setup, but that env var can't be read
+ // until this code completes.
+ for (wchar_t** env = _wenviron; *env != nullptr; ++env) {
+ wchar_t* const equal = wcschr(*env, L'=');
+ if (equal == nullptr) {
+ // Malformed environment variable with no equal sign. Shouldn't
+ // really happen, but we should be resilient to this.
+ continue;
+ }
+
+ const std::string name_utf8(narrow(std::wstring(*env, equal - *env)));
+ char* const value_utf8 = strdup(narrow(equal + 1).c_str());
+
+ // Overwrite any duplicate name, but there shouldn't be a dup in the
+ // first place.
+ g_environ_utf8[name_utf8] = value_utf8;
+ }
+}
+
+// Version of getenv() that takes a UTF-8 environment variable name and
+// retrieves a UTF-8 value.
+char* adb_getenv(const char* name) {
+ _ensure_env_setup();
+
+ const auto it = g_environ_utf8.find(std::string(name));
+ if (it == g_environ_utf8.end()) {
+ return nullptr;
+ }
+
+ return it->second;
+}
+
+// Version of getcwd() that returns the current working directory in UTF-8.
+char* adb_getcwd(char* buf, int size) {
+ wchar_t* wbuf = _wgetcwd(nullptr, 0);
+ if (wbuf == nullptr) {
+ return nullptr;
+ }
+
+ const std::string buf_utf8(narrow(wbuf));
+ free(wbuf);
+ wbuf = nullptr;
+
+ // If size was specified, make sure all the chars will fit.
+ if (size != 0) {
+ if (size < static_cast<int>(buf_utf8.length() + 1)) {
+ errno = ERANGE;
+ return nullptr;
+ }
+ }
+
+ // If buf was not specified, allocate storage.
+ if (buf == nullptr) {
+ if (size == 0) {
+ size = buf_utf8.length() + 1;
+ }
+ buf = reinterpret_cast<char*>(malloc(size));
+ if (buf == nullptr) {
+ return nullptr;
+ }
+ }
+
+ // Destination buffer was allocated with enough space, or we've already
+ // checked an existing buffer size for enough space.
+ strcpy(buf, buf_utf8.c_str());
+
+ return buf;
+}
diff --git a/adb/test_device.py b/adb/test_device.py
index 8003eaa..48a3f6c 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -55,10 +55,15 @@
class GetDeviceTest(unittest.TestCase):
def setUp(self):
self.android_serial = os.getenv('ANDROID_SERIAL')
- del os.environ['ANDROID_SERIAL']
+ if 'ANDROID_SERIAL' in os.environ:
+ del os.environ['ANDROID_SERIAL']
def tearDown(self):
- os.environ['ANDROID_SERIAL'] = self.android_serial
+ if self.android_serial is not None:
+ os.environ['ANDROID_SERIAL'] = self.android_serial
+ else:
+ if 'ANDROID_SERIAL' in os.environ:
+ del os.environ['ANDROID_SERIAL']
@mock.patch('adb.device.get_devices')
def test_explicit(self, mock_get_devices):
@@ -311,7 +316,7 @@
size = random.randrange(min_size, max_size, 1024)
base_name = 'device_tmpfile' + str(file_num)
- full_path = os.path.join(in_dir, base_name)
+ full_path = posixpath.join(in_dir, base_name)
device.shell(['dd', 'if=/dev/urandom', 'of={}'.format(full_path),
'bs={}'.format(size), 'count=1'])
@@ -328,107 +333,96 @@
def _test_push(self, local_file, checksum):
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
- try:
- self.device.push(
- local=local_file, remote=self.DEVICE_TEMP_FILE)
- dev_md5, _ = self.device.shell(
- [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
- self.assertEqual(checksum, dev_md5)
- finally:
- self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+ self.device.push(local=local_file, remote=self.DEVICE_TEMP_FILE)
+ dev_md5, _ = self.device.shell([get_md5_prog(self.device),
+ self.DEVICE_TEMP_FILE]).split()
+ self.assertEqual(checksum, dev_md5)
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
def test_push(self):
"""Push a randomly generated file to specified device."""
kbytes = 512
tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- try:
- rand_str = os.urandom(1024 * kbytes)
- tmp.write(rand_str)
- tmp.close()
- self._test_push(tmp.name, compute_md5(rand_str))
- finally:
- os.remove(tmp.name)
+ rand_str = os.urandom(1024 * kbytes)
+ tmp.write(rand_str)
+ tmp.close()
+ self._test_push(tmp.name, compute_md5(rand_str))
+ os.remove(tmp.name)
# TODO: write push directory test.
def _test_pull(self, remote_file, checksum):
tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- try:
- tmp_write.close()
- self.device.pull(remote=remote_file, local=tmp_write.name)
- with open(tmp_write.name, 'rb') as tmp_read:
- host_contents = tmp_read.read()
- host_md5 = compute_md5(host_contents)
- self.assertEqual(checksum, host_md5)
- finally:
- os.remove(tmp_write.name)
+ tmp_write.close()
+ self.device.pull(remote=remote_file, local=tmp_write.name)
+ with open(tmp_write.name, 'rb') as tmp_read:
+ host_contents = tmp_read.read()
+ host_md5 = compute_md5(host_contents)
+ self.assertEqual(checksum, host_md5)
+ os.remove(tmp_write.name)
def test_pull(self):
"""Pull a randomly generated file from specified device."""
kbytes = 512
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
- try:
- cmd = ['dd', 'if=/dev/urandom',
- 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
- 'count={}'.format(kbytes)]
- self.device.shell(cmd)
- dev_md5, _ = self.device.shell(
- [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
- self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
- finally:
- self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
+ cmd = ['dd', 'if=/dev/urandom',
+ 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
+ 'count={}'.format(kbytes)]
+ self.device.shell(cmd)
+ dev_md5, _ = self.device.shell(
+ [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
+ self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
+ self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
def test_pull_dir(self):
"""Pull a randomly generated directory of files from the device."""
host_dir = tempfile.mkdtemp()
- try:
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
- for temp_file in temp_files:
- host_path = os.path.join(host_dir, temp_file.base_name)
- with open(host_path, 'rb') as host_file:
- host_md5 = compute_md5(host_file.read())
- self.assertEqual(host_md5, temp_file.checksum)
- finally:
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ for temp_file in temp_files:
+ host_path = os.path.join(host_dir, temp_file.base_name)
+ with open(host_path, 'rb') as host_file:
+ host_md5 = compute_md5(host_file.read())
+ self.assertEqual(host_md5, temp_file.checksum)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
def test_sync(self):
"""Sync a randomly generated directory of files to specified device."""
base_dir = tempfile.mkdtemp()
- try:
- # Create mirror device directory hierarchy within base_dir.
- full_dir_path = base_dir + self.DEVICE_TEMP_DIR
- os.makedirs(full_dir_path)
- # Create 32 random files within the host mirror.
- temp_files = make_random_host_files(in_dir=full_dir_path,
- num_files=32)
+ # Create mirror device directory hierarchy within base_dir.
+ full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+ os.makedirs(full_dir_path)
- # Clean up any trash on the device.
- device = adb.get_device(product=base_dir)
- device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ # Create 32 random files within the host mirror.
+ temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
- device.sync('data')
+ # Clean up any trash on the device.
+ device = adb.get_device(product=base_dir)
+ device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- # Confirm that every file on the device mirrors that on the host.
- for temp_file in temp_files:
- device_full_path = posixpath.join(
- self.DEVICE_TEMP_DIR, temp_file.base_name)
- dev_md5, _ = device.shell(
- [get_md5_prog(self.device), device_full_path]).split()
- self.assertEqual(temp_file.checksum, dev_md5)
- finally:
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- shutil.rmtree(base_dir + self.DEVICE_TEMP_DIR)
+ device.sync('data')
+
+ # Confirm that every file on the device mirrors that on the host.
+ for temp_file in temp_files:
+ device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path]).split()
+ self.assertEqual(temp_file.checksum, dev_md5)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ shutil.rmtree(base_dir + self.DEVICE_TEMP_DIR)
def test_unicode_paths(self):
"""Ensure that we can support non-ASCII paths, even on Windows."""
diff --git a/adb/test_track_devices.cpp b/adb/test_track_devices.cpp
index f78daeb..6f658f6 100644
--- a/adb/test_track_devices.cpp
+++ b/adb/test_track_devices.cpp
@@ -1,12 +1,13 @@
// TODO: replace this with a shell/python script.
/* a simple test program, connects to ADB server, and opens a track-devices session */
-#include <netdb.h>
-#include <sys/socket.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <errno.h>
#include <memory.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
#include <base/file.h>
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 87aff88..4a273c4 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -126,19 +126,18 @@
static int
read_packet(int fd, const char* name, apacket** ppacket)
{
- char *p = (char*)ppacket; /* really read a packet address */
- int r;
- int len = sizeof(*ppacket);
- char buff[8];
+ char buff[8];
if (!name) {
snprintf(buff, sizeof buff, "fd=%d", fd);
name = buff;
}
+ char* p = reinterpret_cast<char*>(ppacket); /* really read a packet address */
+ int len = sizeof(apacket*);
while(len > 0) {
- r = adb_read(fd, p, len);
+ int r = adb_read(fd, p, len);
if(r > 0) {
len -= r;
- p += r;
+ p += r;
} else {
D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
if((r < 0) && (errno == EINTR)) continue;
@@ -155,20 +154,18 @@
static int
write_packet(int fd, const char* name, apacket** ppacket)
{
- char *p = (char*) ppacket; /* we really write the packet address */
- int r, len = sizeof(ppacket);
char buff[8];
if (!name) {
snprintf(buff, sizeof buff, "fd=%d", fd);
name = buff;
}
-
if (ADB_TRACING) {
dump_packet(name, "to remote", *ppacket);
}
- len = sizeof(ppacket);
+ char* p = reinterpret_cast<char*>(ppacket); /* we really write the packet address */
+ int len = sizeof(apacket*);
while(len > 0) {
- r = adb_write(fd, p, len);
+ int r = adb_write(fd, p, len);
if(r > 0) {
len -= r;
p += r;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 0dc9581..650e5ea 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -94,13 +94,17 @@
int fd = -1;
#if ADB_HOST
+ if (find_emulator_transport_by_adb_port(adb_port) != nullptr) {
+ return -1;
+ }
+
const char *host = getenv("ADBHOST");
if (host) {
fd = network_connect(host, adb_port, SOCK_STREAM, 0, error);
}
#endif
if (fd < 0) {
- fd = socket_loopback_client(adb_port, SOCK_STREAM);
+ fd = network_loopback_client(adb_port, SOCK_STREAM, error);
}
if (fd >= 0) {
@@ -108,8 +112,10 @@
close_on_exec(fd);
disable_tcp_nagle(fd);
std::string serial = android::base::StringPrintf("emulator-%d", console_port);
- register_socket_transport(fd, serial.c_str(), adb_port, 1);
- return 0;
+ if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
+ return 0;
+ }
+ adb_close(fd);
}
return -1;
}
@@ -118,16 +124,16 @@
static void *client_socket_thread(void *x)
{
#if ADB_HOST
- int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
- int count = ADB_LOCAL_TRANSPORT_MAX;
-
D("transport: client_socket_thread() starting\n");
+ while (true) {
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ int count = ADB_LOCAL_TRANSPORT_MAX;
- /* try to connect to any number of running emulator instances */
- /* this is only done when ADB starts up. later, each new emulator */
- /* will send a message to ADB to indicate that is is starting up */
- for ( ; count > 0; count--, port += 2 ) {
- local_connect(port);
+ // Try to connect to any number of running emulator instances.
+ for ( ; count > 0; count--, port += 2 ) {
+ local_connect(port);
+ }
+ sleep(1);
}
#endif
return 0;
@@ -144,9 +150,10 @@
serverfd = -1;
for(;;) {
if(serverfd == -1) {
- serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
+ std::string error;
+ serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
if(serverfd < 0) {
- D("server: cannot bind socket yet: %s\n", strerror(errno));
+ D("server: cannot bind socket yet: %s\n", error.c_str());
adb_sleep_ms(1000);
continue;
}
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 4b74adf..49deb73 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -63,6 +63,27 @@
}
};
+class TransportSetup {
+public:
+ TransportSetup() {
+#ifdef _WIN32
+ // Use extern instead of including sysdeps.h which brings in various macros
+ // that conflict with APIs used in this file.
+ extern void adb_sysdeps_init(void);
+ adb_sysdeps_init();
+#else
+ // adb_sysdeps_init() is an inline function that we cannot link against.
+#endif
+ }
+};
+
+// Static initializer will call adb_sysdeps_init() before main() to initialize
+// the transport mutex before it is used in the tests. Alternatives would be to
+// use __attribute__((constructor)) here or to use that or a static initializer
+// for adb_sysdeps_init() itself in sysdeps_win32.cpp (caveats of unclear
+// init order), or to use a test fixture whose SetUp() could do the init once.
+static TransportSetup g_TransportSetup;
+
TEST(transport, kick_transport) {
TestTransport t;
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index af65130..939f319 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -34,6 +34,10 @@
#define DBG D
+// There's no strerror equivalent for the errors returned by IOKit.
+// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Handling_Errors/AH_Handling_Errors.html
+// Search the web for "IOReturn.h" to find a complete up to date list.
+
static IONotificationPortRef notificationPort = 0;
static io_iterator_t notificationIterator;
@@ -362,7 +366,7 @@
handle->zero_mask = maxPacketSize - 1;
} else {
- DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
+ DBG("ERR: FindDeviceInterface - could not get pipe properties (%08x)\n", kr);
goto err_get_pipe_props;
}
}
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index 4c9a152..b8cc5cf 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -33,6 +33,10 @@
/** Structure usb_handle describes our connection to the usb device via
AdbWinApi.dll. This structure is returned from usb_open() routine and
is expected in each subsequent call that is accessing the device.
+
+ Most members are protected by usb_lock, except for adb_{read,write}_pipe which
+ rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
+ ability to break a thread out of pipe IO.
*/
struct usb_handle {
/// Previous entry in the list of opened usb handles
@@ -86,6 +90,9 @@
/// registers usb transport for them.
void find_devices();
+/// Kicks all USB devices
+static void kick_devices();
+
/// Entry point for thread that polls (every second) for new usb interfaces.
/// This routine calls find_devices in infinite loop.
void* device_poll_thread(void* unused);
@@ -111,9 +118,6 @@
/// Closes opened usb handle
int usb_close(usb_handle* handle);
-/// Gets interface (device) name for an opened usb handle
-const char *usb_name(usb_handle* handle);
-
int known_device_locked(const char* dev_name) {
usb_handle* usb;
@@ -177,17 +181,99 @@
return NULL;
}
+static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam) {
+ switch (uMsg) {
+ case WM_POWERBROADCAST:
+ switch (wParam) {
+ case PBT_APMRESUMEAUTOMATIC:
+ // Resuming from sleep or hibernation, so kick all existing USB devices
+ // and then allow the device_poll_thread to redetect USB devices from
+ // scratch. If we don't do this, existing USB devices will never respond
+ // to us because they'll be waiting for the connect/auth handshake.
+ D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
+ "so kicking all USB devices\n");
+ kick_devices();
+ return TRUE;
+ }
+ }
+ return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
+
+static void* _power_notification_thread(void* unused) {
+ // This uses a thread with its own window message pump to get power
+ // notifications. If adb runs from a non-interactive service account, this
+ // might not work (not sure). If that happens to not work, we could use
+ // heavyweight WMI APIs to get power notifications. But for the common case
+ // of a developer's interactive session, a window message pump is more
+ // appropriate.
+ D("Created power notification thread\n");
+
+ // Window class names are process specific.
+ static const WCHAR kPowerNotificationWindowClassName[] =
+ L"PowerNotificationWindow";
+
+ // Get the HINSTANCE corresponding to the module that _power_window_proc
+ // is in (the main module).
+ const HINSTANCE instance = GetModuleHandleW(NULL);
+ if (!instance) {
+ // This is such a common API call that this should never fail.
+ fatal("GetModuleHandleW failed: %s",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ WNDCLASSEXW wndclass;
+ memset(&wndclass, 0, sizeof(wndclass));
+ wndclass.cbSize = sizeof(wndclass);
+ wndclass.lpfnWndProc = _power_window_proc;
+ wndclass.hInstance = instance;
+ wndclass.lpszClassName = kPowerNotificationWindowClassName;
+ if (!RegisterClassExW(&wndclass)) {
+ fatal("RegisterClassExW failed: %s",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
+ L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
+ NULL, NULL, instance, NULL)) {
+ fatal("CreateWindowExW failed: %s",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ MSG msg;
+ while (GetMessageW(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+
+ // GetMessageW() will return false if a quit message is posted. We don't
+ // do that, but it might be possible for that to occur when logging off or
+ // shutting down. Not a big deal since the whole process will be going away
+ // soon anyway.
+ D("Power notification thread exiting\n");
+
+ return NULL;
+}
+
void usb_init() {
if (!adb_thread_create(device_poll_thread, nullptr)) {
- fatal_errno("cannot create input thread");
+ fatal_errno("cannot create device poll thread");
+ }
+ if (!adb_thread_create(_power_notification_thread, nullptr)) {
+ fatal_errno("cannot create power notification thread");
}
}
usb_handle* do_usb_open(const wchar_t* interface_name) {
+ unsigned long name_len = 0;
+
// Allocate our handle
- usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
- if (NULL == ret)
- return NULL;
+ usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
+ if (NULL == ret) {
+ D("Could not allocate %u bytes for usb_handle: %s\n", sizeof(usb_handle),
+ strerror(errno));
+ goto fail;
+ }
// Set linkers back to the handle
ret->next = ret;
@@ -195,11 +281,10 @@
// Create interface.
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-
if (NULL == ret->adb_interface) {
- free(ret);
- errno = GetLastError();
- return NULL;
+ D("AdbCreateInterfaceByName failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
}
// Open read pipe (endpoint)
@@ -207,45 +292,60 @@
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
- if (NULL != ret->adb_read_pipe) {
- // Open write pipe (endpoint)
- ret->adb_write_pipe =
- AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
- AdbOpenAccessTypeReadWrite,
- AdbOpenSharingModeReadWrite);
- if (NULL != ret->adb_write_pipe) {
- // Save interface name
- unsigned long name_len = 0;
-
- // First get expected name length
- AdbGetInterfaceName(ret->adb_interface,
- NULL,
- &name_len,
- true);
- if (0 != name_len) {
- ret->interface_name = (char*)malloc(name_len);
-
- if (NULL != ret->interface_name) {
- // Now save the name
- if (AdbGetInterfaceName(ret->adb_interface,
- ret->interface_name,
- &name_len,
- true)) {
- // We're done at this point
- return ret;
- }
- } else {
- SetLastError(ERROR_OUTOFMEMORY);
- }
- }
- }
+ if (NULL == ret->adb_read_pipe) {
+ D("AdbOpenDefaultBulkReadEndpoint failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
}
- // Something went wrong.
- int saved_errno = GetLastError();
- usb_cleanup_handle(ret);
- free(ret);
- SetLastError(saved_errno);
+ // Open write pipe (endpoint)
+ ret->adb_write_pipe =
+ AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL == ret->adb_write_pipe) {
+ D("AdbOpenDefaultBulkWriteEndpoint failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ // Save interface name
+ // First get expected name length
+ AdbGetInterfaceName(ret->adb_interface,
+ NULL,
+ &name_len,
+ true);
+ if (0 == name_len) {
+ D("AdbGetInterfaceName returned name length of zero: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ ret->interface_name = (char*)malloc(name_len);
+ if (NULL == ret->interface_name) {
+ D("Could not allocate %lu bytes for interface_name: %s\n", name_len,
+ strerror(errno));
+ goto fail;
+ }
+
+ // Now save the name
+ if (!AdbGetInterfaceName(ret->adb_interface,
+ ret->interface_name,
+ &name_len,
+ true)) {
+ D("AdbGetInterfaceName failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ goto fail;
+ }
+
+ // We're done at this point
+ return ret;
+
+fail:
+ if (NULL != ret) {
+ usb_cleanup_handle(ret);
+ free(ret);
+ }
return NULL;
}
@@ -253,92 +353,130 @@
int usb_write(usb_handle* handle, const void* data, int len) {
unsigned long time_out = 5000;
unsigned long written = 0;
- int ret;
+ int err = 0;
D("usb_write %d\n", len);
- if (NULL != handle) {
- // Perform write
- ret = AdbWriteEndpointSync(handle->adb_write_pipe,
- (void*)data,
- (unsigned long)len,
- &written,
- time_out);
- int saved_errno = GetLastError();
-
- if (ret) {
- // Make sure that we've written what we were asked to write
- D("usb_write got: %ld, expected: %d\n", written, len);
- if (written == (unsigned long)len) {
- if(handle->zero_mask && (len & handle->zero_mask) == 0) {
- // Send a zero length packet
- AdbWriteEndpointSync(handle->adb_write_pipe,
- (void*)data,
- 0,
- &written,
- time_out);
- }
- return 0;
- }
- } else {
- // assume ERROR_INVALID_HANDLE indicates we are disconnected
- if (saved_errno == ERROR_INVALID_HANDLE)
- usb_kick(handle);
- }
- errno = saved_errno;
- } else {
- D("usb_write NULL handle\n");
- SetLastError(ERROR_INVALID_HANDLE);
+ if (NULL == handle) {
+ D("usb_write was passed NULL handle\n");
+ err = EINVAL;
+ goto fail;
}
- D("usb_write failed: %d\n", errno);
+ // Perform write
+ if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ (unsigned long)len,
+ &written,
+ time_out)) {
+ D("AdbWriteEndpointSync failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ err = EIO;
+ goto fail;
+ }
+ // Make sure that we've written what we were asked to write
+ D("usb_write got: %ld, expected: %d\n", written, len);
+ if (written != (unsigned long)len) {
+ // If this occurs, this code should be changed to repeatedly call
+ // AdbWriteEndpointSync() until all bytes are written.
+ D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld\n",
+ len, written);
+ err = EIO;
+ goto fail;
+ }
+
+ if (handle->zero_mask && (len & handle->zero_mask) == 0) {
+ // Send a zero length packet
+ if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ 0,
+ &written,
+ time_out)) {
+ D("AdbWriteEndpointSync of zero length packet failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ err = EIO;
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ // Any failure should cause us to kick the device instead of leaving it a
+ // zombie state with potential to hang.
+ if (NULL != handle) {
+ D("Kicking device due to error in usb_write\n");
+ usb_kick(handle);
+ }
+
+ D("usb_write failed\n");
+ errno = err;
return -1;
}
int usb_read(usb_handle *handle, void* data, int len) {
unsigned long time_out = 0;
unsigned long read = 0;
+ int err = 0;
D("usb_read %d\n", len);
- if (handle != nullptr) {
- while (len > 0) {
- int ret = AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out);
- int saved_errno = GetLastError();
- D("usb_write got: %ld, expected: %d, errno: %d\n", read, len, saved_errno);
- if (ret) {
- data = (char *)data + read;
- len -= read;
-
- if (len == 0)
- return 0;
- } else {
- // assume ERROR_INVALID_HANDLE indicates we are disconnected
- if (saved_errno == ERROR_INVALID_HANDLE)
- usb_kick(handle);
- break;
- }
- errno = saved_errno;
- }
- } else {
- D("usb_read NULL handle\n");
- SetLastError(ERROR_INVALID_HANDLE);
+ if (NULL == handle) {
+ D("usb_read was passed NULL handle\n");
+ err = EINVAL;
+ goto fail;
}
- D("usb_read failed: %d\n", errno);
+ while (len > 0) {
+ if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
+ time_out)) {
+ D("AdbReadEndpointSync failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ err = EIO;
+ goto fail;
+ }
+ D("usb_read got: %ld, expected: %d\n", read, len);
+ data = (char *)data + read;
+ len -= read;
+ }
+
+ return 0;
+
+fail:
+ // Any failure should cause us to kick the device instead of leaving it a
+ // zombie state with potential to hang.
+ if (NULL != handle) {
+ D("Kicking device due to error in usb_read\n");
+ usb_kick(handle);
+ }
+
+ D("usb_read failed\n");
+ errno = err;
return -1;
}
+// Wrapper around AdbCloseHandle() that logs diagnostics.
+static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
+ if (!AdbCloseHandle(adb_handle)) {
+ D("AdbCloseHandle(%p) failed: %s\n", adb_handle,
+ SystemErrorCodeToString(GetLastError()).c_str());
+ }
+}
+
void usb_cleanup_handle(usb_handle* handle) {
+ D("usb_cleanup_handle\n");
if (NULL != handle) {
if (NULL != handle->interface_name)
free(handle->interface_name);
+ // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
+ // wait until the pipe no longer uses the interface. Then we can
+ // AdbCloseHandle() the interface.
if (NULL != handle->adb_write_pipe)
- AdbCloseHandle(handle->adb_write_pipe);
+ _adb_close_handle(handle->adb_write_pipe);
if (NULL != handle->adb_read_pipe)
- AdbCloseHandle(handle->adb_read_pipe);
+ _adb_close_handle(handle->adb_read_pipe);
if (NULL != handle->adb_interface)
- AdbCloseHandle(handle->adb_interface);
+ _adb_close_handle(handle->adb_interface);
handle->interface_name = NULL;
handle->adb_write_pipe = NULL;
@@ -347,16 +485,22 @@
}
}
+static void usb_kick_locked(usb_handle* handle) {
+ // The reason the lock must be acquired before calling this function is in
+ // case multiple threads are trying to kick the same device at the same time.
+ usb_cleanup_handle(handle);
+}
+
void usb_kick(usb_handle* handle) {
+ D("usb_kick\n");
if (NULL != handle) {
adb_mutex_lock(&usb_lock);
- usb_cleanup_handle(handle);
+ usb_kick_locked(handle);
adb_mutex_unlock(&usb_lock);
} else {
- SetLastError(ERROR_INVALID_HANDLE);
- errno = ERROR_INVALID_HANDLE;
+ errno = EINVAL;
}
}
@@ -384,16 +528,6 @@
return 0;
}
-const char *usb_name(usb_handle* handle) {
- if (NULL == handle) {
- SetLastError(ERROR_INVALID_HANDLE);
- errno = ERROR_INVALID_HANDLE;
- return NULL;
- }
-
- return (const char*)handle->interface_name;
-}
-
int recognized_device(usb_handle* handle) {
if (NULL == handle)
return 0;
@@ -403,6 +537,8 @@
if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
&device_desc)) {
+ D("AdbGetUsbDeviceDescriptor failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
return 0;
}
@@ -411,6 +547,8 @@
if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
&interf_desc)) {
+ D("AdbGetUsbInterfaceDescriptor failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
return 0;
}
@@ -427,6 +565,10 @@
// assuming zero is a valid bulk endpoint ID
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
handle->zero_mask = endpoint_info.max_packet_size - 1;
+ D("device zero_mask: 0x%x\n", handle->zero_mask);
+ } else {
+ D("AdbGetEndpointInformation failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
}
}
@@ -448,8 +590,11 @@
ADBAPIHANDLE enum_handle =
AdbEnumInterfaces(usb_class_id, true, true, true);
- if (NULL == enum_handle)
+ if (NULL == enum_handle) {
+ D("AdbEnumInterfaces failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
return;
+ }
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
// TODO: FIXME - temp hack converting wchar_t into char.
@@ -486,7 +631,8 @@
free(handle);
}
} else {
- D("cannot get serial number\n");
+ D("cannot get serial number: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
usb_cleanup_handle(handle);
free(handle);
}
@@ -500,5 +646,21 @@
entry_buffer_size = sizeof(entry_buffer);
}
- AdbCloseHandle(enum_handle);
+ if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+ // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
+ D("AdbNextInterface failed: %s\n",
+ SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ _adb_close_handle(enum_handle);
+}
+
+static void kick_devices() {
+ // Need to acquire lock to safely walk the list which might be modified
+ // by another thread.
+ adb_mutex_lock(&usb_lock);
+ for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+ usb_kick_locked(usb);
+ }
+ adb_mutex_unlock(&usb_lock);
}
diff --git a/base/Android.mk b/base/Android.mk
index 7bd317b..4e135f6 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -21,6 +21,7 @@
logging.cpp \
stringprintf.cpp \
strings.cpp \
+ test_utils.cpp \
libbase_test_src_files := \
file_test.cpp \
@@ -28,7 +29,6 @@
stringprintf_test.cpp \
strings_test.cpp \
test_main.cpp \
- test_utils.cpp \
libbase_cppflags := \
-Wall \
diff --git a/base/file_test.cpp b/base/file_test.cpp
index b138094..77b9268 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -24,7 +24,7 @@
#include <string>
-#include "test_utils.h"
+#include "base/test_utils.h"
TEST(file, ReadFileToString_ENOENT) {
std::string s("hello");
@@ -34,23 +34,13 @@
EXPECT_EQ("", s); // s was cleared.
}
-TEST(file, ReadFileToString_success) {
- std::string s("hello");
- ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s))
- << strerror(errno);
- EXPECT_GT(s.length(), 6U);
- EXPECT_EQ('\n', s[s.length() - 1]);
- s[5] = 0;
- EXPECT_STREQ("Linux", s.c_str());
-}
-
-TEST(file, WriteStringToFile) {
+TEST(file, ReadFileToString_WriteStringToFile) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename))
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
<< strerror(errno);
std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
<< strerror(errno);
EXPECT_EQ("abc", s);
}
@@ -61,16 +51,16 @@
TEST(file, WriteStringToFile2) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename, 0660,
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
getuid(), getgid()))
<< strerror(errno);
struct stat sb;
- ASSERT_EQ(0, stat(tf.filename, &sb));
+ ASSERT_EQ(0, stat(tf.path, &sb));
ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
ASSERT_EQ(getuid(), sb.st_uid);
ASSERT_EQ(getgid(), sb.st_gid);
std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
<< strerror(errno);
EXPECT_EQ("abc", s);
}
@@ -88,28 +78,21 @@
EXPECT_EQ("abc", s);
}
-TEST(file, ReadFully) {
- int fd = open("/proc/version", O_RDONLY);
- ASSERT_NE(-1, fd) << strerror(errno);
-
- char buf[1024];
- memset(buf, 0, sizeof(buf));
- ASSERT_TRUE(android::base::ReadFully(fd, buf, 5));
- ASSERT_STREQ("Linux", buf);
-
- ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
-
- ASSERT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
-
- close(fd);
-}
-
TEST(file, WriteFully) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+ s.resize(3);
+ ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
<< strerror(errno);
EXPECT_EQ("abc", s);
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ s.resize(1024);
+ ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
}
diff --git a/base/test_utils.h b/base/include/base/test_utils.h
similarity index 69%
rename from base/test_utils.h
rename to base/include/base/test_utils.h
index 132d3a7..402e0a5 100644
--- a/base/test_utils.h
+++ b/base/include/base/test_utils.h
@@ -17,16 +17,35 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
+#include <string>
+
+#include <base/macros.h>
+
class TemporaryFile {
public:
TemporaryFile();
~TemporaryFile();
int fd;
- char filename[1024];
+ char path[1024];
private:
- void init(const char* tmp_dir);
+ void init(const std::string& tmp_dir);
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
+};
+
+class TemporaryDir {
+ public:
+ TemporaryDir();
+ ~TemporaryDir();
+
+ char path[1024];
+
+ private:
+ bool init(const std::string& tmp_dir);
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
};
#endif // TEST_UTILS_H
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index c91857a..1a92c94 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -21,7 +21,7 @@
#include "base/file.h"
#include "base/stringprintf.h"
-#include "test_utils.h"
+#include "base/test_utils.h"
#include <gtest/gtest.h>
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
index 54b2b6c..5cc2086 100644
--- a/base/stringprintf_test.cpp
+++ b/base/stringprintf_test.cpp
@@ -20,14 +20,11 @@
#include <string>
-// The z size sepcifier isn't supported on Windows, so this test isn't useful.
-#if !defined(_WIN32)
TEST(StringPrintfTest, HexSizeT) {
size_t size = 0x00107e59;
EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
}
-#endif
TEST(StringPrintfTest, StringAppendF) {
std::string s("a");
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 0517bc7..b0c5a12 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include "test_utils.h"
+#include "base/logging.h"
+#include "base/test_utils.h"
+#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
#include <fcntl.h>
#include <stdio.h>
@@ -24,36 +26,77 @@
#if defined(_WIN32)
#include <windows.h>
+#include <direct.h>
#endif
-TemporaryFile::TemporaryFile() {
-#if defined(__ANDROID__)
- init("/data/local/tmp");
-#elif defined(_WIN32)
- char wd[MAX_PATH] = {};
- _getcwd(wd, sizeof(wd));
- init(wd);
-#else
- init("/tmp");
+#include <string>
+
+#ifdef _WIN32
+int mkstemp(char* template_name) {
+ if (_mktemp(template_name) == nullptr) {
+ return -1;
+ }
+ // Use open() to match the close() that TemporaryFile's destructor does.
+ // Note that on Windows, this does CR/LF translation and _setmode() should
+ // be used to change that if appropriate.
+ return open(template_name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+}
+
+char* mkdtemp(char* template_name) {
+ if (_mktemp(template_name) == nullptr) {
+ return nullptr;
+ }
+ if (_mkdir(template_name) == -1) {
+ return nullptr;
+ }
+ return template_name;
+}
#endif
+
+static std::string GetSystemTempDir() {
+#if defined(__ANDROID__)
+ return "/data/local/tmp";
+#elif defined(_WIN32)
+ char tmp_dir[MAX_PATH];
+ DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
+ CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
+ CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+
+ // GetTempPath() returns a path with a trailing slash, but init()
+ // does not expect that, so remove it.
+ CHECK_EQ(tmp_dir[result - 1], '\\');
+ tmp_dir[result - 1] = '\0';
+ return tmp_dir;
+#else
+ return "/tmp";
+#endif
+}
+
+TemporaryFile::TemporaryFile() {
+ init(GetSystemTempDir());
}
TemporaryFile::~TemporaryFile() {
close(fd);
- unlink(filename);
+ unlink(path);
}
-void TemporaryFile::init(const char* tmp_dir) {
- snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
-#if !defined(_WIN32)
- fd = mkstemp(filename);
-#else
- // Windows doesn't have mkstemp, and tmpfile creates the file in the root
- // directory, requiring root (?!) permissions. We have to settle for mktemp.
- if (mktemp(filename) == nullptr) {
- abort();
- }
+void TemporaryFile::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
+ OS_PATH_SEPARATOR);
+ fd = mkstemp(path);
+}
- fd = open(filename, O_RDWR | O_NOINHERIT | O_CREAT, _S_IREAD | _S_IWRITE);
-#endif
+TemporaryDir::TemporaryDir() {
+ init(GetSystemTempDir());
+}
+
+TemporaryDir::~TemporaryDir() {
+ rmdir(path);
+}
+
+bool TemporaryDir::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
+ OS_PATH_SEPARATOR);
+ return (mkdtemp(path) != nullptr);
}
diff --git a/crash_reporter/MODULE_LICENSE_BSD b/crash_reporter/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/crash_reporter/MODULE_LICENSE_BSD
diff --git a/crash_reporter/NOTICE b/crash_reporter/NOTICE
new file mode 100644
index 0000000..b9e779f
--- /dev/null
+++ b/crash_reporter/NOTICE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium OS Authors. 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.
diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc
deleted file mode 100644
index ec291c0..0000000
--- a/crash_reporter/chrome_collector.cc
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "crash-reporter/chrome_collector.h"
-
-#include <pcrecpp.h>
-#include <stdint.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <base/files/file_util.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <chromeos/data_encoding.h>
-#include <chromeos/dbus/service_constants.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
-
-using base::FilePath;
-using base::StringPrintf;
-
-namespace {
-
-const char kDefaultMinidumpName[] = "upload_file_minidump";
-
-// Path to the gzip binary.
-const char kGzipPath[] = "/bin/gzip";
-
-// Filenames for logs attached to crash reports. Also used as metadata keys.
-const char kChromeLogFilename[] = "chrome.txt";
-const char kGpuStateFilename[] = "i915_error_state.log.xz";
-
-// From //net/crash/collector/collector.h
-const int kDefaultMaxUploadBytes = 1024 * 1024;
-
-// Extract a string delimited by the given character, from the given offset
-// into a source string. Returns false if the string is zero-sized or no
-// delimiter was found.
-bool GetDelimitedString(const std::string &str, char ch, size_t offset,
- std::string *substr) {
- size_t at = str.find_first_of(ch, offset);
- if (at == std::string::npos || at == offset)
- return false;
- *substr = str.substr(offset, at - offset);
- return true;
-}
-
-// Gets the GPU's error state from debugd and writes it to |error_state_path|.
-// Returns true on success.
-bool GetDriErrorState(const FilePath &error_state_path,
- org::chromium::debugdProxy *proxy) {
- chromeos::ErrorPtr error;
- std::string error_state_str;
-
- proxy->GetLog("i915_error_state", &error_state_str, &error);
-
- if (error) {
- LOG(ERROR) << "Error calling D-Bus proxy call to interface "
- << "'" << proxy->GetObjectPath().value() << "':"
- << error->GetMessage();
- return false;
- }
-
- if (error_state_str == "<empty>")
- return false;
-
- const char kBase64Header[] = "<base64>: ";
- const size_t kBase64HeaderLength = sizeof(kBase64Header) - 1;
- if (error_state_str.compare(0, kBase64HeaderLength, kBase64Header)) {
- LOG(ERROR) << "i915_error_state is missing base64 header";
- return false;
- }
-
- std::string decoded_error_state;
-
- if (!chromeos::data_encoding::Base64Decode(
- error_state_str.c_str() + kBase64HeaderLength,
- &decoded_error_state)) {
- LOG(ERROR) << "Could not decode i915_error_state";
- return false;
- }
-
- int written = base::WriteFile(error_state_path,
- decoded_error_state.c_str(),
- decoded_error_state.length());
- if (written < 0 ||
- static_cast<size_t>(written) != decoded_error_state.length()) {
- LOG(ERROR) << "Could not write file " << error_state_path.value()
- << " Written: " << written << " Len: "
- << decoded_error_state.length();
- base::DeleteFile(error_state_path, false);
- return false;
- }
-
- return true;
-}
-
-// Gzip-compresses |path|, removes the original file, and returns the path of
-// the new file. On failure, the original file is left alone and an empty path
-// is returned.
-FilePath GzipFile(const FilePath& path) {
- chromeos::ProcessImpl proc;
- proc.AddArg(kGzipPath);
- proc.AddArg(path.value());
- const int res = proc.Run();
- if (res != 0) {
- LOG(ERROR) << "Failed to gzip " << path.value();
- return FilePath();
- }
- return path.AddExtension(".gz");
-}
-
-} // namespace
-
-
-ChromeCollector::ChromeCollector() : output_file_ptr_(stdout) {}
-
-ChromeCollector::~ChromeCollector() {}
-
-bool ChromeCollector::HandleCrash(const FilePath &file_path,
- const std::string &pid_string,
- const std::string &uid_string,
- const std::string &exe_name) {
- if (!is_feedback_allowed_function_())
- return true;
-
- LOG(WARNING) << "Received crash notification for " << exe_name << "["
- << pid_string << "] user " << uid_string << " (called directly)";
-
- if (exe_name.find('/') != std::string::npos) {
- LOG(ERROR) << "exe_name contains illegal characters: " << exe_name;
- return false;
- }
-
- FilePath dir;
- uid_t uid = atoi(uid_string.c_str());
- pid_t pid = atoi(pid_string.c_str());
- if (!GetCreatedCrashDirectoryByEuid(uid, &dir, nullptr)) {
- LOG(ERROR) << "Can't create crash directory for uid " << uid;
- return false;
- }
-
- std::string dump_basename = FormatDumpBasename(exe_name, time(nullptr), pid);
- FilePath meta_path = GetCrashPath(dir, dump_basename, "meta");
- FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp");
-
- std::string data;
- if (!base::ReadFileToString(file_path, &data)) {
- LOG(ERROR) << "Can't read crash log: " << file_path.value();
- return false;
- }
-
- if (!ParseCrashLog(data, dir, minidump_path, dump_basename)) {
- LOG(ERROR) << "Failed to parse Chrome's crash log";
- return false;
- }
-
-
- int64_t report_size = 0;
- base::GetFileSize(minidump_path, &report_size);
-
- // Keyed by crash metadata key name.
- const std::map<std::string, base::FilePath> additional_logs =
- GetAdditionalLogs(dir, dump_basename, exe_name);
- for (auto it : additional_logs) {
- int64_t file_size = 0;
- if (!base::GetFileSize(it.second, &file_size)) {
- PLOG(WARNING) << "Unable to get size of " << it.second.value();
- continue;
- }
- if (report_size + file_size > kDefaultMaxUploadBytes) {
- LOG(INFO) << "Skipping upload of " << it.second.value() << "("
- << file_size << "B) because report size would exceed limit ("
- << kDefaultMaxUploadBytes << "B)";
- continue;
- }
- VLOG(1) << "Adding metadata: " << it.first << " -> " << it.second.value();
- // Call AddCrashMetaUploadFile() rather than AddCrashMetaData() here. The
- // former adds a prefix to the key name; without the prefix, only the key
- // "logs" appears to be displayed on the crash server.
- AddCrashMetaUploadFile(it.first, it.second.value());
- report_size += file_size;
- }
-
- // We're done.
- WriteCrashMetaData(meta_path, exe_name, minidump_path.value());
-
- fprintf(output_file_ptr_, "%s", kSuccessMagic);
- fflush(output_file_ptr_);
-
- return true;
-}
-
-void ChromeCollector::SetUpDBus() {
- CrashCollector::SetUpDBus();
-
- debugd_proxy_.reset(
- new org::chromium::debugdProxy(bus_, debugd::kDebugdServiceName));
-}
-
-bool ChromeCollector::ParseCrashLog(const std::string &data,
- const FilePath &dir,
- const FilePath &minidump,
- const std::string &basename) {
- size_t at = 0;
- while (at < data.size()) {
- // Look for a : followed by a decimal number, followed by another :
- // followed by N bytes of data.
- std::string name, size_string;
- if (!GetDelimitedString(data, ':', at, &name)) {
- LOG(ERROR) << "Can't find : after name @ offset " << at;
- break;
- }
- at += name.size() + 1; // Skip the name & : delimiter.
-
- if (!GetDelimitedString(data, ':', at, &size_string)) {
- LOG(ERROR) << "Can't find : after size @ offset " << at;
- break;
- }
- at += size_string.size() + 1; // Skip the size & : delimiter.
-
- size_t size;
- if (!base::StringToSizeT(size_string, &size)) {
- LOG(ERROR) << "String not convertible to integer: " << size_string;
- break;
- }
-
- // Data would run past the end, did we get a truncated file?
- if (at + size > data.size()) {
- LOG(ERROR) << "Overrun, expected " << size << " bytes of data, got "
- << (data.size() - at);
- break;
- }
-
- if (name.find("filename") != std::string::npos) {
- // File.
- // Name will be in a semi-MIME format of
- // <descriptive name>"; filename="<name>"
- // Descriptive name will be upload_file_minidump for the dump.
- std::string desc, filename;
- pcrecpp::RE re("(.*)\" *; *filename=\"(.*)\"");
- if (!re.FullMatch(name.c_str(), &desc, &filename)) {
- LOG(ERROR) << "Filename was not in expected format: " << name;
- break;
- }
-
- if (desc.compare(kDefaultMinidumpName) == 0) {
- // The minidump.
- WriteNewFile(minidump, data.c_str() + at, size);
- } else {
- // Some other file.
- FilePath path = GetCrashPath(dir, basename + "-" + filename, "other");
- if (WriteNewFile(path, data.c_str() + at, size) >= 0) {
- AddCrashMetaUploadFile(desc, path.value());
- }
- }
- } else {
- // Other attribute.
- std::string value_str;
- value_str.reserve(size);
-
- // Since metadata is one line/value the values must be escaped properly.
- for (size_t i = at; i < at + size; i++) {
- switch (data[i]) {
- case '"':
- case '\\':
- value_str.push_back('\\');
- value_str.push_back(data[i]);
- break;
-
- case '\r':
- value_str += "\\r";
- break;
-
- case '\n':
- value_str += "\\n";
- break;
-
- case '\t':
- value_str += "\\t";
- break;
-
- case '\0':
- value_str += "\\0";
- break;
-
- default:
- value_str.push_back(data[i]);
- break;
- }
- }
- AddCrashMetaUploadData(name, value_str);
- }
-
- at += size;
- }
-
- return at == data.size();
-}
-
-std::map<std::string, base::FilePath> ChromeCollector::GetAdditionalLogs(
- const FilePath &dir,
- const std::string &basename,
- const std::string &exe_name) {
- std::map<std::string, base::FilePath> logs;
-
- // Run the command specified by the config file to gather logs.
- const FilePath chrome_log_path =
- GetCrashPath(dir, basename, kChromeLogFilename);
- if (GetLogContents(log_config_path_, exe_name, chrome_log_path)) {
- const FilePath compressed_path = GzipFile(chrome_log_path);
- if (!compressed_path.empty())
- logs[kChromeLogFilename] = compressed_path;
- else
- base::DeleteFile(chrome_log_path, false /* recursive */);
- }
-
- // For unit testing, debugd_proxy_ isn't initialized, so skip attempting to
- // get the GPU error state from debugd.
- if (debugd_proxy_) {
- const FilePath dri_error_state_path =
- GetCrashPath(dir, basename, kGpuStateFilename);
- if (GetDriErrorState(dri_error_state_path, debugd_proxy_.get()))
- logs[kGpuStateFilename] = dri_error_state_path;
- }
-
- return logs;
-}
-
-// static
-const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished";
diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h
deleted file mode 100644
index 0b58c19..0000000
--- a/crash_reporter/chrome_collector.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CRASH_REPORTER_CHROME_COLLECTOR_H_
-#define CRASH_REPORTER_CHROME_COLLECTOR_H_
-
-#include <map>
-#include <string>
-
-#include <base/files/file_path.h>
-#include <base/macros.h>
-#include <gtest/gtest_prod.h> // for FRIEND_TEST
-
-#include "crash-reporter/crash_collector.h"
-#include "debugd/dbus-proxies.h"
-
-class SystemLogging;
-
-// Chrome crash collector.
-class ChromeCollector : public CrashCollector {
- public:
- ChromeCollector();
- ~ChromeCollector() override;
-
- // Magic string to let Chrome know the crash report succeeded.
- static const char kSuccessMagic[];
-
- // Handle a specific chrome crash. Returns true on success.
- bool HandleCrash(const base::FilePath &file_path,
- const std::string &pid_string,
- const std::string &uid_string,
- const std::string &exe_name);
-
- protected:
- void SetUpDBus() override;
-
- private:
- friend class ChromeCollectorTest;
- FRIEND_TEST(ChromeCollectorTest, GoodValues);
- FRIEND_TEST(ChromeCollectorTest, BadValues);
- FRIEND_TEST(ChromeCollectorTest, Newlines);
- FRIEND_TEST(ChromeCollectorTest, File);
- FRIEND_TEST(ChromeCollectorTest, HandleCrash);
-
- // Crashes are expected to be in a TLV-style format of:
- // <name>:<length>:<value>
- // Length is encoded as a decimal number. It can be zero, but must consist of
- // at least one character
- // For file values, name actually contains both a description and a filename,
- // in a fixed format of: <description>"; filename="<filename>"
- bool ParseCrashLog(const std::string &data, const base::FilePath &dir,
- const base::FilePath &minidump,
- const std::string &basename);
-
- // Writes additional logs for |exe_name| to files based on |basename| within
- // |dir|. Crash report metadata key names and the corresponding file paths are
- // returned.
- std::map<std::string, base::FilePath> GetAdditionalLogs(
- const base::FilePath &dir,
- const std::string &basename,
- const std::string &exe_name);
-
- FILE *output_file_ptr_;
-
- // D-Bus proxy for debugd interface. Unset in unit tests.
- std::unique_ptr<org::chromium::debugdProxy> debugd_proxy_;
-
- DISALLOW_COPY_AND_ASSIGN(ChromeCollector);
-};
-
-#endif // CRASH_REPORTER_CHROME_COLLECTOR_H_
diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc
deleted file mode 100644
index 0d6a7ce..0000000
--- a/crash_reporter/chrome_collector_test.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "crash-reporter/chrome_collector.h"
-
-#include <stdio.h>
-
-#include <base/auto_reset.h>
-#include <base/files/file_util.h>
-#include <base/files/scoped_temp_dir.h>
-#include <chromeos/syslog_logging.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-using base::FilePath;
-
-namespace {
-
-const char kCrashFormatGood[] = "value1:10:abcdefghijvalue2:5:12345";
-const char kCrashFormatEmbeddedNewline[] =
- "value1:10:abcd\r\nghijvalue2:5:12\n34";
-const char kCrashFormatBad1[] = "value1:10:abcdefghijvalue2:6=12345";
-const char kCrashFormatBad2[] = "value1:10:abcdefghijvalue2:512345";
-const char kCrashFormatBad3[] = "value1:10::abcdefghijvalue2:5=12345";
-const char kCrashFormatBad4[] = "value1:10:abcdefghijvalue2:4=12345";
-
-const char kCrashFormatWithFile[] =
- "value1:10:abcdefghijvalue2:5:12345"
- "some_file\"; filename=\"foo.txt\":15:12345\n789\n12345"
- "value3:2:ok";
-
-void CountCrash() {
-}
-
-bool s_allow_crash = false;
-
-bool IsMetrics() {
- return s_allow_crash;
-}
-
-} // namespace
-
-class ChromeCollectorMock : public ChromeCollector {
- public:
- MOCK_METHOD0(SetUpDBus, void());
-};
-
-class ChromeCollectorTest : public ::testing::Test {
- protected:
- void ExpectFileEquals(const char *golden,
- const FilePath &file_path) {
- std::string contents;
- EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
- EXPECT_EQ(golden, contents);
- }
-
- ChromeCollectorMock collector_;
-
- private:
- void SetUp() override {
- EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
-
- collector_.Initialize(CountCrash, IsMetrics);
- chromeos::ClearLog();
- }
-};
-
-TEST_F(ChromeCollectorTest, GoodValues) {
- FilePath dir(".");
- EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatGood,
- dir, dir.Append("minidump.dmp"),
- "base"));
-
- // Check to see if the values made it in properly.
- std::string meta = collector_.extra_metadata_;
- EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
- EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
-}
-
-TEST_F(ChromeCollectorTest, Newlines) {
- FilePath dir(".");
- EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatEmbeddedNewline,
- dir, dir.Append("minidump.dmp"),
- "base"));
-
- // Check to see if the values were escaped.
- std::string meta = collector_.extra_metadata_;
- EXPECT_TRUE(meta.find("value1=abcd\\r\\nghij") != std::string::npos);
- EXPECT_TRUE(meta.find("value2=12\\n34") != std::string::npos);
-}
-
-TEST_F(ChromeCollectorTest, BadValues) {
- FilePath dir(".");
- const struct {
- const char *data;
- } list[] = {
- {kCrashFormatBad1, },
- {kCrashFormatBad2, },
- {kCrashFormatBad3, },
- {kCrashFormatBad4, },
- };
-
- for (size_t i = 0; i < sizeof(list) / sizeof(list[0]); i++) {
- chromeos::ClearLog();
- EXPECT_FALSE(collector_.ParseCrashLog(list[i].data,
- dir, dir.Append("minidump.dmp"),
- "base"));
- }
-}
-
-TEST_F(ChromeCollectorTest, File) {
- base::ScopedTempDir scoped_temp_dir;
- ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
- const FilePath& dir = scoped_temp_dir.path();
- EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatWithFile,
- dir, dir.Append("minidump.dmp"),
- "base"));
-
- // Check to see if the values are still correct and that the file was
- // written with the right data.
- std::string meta = collector_.extra_metadata_;
- EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
- EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
- EXPECT_TRUE(meta.find("value3=ok") != std::string::npos);
- ExpectFileEquals("12345\n789\n12345", dir.Append("base-foo.txt.other"));
-}
-
-TEST_F(ChromeCollectorTest, HandleCrash) {
- base::AutoReset<bool> auto_reset(&s_allow_crash, true);
- base::ScopedTempDir scoped_temp_dir;
- ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
- const FilePath& dir = scoped_temp_dir.path();
- FilePath dump_file = dir.Append("test.dmp");
- ASSERT_EQ(strlen(kCrashFormatWithFile),
- base::WriteFile(dump_file, kCrashFormatWithFile,
- strlen(kCrashFormatWithFile)));
- collector_.ForceCrashDirectory(dir);
-
- FilePath log_file;
- {
- base::ScopedFILE output(
- base::CreateAndOpenTemporaryFileInDir(dir, &log_file));
- ASSERT_TRUE(output.get());
- base::AutoReset<FILE*> auto_reset_file_ptr(&collector_.output_file_ptr_,
- output.get());
- EXPECT_TRUE(collector_.HandleCrash(dump_file, "123", "456", "chrome_test"));
- }
- ExpectFileEquals(ChromeCollector::kSuccessMagic, log_file);
-}
diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp
deleted file mode 100644
index a7f0e7e..0000000
--- a/crash_reporter/crash-reporter.gyp
+++ /dev/null
@@ -1,147 +0,0 @@
-{
- # Shouldn't need this, but doesn't work otherwise.
- # http://crbug.com/340086 and http://crbug.com/385186
- # Note: the unused dependencies are optimized out by the compiler.
- 'target_defaults': {
- 'variables': {
- 'deps': [
- 'libchromeos-<(libbase_ver)',
- ],
- },
- },
- 'targets': [
- {
- 'target_name': 'libcrash',
- 'type': 'static_library',
- 'variables': {
- 'exported_deps': [
- 'libchrome-<(libbase_ver)',
- 'libpcrecpp',
- ],
- 'deps': ['<@(exported_deps)'],
- },
- 'all_dependent_settings': {
- 'variables': {
- 'deps': [
- '<@(exported_deps)',
- ],
- },
- },
- 'sources': [
- 'chrome_collector.cc',
- 'crash_collector.cc',
- 'kernel_collector.cc',
- 'kernel_warning_collector.cc',
- 'udev_collector.cc',
- 'unclean_shutdown_collector.cc',
- 'user_collector.cc',
- ],
- 'actions': [
- {
- 'action_name': 'generate-session-manager-proxies',
- 'variables': {
- 'proxy_output_file': 'include/session_manager/dbus-proxies.h'
- },
- 'sources': [
- '../login_manager/org.chromium.SessionManagerInterface.xml',
- ],
- 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
- },
- {
- 'action_name': 'generate-debugd-proxies',
- 'variables': {
- 'proxy_output_file': 'include/debugd/dbus-proxies.h'
- },
- 'sources': [
- '../debugd/share/org.chromium.debugd.xml',
- ],
- 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
- },
- ],
- },
- {
- 'target_name': 'crash_reporter',
- 'type': 'executable',
- 'variables': {
- 'deps': [
- 'dbus-1',
- 'libmetrics-<(libbase_ver)',
- ],
- },
- 'dependencies': [
- 'libcrash',
- ],
- 'sources': [
- 'crash_reporter.cc',
- ],
- },
- {
- 'target_name': 'list_proxies',
- 'type': 'executable',
- 'variables': {
- 'deps': [
- 'dbus-1',
- 'libchrome-<(libbase_ver)',
- ],
- },
- 'sources': [
- 'list_proxies.cc',
- ],
- 'actions': [
- {
- 'action_name': 'generate-lib-cros-service-proxies',
- 'variables': {
- 'proxy_output_file': 'include/libcrosservice/dbus-proxies.h'
- },
- 'sources': [
- './dbus_bindings/org.chromium.LibCrosService.xml',
- ],
- 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
- },
- ],
- },
- {
- 'target_name': 'warn_collector',
- 'type': 'executable',
- 'variables': {
- 'lexer_out_dir': 'crash-reporter',
- 'deps': [
- 'libmetrics-<(libbase_ver)',
- ],
- },
- 'link_settings': {
- 'libraries': [
- '-lfl',
- ],
- },
- 'sources': [
- 'warn_collector.l',
- ],
- 'includes': ['../common-mk/lex.gypi'],
- },
- ],
- 'conditions': [
- ['USE_test == 1', {
- 'targets': [
- {
- 'target_name': 'crash_reporter_test',
- 'type': 'executable',
- 'includes': ['../common-mk/common_test.gypi'],
- 'dependencies': ['libcrash'],
- 'sources': [
- 'chrome_collector_test.cc',
- 'crash_collector_test.cc',
- 'crash_collector_test.h',
- 'crash_reporter_logs_test.cc',
- 'kernel_collector_test.cc',
- 'kernel_collector_test.h',
- 'testrunner.cc',
- 'udev_collector_test.cc',
- 'unclean_shutdown_collector_test.cc',
- 'user_collector_test.cc',
- ],
- },
- ],
- }],
- ],
-}
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index 04f3ba8..69fe939 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
#include <dirent.h>
#include <fcntl.h> // For file creation modes.
@@ -23,8 +23,6 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/cryptohome.h>
-#include <chromeos/dbus/service_constants.h>
#include <chromeos/key_value_store.h>
#include <chromeos/process.h>
@@ -87,8 +85,6 @@
}
CrashCollector::~CrashCollector() {
- if (bus_)
- bus_->ShutdownAndBlock();
}
void CrashCollector::Initialize(
@@ -99,21 +95,6 @@
count_crash_function_ = count_crash_function;
is_feedback_allowed_function_ = is_feedback_allowed_function;
-
- SetUpDBus();
-}
-
-void CrashCollector::SetUpDBus() {
- dbus::Bus::Options options;
- options.bus_type = dbus::Bus::SYSTEM;
-
- bus_ = new dbus::Bus(options);
- CHECK(bus_->Connect());
-
- session_manager_proxy_.reset(
- new org::chromium::SessionManagerInterfaceProxy(
- bus_,
- login_manager::kSessionManagerServiceName));
}
int CrashCollector::WriteNewFile(const FilePath &filename,
@@ -166,38 +147,6 @@
extension.c_str()));
}
-bool CrashCollector::GetActiveUserSessions(
- std::map<std::string, std::string> *sessions) {
- chromeos::ErrorPtr error;
- session_manager_proxy_->RetrieveActiveSessions(sessions, &error);
-
- if (error) {
- LOG(ERROR) << "Error calling D-Bus proxy call to interface "
- << "'" << session_manager_proxy_->GetObjectPath().value() << "':"
- << error->GetMessage();
- return false;
- }
-
- return true;
-}
-
-FilePath CrashCollector::GetUserCrashPath() {
- // In this multiprofile world, there is no one-specific user dir anymore.
- // Ask the session manager for the active ones, then just run with the
- // first result we get back.
- FilePath user_path = FilePath(kFallbackUserCrashPath);
- std::map<std::string, std::string> active_sessions;
- if (!GetActiveUserSessions(&active_sessions) || active_sessions.empty()) {
- LOG(ERROR) << "Could not get active user sessions, using default.";
- return user_path;
- }
-
- user_path = chromeos::cryptohome::home::GetHashedUserPath(
- active_sessions.begin()->second).Append("crash");
-
- return user_path;
-}
-
FilePath CrashCollector::GetCrashDirectoryInfo(
uid_t process_euid,
uid_t default_user_id,
@@ -205,17 +154,11 @@
mode_t *mode,
uid_t *directory_owner,
gid_t *directory_group) {
- // TODO(mkrebs): This can go away once Chrome crashes are handled
- // normally (see crosbug.com/5872).
- // Check if the user crash directory should be used. If we are
- // collecting chrome crashes during autotesting, we want to put them in
- // the system crash directory so they are outside the cryptohome -- in
- // case we are being run during logout (see crosbug.com/18637).
- if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) {
+ if (process_euid == default_user_id) {
*mode = kUserCrashPathMode;
*directory_owner = default_user_id;
*directory_group = default_user_group;
- return GetUserCrashPath();
+ return FilePath(kFallbackUserCrashPath);
} else {
*mode = kSystemCrashPathMode;
*directory_owner = kRootOwner;
@@ -491,22 +434,3 @@
return false;
return base::PathExists(FilePath(kLeaveCoreFile));
}
-
-bool CrashCollector::ShouldHandleChromeCrashes() {
- // If we're testing crash reporter itself, we don't want to allow an
- // override for chrome crashes. And, let's be conservative and only
- // allow an override for developer images.
- if (!IsCrashTestInProgress() && IsDeveloperImage()) {
- // Check if there's an override to indicate we should indeed collect
- // chrome crashes. This allows the crashes to still be tracked when
- // they occur in autotests. See "crosbug.com/17987".
- if (base::PathExists(FilePath(kCollectChromeFile)))
- return true;
- }
- // We default to ignoring chrome crashes.
- return false;
-}
-
-bool CrashCollector::IsUserSpecificDirectoryEnabled() {
- return !ShouldHandleChromeCrashes();
-}
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
index ef443d3..0d335cd 100644
--- a/crash_reporter/crash_collector.h
+++ b/crash_reporter/crash_collector.h
@@ -12,11 +12,8 @@
#include <base/files/file_path.h>
#include <base/macros.h>
-#include <base/memory/scoped_ptr.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "session_manager/dbus-proxies.h"
-
// User crash collector.
class CrashCollector {
public:
@@ -44,7 +41,6 @@
FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe);
FRIEND_TEST(CrashCollectorTest, FormatDumpBasename);
FRIEND_TEST(CrashCollectorTest, Initialize);
- FRIEND_TEST(CrashCollectorTest, IsUserSpecificDirectoryEnabled);
FRIEND_TEST(CrashCollectorTest, MetaData);
FRIEND_TEST(CrashCollectorTest, Sanitize);
FRIEND_TEST(CrashCollectorTest, WriteNewFile);
@@ -61,9 +57,6 @@
// Set maximum enqueued crashes in a crash directory.
static const int kMaxCrashDirectorySize;
- // Set up D-Bus.
- virtual void SetUpDBus();
-
// Writes |data| of |size| to |filename|, which must be a new file.
// If the file already exists or writing fails, return a negative value.
// Otherwise returns the number of bytes written.
@@ -79,9 +72,6 @@
forced_crash_directory_ = forced_directory;
}
- virtual bool GetActiveUserSessions(
- std::map<std::string, std::string> *sessions);
- base::FilePath GetUserCrashPath();
base::FilePath GetCrashDirectoryInfo(uid_t process_euid,
uid_t default_user_id,
gid_t default_user_group,
@@ -154,10 +144,6 @@
// Returns true if we should consider ourselves to be running on a
// developer image.
bool IsDeveloperImage();
- // Returns true if chrome crashes should be handled.
- bool ShouldHandleChromeCrashes();
- // Returns true if user crash directory may be used.
- bool IsUserSpecificDirectoryEnabled();
CountCrashFunction count_crash_function_;
IsFeedbackAllowedFunction is_feedback_allowed_function_;
@@ -166,13 +152,7 @@
std::string lsb_release_;
base::FilePath log_config_path_;
- scoped_refptr<dbus::Bus> bus_;
-
private:
- // D-Bus proxy for session manager interface.
- std::unique_ptr<org::chromium::SessionManagerInterfaceProxy>
- session_manager_proxy_;
-
DISALLOW_COPY_AND_ASSIGN(CrashCollector);
};
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index ce9af2b..13fb76a 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/crash_collector_test.h"
+#include "crash_collector_test.h"
#include <unistd.h>
#include <utility>
@@ -13,7 +13,7 @@
#include <chromeos/syslog_logging.h>
#include <gtest/gtest.h>
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
using base::FilePath;
using base::StringPrintf;
@@ -32,13 +32,6 @@
return false;
}
-bool GetActiveUserSessionsImpl(std::map<std::string, std::string> *sessions) {
- char kUser[] = "chicken@butt.com";
- char kHash[] = "hashcakes";
- sessions->insert(std::pair<std::string, std::string>(kUser, kHash));
- return true;
-}
-
} // namespace
class CrashCollectorTest : public ::testing::Test {
@@ -126,18 +119,13 @@
EXPECT_EQ(kRootUid, directory_owner);
EXPECT_EQ(kRootGid, directory_group);
- EXPECT_CALL(collector_, GetActiveUserSessions(testing::_))
- .WillOnce(Invoke(&GetActiveUserSessionsImpl));
-
- EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true);
-
path = collector_.GetCrashDirectoryInfo(kChronosUid,
kChronosUid,
kChronosGid,
&directory_mode,
&directory_owner,
&directory_group);
- EXPECT_EQ("/home/user/hashcakes/crash", path.value());
+ EXPECT_EQ("/var/spool/crash", path.value());
EXPECT_EQ(kExpectedUserMode, directory_mode);
EXPECT_EQ(kChronosUid, directory_owner);
EXPECT_EQ(kChronosGid, directory_group);
diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h
index 8339fa0..c875d44 100644
--- a/crash_reporter/crash_collector_test.h
+++ b/crash_reporter/crash_collector_test.h
@@ -5,7 +5,7 @@
#ifndef CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
#define CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
#include <map>
#include <string>
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
index 1528b3f..0e45992 100644
--- a/crash_reporter/crash_reporter.cc
+++ b/crash_reporter/crash_reporter.cc
@@ -16,12 +16,11 @@
#include <chromeos/syslog_logging.h>
#include <metrics/metrics_library.h>
-#include "crash-reporter/chrome_collector.h"
-#include "crash-reporter/kernel_collector.h"
-#include "crash-reporter/kernel_warning_collector.h"
-#include "crash-reporter/udev_collector.h"
-#include "crash-reporter/unclean_shutdown_collector.h"
-#include "crash-reporter/user_collector.h"
+#include "kernel_collector.h"
+#include "kernel_warning_collector.h"
+#include "udev_collector.h"
+#include "unclean_shutdown_collector.h"
+#include "user_collector.h"
static const char kCrashCounterHistogram[] = "Logging.CrashCounter";
static const char kUserCrashSignal[] =
@@ -97,12 +96,6 @@
LOG_IF(WARNING, status != 0) << "dbus-send running failed";
}
-static void CountChromeCrash() {
- // For now, consider chrome crashes the same as user crashes for reporting
- // purposes.
- CountUserCrash();
-}
-
static int Initialize(KernelCollector *kernel_collector,
UserCollector *user_collector,
@@ -164,25 +157,6 @@
return 0;
}
-static int HandleChromeCrash(ChromeCollector *chrome_collector,
- const std::string& chrome_dump_file,
- const std::string& pid,
- const std::string& uid,
- const std::string& exe) {
- CHECK(!chrome_dump_file.empty()) << "--chrome= must be set";
- CHECK(!pid.empty()) << "--pid= must be set";
- CHECK(!uid.empty()) << "--uid= must be set";
- CHECK(!exe.empty()) << "--exe= must be set";
-
- chromeos::LogToString(true);
- bool handled = chrome_collector->HandleCrash(FilePath(chrome_dump_file),
- pid, uid, exe);
- chromeos::LogToString(false);
- if (!handled)
- return 1;
- return 0;
-}
-
static int HandleUdevCrash(UdevCollector *udev_collector,
const std::string& udev_event) {
// Handle a crash indicated by a udev event.
@@ -258,7 +232,6 @@
DEFINE_bool(unclean_check, true, "Check for unclean shutdown");
DEFINE_string(udev, "", "Udev event description (type:device:subsystem)");
DEFINE_bool(kernel_warning, false, "Report collected kernel warning");
- DEFINE_string(chrome, "", "Chrome crash dump file");
DEFINE_string(pid, "", "PID of crashing process");
DEFINE_string(uid, "", "UID of crashing process");
DEFINE_string(exe, "", "Executable name of crashing process");
@@ -289,8 +262,6 @@
IsFeedbackAllowed);
UdevCollector udev_collector;
udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
- ChromeCollector chrome_collector;
- chrome_collector.Initialize(CountChromeCrash, IsFeedbackAllowed);
KernelWarningCollector kernel_warning_collector;
kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
@@ -322,13 +293,5 @@
return HandleKernelWarning(&kernel_warning_collector);
}
- if (!FLAGS_chrome.empty()) {
- return HandleChromeCrash(&chrome_collector,
- FLAGS_chrome,
- FLAGS_pid,
- FLAGS_uid,
- FLAGS_exe);
- }
-
return HandleUserCrash(&user_collector, FLAGS_user, FLAGS_crash_test);
}
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
index d86efbd..b3cbede 100644
--- a/crash_reporter/kernel_collector.cc
+++ b/crash_reporter/kernel_collector.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/kernel_collector.h"
+#include "kernel_collector.h"
#include <map>
#include <sys/stat.h>
diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h
index c8aedfe..5fc4ac2 100644
--- a/crash_reporter/kernel_collector.h
+++ b/crash_reporter/kernel_collector.h
@@ -13,7 +13,7 @@
#include <base/macros.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
// Kernel crash collector.
class KernelCollector : public CrashCollector {
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index 48d94ea..a534803 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/kernel_collector_test.h"
+#include "kernel_collector_test.h"
#include <unistd.h>
diff --git a/crash_reporter/kernel_collector_test.h b/crash_reporter/kernel_collector_test.h
index 75ac01e..d450134 100644
--- a/crash_reporter/kernel_collector_test.h
+++ b/crash_reporter/kernel_collector_test.h
@@ -5,7 +5,7 @@
#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
#define CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
-#include "crash-reporter/kernel_collector.h"
+#include "kernel_collector.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc
index 5dcd1f6..4cf7640 100644
--- a/crash_reporter/kernel_warning_collector.cc
+++ b/crash_reporter/kernel_warning_collector.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/kernel_warning_collector.h"
+#include "kernel_warning_collector.h"
#include <base/files/file_util.h>
#include <base/logging.h>
diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h
index f326b23..82c509c 100644
--- a/crash_reporter/kernel_warning_collector.h
+++ b/crash_reporter/kernel_warning_collector.h
@@ -10,7 +10,7 @@
#include <base/macros.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
// Kernel warning collector.
class KernelWarningCollector : public CrashCollector {
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
index 908bbc9..85f40db 100644
--- a/crash_reporter/udev_collector.cc
+++ b/crash_reporter/udev_collector.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/udev_collector.h"
+#include "udev_collector.h"
#include <map>
#include <utility>
diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h
index 1689dd3..d9b37eb 100644
--- a/crash_reporter/udev_collector.h
+++ b/crash_reporter/udev_collector.h
@@ -11,7 +11,7 @@
#include <base/macros.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
// Udev crash collector.
class UdevCollector : public CrashCollector {
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
index 08d9b2c..4897b91 100644
--- a/crash_reporter/udev_collector_test.cc
+++ b/crash_reporter/udev_collector_test.cc
@@ -10,7 +10,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "crash-reporter/udev_collector.h"
+#include "udev_collector.h"
using base::FilePath;
diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc
index e8273b5..a6da1bb 100644
--- a/crash_reporter/unclean_shutdown_collector.cc
+++ b/crash_reporter/unclean_shutdown_collector.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/unclean_shutdown_collector.h"
+#include "unclean_shutdown_collector.h"
#include <base/files/file_util.h>
#include <base/logging.h>
diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h
index d30a0b2..2ce0842 100644
--- a/crash_reporter/unclean_shutdown_collector.h
+++ b/crash_reporter/unclean_shutdown_collector.h
@@ -11,7 +11,7 @@
#include <base/macros.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
// Unclean shutdown collector.
class UncleanShutdownCollector : public CrashCollector {
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index f5e1b32..dc420fb 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/unclean_shutdown_collector.h"
+#include "unclean_shutdown_collector.h"
#include <unistd.h>
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index 302b130..069b581 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -2,25 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/user_collector.h"
+#include "user_collector.h"
-#include <bits/wordsize.h>
#include <elf.h>
#include <fcntl.h>
#include <grp.h> // For struct group.
#include <pcrecpp.h>
#include <pwd.h> // For struct passwd.
#include <stdint.h>
+#include <sys/cdefs.h> // For __WORDSIZE
#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
-#include <set>
#include <string>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
-#include <base/stl_util.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
@@ -495,101 +493,11 @@
kernel_supplied_name);
}
-// Returns true if the given executable name matches that of Chrome. This
-// includes checks for threads that Chrome has renamed.
-static bool IsChromeExecName(const std::string &exec) {
- static const char *kChromeNames[] = {
- "chrome",
- // These are additional thread names seen in http://crash/
- "MediaPipeline",
- // These come from the use of base::PlatformThread::SetName() directly
- "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain",
- "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain",
- "BrokerEvent", "CrVideoRenderer", "CrShutdownDetector",
- "UsbEventHandler", "CrNaClMain", "CrServiceMain",
- // These thread names come from the use of base::Thread
- "Gamepad polling thread", "Chrome_InProcGpuThread",
- "Chrome_DragDropThread", "Renderer::FILE", "VC manager",
- "VideoCaptureModuleImpl", "JavaBridge", "VideoCaptureManagerThread",
- "Geolocation", "Geolocation_wifi_provider",
- "Device orientation polling thread", "Chrome_InProcRendererThread",
- "NetworkChangeNotifier", "Watchdog", "inotify_reader",
- "cf_iexplore_background_thread", "BrowserWatchdog",
- "Chrome_HistoryThread", "Chrome_SyncThread", "Chrome_ShellDialogThread",
- "Printing_Worker", "Chrome_SafeBrowsingThread", "SimpleDBThread",
- "D-Bus thread", "AudioThread", "NullAudioThread", "V4L2Thread",
- "ChromotingClientDecodeThread", "Profiling_Flush",
- "worker_thread_ticker", "AudioMixerAlsa", "AudioMixerCras",
- "FakeAudioRecordingThread", "CaptureThread",
- "Chrome_WebSocketproxyThread", "ProcessWatcherThread",
- "Chrome_CameraThread", "import_thread", "NaCl_IOThread",
- "Chrome_CloudPrintJobPrintThread", "Chrome_CloudPrintProxyCoreThread",
- "DaemonControllerFileIO", "ChromotingMainThread",
- "ChromotingEncodeThread", "ChromotingDesktopThread",
- "ChromotingIOThread", "ChromotingFileIOThread",
- "Chrome_libJingle_WorkerThread", "Chrome_ChildIOThread",
- "GLHelperThread", "RemotingHostPlugin",
- // "PAC thread #%d", // not easy to check because of "%d"
- "Chrome_DBThread", "Chrome_WebKitThread", "Chrome_FileThread",
- "Chrome_FileUserBlockingThread", "Chrome_ProcessLauncherThread",
- "Chrome_CacheThread", "Chrome_IOThread", "Cache Thread", "File Thread",
- "ServiceProcess_IO", "ServiceProcess_File",
- "extension_crash_uploader", "gpu-process_crash_uploader",
- "plugin_crash_uploader", "renderer_crash_uploader",
- // These come from the use of webkit_glue::WebThreadImpl
- "Compositor", "Browser Compositor",
- // "WorkerPool/%d", // not easy to check because of "%d"
- // These come from the use of base::Watchdog
- "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog",
- // These come from the use of AudioDeviceThread::Start
- "AudioDevice", "AudioInputDevice", "AudioOutputDevice",
- // These come from the use of MessageLoopFactory::GetMessageLoop
- "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread",
- "AudioDecoderThread", "VideoDecoderThread",
- // These come from the use of MessageLoopFactory::GetMessageLoopProxy
- "CaptureVideoDecoderThread", "CaptureVideoDecoder",
- // These come from the use of base::SimpleThread
- "LocalInputMonitor/%d", // "%d" gets lopped off for kernel-supplied
- // These come from the use of base::DelegateSimpleThread
- "ipc_channel_nacl reader thread/%d", "plugin_audio_input_thread/%d",
- "plugin_audio_thread/%d",
- // These come from the use of base::SequencedWorkerPool
- "BrowserBlockingWorker%d/%d", // "%d" gets lopped off for kernel-supplied
- };
- static std::set<std::string> chrome_names;
-
- // Initialize a set of chrome names, for efficient lookup
- if (chrome_names.empty()) {
- for (size_t i = 0; i < arraysize(kChromeNames); i++) {
- std::string check_name(kChromeNames[i]);
- chrome_names.insert(check_name);
- // When checking a kernel-supplied name, it should be truncated to 15
- // chars. See PR_SET_NAME in
- // http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html,
- // although that page misleads by saying "16 bytes".
- chrome_names.insert("supplied_" + std::string(check_name, 0, 15));
- }
- }
-
- return ContainsKey(chrome_names, exec);
-}
-
bool UserCollector::ShouldDump(bool has_owner_consent,
bool is_developer,
- bool handle_chrome_crashes,
- const std::string &exec,
std::string *reason) {
reason->clear();
- // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
- // crashes towards user crashes, so user crashes really mean non-Chrome
- // user-space crashes.
- if (!handle_chrome_crashes && IsChromeExecName(exec)) {
- *reason = "ignoring call by kernel - chrome crash; "
- "waiting for chrome to call us directly";
- return false;
- }
-
// For developer builds, we always want to keep the crash reports unless
// we're testing the crash facilities themselves. This overrides
// feedback. Crash sending still obeys consent.
@@ -646,8 +554,6 @@
std::string reason;
bool dump = ShouldDump(is_feedback_allowed_function_(),
IsDeveloperImage(),
- ShouldHandleChromeCrashes(),
- exec,
&reason);
LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
index aac94cb..7b356ee 100644
--- a/crash_reporter/user_collector.h
+++ b/crash_reporter/user_collector.h
@@ -12,7 +12,7 @@
#include <base/macros.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "crash-reporter/crash_collector.h"
+#include "crash_collector.h"
class SystemLogging;
@@ -169,8 +169,6 @@
bool ShouldDump(bool has_owner_consent,
bool is_developer,
- bool handle_chrome_crashes,
- const std::string &exec,
std::string *reason);
bool generate_diagnostics_;
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 823d8b4..ee3ca12 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "crash-reporter/user_collector.h"
+#include "user_collector.h"
-#include <bits/wordsize.h>
#include <elf.h>
+#include <sys/cdefs.h> // For __WORDSIZE
#include <unistd.h>
#include <base/files/file_util.h>
@@ -164,81 +164,20 @@
TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
std::string reason;
- EXPECT_TRUE(collector_.ShouldDump(false, true, false,
- "chrome-wm", &reason));
+ EXPECT_TRUE(collector_.ShouldDump(false, true, &reason));
EXPECT_EQ("developer build - not testing - always dumping", reason);
// When running a crash test, behave as normal.
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "chrome-wm", &reason));
+ EXPECT_FALSE(collector_.ShouldDump(false, false, &reason));
EXPECT_EQ("ignoring - no consent", reason);
}
-TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) {
- std::string reason;
- // When running a crash test, behave as normal.
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "chrome", &reason));
- EXPECT_EQ(kChromeIgnoreMsg, reason);
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "supplied_Compositor", &reason));
- EXPECT_EQ(kChromeIgnoreMsg, reason);
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "supplied_PipelineThread", &reason));
- EXPECT_EQ(kChromeIgnoreMsg, reason);
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "Chrome_ChildIOThread", &reason));
- EXPECT_EQ(kChromeIgnoreMsg, reason);
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "supplied_Chrome_ChildIOT", &reason));
- EXPECT_EQ(kChromeIgnoreMsg, reason);
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "supplied_ChromotingClien", &reason));
- EXPECT_EQ(kChromeIgnoreMsg, reason);
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "supplied_LocalInputMonit", &reason));
- EXPECT_EQ(kChromeIgnoreMsg, reason);
-
- // When running a developer image, test that chrome crashes are handled
- // when the "handle_chrome_crashes" flag is set.
- EXPECT_TRUE(collector_.ShouldDump(false, true, true,
- "chrome", &reason));
- EXPECT_EQ("developer build - not testing - always dumping",
- reason);
- EXPECT_TRUE(collector_.ShouldDump(false, true, true,
- "supplied_Compositor", &reason));
- EXPECT_EQ("developer build - not testing - always dumping",
- reason);
- EXPECT_TRUE(collector_.ShouldDump(false, true, true,
- "supplied_PipelineThread", &reason));
- EXPECT_EQ("developer build - not testing - always dumping",
- reason);
- EXPECT_TRUE(collector_.ShouldDump(false, true, true,
- "Chrome_ChildIOThread", &reason));
- EXPECT_EQ("developer build - not testing - always dumping",
- reason);
- EXPECT_TRUE(collector_.ShouldDump(false, true, true,
- "supplied_Chrome_ChildIOT", &reason));
- EXPECT_EQ("developer build - not testing - always dumping",
- reason);
- EXPECT_TRUE(collector_.ShouldDump(false, true, true,
- "supplied_ChromotingClien", &reason));
- EXPECT_EQ("developer build - not testing - always dumping",
- reason);
- EXPECT_TRUE(collector_.ShouldDump(false, true, true,
- "supplied_LocalInputMonit", &reason));
- EXPECT_EQ("developer build - not testing - always dumping",
- reason);
-}
-
TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) {
std::string result;
- EXPECT_FALSE(collector_.ShouldDump(false, false, false,
- "chrome-wm", &result));
+ EXPECT_FALSE(collector_.ShouldDump(false, false, &result));
EXPECT_EQ("ignoring - no consent", result);
- EXPECT_TRUE(collector_.ShouldDump(true, false, false,
- "chrome-wm", &result));
+ EXPECT_TRUE(collector_.ShouldDump(true, false, &result));
EXPECT_EQ("handling", result);
}
diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l
index 691ef99..de746fe 100644
--- a/crash_reporter/warn_collector.l
+++ b/crash_reporter/warn_collector.l
@@ -11,6 +11,8 @@
* shipment to the crash server.
*/
+%option noyywrap
+
%{
#include <fcntl.h>
#include <inttypes.h>
@@ -122,6 +124,7 @@
hash_bitmap[word_index] |= 1 << bit_index;
}
+#pragma GCC diagnostic ignored "-Wwrite-strings"
int WarnStart(void) {
uint32_t hash;
char *spacep;
@@ -140,7 +143,7 @@
yyout = fopen(warn_dump_path, "w");
if (yyout == NULL)
Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno));
- spacep = index(yytext, ' ');
+ spacep = strchr(yytext, ' ');
if (spacep == NULL || spacep[1] == '\0')
spacep = "unknown-function";
fprintf(yyout, "%08x-%s\n", hash, spacep + 1);
@@ -318,5 +321,4 @@
*/
void UnusedFunctionWarningSuppressor(void) {
yyunput(0, 0);
- (void) input();
}
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index af86fe9..b5064b4 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -157,12 +157,6 @@
} else if (!strcmp(arg, "SIGFPE")) {
raise(SIGFPE);
return EXIT_SUCCESS;
- } else if (!strcmp(arg, "SIGPIPE")) {
- int pipe_fds[2];
- pipe(pipe_fds);
- close(pipe_fds[0]);
- write(pipe_fds[1], "oops", 4);
- return EXIT_SUCCESS;
} else if (!strcmp(arg, "SIGTRAP")) {
raise(SIGTRAP);
return EXIT_SUCCESS;
@@ -189,7 +183,6 @@
fprintf(stderr, " LOG_ALWAYS_FATAL call LOG_ALWAYS_FATAL\n");
fprintf(stderr, " LOG_ALWAYS_FATAL_IF call LOG_ALWAYS_FATAL\n");
fprintf(stderr, " SIGFPE cause a SIGFPE\n");
- fprintf(stderr, " SIGPIPE cause a SIGPIPE\n");
fprintf(stderr, " SIGSEGV cause a SIGSEGV at address 0x0 (synonym: crash)\n");
fprintf(stderr, " SIGSEGV-non-null cause a SIGSEGV at a non-zero address\n");
fprintf(stderr, " SIGSEGV-unmapped mmap/munmap a region of memory and then attempt to access it\n");
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 787b7aa..599995c 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -408,7 +408,6 @@
case SIGBUS:
case SIGFPE:
case SIGILL:
- case SIGPIPE:
case SIGSEGV:
#ifdef SIGSTKFLT
case SIGSTKFLT:
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/test/tombstone_test.cpp
index a771a39..d945d27 100644
--- a/debuggerd/test/tombstone_test.cpp
+++ b/debuggerd/test/tombstone_test.cpp
@@ -76,7 +76,7 @@
resetLogs();
elf_set_fake_build_id("");
siginfo_t si;
- si.si_signo = SIGPIPE;
+ si.si_signo = SIGABRT;
ptrace_set_fake_getsiginfo(si);
}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 114c7e4..e283923 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -81,7 +81,6 @@
case SIGBUS: return "SIGBUS";
case SIGFPE: return "SIGFPE";
case SIGILL: return "SIGILL";
- case SIGPIPE: return "SIGPIPE";
case SIGSEGV: return "SIGSEGV";
#if defined(SIGSTKFLT)
case SIGSTKFLT: return "SIGSTKFLT";
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index b964a36..5d7b151 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -189,25 +189,16 @@
return load_fd(fd, _sz);
}
-int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial)
-{
- if(!(vendor_id && (info->dev_vendor == vendor_id)) &&
- (info->dev_vendor != 0x18d1) && // Google
- (info->dev_vendor != 0x8087) && // Intel
- (info->dev_vendor != 0x0451) &&
- (info->dev_vendor != 0x0502) &&
- (info->dev_vendor != 0x0fce) && // Sony Ericsson
- (info->dev_vendor != 0x05c6) && // Qualcomm
- (info->dev_vendor != 0x22b8) && // Motorola
- (info->dev_vendor != 0x0955) && // Nvidia
- (info->dev_vendor != 0x413c) && // DELL
- (info->dev_vendor != 0x2314) && // INQ Mobile
- (info->dev_vendor != 0x0b05) && // Asus
- (info->dev_vendor != 0x0bb4)) // HTC
- return -1;
- if(info->ifc_class != 0xff) return -1;
- if(info->ifc_subclass != 0x42) return -1;
- if(info->ifc_protocol != 0x03) return -1;
+int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
+ // Require a matching vendor id if the user specified one with -i.
+ if (vendor_id != 0 && info->dev_vendor != vendor_id) {
+ return -1;
+ }
+
+ if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
+ return -1;
+ }
+
// require matching serial number or device path if requested
// at the command line with the -s option.
if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index a959566..abd6eb9 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -75,7 +75,6 @@
HRESULT result;
SInt32 score;
UInt8 interfaceNumEndpoints;
- UInt8 endpoint;
UInt8 configuration;
// Placing the constant KIOUSBFindInterfaceDontCare into the following
@@ -182,7 +181,7 @@
// Iterate over the endpoints for this interface and see if there
// are any that do bulk in/out.
- for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+ for (UInt8 endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
UInt8 transferType;
UInt16 maxPacketSize;
UInt8 interval;
@@ -210,7 +209,7 @@
handle->zero_mask = maxPacketSize - 1;
}
} else {
- ERR("could not get pipe properties\n");
+ ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
}
if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index f467f81..459daec 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -524,6 +524,14 @@
continue;
}
+ /* Skip mounting the root partition, as it will already have been mounted */
+ if (!strcmp(fstab->recs[i].mount_point, "/")) {
+ if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
+ fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
+ }
+ continue;
+ }
+
/* Translate LABEL= file system labels into block devices */
if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
!strcmp(fstab->recs[i].fs_type, "ext3") ||
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
index 07d1351..2d3c743 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -23,7 +23,7 @@
#include <string.h>
#include <stdbool.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
#include <winsock2.h>
typedef int socklen_t;
#else
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index e4ed179..6d9b3bc 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -67,7 +67,8 @@
#define ATRACE_TAG_RS (1<<15)
#define ATRACE_TAG_BIONIC (1<<16)
#define ATRACE_TAG_POWER (1<<17)
-#define ATRACE_TAG_LAST ATRACE_TAG_POWER
+#define ATRACE_TAG_PACKAGE_MANAGER (1<<18)
+#define ATRACE_TAG_LAST ATRACE_TAG_PACKAGE_MANAGER
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1LL<<63)
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 0c071ca..7047e0f 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -26,7 +26,7 @@
#include <sys/types.h>
#include <stdint.h>
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
#include <linux/capability.h>
#else
#include "android_filesystem_capability.h"
@@ -99,6 +99,10 @@
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */
+/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
+#define AID_OEM_RESERVED_2_START 5000
+#define AID_OEM_RESERVED_2_END 5999
+
#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
#define AID_MISC 9998 /* access to misc storage */
#define AID_NOBODY 9999
diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h
index aad1e82..4c2dd49 100644
--- a/include/utils/AndroidThreads.h
+++ b/include/utils/AndroidThreads.h
@@ -73,7 +73,7 @@
// ------------------------------------------------------------------
// Extra functions working with raw pids.
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
// Change the priority AND scheduling group of a particular thread. The priority
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
// if the priority set failed, else another value if just the group set failed;
diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h
index baa3a83..44ea13d 100644
--- a/include/utils/ByteOrder.h
+++ b/include/utils/ByteOrder.h
@@ -21,7 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
#include <winsock2.h>
#else
#include <netinet/in.h>
diff --git a/include/utils/Compat.h b/include/utils/Compat.h
index 7d96310..c5f9d6a 100644
--- a/include/utils/Compat.h
+++ b/include/utils/Compat.h
@@ -75,4 +75,10 @@
_rc; })
#endif
+#if defined(_WIN32)
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
+
#endif /* __LIB_UTILS_COMPAT_H */
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
index 46173db..9402614 100644
--- a/include/utils/Errors.h
+++ b/include/utils/Errors.h
@@ -58,7 +58,6 @@
ALREADY_EXISTS = -EEXIST,
DEAD_OBJECT = -EPIPE,
FAILED_TRANSACTION = (UNKNOWN_ERROR + 2),
- JPARKS_BROKE_IT = -EPIPE,
#if !defined(HAVE_MS_C_RUNTIME)
BAD_INDEX = -EOVERFLOW,
NOT_ENOUGH_DATA = -ENODATA,
diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h
index f70fc92..afd7bfd 100644
--- a/include/utils/FileMap.h
+++ b/include/utils/FileMap.h
@@ -26,7 +26,7 @@
#if defined(__MINGW32__)
// Ensure that we always pull in winsock2.h before windows.h
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
#include <winsock2.h>
#endif
#include <windows.h>
diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h
index 757519b..f027c79 100644
--- a/include/utils/Mutex.h
+++ b/include/utils/Mutex.h
@@ -59,7 +59,7 @@
// lock if possible; returns 0 on success, error otherwise
status_t tryLock();
-#if HAVE_ANDROID_OS
+#if defined(__ANDROID__)
// lock the mutex, but don't wait longer than timeoutMilliseconds.
// Returns 0 on success, TIMED_OUT for failure due to timeout expiration.
//
@@ -128,7 +128,7 @@
inline status_t Mutex::tryLock() {
return -pthread_mutex_trylock(&mMutex);
}
-#if HAVE_ANDROID_OS
+#if defined(__ANDROID__)
inline status_t Mutex::timedLock(nsecs_t timeoutNs) {
const struct timespec ts = {
/* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),
diff --git a/include/utils/Thread.h b/include/utils/Thread.h
index 28839fd..1532b7e 100644
--- a/include/utils/Thread.h
+++ b/include/utils/Thread.h
@@ -70,7 +70,7 @@
// Indicates whether this thread is running or not.
bool isRunning() const;
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
// Return the thread's kernel ID, same as the thread itself calling gettid(),
// or -1 if the thread is not running.
pid_t getTid() const;
@@ -101,7 +101,7 @@
volatile bool mExitPending;
volatile bool mRunning;
sp<Thread> mHoldSelf;
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
// legacy for debugging, not used by getTid() as it is set by the child thread
// and so is not initialized until the child reaches that point
pid_t mTid;
diff --git a/include/utils/Trace.h b/include/utils/Trace.h
index 6ee343d..6ba68f6 100644
--- a/include/utils/Trace.h
+++ b/include/utils/Trace.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_TRACE_H
#define ANDROID_TRACE_H
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
#include <fcntl.h>
#include <stdint.h>
@@ -59,11 +59,11 @@
}; // namespace android
-#else // HAVE_ANDROID_OS
+#else // !__ANDROID__
#define ATRACE_NAME(...)
#define ATRACE_CALL()
-#endif // HAVE_ANDROID_OS
+#endif // __ANDROID__
#endif // ANDROID_TRACE_H
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 5ef2ab0..3591a6b 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <string.h>
+#include <sys/cdefs.h>
#include <sys/types.h>
#include <utils/Compat.h>
diff --git a/init/Android.mk b/init/Android.mk
index 45b002d..6737be4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -20,12 +20,35 @@
# --
+# If building on Linux, then build unit test for the host.
+ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
+ parser/tokenizer.cpp \
+
+LOCAL_MODULE := libinit_parser
+LOCAL_CLANG := true
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := init_parser_tests
+LOCAL_SRC_FILES := \
+ parser/tokenizer_test.cpp \
+
+LOCAL_STATIC_LIBRARIES := libinit_parser
+LOCAL_CLANG := true
+include $(BUILD_HOST_NATIVE_TEST)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES:= \
+ action.cpp \
init_parser.cpp \
log.cpp \
parser.cpp \
+ service.cpp \
util.cpp \
LOCAL_STATIC_LIBRARIES := libbase
diff --git a/init/action.cpp b/init/action.cpp
new file mode 100644
index 0000000..2eb809e
--- /dev/null
+++ b/init/action.cpp
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2015 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 "action.h"
+
+#include <errno.h>
+
+#include <base/strings.h>
+#include <base/stringprintf.h>
+
+#include "error.h"
+#include "init_parser.h"
+#include "log.h"
+#include "property_service.h"
+#include "util.h"
+
+class Action::Command
+{
+public:
+ Command(int (*f)(const std::vector<std::string>& args),
+ const std::vector<std::string>& args,
+ const std::string& filename,
+ int line);
+
+ int InvokeFunc() const;
+ std::string BuildCommandString() const;
+ std::string BuildSourceString() const;
+
+private:
+ int (*func_)(const std::vector<std::string>& args);
+ const std::vector<std::string> args_;
+ const std::string filename_;
+ int line_;
+};
+
+Action::Command::Command(int (*f)(const std::vector<std::string>& args),
+ const std::vector<std::string>& args,
+ const std::string& filename,
+ int line) :
+ func_(f), args_(args), filename_(filename), line_(line)
+{
+}
+
+int Action::Command::InvokeFunc() const
+{
+ std::vector<std::string> expanded_args;
+ expanded_args.resize(args_.size());
+ expanded_args[0] = args_[0];
+ for (std::size_t i = 1; i < args_.size(); ++i) {
+ if (expand_props(args_[i], &expanded_args[i]) == -1) {
+ ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
+ return -EINVAL;
+ }
+ }
+
+ return func_(expanded_args);
+}
+
+std::string Action::Command::BuildCommandString() const
+{
+ return android::base::Join(args_, ' ');
+}
+
+std::string Action::Command::BuildSourceString() const
+{
+ if (!filename_.empty()) {
+ return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
+ } else {
+ return std::string();
+ }
+}
+
+Action::Action()
+{
+}
+
+void Action::AddCommand(int (*f)(const std::vector<std::string>& args),
+ const std::vector<std::string>& args,
+ const std::string& filename, int line)
+{
+ Action::Command* cmd = new Action::Command(f, args, filename, line);
+ commands_.push_back(cmd);
+}
+
+std::size_t Action::NumCommands() const
+{
+ return commands_.size();
+}
+
+void Action::ExecuteOneCommand(std::size_t command) const
+{
+ ExecuteCommand(*commands_[command]);
+}
+
+void Action::ExecuteAllCommands() const
+{
+ for (const auto& c : commands_) {
+ ExecuteCommand(*c);
+ }
+}
+
+void Action::ExecuteCommand(const Command& command) const
+{
+ Timer t;
+ int result = command.InvokeFunc();
+
+ if (klog_get_level() >= KLOG_INFO_LEVEL) {
+ std::string trigger_name = BuildTriggersString();
+ std::string cmd_str = command.BuildCommandString();
+ std::string source = command.BuildSourceString();
+
+ INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
+ cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
+ result, t.duration());
+ }
+}
+
+bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
+{
+ const static std::string prop_str("property:");
+ std::string prop_name(trigger.substr(prop_str.length()));
+ size_t equal_pos = prop_name.find('=');
+ if (equal_pos == std::string::npos) {
+ *err = "property trigger found without matching '='";
+ return false;
+ }
+
+ std::string prop_value(prop_name.substr(equal_pos + 1));
+ prop_name.erase(equal_pos);
+
+ auto res = property_triggers_.emplace(prop_name, prop_value);
+ if (res.second == false) {
+ *err = "multiple property triggers found for same property";
+ return false;
+ }
+ return true;
+}
+
+bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
+{
+ const static std::string prop_str("property:");
+ for (std::size_t i = 0; i < args.size(); ++i) {
+ if (i % 2) {
+ if (args[i].compare("&&")) {
+ *err = "&& is the only symbol allowed to concatenate actions";
+ return false;
+ } else {
+ continue;
+ }
+ }
+
+ if (!args[i].compare(0, prop_str.length(), prop_str)) {
+ if (!ParsePropertyTrigger(args[i], err)) {
+ return false;
+ }
+ } else {
+ if (!event_trigger_.empty()) {
+ *err = "multiple event triggers are not allowed";
+ return false;
+ }
+
+ event_trigger_ = args[i];
+ }
+ }
+
+ return true;
+}
+
+bool Action::InitSingleTrigger(const std::string& trigger)
+{
+ std::vector<std::string> name_vector{trigger};
+ std::string err;
+ return InitTriggers(name_vector, &err);
+}
+
+bool Action::CheckPropertyTriggers(const std::string& name,
+ const std::string& value) const
+{
+ bool found = !name.compare("");
+ if (property_triggers_.empty()) {
+ return true;
+ }
+
+ for (const auto& t : property_triggers_) {
+ if (!t.first.compare(name)) {
+ if (t.second.compare("*") &&
+ t.second.compare(value)) {
+ return false;
+ } else {
+ found = true;
+ }
+ } else {
+ std::string prop_val = property_get(t.first.c_str());
+ if (prop_val.empty() ||
+ (t.second.compare("*") &&
+ t.second.compare(prop_val))) {
+ return false;
+ }
+ }
+ }
+ return found;
+}
+
+bool Action::CheckEventTrigger(const std::string& trigger) const
+{
+ return !event_trigger_.empty() &&
+ !trigger.compare(event_trigger_) &&
+ CheckPropertyTriggers();
+}
+
+bool Action::CheckPropertyTrigger(const std::string& name,
+ const std::string& value) const
+{
+ return event_trigger_.empty() && CheckPropertyTriggers(name, value);
+}
+
+bool Action::TriggersEqual(const class Action& other) const
+{
+ return property_triggers_.size() == other.property_triggers_.size() &&
+ std::equal(property_triggers_.begin(), property_triggers_.end(),
+ other.property_triggers_.begin()) &&
+ !event_trigger_.compare(other.event_trigger_);
+}
+
+std::string Action::BuildTriggersString() const
+{
+ std::string result;
+
+ for (const auto& t : property_triggers_) {
+ result += t.first;
+ result += '=';
+ result += t.second;
+ result += ' ';
+ }
+ if (!event_trigger_.empty()) {
+ result += event_trigger_;
+ result += ' ';
+ }
+ result.pop_back();
+ return result;
+}
+
+void Action::DumpState() const
+{
+ INFO("on ");
+ std::string trigger_name = BuildTriggersString();
+ INFO("%s", trigger_name.c_str());
+ INFO("\n");
+
+ for (const auto& c : commands_) {
+ std::string cmd_str = c->BuildCommandString();
+ INFO(" %s", cmd_str.c_str());
+ }
+ INFO("\n");
+}
+
+ActionManager::ActionManager() : cur_command_(0)
+{
+}
+
+ActionManager& ActionManager::GetInstance() {
+ static ActionManager instance;
+ return instance;
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger)
+{
+ for (const auto& a : action_list_) {
+ if (a->CheckEventTrigger(trigger)) {
+ action_queue_.push(a);
+ }
+ }
+}
+
+void ActionManager::QueuePropertyTrigger(const std::string& name,
+ const std::string& value)
+{
+ for (const auto& a : action_list_) {
+ if (a->CheckPropertyTrigger(name, value)) {
+ action_queue_.push(a);
+ }
+ }
+}
+
+void ActionManager::QueueAllPropertyTriggers()
+{
+ QueuePropertyTrigger("", "");
+}
+
+void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
+ const std::string& name)
+{
+ Action* act = new Action();
+ std::vector<std::string> name_vector{name};
+
+ if (!act->InitSingleTrigger(name)) {
+ return;
+ }
+
+ act->AddCommand(func, name_vector);
+
+ action_queue_.push(act);
+}
+
+void ActionManager::ExecuteOneCommand() {
+ if (action_queue_.empty()) {
+ return;
+ }
+
+ Action* action = action_queue_.front();
+ if (!action->NumCommands()) {
+ action_queue_.pop();
+ return;
+ }
+
+ if (cur_command_ == 0) {
+ std::string trigger_name = action->BuildTriggersString();
+ INFO("processing action %p (%s)\n", action, trigger_name.c_str());
+ }
+
+ action->ExecuteOneCommand(cur_command_++);
+ if (cur_command_ == action->NumCommands()) {
+ cur_command_ = 0;
+ action_queue_.pop();
+ }
+}
+
+bool ActionManager::HasMoreCommands() const
+{
+ return !action_queue_.empty();
+}
+
+Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
+ std::string* err)
+{
+ if (triggers.size() < 1) {
+ *err = "actions must have a trigger\n";
+ return nullptr;
+ }
+
+ Action* act = new Action();
+ if (!act->InitTriggers(triggers, err)) {
+ return nullptr;
+ }
+
+ auto old_act_it =
+ std::find_if(action_list_.begin(), action_list_.end(),
+ [&act] (Action* a) { return act->TriggersEqual(*a); });
+
+ if (old_act_it != action_list_.end()) {
+ delete act;
+ return *old_act_it;
+ }
+
+ action_list_.push_back(act);
+ return act;
+}
+
+void ActionManager::DumpState() const
+{
+ for (const auto& a : action_list_) {
+ a->DumpState();
+ }
+ INFO("\n");
+}
diff --git a/init/action.h b/init/action.h
new file mode 100644
index 0000000..ae28fe1
--- /dev/null
+++ b/init/action.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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 _INIT_ACTION_H
+#define _INIT_ACTION_H
+
+#include <map>
+#include <queue>
+#include <string>
+#include <vector>
+
+class Action {
+public:
+ Action();
+
+ void AddCommand(int (*f)(const std::vector<std::string>& args),
+ const std::vector<std::string>& args,
+ const std::string& filename = "", int line = 0);
+ bool InitTriggers(const std::vector<std::string>& args, std::string* err);
+ bool InitSingleTrigger(const std::string& trigger);
+ std::size_t NumCommands() const;
+ void ExecuteOneCommand(std::size_t command) const;
+ void ExecuteAllCommands() const;
+ bool CheckEventTrigger(const std::string& trigger) const;
+ bool CheckPropertyTrigger(const std::string& name,
+ const std::string& value) const;
+ bool TriggersEqual(const class Action& other) const;
+ std::string BuildTriggersString() const;
+ void DumpState() const;
+
+private:
+ class Command;
+
+ void ExecuteCommand(const Command& command) const;
+ bool CheckPropertyTriggers(const std::string& name = "",
+ const std::string& value = "") const;
+ bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+
+ std::map<std::string, std::string> property_triggers_;
+ std::string event_trigger_;
+ std::vector<Command*> commands_;
+};
+
+class ActionManager {
+public:
+ static ActionManager& GetInstance();
+ void QueueEventTrigger(const std::string& trigger);
+ void QueuePropertyTrigger(const std::string& name, const std::string& value);
+ void QueueAllPropertyTriggers();
+ void QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
+ const std::string& name);
+ void ExecuteOneCommand();
+ bool HasMoreCommands() const;
+ Action* AddNewAction(const std::vector<std::string>& triggers,
+ std::string* err);
+ void DumpState() const;
+
+private:
+ ActionManager();
+
+ ActionManager(ActionManager const&) = delete;
+ void operator=(ActionManager const&) = delete;
+
+ std::vector<Action*> action_list_;
+ std::queue<Action*> action_queue_;
+ std::size_t cur_command_;
+};
+
+#endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index e5b153a..efaee1c 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -203,7 +203,7 @@
return count;
}
-int do_bootchart_init(int nargs, char** args) {
+int do_bootchart_init(const std::vector<std::string>& args) {
g_remaining_samples = bootchart_init();
if (g_remaining_samples < 0) {
ERROR("Bootcharting init failure: %s\n", strerror(errno));
diff --git a/init/builtins.cpp b/init/builtins.cpp
index d05f046..470437c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -43,24 +43,24 @@
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
-#include "init.h"
-#include "keywords.h"
-#include "property_service.h"
+#include "action.h"
#include "devices.h"
+#include "init.h"
#include "init_parser.h"
-#include "util.h"
+#include "keywords.h"
#include "log.h"
+#include "property_service.h"
+#include "service.h"
+#include "util.h"
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
#define UNMOUNT_CHECK_MS 5000
#define UNMOUNT_CHECK_TIMES 10
-int add_environment(const char *name, const char *value);
-
// System call provided by bionic but not in any header file.
extern "C" int init_module(void *, unsigned long, const char *);
-static int insmod(const char *filename, char *options)
+static int insmod(const char *filename, const char *options)
{
std::string module;
if (!read_file(filename, &module)) {
@@ -99,15 +99,6 @@
return ret;
}
-static void service_start_if_not_disabled(struct service *svc)
-{
- if (!(svc->flags & SVC_DISABLED)) {
- service_start(svc, NULL);
- } else {
- svc->flags |= SVC_DISABLED_START;
- }
-}
-
static void unmount_and_fsck(const struct mntent *entry)
{
if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
@@ -130,7 +121,7 @@
* automatically restart after kill(), but that is not really a problem
* because |entry->mnt_dir| is no longer visible to such new processes.
*/
- service_for_each(service_stop);
+ ServiceManager::GetInstance().ForEachService([] (Service* s) { s->Stop(); });
TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
int count = 0;
@@ -169,145 +160,129 @@
}
}
-int do_class_start(int nargs, char **args)
+int do_class_start(const std::vector<std::string>& args)
{
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
*/
- service_for_each_class(args[1], service_start_if_not_disabled);
+ ServiceManager::GetInstance().
+ ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
return 0;
}
-int do_class_stop(int nargs, char **args)
+int do_class_stop(const std::vector<std::string>& args)
{
- service_for_each_class(args[1], service_stop);
+ ServiceManager::GetInstance().
+ ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
return 0;
}
-int do_class_reset(int nargs, char **args)
+int do_class_reset(const std::vector<std::string>& args)
{
- service_for_each_class(args[1], service_reset);
+ ServiceManager::GetInstance().
+ ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
return 0;
}
-int do_domainname(int nargs, char **args)
+int do_domainname(const std::vector<std::string>& args)
{
- return write_file("/proc/sys/kernel/domainname", args[1]);
+ return write_file("/proc/sys/kernel/domainname", args[1].c_str());
}
-int do_enable(int nargs, char **args)
+int do_enable(const std::vector<std::string>& args)
{
- struct service *svc;
- svc = service_find_by_name(args[1]);
- if (svc) {
- svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED);
- if (svc->flags & SVC_DISABLED_START) {
- service_start(svc, NULL);
- }
- } else {
+ Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+ if (!svc) {
return -1;
}
- return 0;
+ return svc->Enable();
}
-int do_exec(int nargs, char** args) {
- service* svc = make_exec_oneshot_service(nargs, args);
- if (svc == NULL) {
+int do_exec(const std::vector<std::string>& args) {
+ Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
+ if (!svc) {
return -1;
}
- service_start(svc, NULL);
+ if (!svc->Start()) {
+ return -1;
+ }
+ waiting_for_exec = true;
return 0;
}
-int do_export(int nargs, char **args)
+int do_export(const std::vector<std::string>& args)
{
- return add_environment(args[1], args[2]);
+ return add_environment(args[1].c_str(), args[2].c_str());
}
-int do_hostname(int nargs, char **args)
+int do_hostname(const std::vector<std::string>& args)
{
- return write_file("/proc/sys/kernel/hostname", args[1]);
+ return write_file("/proc/sys/kernel/hostname", args[1].c_str());
}
-int do_ifup(int nargs, char **args)
+int do_ifup(const std::vector<std::string>& args)
{
- return __ifupdown(args[1], 1);
+ return __ifupdown(args[1].c_str(), 1);
}
-
-static int do_insmod_inner(int nargs, char **args, int opt_len)
+int do_insmod(const std::vector<std::string>& args)
{
- char options[opt_len + 1];
- int i;
+ std::string options;
- options[0] = '\0';
- if (nargs > 2) {
- strcpy(options, args[2]);
- for (i = 3; i < nargs; ++i) {
- strcat(options, " ");
- strcat(options, args[i]);
+ if (args.size() > 2) {
+ options += args[2];
+ for (std::size_t i = 3; i < args.size(); ++i) {
+ options += ' ';
+ options += args[i];
}
}
- return insmod(args[1], options);
+ return insmod(args[1].c_str(), options.c_str());
}
-int do_insmod(int nargs, char **args)
-{
- int i;
- int size = 0;
-
- if (nargs > 2) {
- for (i = 2; i < nargs; ++i)
- size += strlen(args[i]) + 1;
- }
-
- return do_insmod_inner(nargs, args, size);
-}
-
-int do_mkdir(int nargs, char **args)
+int do_mkdir(const std::vector<std::string>& args)
{
mode_t mode = 0755;
int ret;
/* mkdir <path> [mode] [owner] [group] */
- if (nargs >= 3) {
- mode = strtoul(args[2], 0, 8);
+ if (args.size() >= 3) {
+ mode = std::stoul(args[2], 0, 8);
}
- ret = make_dir(args[1], mode);
+ ret = make_dir(args[1].c_str(), mode);
/* chmod in case the directory already exists */
if (ret == -1 && errno == EEXIST) {
- ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+ ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
}
if (ret == -1) {
return -errno;
}
- if (nargs >= 4) {
- uid_t uid = decode_uid(args[3]);
+ if (args.size() >= 4) {
+ uid_t uid = decode_uid(args[3].c_str());
gid_t gid = -1;
- if (nargs == 5) {
- gid = decode_uid(args[4]);
+ if (args.size() == 5) {
+ gid = decode_uid(args[4].c_str());
}
- if (lchown(args[1], uid, gid) == -1) {
+ if (lchown(args[1].c_str(), uid, gid) == -1) {
return -errno;
}
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
if (mode & (S_ISUID | S_ISGID)) {
- ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+ ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
return -errno;
}
}
}
- return e4crypt_set_directory_policy(args[1]);
+ return e4crypt_set_directory_policy(args[1].c_str());
}
static struct {
@@ -335,35 +310,36 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-int do_mount(int nargs, char **args)
+int do_mount(const std::vector<std::string>& args)
{
char tmp[64];
- char *source, *target, *system;
- char *options = NULL;
+ const char *source, *target, *system;
+ const char *options = NULL;
unsigned flags = 0;
+ std::size_t na = 0;
int n, i;
int wait = 0;
- for (n = 4; n < nargs; n++) {
+ for (na = 4; na < args.size(); na++) {
for (i = 0; mount_flags[i].name; i++) {
- if (!strcmp(args[n], mount_flags[i].name)) {
+ if (!args[na].compare(mount_flags[i].name)) {
flags |= mount_flags[i].flag;
break;
}
}
if (!mount_flags[i].name) {
- if (!strcmp(args[n], "wait"))
+ if (!args[na].compare("wait"))
wait = 1;
/* if our last argument isn't a flag, wolf it up as an option string */
- else if (n + 1 == nargs)
- options = args[n];
+ else if (na + 1 == args.size())
+ options = args[na].c_str();
}
}
- system = args[1];
- source = args[2];
- target = args[3];
+ system = args[1].c_str();
+ source = args[2].c_str();
+ target = args[3].c_str();
if (!strncmp(source, "mtd@", 4)) {
n = mtd_name_to_number(source + 4);
@@ -455,7 +431,7 @@
* This function might request a reboot, in which case it will
* not return.
*/
-int do_mount_all(int nargs, char **args)
+int do_mount_all(const std::vector<std::string>& args)
{
pid_t pid;
int ret = -1;
@@ -463,10 +439,10 @@
int status;
struct fstab *fstab;
- if (nargs != 2) {
+ if (args.size() != 2) {
return -1;
}
- const char* fstabfile = args[1];
+ const char* fstabfile = args[1].c_str();
/*
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
* do the call in the child to provide protection to the main init
@@ -513,7 +489,7 @@
/* If fs_mgr determined this is an unencrypted device, then trigger
* that action.
*/
- action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
} else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
@@ -528,7 +504,7 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
- action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
} else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -1;
@@ -544,78 +520,81 @@
return ret;
}
-int do_swapon_all(int nargs, char **args)
+int do_swapon_all(const std::vector<std::string>& args)
{
struct fstab *fstab;
int ret;
- fstab = fs_mgr_read_fstab(args[1]);
+ fstab = fs_mgr_read_fstab(args[1].c_str());
ret = fs_mgr_swapon_all(fstab);
fs_mgr_free_fstab(fstab);
return ret;
}
-int do_setprop(int nargs, char **args)
+int do_setprop(const std::vector<std::string>& args)
{
- const char *name = args[1];
- const char *value = args[2];
+ const char* name = args[1].c_str();
+ const char* value = args[2].c_str();
property_set(name, value);
return 0;
}
-int do_setrlimit(int nargs, char **args)
+int do_setrlimit(const std::vector<std::string>& args)
{
struct rlimit limit;
int resource;
- resource = atoi(args[1]);
- limit.rlim_cur = atoi(args[2]);
- limit.rlim_max = atoi(args[3]);
+ resource = std::stoi(args[1]);
+ limit.rlim_cur = std::stoi(args[2]);
+ limit.rlim_max = std::stoi(args[3]);
return setrlimit(resource, &limit);
}
-int do_start(int nargs, char **args)
+int do_start(const std::vector<std::string>& args)
{
- struct service *svc;
- svc = service_find_by_name(args[1]);
- if (svc) {
- service_start(svc, NULL);
+ Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+ if (!svc) {
+ ERROR("do_start: Service %s not found\n", args[1].c_str());
+ return -1;
}
+ if (!svc->Start())
+ return -1;
return 0;
}
-int do_stop(int nargs, char **args)
+int do_stop(const std::vector<std::string>& args)
{
- struct service *svc;
- svc = service_find_by_name(args[1]);
- if (svc) {
- service_stop(svc);
+ Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+ if (!svc) {
+ ERROR("do_stop: Service %s not found\n", args[1].c_str());
+ return -1;
}
+ svc->Stop();
return 0;
}
-int do_restart(int nargs, char **args)
+int do_restart(const std::vector<std::string>& args)
{
- struct service *svc;
- svc = service_find_by_name(args[1]);
- if (svc) {
- service_restart(svc);
+ Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+ if (!svc) {
+ ERROR("do_restart: Service %s not found\n", args[1].c_str());
+ return -1;
}
+ svc->Restart();
return 0;
}
-int do_powerctl(int nargs, char **args)
+int do_powerctl(const std::vector<std::string>& args)
{
- const char* command = args[1];
+ const char* command = args[1].c_str();
int len = 0;
- int cmd = 0;
- const char *reboot_target;
+ unsigned int cmd = 0;
+ const char *reboot_target = "";
void (*callback_on_ro_remount)(const struct mntent*) = NULL;
if (strncmp(command, "shutdown", 8) == 0) {
cmd = ANDROID_RB_POWEROFF;
len = 8;
- callback_on_ro_remount = unmount_and_fsck;
} else if (strncmp(command, "reboot", 6) == 0) {
cmd = ANDROID_RB_RESTART2;
len = 6;
@@ -625,10 +604,15 @@
}
if (command[len] == ',') {
- reboot_target = &command[len + 1];
- } else if (command[len] == '\0') {
- reboot_target = "";
- } else {
+ if (cmd == ANDROID_RB_POWEROFF &&
+ !strcmp(&command[len + 1], "userrequested")) {
+ // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+ // Run fsck once the file system is remounted in read-only mode.
+ callback_on_ro_remount = unmount_and_fsck;
+ } else if (cmd == ANDROID_RB_RESTART2) {
+ reboot_target = &command[len + 1];
+ }
+ } else if (command[len] != '\0') {
ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
return -EINVAL;
}
@@ -637,46 +621,46 @@
callback_on_ro_remount);
}
-int do_trigger(int nargs, char **args)
+int do_trigger(const std::vector<std::string>& args)
{
- action_for_each_trigger(args[1], action_add_queue_tail);
+ ActionManager::GetInstance().QueueEventTrigger(args[1]);
return 0;
}
-int do_symlink(int nargs, char **args)
+int do_symlink(const std::vector<std::string>& args)
{
- return symlink(args[1], args[2]);
+ return symlink(args[1].c_str(), args[2].c_str());
}
-int do_rm(int nargs, char **args)
+int do_rm(const std::vector<std::string>& args)
{
- return unlink(args[1]);
+ return unlink(args[1].c_str());
}
-int do_rmdir(int nargs, char **args)
+int do_rmdir(const std::vector<std::string>& args)
{
- return rmdir(args[1]);
+ return rmdir(args[1].c_str());
}
-int do_sysclktz(int nargs, char **args)
+int do_sysclktz(const std::vector<std::string>& args)
{
struct timezone tz;
- if (nargs != 2)
+ if (args.size() != 2)
return -1;
memset(&tz, 0, sizeof(tz));
- tz.tz_minuteswest = atoi(args[1]);
+ tz.tz_minuteswest = std::stoi(args[1]);
if (settimeofday(NULL, &tz))
return -1;
return 0;
}
-int do_verity_load_state(int nargs, char **args) {
+int do_verity_load_state(const std::vector<std::string>& args) {
int mode = -1;
int rc = fs_mgr_load_verity_state(&mode);
if (rc == 0 && mode == VERITY_MODE_LOGGING) {
- action_for_each_trigger("verity-logging", action_add_queue_tail);
+ ActionManager::GetInstance().QueueEventTrigger("verity-logging");
}
return rc;
}
@@ -686,18 +670,18 @@
android::base::StringPrintf("%d", mode).c_str());
}
-int do_verity_update_state(int nargs, char** args) {
+int do_verity_update_state(const std::vector<std::string>& args) {
return fs_mgr_update_verity_state(verity_update_property);
}
-int do_write(int nargs, char **args)
+int do_write(const std::vector<std::string>& args)
{
- const char *path = args[1];
- const char *value = args[2];
+ const char* path = args[1].c_str();
+ const char* value = args[2].c_str();
return write_file(path, value);
}
-int do_copy(int nargs, char **args)
+int do_copy(const std::vector<std::string>& args)
{
char *buffer = NULL;
int rc = 0;
@@ -706,16 +690,16 @@
int brtw, brtr;
char *p;
- if (nargs != 3)
+ if (args.size() != 3)
return -1;
- if (stat(args[1], &info) < 0)
+ if (stat(args[1].c_str(), &info) < 0)
return -1;
- if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0)
+ if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
goto out_err;
- if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
+ if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
goto out_err;
if (!(buffer = (char*) malloc(info.st_size)))
@@ -759,13 +743,14 @@
return rc;
}
-int do_chown(int nargs, char **args) {
+int do_chown(const std::vector<std::string>& args) {
/* GID is optional. */
- if (nargs == 3) {
- if (lchown(args[2], decode_uid(args[1]), -1) == -1)
+ if (args.size() == 3) {
+ if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
return -errno;
- } else if (nargs == 4) {
- if (lchown(args[3], decode_uid(args[1]), decode_uid(args[2])) == -1)
+ } else if (args.size() == 4) {
+ if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
+ decode_uid(args[2].c_str())) == -1)
return -errno;
} else {
return -1;
@@ -786,43 +771,41 @@
return mode;
}
-int do_chmod(int nargs, char **args) {
- mode_t mode = get_mode(args[1]);
- if (fchmodat(AT_FDCWD, args[2], mode, AT_SYMLINK_NOFOLLOW) < 0) {
+int do_chmod(const std::vector<std::string>& args) {
+ mode_t mode = get_mode(args[1].c_str());
+ if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
return -errno;
}
return 0;
}
-int do_restorecon(int nargs, char **args) {
- int i;
+int do_restorecon(const std::vector<std::string>& args) {
int ret = 0;
- for (i = 1; i < nargs; i++) {
- if (restorecon(args[i]) < 0)
+ for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+ if (restorecon(it->c_str()) < 0)
ret = -errno;
}
return ret;
}
-int do_restorecon_recursive(int nargs, char **args) {
- int i;
+int do_restorecon_recursive(const std::vector<std::string>& args) {
int ret = 0;
- for (i = 1; i < nargs; i++) {
- if (restorecon_recursive(args[i]) < 0)
+ for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+ if (restorecon_recursive(it->c_str()) < 0)
ret = -errno;
}
return ret;
}
-int do_loglevel(int nargs, char **args) {
- if (nargs != 2) {
+int do_loglevel(const std::vector<std::string>& args) {
+ if (args.size() != 2) {
ERROR("loglevel: missing argument\n");
return -EINVAL;
}
- int log_level = atoi(args[1]);
+ int log_level = std::stoi(args[1]);
if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
ERROR("loglevel: invalid log level'%d'\n", log_level);
return -EINVAL;
@@ -831,28 +814,28 @@
return 0;
}
-int do_load_persist_props(int nargs, char **args) {
- if (nargs == 1) {
+int do_load_persist_props(const std::vector<std::string>& args) {
+ if (args.size() == 1) {
load_persist_props();
return 0;
}
return -1;
}
-int do_load_all_props(int nargs, char **args) {
- if (nargs == 1) {
+int do_load_all_props(const std::vector<std::string>& args) {
+ if (args.size() == 1) {
load_all_props();
return 0;
}
return -1;
}
-int do_wait(int nargs, char **args)
+int do_wait(const std::vector<std::string>& args)
{
- if (nargs == 2) {
- return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
- } else if (nargs == 3) {
- return wait_for_file(args[1], atoi(args[2]));
+ if (args.size() == 2) {
+ return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
+ } else if (args.size() == 3) {
+ return wait_for_file(args[1].c_str(), std::stoi(args[2]));
} else
return -1;
}
@@ -869,9 +852,9 @@
return 0;
}
-int do_installkey(int nargs, char **args)
+int do_installkey(const std::vector<std::string>& args)
{
- if (nargs != 2) {
+ if (args.size() != 2) {
return -1;
}
@@ -880,6 +863,6 @@
return 0;
}
- return e4crypt_create_device_key(args[1],
+ return e4crypt_create_device_key(args[1].c_str(),
do_installkeys_ensure_dir_exists);
}
diff --git a/init/init.cpp b/init/init.cpp
index 4be16ea..c94a6fe 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -32,7 +32,6 @@
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
-#include <termios.h>
#include <unistd.h>
#include <mtd/mtd-user.h>
@@ -53,16 +52,18 @@
#include <memory>
+#include "action.h"
+#include "bootchart.h"
#include "devices.h"
#include "init.h"
+#include "init_parser.h"
+#include "keychords.h"
#include "log.h"
#include "property_service.h"
-#include "bootchart.h"
+#include "service.h"
#include "signal_handler.h"
-#include "keychords.h"
-#include "init_parser.h"
-#include "util.h"
#include "ueventd.h"
+#include "util.h"
#include "watchdogd.h"
struct selabel_handle *sehandle;
@@ -72,14 +73,11 @@
static char qemu[32];
-static struct action *cur_action = NULL;
-static struct command *cur_command = NULL;
-
-static int have_console;
-static std::string console_name = "/dev/console";
+int have_console;
+std::string console_name = "/dev/console";
static time_t process_needs_restart;
-static const char *ENV[32];
+const char *ENV[32];
bool waiting_for_exec = false;
@@ -94,27 +92,6 @@
}
}
-void service::NotifyStateChange(const char* new_state) {
- if (!properties_initialized()) {
- // If properties aren't available yet, we can't set them.
- return;
- }
-
- if ((flags & SVC_EXEC) != 0) {
- // 'exec' commands don't have properties tracking their state.
- return;
- }
-
- char prop_name[PROP_NAME_MAX];
- if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) {
- // If the property name would be too long, we can't set it.
- ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state);
- return;
- }
-
- property_set(prop_name, new_state);
-}
-
/* add_environment - add "key=value" to the current environment */
int add_environment(const char *key, const char *val)
{
@@ -147,510 +124,80 @@
return -1;
}
-void zap_stdio(void)
-{
- int fd;
- fd = open("/dev/null", O_RDWR);
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- close(fd);
-}
-
-static void open_console()
-{
- int fd;
- if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
- fd = open("/dev/null", O_RDWR);
- }
- ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- close(fd);
-}
-
-static void publish_socket(const char *name, int fd)
-{
- char key[64] = ANDROID_SOCKET_ENV_PREFIX;
- char val[64];
-
- strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
- name,
- sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
- snprintf(val, sizeof(val), "%d", fd);
- add_environment(key, val);
-
- /* make sure we don't close-on-exec */
- fcntl(fd, F_SETFD, 0);
-}
-
-void service_start(struct service *svc, const char *dynamic_args)
-{
- // Starting a service removes it from the disabled or reset state and
- // immediately takes it out of the restarting state if it was in there.
- svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
- svc->time_started = 0;
-
- // Running processes require no additional work --- if they're in the
- // process of exiting, we've ensured that they will immediately restart
- // on exit, unless they are ONESHOT.
- if (svc->flags & SVC_RUNNING) {
- return;
- }
-
- bool needs_console = (svc->flags & SVC_CONSOLE);
- if (needs_console && !have_console) {
- ERROR("service '%s' requires console\n", svc->name);
- svc->flags |= SVC_DISABLED;
- return;
- }
-
- struct stat sb;
- if (stat(svc->args[0], &sb) == -1) {
- ERROR("cannot find '%s' (%s), disabling '%s'\n", svc->args[0], strerror(errno), svc->name);
- svc->flags |= SVC_DISABLED;
- return;
- }
-
- if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
- ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]);
- svc->flags |= SVC_DISABLED;
- return;
- }
-
- char* scon = NULL;
- if (svc->seclabel) {
- scon = strdup(svc->seclabel);
- if (!scon) {
- ERROR("Out of memory while starting '%s'\n", svc->name);
- return;
- }
- } else {
- char *mycon = NULL, *fcon = NULL;
-
- INFO("computing context for service '%s'\n", svc->args[0]);
- int rc = getcon(&mycon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", svc->name);
- return;
- }
-
- rc = getfilecon(svc->args[0], &fcon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", svc->name);
- free(mycon);
- return;
- }
-
- rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
- if (rc == 0 && !strcmp(scon, mycon)) {
- ERROR("Service %s does not have a SELinux domain defined.\n", svc->name);
- free(mycon);
- free(fcon);
- free(scon);
- return;
- }
- free(mycon);
- free(fcon);
- if (rc < 0) {
- ERROR("could not get context while starting '%s'\n", svc->name);
- return;
- }
- }
-
- NOTICE("Starting service '%s'...\n", svc->name);
-
- pid_t pid = fork();
- if (pid == 0) {
- struct socketinfo *si;
- struct svcenvinfo *ei;
- char tmp[32];
- int fd, sz;
-
- umask(077);
- if (properties_initialized()) {
- get_property_workspace(&fd, &sz);
- snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
- add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
- }
-
- for (ei = svc->envvars; ei; ei = ei->next)
- add_environment(ei->name, ei->value);
-
- for (si = svc->sockets; si; si = si->next) {
- int socket_type = (
- !strcmp(si->type, "stream") ? SOCK_STREAM :
- (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
- int s = create_socket(si->name, socket_type,
- si->perm, si->uid, si->gid, si->socketcon ?: scon);
- if (s >= 0) {
- publish_socket(si->name, s);
- }
- }
-
- free(scon);
- scon = NULL;
-
- if (svc->writepid_files_) {
- std::string pid_str = android::base::StringPrintf("%d", pid);
- for (auto& file : *svc->writepid_files_) {
- if (!android::base::WriteStringToFile(pid_str, file)) {
- ERROR("couldn't write %s to %s: %s\n",
- pid_str.c_str(), file.c_str(), strerror(errno));
- }
- }
- }
-
- if (svc->ioprio_class != IoSchedClass_NONE) {
- if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
- ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
- getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
- }
- }
-
- if (needs_console) {
- setsid();
- open_console();
- } else {
- zap_stdio();
- }
-
- if (false) {
- for (size_t n = 0; svc->args[n]; n++) {
- INFO("args[%zu] = '%s'\n", n, svc->args[n]);
- }
- for (size_t n = 0; ENV[n]; n++) {
- INFO("env[%zu] = '%s'\n", n, ENV[n]);
- }
- }
-
- setpgid(0, getpid());
-
- // As requested, set our gid, supplemental gids, and uid.
- if (svc->gid) {
- if (setgid(svc->gid) != 0) {
- ERROR("setgid failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (svc->nr_supp_gids) {
- if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
- ERROR("setgroups failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (svc->uid) {
- if (setuid(svc->uid) != 0) {
- ERROR("setuid failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (svc->seclabel) {
- if (setexeccon(svc->seclabel) < 0) {
- ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
- _exit(127);
- }
- }
-
- if (!dynamic_args) {
- if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
- ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
- }
- } else {
- char *arg_ptrs[INIT_PARSER_MAXARGS+1];
- int arg_idx = svc->nargs;
- char *tmp = strdup(dynamic_args);
- char *next = tmp;
- char *bword;
-
- /* Copy the static arguments */
- memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
-
- while((bword = strsep(&next, " "))) {
- arg_ptrs[arg_idx++] = bword;
- if (arg_idx == INIT_PARSER_MAXARGS)
- break;
- }
- arg_ptrs[arg_idx] = NULL;
- execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
- }
- _exit(127);
- }
-
- free(scon);
-
- if (pid < 0) {
- ERROR("failed to start '%s'\n", svc->name);
- svc->pid = 0;
- return;
- }
-
- svc->time_started = gettime();
- svc->pid = pid;
- svc->flags |= SVC_RUNNING;
-
- if ((svc->flags & SVC_EXEC) != 0) {
- INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
- svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
- svc->seclabel ? : "default");
- waiting_for_exec = true;
- }
-
- svc->NotifyStateChange("running");
-}
-
-/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
-static void service_stop_or_reset(struct service *svc, int how)
-{
- /* The service is still SVC_RUNNING until its process exits, but if it has
- * already exited it shoudn't attempt a restart yet. */
- svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);
-
- if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
- /* Hrm, an illegal flag. Default to SVC_DISABLED */
- how = SVC_DISABLED;
- }
- /* if the service has not yet started, prevent
- * it from auto-starting with its class
- */
- if (how == SVC_RESET) {
- svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
- } else {
- svc->flags |= how;
- }
-
- if (svc->pid) {
- NOTICE("Service '%s' is being killed...\n", svc->name);
- kill(-svc->pid, SIGKILL);
- svc->NotifyStateChange("stopping");
- } else {
- svc->NotifyStateChange("stopped");
- }
-}
-
-void service_reset(struct service *svc)
-{
- service_stop_or_reset(svc, SVC_RESET);
-}
-
-void service_stop(struct service *svc)
-{
- service_stop_or_reset(svc, SVC_DISABLED);
-}
-
-void service_restart(struct service *svc)
-{
- if (svc->flags & SVC_RUNNING) {
- /* Stop, wait, then start the service. */
- service_stop_or_reset(svc, SVC_RESTART);
- } else if (!(svc->flags & SVC_RESTARTING)) {
- /* Just start the service since it's not running. */
- service_start(svc, NULL);
- } /* else: Service is restarting anyways. */
-}
-
void property_changed(const char *name, const char *value)
{
if (property_triggers_enabled)
- queue_property_triggers(name, value);
-}
-
-static void restart_service_if_needed(struct service *svc)
-{
- time_t next_start_time = svc->time_started + 5;
-
- if (next_start_time <= gettime()) {
- svc->flags &= (~SVC_RESTARTING);
- service_start(svc, NULL);
- return;
- }
-
- if ((next_start_time < process_needs_restart) ||
- (process_needs_restart == 0)) {
- process_needs_restart = next_start_time;
- }
+ ActionManager::GetInstance().QueuePropertyTrigger(name, value);
}
static void restart_processes()
{
process_needs_restart = 0;
- service_for_each_flags(SVC_RESTARTING,
- restart_service_if_needed);
+ ServiceManager::GetInstance().
+ ForEachServiceWithFlags(SVC_RESTARTING, [] (Service* s) {
+ s->RestartIfNeeded(process_needs_restart);
+ });
}
-static void msg_start(const char *name)
+static void msg_start(const std::string& name)
{
- struct service *svc = NULL;
- char *tmp = NULL;
- char *args = NULL;
+ Service* svc = nullptr;
+ std::vector<std::string> vargs;
- if (!strchr(name, ':'))
- svc = service_find_by_name(name);
- else {
- tmp = strdup(name);
- if (tmp) {
- args = strchr(tmp, ':');
- *args = '\0';
- args++;
+ size_t colon_pos = name.find(':');
+ if (colon_pos == std::string::npos) {
+ svc = ServiceManager::GetInstance().FindServiceByName(name);
+ } else {
+ std::string service_name(name.substr(0, colon_pos));
+ std::string args(name.substr(colon_pos + 1));
+ vargs = android::base::Split(args, " ");
- svc = service_find_by_name(tmp);
- }
+ svc = ServiceManager::GetInstance().FindServiceByName(service_name);
}
if (svc) {
- service_start(svc, args);
+ svc->Start(vargs);
} else {
- ERROR("no such service '%s'\n", name);
+ ERROR("no such service '%s'\n", name.c_str());
}
- if (tmp)
- free(tmp);
}
-static void msg_stop(const char *name)
+static void msg_stop(const std::string& name)
{
- struct service *svc = service_find_by_name(name);
+ Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
if (svc) {
- service_stop(svc);
+ svc->Stop();
} else {
- ERROR("no such service '%s'\n", name);
+ ERROR("no such service '%s'\n", name.c_str());
}
}
-static void msg_restart(const char *name)
+static void msg_restart(const std::string& name)
{
- struct service *svc = service_find_by_name(name);
+ Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
if (svc) {
- service_restart(svc);
+ svc->Restart();
} else {
- ERROR("no such service '%s'\n", name);
+ ERROR("no such service '%s'\n", name.c_str());
}
}
-void handle_control_message(const char *msg, const char *arg)
+void handle_control_message(const std::string& msg, const std::string& arg)
{
- if (!strcmp(msg,"start")) {
+ if (msg == "start") {
msg_start(arg);
- } else if (!strcmp(msg,"stop")) {
+ } else if (msg == "stop") {
msg_stop(arg);
- } else if (!strcmp(msg,"restart")) {
+ } else if (msg == "restart") {
msg_restart(arg);
} else {
- ERROR("unknown control msg '%s'\n", msg);
+ ERROR("unknown control msg '%s'\n", msg.c_str());
}
}
-static struct command *get_first_command(struct action *act)
-{
- struct listnode *node;
- node = list_head(&act->commands);
- if (!node || list_empty(&act->commands))
- return NULL;
-
- return node_to_item(node, struct command, clist);
-}
-
-static struct command *get_next_command(struct action *act, struct command *cmd)
-{
- struct listnode *node;
- node = cmd->clist.next;
- if (!node)
- return NULL;
- if (node == &act->commands)
- return NULL;
-
- return node_to_item(node, struct command, clist);
-}
-
-static int is_last_command(struct action *act, struct command *cmd)
-{
- return (list_tail(&act->commands) == &cmd->clist);
-}
-
-
-std::string build_triggers_string(struct action *cur_action) {
- std::string result;
- struct listnode *node;
- struct trigger *cur_trigger;
-
- list_for_each(node, &cur_action->triggers) {
- cur_trigger = node_to_item(node, struct trigger, nlist);
- if (node != cur_action->triggers.next) {
- result.push_back(' ');
- }
- result += cur_trigger->name;
- }
- return result;
-}
-
-bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args) {
- std::vector<std::string>& strs = *expanded_args;
- strs.resize(nargs);
- strs[0] = args[0];
- for (int i = 1; i < nargs; ++i) {
- if (expand_props(args[i], &strs[i]) == -1) {
- ERROR("%s: cannot expand '%s'\n", args[0], args[i]);
- return false;
- }
- }
- return true;
-}
-
-void execute_one_command() {
- Timer t;
-
- if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
- cur_action = action_remove_queue_head();
- cur_command = NULL;
- if (!cur_action) {
- return;
- }
-
- std::string trigger_name = build_triggers_string(cur_action);
- INFO("processing action %p (%s)\n", cur_action, trigger_name.c_str());
- cur_command = get_first_command(cur_action);
- } else {
- cur_command = get_next_command(cur_action, cur_command);
- }
-
- if (!cur_command) {
- return;
- }
- int result = 0;
- std::vector<std::string> arg_strs;
- if (!expand_command_arguments(cur_command->nargs, cur_command->args, &arg_strs)) {
- result = -EINVAL;
- }
- if (result == 0) {
- std::vector<char*> args;
- for (auto& s : arg_strs) {
- args.push_back(&s[0]);
- }
- result = cur_command->func(args.size(), &args[0]);
- }
- if (klog_get_level() >= KLOG_INFO_LEVEL) {
- std::string cmd_str;
- for (int i = 0; i < cur_command->nargs; ++i) {
- if (i > 0) {
- cmd_str.push_back(' ');
- }
- cmd_str += cur_command->args[i];
- }
- std::string trigger_name = build_triggers_string(cur_action);
-
- std::string source;
- if (cur_command->filename) {
- source = android::base::StringPrintf(" (%s:%d)", cur_command->filename, cur_command->line);
- }
-
- INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
- cmd_str.c_str(), trigger_name.c_str(), source.c_str(), result, t.duration());
- }
-}
-
-static int wait_for_coldboot_done_action(int nargs, char **args) {
+static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
Timer t;
NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
@@ -680,7 +227,7 @@
* time. We do not reboot or halt on failures, as this is a best-effort
* attempt.
*/
-static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
+static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
{
int result = -1;
int hwrandom_fd = -1;
@@ -742,13 +289,13 @@
return result;
}
-static int keychord_init_action(int nargs, char **args)
+static int keychord_init_action(const std::vector<std::string>& args)
{
keychord_init();
return 0;
}
-static int console_init_action(int nargs, char **args)
+static int console_init_action(const std::vector<std::string>& args)
{
std::string console = property_get("ro.boot.console");
if (!console.empty()) {
@@ -863,9 +410,9 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
-static int queue_property_triggers_action(int nargs, char **args)
+static int queue_property_triggers_action(const std::vector<std::string>& args)
{
- queue_all_property_triggers();
+ ActionManager::GetInstance().QueueAllPropertyTriggers();
/* enable property triggers */
property_triggers_enabled = 1;
return 0;
@@ -1059,36 +606,38 @@
init_parse_config("/init.rc");
- action_for_each_trigger("early-init", action_add_queue_tail);
+ ActionManager& am = ActionManager::GetInstance();
+
+ am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
- queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
- queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
- queue_builtin_action(keychord_init_action, "keychord_init");
- queue_builtin_action(console_init_action, "console_init");
+ am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+ am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+ am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
- action_for_each_trigger("init", action_add_queue_tail);
+ am.QueueEventTrigger("init");
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
- queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+ am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = property_get("ro.bootmode");
if (bootmode == "charger") {
- action_for_each_trigger("charger", action_add_queue_tail);
+ am.QueueEventTrigger("charger");
} else {
- action_for_each_trigger("late-init", action_add_queue_tail);
+ am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
- queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
+ am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
if (!waiting_for_exec) {
- execute_one_command();
+ am.ExecuteOneCommand();
restart_processes();
}
@@ -1099,7 +648,7 @@
timeout = 0;
}
- if (!action_queue_empty() || cur_action) {
+ if (am.HasMoreCommands()) {
timeout = 0;
}
diff --git a/init/init.h b/init/init.h
index d2b2dfb..345d442 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,150 +17,28 @@
#ifndef _INIT_INIT_H
#define _INIT_INIT_H
-#include <sys/types.h>
-
#include <string>
-#include <vector>
-#include <cutils/list.h>
-#include <cutils/iosched_policy.h>
-
-struct command
-{
- /* list of commands in an action */
- struct listnode clist;
-
- int (*func)(int nargs, char **args);
-
- int line;
- const char *filename;
-
- int nargs;
- char *args[1];
-};
-
-struct trigger {
- struct listnode nlist;
- const char *name;
-};
-
-struct action {
- /* node in list of all actions */
- struct listnode alist;
- /* node in the queue of pending actions */
- struct listnode qlist;
- /* node in list of actions for a trigger */
- struct listnode tlist;
-
- unsigned hash;
-
- /* list of actions which triggers the commands*/
- struct listnode triggers;
- struct listnode commands;
- struct command *current;
-};
-
-struct socketinfo {
- struct socketinfo *next;
- const char *name;
- const char *type;
- uid_t uid;
- gid_t gid;
- int perm;
- const char *socketcon;
-};
-
-struct svcenvinfo {
- struct svcenvinfo *next;
- const char *name;
- const char *value;
-};
-
-#define SVC_DISABLED 0x001 // do not autostart with class
-#define SVC_ONESHOT 0x002 // do not restart on exit
-#define SVC_RUNNING 0x004 // currently active
-#define SVC_RESTARTING 0x008 // waiting to restart
-#define SVC_CONSOLE 0x010 // requires console
-#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
-#define SVC_RESET 0x040 // Use when stopping a process, but not disabling so it can be restarted with its class.
-#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
-#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service.
-#define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time.
-#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'.
-
-#define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */
+class Action;
+class Service;
#define COMMAND_RETRY_TIMEOUT 5
-struct service {
- void NotifyStateChange(const char* new_state);
-
- /* list of all services */
- struct listnode slist;
-
- char *name;
- const char *classname;
-
- unsigned flags;
- pid_t pid;
- time_t time_started; /* time of last start */
- time_t time_crashed; /* first crash within inspection window */
- int nr_crashed; /* number of times crashed within window */
-
- uid_t uid;
- gid_t gid;
- gid_t supp_gids[NR_SVC_SUPP_GIDS];
- size_t nr_supp_gids;
-
- const char* seclabel;
-
- struct socketinfo *sockets;
- struct svcenvinfo *envvars;
-
- struct action onrestart; /* Actions to execute on restart. */
-
- std::vector<std::string>* writepid_files_;
-
- /* keycodes for triggering this service via /dev/keychord */
- int *keycodes;
- int nkeycodes;
- int keychord_id;
-
- IoSchedClass ioprio_class;
- int ioprio_pri;
-
- int nargs;
- /* "MUST BE AT THE END OF THE STRUCT" */
- char *args[1];
-}; /* ^-------'args' MUST be at the end of this struct! */
-
+extern const char *ENV[32];
extern bool waiting_for_exec;
+extern int have_console;
+extern std::string console_name;
extern struct selabel_handle *sehandle;
extern struct selabel_handle *sehandle_prop;
-std::string build_triggers_string(struct action *cur_action);
+void handle_control_message(const std::string& msg, const std::string& arg);
-void handle_control_message(const char *msg, const char *arg);
-
-struct service *service_find_by_name(const char *name);
-struct service *service_find_by_pid(pid_t pid);
-struct service *service_find_by_keychord(int keychord_id);
-void service_for_each(void (*func)(struct service *svc));
-void service_for_each_class(const char *classname,
- void (*func)(struct service *svc));
-void service_for_each_flags(unsigned matchflags,
- void (*func)(struct service *svc));
-void service_stop(struct service *svc);
-void service_reset(struct service *svc);
-void service_restart(struct service *svc);
-void service_start(struct service *svc, const char *dynamic_args);
void property_changed(const char *name, const char *value);
int selinux_reload_policy(void);
-void zap_stdio(void);
-
void register_epoll_handler(int fd, void (*fn)());
-bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args);
-#endif /* _INIT_INIT_H */
+int add_environment(const char* key, const char* val);
+
+#endif /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 460f5ac..12f44f7 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -26,11 +26,13 @@
#include <string.h>
#include <unistd.h>
+#include "action.h"
#include "init.h"
-#include "parser.h"
#include "init_parser.h"
#include "log.h"
+#include "parser.h"
#include "property_service.h"
+#include "service.h"
#include "util.h"
#include <base/stringprintf.h>
@@ -38,8 +40,6 @@
#include <cutils/list.h>
static list_declare(service_list);
-static list_declare(action_list);
-static list_declare(action_queue);
struct import {
struct listnode list;
@@ -63,8 +63,8 @@
static struct {
const char *name;
- int (*func)(int nargs, char **args);
- unsigned char nargs;
+ int (*func)(const std::vector<std::string>& args);
+ size_t nargs;
unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
[ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
@@ -78,41 +78,8 @@
#define kw_nargs(kw) (keyword_info[kw].nargs)
void dump_parser_state() {
- if (false) {
- struct listnode* node;
- list_for_each(node, &service_list) {
- service* svc = node_to_item(node, struct service, slist);
- INFO("service %s\n", svc->name);
- INFO(" class '%s'\n", svc->classname);
- INFO(" exec");
- for (int n = 0; n < svc->nargs; n++) {
- INFO(" '%s'", svc->args[n]);
- }
- INFO("\n");
- for (socketinfo* si = svc->sockets; si; si = si->next) {
- INFO(" socket %s %s 0%o\n", si->name, si->type, si->perm);
- }
- }
-
- list_for_each(node, &action_list) {
- action* act = node_to_item(node, struct action, alist);
- INFO("on ");
- std::string trigger_name = build_triggers_string(act);
- INFO("%s", trigger_name.c_str());
- INFO("\n");
-
- struct listnode* node2;
- list_for_each(node2, &act->commands) {
- command* cmd = node_to_item(node2, struct command, clist);
- INFO(" %p", cmd->func);
- for (int n = 0; n < cmd->nargs; n++) {
- INFO(" %s", cmd->args[n]);
- }
- INFO("\n");
- }
- INFO("\n");
- }
- }
+ ServiceManager::GetInstance().DumpState();
+ ActionManager::GetInstance().DumpState();
}
static int lookup_keyword(const char *s)
@@ -217,10 +184,10 @@
static void parse_line_no_op(struct parse_state*, int, char**) {
}
-int expand_props(const char *src, std::string *dst) {
- const char *src_ptr = src;
+int expand_props(const std::string& src, std::string* dst) {
+ const char *src_ptr = src.c_str();
- if (!src || !dst) {
+ if (!dst) {
return -1;
}
@@ -256,7 +223,7 @@
const char* end = strchr(c, '}');
if (!end) {
// failed to find closing brace, abort.
- ERROR("unexpected end of string in '%s', looking for }\n", src);
+ ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
goto err;
}
prop_name = std::string(c, end);
@@ -269,14 +236,14 @@
}
if (prop_name.empty()) {
- ERROR("invalid zero-length prop name in '%s'\n", src);
+ ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
goto err;
}
std::string prop_val = property_get(prop_name.c_str());
if (prop_val.empty()) {
ERROR("property '%s' doesn't exist while expanding '%s'\n",
- prop_name.c_str(), src);
+ prop_name.c_str(), src.c_str());
goto err;
}
@@ -348,10 +315,14 @@
int nargs = 0;
+ //TODO: Use a parser with const input and remove this copy
+ std::vector<char> data_copy(data.begin(), data.end());
+ data_copy.push_back('\0');
+
parse_state state;
state.filename = fn;
state.line = 0;
- state.ptr = strdup(data.c_str()); // TODO: fix this code!
+ state.ptr = &data_copy[0];
state.nexttoken = 0;
state.parse_line = parse_line_no_op;
@@ -438,557 +409,90 @@
return init_parse_config_file(path);
}
-static int valid_name(const char *name)
-{
- if (strlen(name) > 16) {
- return 0;
- }
- while (*name) {
- if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
- return 0;
- }
- name++;
- }
- return 1;
-}
-
-struct service *service_find_by_name(const char *name)
-{
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (!strcmp(svc->name, name)) {
- return svc;
- }
- }
- return 0;
-}
-
-struct service *service_find_by_pid(pid_t pid)
-{
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (svc->pid == pid) {
- return svc;
- }
- }
- return 0;
-}
-
-struct service *service_find_by_keychord(int keychord_id)
-{
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (svc->keychord_id == keychord_id) {
- return svc;
- }
- }
- return 0;
-}
-
-void service_for_each(void (*func)(struct service *svc))
-{
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- func(svc);
- }
-}
-
-void service_for_each_class(const char *classname,
- void (*func)(struct service *svc))
-{
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (!strcmp(svc->classname, classname)) {
- func(svc);
- }
- }
-}
-
-void service_for_each_flags(unsigned matchflags,
- void (*func)(struct service *svc))
-{
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (svc->flags & matchflags) {
- func(svc);
- }
- }
-}
-
-void action_for_each_trigger(const char *trigger,
- void (*func)(struct action *act))
-{
- struct listnode *node, *node2;
- struct action *act;
- struct trigger *cur_trigger;
-
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- list_for_each(node2, &act->triggers) {
- cur_trigger = node_to_item(node2, struct trigger, nlist);
- if (!strcmp(cur_trigger->name, trigger)) {
- func(act);
- }
- }
- }
-}
-
-
-void queue_property_triggers(const char *name, const char *value)
-{
- struct listnode *node, *node2;
- struct action *act;
- struct trigger *cur_trigger;
- bool match;
- int name_length;
-
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- match = !name;
- list_for_each(node2, &act->triggers) {
- cur_trigger = node_to_item(node2, struct trigger, nlist);
- if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
- const char *test = cur_trigger->name + strlen("property:");
- if (!match) {
- name_length = strlen(name);
- if (!strncmp(name, test, name_length) &&
- test[name_length] == '=' &&
- (!strcmp(test + name_length + 1, value) ||
- !strcmp(test + name_length + 1, "*"))) {
- match = true;
- continue;
- }
- } else {
- const char* equals = strchr(test, '=');
- if (equals) {
- int length = equals - test;
- if (length <= PROP_NAME_MAX) {
- std::string prop_name(test, length);
- std::string value = property_get(prop_name.c_str());
-
- /* does the property exist, and match the trigger value? */
- if (!value.empty() && (!strcmp(equals + 1, value.c_str()) ||
- !strcmp(equals + 1, "*"))) {
- continue;
- }
- }
- }
- }
- }
- match = false;
- break;
- }
- if (match) {
- action_add_queue_tail(act);
- }
- }
-}
-
-void queue_all_property_triggers()
-{
- queue_property_triggers(NULL, NULL);
-}
-
-void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
-{
- action* act = (action*) calloc(1, sizeof(*act));
- trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
- cur_trigger->name = name;
- list_init(&act->triggers);
- list_add_tail(&act->triggers, &cur_trigger->nlist);
- list_init(&act->commands);
- list_init(&act->qlist);
-
- command* cmd = (command*) calloc(1, sizeof(*cmd));
- cmd->func = func;
- cmd->args[0] = const_cast<char*>(name);
- cmd->nargs = 1;
- list_add_tail(&act->commands, &cmd->clist);
-
- list_add_tail(&action_list, &act->alist);
- action_add_queue_tail(act);
-}
-
-void action_add_queue_tail(struct action *act)
-{
- if (list_empty(&act->qlist)) {
- list_add_tail(&action_queue, &act->qlist);
- }
-}
-
-struct action *action_remove_queue_head(void)
-{
- if (list_empty(&action_queue)) {
- return 0;
- } else {
- struct listnode *node = list_head(&action_queue);
- struct action *act = node_to_item(node, struct action, qlist);
- list_remove(node);
- list_init(node);
- return act;
- }
-}
-
-int action_queue_empty()
-{
- return list_empty(&action_queue);
-}
-
-service* make_exec_oneshot_service(int nargs, char** args) {
- // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
- // SECLABEL can be a - to denote default
- int command_arg = 1;
- for (int i = 1; i < nargs; ++i) {
- if (strcmp(args[i], "--") == 0) {
- command_arg = i + 1;
- break;
- }
- }
- if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
- ERROR("exec called with too many supplementary group ids\n");
- return NULL;
- }
-
- int argc = nargs - command_arg;
- char** argv = (args + command_arg);
- if (argc < 1) {
- ERROR("exec called without command\n");
- return NULL;
- }
-
- service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc);
- if (svc == NULL) {
- ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno));
- return NULL;
- }
-
- if ((command_arg > 2) && strcmp(args[1], "-")) {
- svc->seclabel = args[1];
- }
- if (command_arg > 3) {
- svc->uid = decode_uid(args[2]);
- }
- if (command_arg > 4) {
- svc->gid = decode_uid(args[3]);
- svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
- for (size_t i = 0; i < svc->nr_supp_gids; ++i) {
- svc->supp_gids[i] = decode_uid(args[4 + i]);
- }
- }
-
- static int exec_count; // Every service needs a unique name.
- char* name = NULL;
- asprintf(&name, "exec %d (%s)", exec_count++, argv[0]);
- if (name == NULL) {
- ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]);
- free(svc);
- return NULL;
- }
- svc->name = name;
- svc->classname = "default";
- svc->flags = SVC_EXEC | SVC_ONESHOT;
- svc->nargs = argc;
- memcpy(svc->args, argv, sizeof(char*) * svc->nargs);
- svc->args[argc] = NULL;
- list_add_tail(&service_list, &svc->slist);
- return svc;
-}
-
static void *parse_service(struct parse_state *state, int nargs, char **args)
{
if (nargs < 3) {
parse_error(state, "services must have a name and a program\n");
- return 0;
+ return nullptr;
}
- if (!valid_name(args[1])) {
- parse_error(state, "invalid service name '%s'\n", args[1]);
- return 0;
- }
+ std::vector<std::string> str_args(args + 2, args + nargs);
+ std::string ret_err;
+ Service* svc = ServiceManager::GetInstance().AddNewService(args[1], "default",
+ str_args, &ret_err);
- service* svc = (service*) service_find_by_name(args[1]);
- if (svc) {
- parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
- return 0;
- }
-
- nargs -= 2;
- svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
if (!svc) {
- parse_error(state, "out of memory\n");
- return 0;
+ parse_error(state, "%s\n", ret_err.c_str());
}
- svc->name = strdup(args[1]);
- svc->classname = "default";
- memcpy(svc->args, args + 2, sizeof(char*) * nargs);
- trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
- svc->args[nargs] = 0;
- svc->nargs = nargs;
- list_init(&svc->onrestart.triggers);
- cur_trigger->name = "onrestart";
- list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
- list_init(&svc->onrestart.commands);
- list_add_tail(&service_list, &svc->slist);
+
return svc;
}
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
- struct service *svc = (service*) state->context;
- struct command *cmd;
- int i, kw, kw_nargs;
-
if (nargs == 0) {
return;
}
- svc->ioprio_class = IoSchedClass_NONE;
+ Service* svc = static_cast<Service*>(state->context);
+ int kw = lookup_keyword(args[0]);
+ std::vector<std::string> str_args(args, args + nargs);
+ std::string ret_err;
+ bool ret = svc->HandleLine(kw, str_args, &ret_err);
- kw = lookup_keyword(args[0]);
- switch (kw) {
- case K_class:
- if (nargs != 2) {
- parse_error(state, "class option requires a classname\n");
- } else {
- svc->classname = args[1];
- }
- break;
- case K_console:
- svc->flags |= SVC_CONSOLE;
- break;
- case K_disabled:
- svc->flags |= SVC_DISABLED;
- svc->flags |= SVC_RC_DISABLED;
- break;
- case K_ioprio:
- if (nargs != 3) {
- parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
- } else {
- svc->ioprio_pri = strtoul(args[2], 0, 8);
-
- if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
- parse_error(state, "priority value must be range 0 - 7\n");
- break;
- }
-
- if (!strcmp(args[1], "rt")) {
- svc->ioprio_class = IoSchedClass_RT;
- } else if (!strcmp(args[1], "be")) {
- svc->ioprio_class = IoSchedClass_BE;
- } else if (!strcmp(args[1], "idle")) {
- svc->ioprio_class = IoSchedClass_IDLE;
- } else {
- parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
- }
- }
- break;
- case K_group:
- if (nargs < 2) {
- parse_error(state, "group option requires a group id\n");
- } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
- parse_error(state, "group option accepts at most %d supp. groups\n",
- NR_SVC_SUPP_GIDS);
- } else {
- int n;
- svc->gid = decode_uid(args[1]);
- for (n = 2; n < nargs; n++) {
- svc->supp_gids[n-2] = decode_uid(args[n]);
- }
- svc->nr_supp_gids = n - 2;
- }
- break;
- case K_keycodes:
- if (nargs < 2) {
- parse_error(state, "keycodes option requires atleast one keycode\n");
- } else {
- svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));
- if (!svc->keycodes) {
- parse_error(state, "could not allocate keycodes\n");
- } else {
- svc->nkeycodes = nargs - 1;
- for (i = 1; i < nargs; i++) {
- svc->keycodes[i - 1] = atoi(args[i]);
- }
- }
- }
- break;
- case K_oneshot:
- svc->flags |= SVC_ONESHOT;
- break;
- case K_onrestart:
- nargs--;
- args++;
- kw = lookup_keyword(args[0]);
- if (!kw_is(kw, COMMAND)) {
- parse_error(state, "invalid command '%s'\n", args[0]);
- break;
- }
- kw_nargs = kw_nargs(kw);
- if (nargs < kw_nargs) {
- parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
- kw_nargs > 2 ? "arguments" : "argument");
- break;
- }
-
- cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
- cmd->func = kw_func(kw);
- cmd->nargs = nargs;
- memcpy(cmd->args, args, sizeof(char*) * nargs);
- list_add_tail(&svc->onrestart.commands, &cmd->clist);
- break;
- case K_critical:
- svc->flags |= SVC_CRITICAL;
- break;
- case K_setenv: { /* name value */
- if (nargs < 3) {
- parse_error(state, "setenv option requires name and value arguments\n");
- break;
- }
- svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));
- if (!ei) {
- parse_error(state, "out of memory\n");
- break;
- }
- ei->name = args[1];
- ei->value = args[2];
- ei->next = svc->envvars;
- svc->envvars = ei;
- break;
- }
- case K_socket: {/* name type perm [ uid gid context ] */
- if (nargs < 4) {
- parse_error(state, "socket option requires name, type, perm arguments\n");
- break;
- }
- if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
- && strcmp(args[2],"seqpacket")) {
- parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
- break;
- }
- socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));
- if (!si) {
- parse_error(state, "out of memory\n");
- break;
- }
- si->name = args[1];
- si->type = args[2];
- si->perm = strtoul(args[3], 0, 8);
- if (nargs > 4)
- si->uid = decode_uid(args[4]);
- if (nargs > 5)
- si->gid = decode_uid(args[5]);
- if (nargs > 6)
- si->socketcon = args[6];
- si->next = svc->sockets;
- svc->sockets = si;
- break;
- }
- case K_user:
- if (nargs != 2) {
- parse_error(state, "user option requires a user id\n");
- } else {
- svc->uid = decode_uid(args[1]);
- }
- break;
- case K_seclabel:
- if (nargs != 2) {
- parse_error(state, "seclabel option requires a label string\n");
- } else {
- svc->seclabel = args[1];
- }
- break;
- case K_writepid:
- if (nargs < 2) {
- parse_error(state, "writepid option requires at least one filename\n");
- break;
- }
- svc->writepid_files_ = new std::vector<std::string>;
- for (int i = 1; i < nargs; ++i) {
- svc->writepid_files_->push_back(args[i]);
- }
- break;
-
- default:
- parse_error(state, "invalid option '%s'\n", args[0]);
+ if (!ret) {
+ parse_error(state, "%s\n", ret_err.c_str());
}
}
-static void *parse_action(struct parse_state *state, int nargs, char **args)
+static void *parse_action(struct parse_state* state, int nargs, char **args)
{
- struct trigger *cur_trigger;
- int i;
- if (nargs < 2) {
- parse_error(state, "actions must have a trigger\n");
- return 0;
+ std::string ret_err;
+ std::vector<std::string> triggers(args + 1, args + nargs);
+ Action* ret = ActionManager::GetInstance().AddNewAction(triggers, &ret_err);
+
+ if (!ret) {
+ parse_error(state, "%s\n", ret_err.c_str());
}
- action* act = (action*) calloc(1, sizeof(*act));
- list_init(&act->triggers);
+ return ret;
+}
- for (i = 1; i < nargs; i++) {
- if (!(i % 2)) {
- if (strcmp(args[i], "&&")) {
- struct listnode *node;
- struct listnode *node2;
- parse_error(state, "& is the only symbol allowed to concatenate actions\n");
- list_for_each_safe(node, node2, &act->triggers) {
- struct trigger *trigger = node_to_item(node, struct trigger, nlist);
- free(trigger);
- }
- free(act);
- return 0;
- } else
- continue;
- }
- cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
- cur_trigger->name = args[i];
- list_add_tail(&act->triggers, &cur_trigger->nlist);
+bool add_command_to_action(Action* action, const std::vector<std::string>& args,
+ const std::string& filename, int line, std::string* err)
+{
+ int kw;
+ size_t n;
+
+ kw = lookup_keyword(args[0].c_str());
+ if (!kw_is(kw, COMMAND)) {
+ *err = android::base::StringPrintf("invalid command '%s'\n", args[0].c_str());
+ return false;
}
- list_init(&act->commands);
- list_init(&act->qlist);
- list_add_tail(&action_list, &act->alist);
- /* XXX add to hash */
- return act;
+ n = kw_nargs(kw);
+ if (args.size() < n) {
+ *err = android::base::StringPrintf("%s requires %zu %s\n",
+ args[0].c_str(), n - 1,
+ n > 2 ? "arguments" : "argument");
+ return false;
+ }
+
+ action->AddCommand(kw_func(kw), args, filename, line);
+ return true;
}
static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
- struct action *act = (action*) state->context;
- int kw, n;
-
if (nargs == 0) {
return;
}
- kw = lookup_keyword(args[0]);
- if (!kw_is(kw, COMMAND)) {
- parse_error(state, "invalid command '%s'\n", args[0]);
- return;
+ Action* action = static_cast<Action*>(state->context);
+ std::vector<std::string> str_args(args, args + nargs);
+ std::string ret_err;
+ bool ret = add_command_to_action(action, str_args, state->filename,
+ state->line, &ret_err);
+ if (!ret) {
+ parse_error(state, "%s\n", ret_err.c_str());
}
-
- n = kw_nargs(kw);
- if (nargs < n) {
- parse_error(state, "%s requires %d %s\n", args[0], n - 1,
- n > 2 ? "arguments" : "argument");
- return;
- }
- command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
- cmd->func = kw_func(kw);
- cmd->line = state->line;
- cmd->filename = state->filename;
- cmd->nargs = nargs;
- memcpy(cmd->args, args, sizeof(char*) * nargs);
- list_add_tail(&act->commands, &cmd->clist);
}
diff --git a/init/init_parser.h b/init/init_parser.h
index 1ebb1ef..709dca8 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -18,24 +18,15 @@
#define _INIT_INIT_PARSER_H_
#include <string>
+#include <vector>
#define INIT_PARSER_MAXARGS 64
-struct action;
-struct service;
-
-struct action *action_remove_queue_head(void);
-void action_add_queue_tail(struct action *act);
-void action_for_each_trigger(const char *trigger,
- void (*func)(struct action *act));
-int action_queue_empty(void);
-void queue_property_triggers(const char *name, const char *value);
-void queue_all_property_triggers();
-void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
+class Action;
bool init_parse_config(const char* path);
-int expand_props(const char *src, std::string *dst);
-
-service* make_exec_oneshot_service(int argc, char** argv);
+int expand_props(const std::string& src, std::string* dst);
+bool add_command_to_action(Action* action, const std::vector<std::string>& args,
+ const std::string& filename, int line, std::string* err);
#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 170a73a..52aaa37 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -17,96 +17,97 @@
#include "init_parser.h"
#include "init.h"
+#include "service.h"
#include "util.h"
#include <errno.h>
#include <gtest/gtest.h>
-TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
- char* argv[10];
- memset(argv, 0, sizeof(argv));
+#include <string>
+#include <vector>
+TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
+ ServiceManager& sm = ServiceManager::GetInstance();
+ std::vector<std::string> args;
// Nothing.
- ASSERT_EQ(nullptr, make_exec_oneshot_service(0, argv));
+ ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
// No arguments to 'exec'.
- argv[0] = const_cast<char*>("exec");
- ASSERT_EQ(nullptr, make_exec_oneshot_service(1, argv));
+ args.push_back("exec");
+ ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
// No command in "exec --".
- argv[1] = const_cast<char*>("--");
- ASSERT_EQ(nullptr, make_exec_oneshot_service(2, argv));
+ args.push_back("--");
+ ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
}
TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
- int argc = 0;
- char* argv[4 + NR_SVC_SUPP_GIDS + 3];
- argv[argc++] = const_cast<char*>("exec");
- argv[argc++] = const_cast<char*>("seclabel");
- argv[argc++] = const_cast<char*>("root"); // uid.
- argv[argc++] = const_cast<char*>("root"); // gid.
+ ServiceManager& sm = ServiceManager::GetInstance();
+ std::vector<std::string> args;
+ args.push_back("exec");
+ args.push_back("seclabel");
+ args.push_back("root"); // uid.
+ args.push_back("root"); // gid.
for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
- argv[argc++] = const_cast<char*>("root"); // Supplementary gid.
+ args.push_back("root"); // Supplementary gid.
}
- argv[argc++] = const_cast<char*>("--");
- argv[argc++] = const_cast<char*>("/system/bin/id");
- argv[argc] = nullptr;
- ASSERT_EQ(nullptr, make_exec_oneshot_service(argc, argv));
+ args.push_back("--");
+ args.push_back("/system/bin/id");
+ ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
}
-static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid, bool supplementary_gids) {
- int argc = 0;
- char* argv[10];
- argv[argc++] = const_cast<char*>("exec");
+static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
+ bool gid, bool supplementary_gids) {
+ ServiceManager& sm = ServiceManager::GetInstance();
+ std::vector<std::string> args;
+ args.push_back("exec");
if (seclabel) {
- argv[argc++] = const_cast<char*>("u:r:su:s0"); // seclabel
+ args.push_back("u:r:su:s0"); // seclabel
if (uid) {
- argv[argc++] = const_cast<char*>("log"); // uid
+ args.push_back("log"); // uid
if (gid) {
- argv[argc++] = const_cast<char*>("shell"); // gid
+ args.push_back("shell"); // gid
if (supplementary_gids) {
- argv[argc++] = const_cast<char*>("system"); // supplementary gid 0
- argv[argc++] = const_cast<char*>("adb"); // supplementary gid 1
+ args.push_back("system"); // supplementary gid 0
+ args.push_back("adb"); // supplementary gid 1
}
}
}
}
if (dash_dash) {
- argv[argc++] = const_cast<char*>("--");
+ args.push_back("--");
}
- argv[argc++] = const_cast<char*>("/system/bin/toybox");
- argv[argc++] = const_cast<char*>("id");
- argv[argc] = nullptr;
- service* svc = make_exec_oneshot_service(argc, argv);
+ args.push_back("/system/bin/toybox");
+ args.push_back("id");
+ Service* svc = sm.MakeExecOneshotService(args);
ASSERT_NE(nullptr, svc);
if (seclabel) {
- ASSERT_STREQ("u:r:su:s0", svc->seclabel);
+ ASSERT_EQ("u:r:su:s0", svc->seclabel());
} else {
- ASSERT_EQ(nullptr, svc->seclabel);
+ ASSERT_EQ("", svc->seclabel());
}
if (uid) {
- ASSERT_EQ(decode_uid("log"), svc->uid);
+ ASSERT_EQ(decode_uid("log"), svc->uid());
} else {
- ASSERT_EQ(0U, svc->uid);
+ ASSERT_EQ(0U, svc->uid());
}
if (gid) {
- ASSERT_EQ(decode_uid("shell"), svc->gid);
+ ASSERT_EQ(decode_uid("shell"), svc->gid());
} else {
- ASSERT_EQ(0U, svc->gid);
+ ASSERT_EQ(0U, svc->gid());
}
if (supplementary_gids) {
- ASSERT_EQ(2U, svc->nr_supp_gids);
- ASSERT_EQ(decode_uid("system"), svc->supp_gids[0]);
- ASSERT_EQ(decode_uid("adb"), svc->supp_gids[1]);
+ ASSERT_EQ(2U, svc->supp_gids().size());
+ ASSERT_EQ(decode_uid("system"), svc->supp_gids()[0]);
+ ASSERT_EQ(decode_uid("adb"), svc->supp_gids()[1]);
} else {
- ASSERT_EQ(0U, svc->nr_supp_gids);
+ ASSERT_EQ(0U, svc->supp_gids().size());
}
- ASSERT_EQ(2, svc->nargs);
- ASSERT_EQ("/system/bin/toybox", svc->args[0]);
- ASSERT_EQ("id", svc->args[1]);
- ASSERT_EQ(nullptr, svc->args[2]);
+ ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
+ ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
+ ASSERT_EQ("id", svc->args()[1]);
}
TEST(init_parser, make_exec_oneshot_service_with_everything) {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index c4ebdf9..7a7838d 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -26,20 +26,21 @@
#include "init.h"
#include "log.h"
#include "property_service.h"
+#include "service.h"
static struct input_keychord *keychords = 0;
static int keychords_count = 0;
static int keychords_length = 0;
static int keychord_fd = -1;
-void add_service_keycodes(struct service *svc)
+void add_service_keycodes(Service* svc)
{
struct input_keychord *keychord;
- int i, size;
+ size_t i, size;
- if (svc->keycodes) {
+ if (!svc->keycodes().empty()) {
/* add a new keychord to the list */
- size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
+ size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
keychords = (input_keychord*) realloc(keychords, keychords_length + size);
if (!keychords) {
ERROR("could not allocate keychords\n");
@@ -51,11 +52,11 @@
keychord = (struct input_keychord *)((char *)keychords + keychords_length);
keychord->version = KEYCHORD_VERSION;
keychord->id = keychords_count + 1;
- keychord->count = svc->nkeycodes;
- svc->keychord_id = keychord->id;
+ keychord->count = svc->keycodes().size();
+ svc->set_keychord_id(keychord->id);
- for (i = 0; i < svc->nkeycodes; i++) {
- keychord->keycodes[i] = svc->keycodes[i];
+ for (i = 0; i < svc->keycodes().size(); i++) {
+ keychord->keycodes[i] = svc->keycodes()[i];
}
keychords_count++;
keychords_length += size;
@@ -63,7 +64,6 @@
}
static void handle_keychord() {
- struct service *svc;
int ret;
__u16 id;
@@ -76,10 +76,10 @@
// Only handle keychords if adb is enabled.
std::string adb_enabled = property_get("init.svc.adbd");
if (adb_enabled == "running") {
- svc = service_find_by_keychord(id);
+ Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
if (svc) {
- INFO("Starting service %s from keychord\n", svc->name);
- service_start(svc, NULL);
+ INFO("Starting service %s from keychord\n", svc->name().c_str());
+ svc->Start();
} else {
ERROR("service for keychord %d not found\n", id);
}
@@ -87,7 +87,7 @@
}
void keychord_init() {
- service_for_each(add_service_keycodes);
+ ServiceManager::GetInstance().ForEachService(add_service_keycodes);
// Nothing to do if no services require keychords.
if (!keychords) {
diff --git a/init/keywords.h b/init/keywords.h
index e637d7d..922feee 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -1,43 +1,45 @@
#ifndef KEYWORD
-int do_bootchart_init(int nargs, char **args);
-int do_class_start(int nargs, char **args);
-int do_class_stop(int nargs, char **args);
-int do_class_reset(int nargs, char **args);
-int do_domainname(int nargs, char **args);
-int do_enable(int nargs, char **args);
-int do_exec(int nargs, char **args);
-int do_export(int nargs, char **args);
-int do_hostname(int nargs, char **args);
-int do_ifup(int nargs, char **args);
-int do_insmod(int nargs, char **args);
-int do_installkey(int nargs, char **args);
-int do_mkdir(int nargs, char **args);
-int do_mount_all(int nargs, char **args);
-int do_mount(int nargs, char **args);
-int do_powerctl(int nargs, char **args);
-int do_restart(int nargs, char **args);
-int do_restorecon(int nargs, char **args);
-int do_restorecon_recursive(int nargs, char **args);
-int do_rm(int nargs, char **args);
-int do_rmdir(int nargs, char **args);
-int do_setprop(int nargs, char **args);
-int do_setrlimit(int nargs, char **args);
-int do_start(int nargs, char **args);
-int do_stop(int nargs, char **args);
-int do_swapon_all(int nargs, char **args);
-int do_trigger(int nargs, char **args);
-int do_symlink(int nargs, char **args);
-int do_sysclktz(int nargs, char **args);
-int do_write(int nargs, char **args);
-int do_copy(int nargs, char **args);
-int do_chown(int nargs, char **args);
-int do_chmod(int nargs, char **args);
-int do_loglevel(int nargs, char **args);
-int do_load_persist_props(int nargs, char **args);
-int do_load_all_props(int nargs, char **args);
-int do_verity_load_state(int nargs, char **args);
-int do_verity_update_state(int nargs, char **args);
-int do_wait(int nargs, char **args);
+#include <string>
+#include <vector>
+int do_bootchart_init(const std::vector<std::string>& args);
+int do_class_start(const std::vector<std::string>& args);
+int do_class_stop(const std::vector<std::string>& args);
+int do_class_reset(const std::vector<std::string>& args);
+int do_domainname(const std::vector<std::string>& args);
+int do_enable(const std::vector<std::string>& args);
+int do_exec(const std::vector<std::string>& args);
+int do_export(const std::vector<std::string>& args);
+int do_hostname(const std::vector<std::string>& args);
+int do_ifup(const std::vector<std::string>& args);
+int do_insmod(const std::vector<std::string>& args);
+int do_installkey(const std::vector<std::string>& args);
+int do_mkdir(const std::vector<std::string>& args);
+int do_mount_all(const std::vector<std::string>& args);
+int do_mount(const std::vector<std::string>& args);
+int do_powerctl(const std::vector<std::string>& args);
+int do_restart(const std::vector<std::string>& args);
+int do_restorecon(const std::vector<std::string>& args);
+int do_restorecon_recursive(const std::vector<std::string>& args);
+int do_rm(const std::vector<std::string>& args);
+int do_rmdir(const std::vector<std::string>& args);
+int do_setprop(const std::vector<std::string>& args);
+int do_setrlimit(const std::vector<std::string>& args);
+int do_start(const std::vector<std::string>& args);
+int do_stop(const std::vector<std::string>& args);
+int do_swapon_all(const std::vector<std::string>& args);
+int do_trigger(const std::vector<std::string>& args);
+int do_symlink(const std::vector<std::string>& args);
+int do_sysclktz(const std::vector<std::string>& args);
+int do_write(const std::vector<std::string>& args);
+int do_copy(const std::vector<std::string>& args);
+int do_chown(const std::vector<std::string>& args);
+int do_chmod(const std::vector<std::string>& args);
+int do_loglevel(const std::vector<std::string>& args);
+int do_load_persist_props(const std::vector<std::string>& args);
+int do_load_all_props(const std::vector<std::string>& args);
+int do_verity_load_state(const std::vector<std::string>& args);
+int do_verity_update_state(const std::vector<std::string>& args);
+int do_wait(const std::vector<std::string>& args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {
diff --git a/init/parser/tokenizer.cpp b/init/parser/tokenizer.cpp
new file mode 100644
index 0000000..340e0d9
--- /dev/null
+++ b/init/parser/tokenizer.cpp
@@ -0,0 +1,129 @@
+// Copyright (C) 2015 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 "tokenizer.h"
+
+namespace init {
+
+Tokenizer::Tokenizer(const std::string& data)
+ : data_(data), eof_(false), pos_(0), tok_start_(0) {
+ current_.type = TOK_START;
+
+ if (data.size() > 0) {
+ cur_char_ = data[0];
+ } else {
+ eof_ = true;
+ cur_char_ = '\0';
+ }
+}
+
+Tokenizer::~Tokenizer() {}
+
+const Tokenizer::Token& Tokenizer::current() {
+ return current_;
+}
+
+bool Tokenizer::Next() {
+ while (!eof_) {
+ AdvWhiteSpace();
+
+ // Check for comments.
+ if (cur_char_ == '#') {
+ AdvChar();
+ // Skip rest of line
+ while (!eof_ && cur_char_ != '\n') {
+ AdvChar();
+ }
+ }
+
+ if (eof_) {
+ break;
+ }
+
+ if (cur_char_ == '\0') {
+ AdvChar();
+ } else if (cur_char_ == '\n') {
+ current_.type = TOK_NEWLINE;
+ current_.text.clear();
+ AdvChar();
+ return true;
+ } else if (cur_char_ == '\\') {
+ AdvChar(); // skip backslash
+ // This is line continuation so
+ // do not generated TOK_NEWLINE at
+ // the next \n.
+ AdvUntil('\n');
+ AdvChar(); // skip \n
+ } else if (cur_char_ == '\"') {
+ AdvChar();
+ StartText();
+ // Grab everything until the next quote.
+ AdvUntil('\"');
+ EndText();
+ AdvChar(); // skip quote.
+ return true;
+ } else {
+ StartText();
+ AdvText();
+ EndText();
+ return true;
+ }
+ }
+ current_.type = TOK_END;
+ current_.text.clear();
+ return false;
+}
+
+void Tokenizer::AdvChar() {
+ pos_++;
+ if (pos_ < data_.size()) {
+ cur_char_ = data_[pos_];
+ } else {
+ eof_ = true;
+ cur_char_ = '\0';
+ }
+}
+
+void Tokenizer::AdvWhiteSpace() {
+ while (cur_char_ == '\t' || cur_char_ == '\r' || cur_char_ == ' ') {
+ AdvChar();
+ }
+}
+
+void Tokenizer::AdvUntil(char x) {
+ while (!eof_ && cur_char_ != x) {
+ AdvChar();
+ }
+}
+
+void Tokenizer::AdvText() {
+ while (cur_char_ != '\t' && cur_char_ != '\r' && cur_char_ != '\0' &&
+ cur_char_ != ' ' && cur_char_ != '\n' && cur_char_ != '#') {
+ AdvChar();
+ }
+}
+
+void Tokenizer::StartText() {
+ current_.text.clear();
+ tok_start_ = pos_;
+ current_.type = TOK_TEXT;
+}
+
+void Tokenizer::EndText() {
+ if (pos_ != tok_start_) {
+ current_.text.append(data_, tok_start_, pos_ - tok_start_);
+ }
+}
+
+} // namespace init
\ No newline at end of file
diff --git a/init/parser/tokenizer.h b/init/parser/tokenizer.h
new file mode 100644
index 0000000..8312a08
--- /dev/null
+++ b/init/parser/tokenizer.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2015 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 _INIT_PARSER_TOKENIZER_H
+#define _INIT_PARSER_TOKENIZER_H
+
+#include <string>
+
+namespace init {
+
+// Used to tokenize a std::string.
+// Call Next() to advance through each token until it returns false,
+// indicating there are no more tokens left in the string.
+// The current token can be accessed with current(), which returns
+// a Token.
+// Supported tokens are:
+// TOK_START - Next() has yet to be called
+// TOK_END - At the end of string
+// TOK_NEWLINE - The end of a line denoted by \n.
+// TOK_TEXT - A word.
+// Comments are denoted with '#' and the tokenizer will ignore
+// the rest of the line.
+// Double quotes can be used to insert whitespace into words.
+// A backslash at the end of a line denotes continuation and
+// a TOK_NEWLINE will not be generated for that line.
+class Tokenizer {
+ public:
+ Tokenizer(const std::string& data);
+ ~Tokenizer();
+
+ enum TokenType { TOK_START, TOK_END, TOK_NEWLINE, TOK_TEXT };
+ struct Token {
+ TokenType type;
+ std::string text;
+ };
+
+ // Returns the curret token.
+ const Token& current();
+
+ // Move to the next token, returns false at the end of input.
+ bool Next();
+
+ private:
+ void GetData();
+ void AdvChar();
+ void AdvText();
+ void AdvUntil(char x);
+ void AdvWhiteSpace();
+ void StartText();
+ void EndText();
+
+ const std::string& data_;
+ Token current_;
+
+ bool eof_;
+ size_t pos_;
+ char cur_char_;
+ size_t tok_start_;
+};
+
+} // namespace init
+
+#endif
diff --git a/init/parser/tokenizer_test.cpp b/init/parser/tokenizer_test.cpp
new file mode 100644
index 0000000..c4a48df
--- /dev/null
+++ b/init/parser/tokenizer_test.cpp
@@ -0,0 +1,230 @@
+// Copyright (C) 2015 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 "tokenizer.h"
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+namespace init {
+
+#define SETUP_TEST(test_data) \
+ std::string data(test_data); \
+ Tokenizer tokenizer(data); \
+ ASSERT_EQ(Tokenizer::TOK_START, tokenizer.current().type)
+
+#define ASSERT_TEXT_TOKEN(test_text) \
+ ASSERT_TRUE(tokenizer.Next()); \
+ ASSERT_EQ(test_text, tokenizer.current().text); \
+ ASSERT_EQ(Tokenizer::TOK_TEXT, tokenizer.current().type)
+
+#define ASSERT_NEWLINE_TOKEN() \
+ ASSERT_TRUE(tokenizer.Next()); \
+ ASSERT_EQ(Tokenizer::TOK_NEWLINE, tokenizer.current().type)
+
+TEST(Tokenizer, Empty) {
+ SETUP_TEST("");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, Simple) {
+ SETUP_TEST("test");
+ ASSERT_TEXT_TOKEN("test");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, LeadingWhiteSpace) {
+ SETUP_TEST(" \t \r test");
+ ASSERT_TEXT_TOKEN("test");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TrailingWhiteSpace) {
+ SETUP_TEST("test \t \r ");
+ ASSERT_TEXT_TOKEN("test");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, WhiteSpace) {
+ SETUP_TEST(" \t \r test \t \r ");
+ ASSERT_TEXT_TOKEN("test");
+
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TwoTokens) {
+ SETUP_TEST(" foo bar ");
+ ASSERT_TEXT_TOKEN("foo");
+ ASSERT_TEXT_TOKEN("bar");
+
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiToken) {
+ SETUP_TEST("one two three four five");
+ ASSERT_TEXT_TOKEN("one");
+ ASSERT_TEXT_TOKEN("two");
+ ASSERT_TEXT_TOKEN("three");
+ ASSERT_TEXT_TOKEN("four");
+ ASSERT_TEXT_TOKEN("five");
+
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, NewLine) {
+ SETUP_TEST("\n");
+ ASSERT_NEWLINE_TOKEN();
+
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TextNewLine) {
+ SETUP_TEST("test\n");
+ ASSERT_TEXT_TOKEN("test");
+ ASSERT_NEWLINE_TOKEN();
+
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiTextNewLine) {
+ SETUP_TEST("one\ntwo\nthree\n");
+ ASSERT_TEXT_TOKEN("one");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_TEXT_TOKEN("two");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_TEXT_TOKEN("three");
+ ASSERT_NEWLINE_TOKEN();
+
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiTextNewLineNoLineEnding) {
+ SETUP_TEST("one\ntwo\nthree");
+ ASSERT_TEXT_TOKEN("one");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_TEXT_TOKEN("two");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_TEXT_TOKEN("three");
+
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, Comment) {
+ SETUP_TEST("#test");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWhiteSpace) {
+ SETUP_TEST(" \t \r #test \t \r ");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentNewLine) {
+ SETUP_TEST(" #test \n");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentTwoNewLine) {
+ SETUP_TEST(" #test \n#test");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithText) {
+ SETUP_TEST("foo bar #test");
+ ASSERT_TEXT_TOKEN("foo");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithTextNoSpace) {
+ SETUP_TEST("foo bar#test");
+ ASSERT_TEXT_TOKEN("foo");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithTextLineFeed) {
+ SETUP_TEST("foo bar #test\n");
+ ASSERT_TEXT_TOKEN("foo");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithMultiTextLineFeed) {
+ SETUP_TEST("#blah\nfoo bar #test\n#blah");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_TEXT_TOKEN("foo");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_NEWLINE_TOKEN();
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, SimpleEscaped) {
+ SETUP_TEST("fo\\no bar");
+ ASSERT_TEXT_TOKEN("fo\\no");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContNoLineFeed) {
+ SETUP_TEST("fo\\no bar \\ hello");
+ ASSERT_TEXT_TOKEN("fo\\no");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContLineFeed) {
+ SETUP_TEST("fo\\no bar \\ hello\n");
+ ASSERT_TEXT_TOKEN("fo\\no");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineCont) {
+ SETUP_TEST("fo\\no bar \\\ntest");
+ ASSERT_TEXT_TOKEN("fo\\no");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_TEXT_TOKEN("test");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContWithBadChars) {
+ SETUP_TEST("fo\\no bar \\bad bad bad\ntest");
+ ASSERT_TEXT_TOKEN("fo\\no");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_TEXT_TOKEN("test");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, SimpleQuotes) {
+ SETUP_TEST("foo \"single token\" bar");
+ ASSERT_TEXT_TOKEN("foo");
+ ASSERT_TEXT_TOKEN("single token");
+ ASSERT_TEXT_TOKEN("bar");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, BadQuotes) {
+ SETUP_TEST("foo \"single token");
+ ASSERT_TEXT_TOKEN("foo");
+ ASSERT_TEXT_TOKEN("single token");
+ ASSERT_FALSE(tokenizer.Next());
+}
+
+} // namespace init
diff --git a/init/perfboot.py b/init/perfboot.py
new file mode 100755
index 0000000..b0efb11
--- /dev/null
+++ b/init/perfboot.py
@@ -0,0 +1,454 @@
+#!/usr/bin/env python
+# Copyright (C) 2015 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.
+"""Record the event logs during boot and output them to a file.
+
+This script repeats the record of each event log during Android boot specified
+times. By default, interval between measurements is adjusted in such a way that
+CPUs are cooled down sufficiently to avoid boot time slowdown caused by CPU
+thermal throttling. The result is output in a tab-separated value format.
+
+Examples:
+
+Repeat measurements 10 times. Interval between iterations is adjusted based on
+CPU temperature of the device.
+
+$ ./perfboot.py --iterations=10
+
+Repeat measurements 20 times. 60 seconds interval is taken between each
+iteration.
+
+$ ./perfboot.py --iterations=20 --interval=60
+
+Repeat measurements 20 times, show verbose output, output the result to
+data.tsv, and read event tags from eventtags.txt.
+
+$ ./perfboot.py --iterations=30 -v --output=data.tsv --tags=eventtags.txt
+"""
+
+import argparse
+import atexit
+import cStringIO
+import glob
+import inspect
+import logging
+import math
+import os
+import re
+import subprocess
+import sys
+import threading
+import time
+
+sys.path.append(os.path.dirname(os.path.dirname(__file__)))
+import adb
+
+# The default event tags to record.
+_DEFAULT_EVENT_TAGS = [
+ 'boot_progress_start',
+ 'boot_progress_preload_start',
+ 'boot_progress_preload_end',
+ 'boot_progress_system_run',
+ 'boot_progress_pms_start',
+ 'boot_progress_pms_system_scan_start',
+ 'boot_progress_pms_data_scan_start',
+ 'boot_progress_pms_scan_end',
+ 'boot_progress_pms_ready',
+ 'boot_progress_ams_ready',
+ 'boot_progress_enable_screen',
+ 'sf_stop_bootanim',
+ 'wm_boot_animation_done',
+]
+
+
+class IntervalAdjuster(object):
+ """A helper class to take suffficient interval between iterations."""
+
+ # CPU temperature values per product used to decide interval
+ _CPU_COOL_DOWN_THRESHOLDS = {
+ 'flo': 40,
+ 'flounder': 40000,
+ 'razor': 40,
+ 'volantis': 40000,
+ }
+ # The interval between CPU temperature checks
+ _CPU_COOL_DOWN_WAIT_INTERVAL = 10
+ # The wait time used when the value of _CPU_COOL_DOWN_THRESHOLDS for
+ # the product is not defined.
+ _CPU_COOL_DOWN_WAIT_TIME_DEFAULT = 120
+
+ def __init__(self, interval, device):
+ self._interval = interval
+ self._device = device
+ self._temp_paths = device.shell(
+ ['ls', '/sys/class/thermal/thermal_zone*/temp']).splitlines()
+ self._product = device.get_prop('ro.build.product')
+ self._waited = False
+
+ def wait(self):
+ """Waits certain amount of time for CPUs cool-down."""
+ if self._interval is None:
+ self._wait_cpu_cool_down(self._product, self._temp_paths)
+ else:
+ if self._waited:
+ print 'Waiting for %d seconds' % self._interval
+ time.sleep(self._interval)
+ self._waited = True
+
+ def _get_cpu_temp(self, threshold):
+ max_temp = 0
+ for temp_path in self._temp_paths:
+ temp = int(self._device.shell(['cat', temp_path]).rstrip())
+ max_temp = max(max_temp, temp)
+ if temp >= threshold:
+ return temp
+ return max_temp
+
+ def _wait_cpu_cool_down(self, product, temp_paths):
+ threshold = IntervalAdjuster._CPU_COOL_DOWN_THRESHOLDS.get(
+ self._product)
+ if threshold is None:
+ print 'No CPU temperature threshold is set for ' + self._product
+ print ('Just wait %d seconds' %
+ IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
+ time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
+ return
+ while True:
+ temp = self._get_cpu_temp(threshold)
+ if temp < threshold:
+ logging.info('Current CPU temperature %s' % temp)
+ return
+ print 'Waiting until CPU temperature (%d) falls below %d' % (
+ temp, threshold)
+ time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_INTERVAL)
+
+
+class WatchdogTimer(object):
+ """A timer that makes is_timedout() return true in |timeout| seconds."""
+ def __init__(self, timeout):
+ self._timedout = False
+
+ def notify_timeout():
+ self._timedout = True
+ self._timer = threading.Timer(timeout, notify_timeout)
+ self._timer.daemon = True
+ self._timer.start()
+
+ def is_timedout(self):
+ return self._timedout
+
+ def cancel(self):
+ self._timer.cancel()
+
+
+def readlines_unbuffered(proc):
+ """Read lines from |proc|'s standard out without buffering."""
+ while True:
+ buf = []
+ c = proc.stdout.read(1)
+ if c == '' and proc.poll() is not None:
+ break
+ while c != '\n':
+ if c == '' and proc.poll() is not None:
+ break
+ buf.append(c)
+ c = proc.stdout.read(1)
+ yield ''.join(buf)
+
+
+def disable_dropbox(device):
+ """Removes the files created by Dropbox and avoids creating the files."""
+ device.root()
+ device.wait()
+ device.shell(['rm', '-rf', '/system/data/dropbox'])
+ original_dropbox_max_files = device.shell(
+ ['settings', 'get', 'global', 'dropbox_max_files']).rstrip()
+ device.shell(['settings', 'put', 'global', 'dropbox_max_files', '0'])
+ return original_dropbox_max_files
+
+
+def restore_dropbox(device, original_dropbox_max_files):
+ """Restores the dropbox_max_files setting."""
+ device.root()
+ device.wait()
+ if original_dropbox_max_files == 'null':
+ device.shell(['settings', 'delete', 'global', 'dropbox_max_files'])
+ else:
+ device.shell(['settings', 'put', 'global', 'dropbox_max_files',
+ original_dropbox_max_files])
+
+
+def init_perf(device, output, record_list, tags):
+ device.wait()
+ build_type = device.get_prop('ro.build.type')
+ original_dropbox_max_files = None
+ if build_type != 'user':
+ # Workaround for Dropbox issue (http://b/20890386).
+ original_dropbox_max_files = disable_dropbox(device)
+
+ def cleanup():
+ try:
+ if record_list:
+ print_summary(record_list, tags[-1])
+ output_results(output, record_list, tags)
+ if original_dropbox_max_files is not None:
+ restore_dropbox(device, original_dropbox_max_files)
+ except (subprocess.CalledProcessError, RuntimeError):
+ pass
+ atexit.register(cleanup)
+
+
+def check_dm_verity_settings(device):
+ device.wait()
+ for partition in ['system', 'vendor']:
+ verity_mode = device.get_prop('partition.%s.verified' % partition)
+ if verity_mode is None:
+ logging.warning('dm-verity is not enabled for /%s. Did you run '
+ 'adb disable-verity? That may skew the result.',
+ partition)
+
+
+def read_event_tags(tags_file):
+ """Reads event tags from |tags_file|."""
+ if not tags_file:
+ return _DEFAULT_EVENT_TAGS
+ tags = []
+ with open(tags_file) as f:
+ for line in f:
+ if '#' in line:
+ line = line[:line.find('#')]
+ line = line.strip()
+ if line:
+ tags.append(line)
+ return tags
+
+
+def make_event_tags_re(tags):
+ """Makes a regular expression object that matches event logs of |tags|."""
+ return re.compile(r'(?P<pid>[0-9]+) +[0-9]+ I (?P<tag>%s): (?P<time>\d+)' %
+ '|'.join(tags))
+
+
+def filter_event_tags(tags, device):
+ """Drop unknown tags not listed in device's event-log-tags file."""
+ device.wait()
+ supported_tags = set()
+ for l in device.shell(['cat', '/system/etc/event-log-tags']).splitlines():
+ tokens = l.split(' ')
+ if len(tokens) >= 2:
+ supported_tags.add(tokens[1])
+ filtered = []
+ for tag in tags:
+ if tag in supported_tags:
+ filtered.append(tag)
+ else:
+ logging.warning('Unknown tag \'%s\'. Ignoring...', tag)
+ return filtered
+
+
+def get_values(record, tag):
+ """Gets values that matches |tag| from |record|."""
+ keys = [key for key in record.keys() if key[0] == tag]
+ return [record[k] for k in sorted(keys)]
+
+
+def get_last_value(record, tag):
+ """Gets the last value that matches |tag| from |record|."""
+ values = get_values(record, tag)
+ if not values:
+ return 0
+ return values[-1]
+
+
+def output_results(filename, record_list, tags):
+ """Outputs |record_list| into |filename| in a TSV format."""
+ # First, count the number of the values of each tag.
+ # This is for dealing with events that occur multiple times.
+ # For instance, boot_progress_preload_start and boot_progress_preload_end
+ # are recorded twice on 64-bit system. One is for 64-bit zygote process
+ # and the other is for 32-bit zygote process.
+ values_counter = {}
+ for record in record_list:
+ for tag in tags:
+ # Some record might lack values for some tags due to unanticipated
+ # problems (e.g. timeout), so take the maximum count among all the
+ # record.
+ values_counter[tag] = max(values_counter.get(tag, 1),
+ len(get_values(record, tag)))
+
+ # Then creates labels for the data. If there are multiple values for one
+ # tag, labels for these values are numbered except the first one as
+ # follows:
+ #
+ # event_tag event_tag2 event_tag3
+ #
+ # The corresponding values are sorted in an ascending order of PID.
+ labels = []
+ for tag in tags:
+ for i in range(1, values_counter[tag] + 1):
+ labels.append('%s%s' % (tag, '' if i == 1 else str(i)))
+
+ # Finally write the data into the file.
+ with open(filename, 'w') as f:
+ f.write('\t'.join(labels) + '\n')
+ for record in record_list:
+ line = cStringIO.StringIO()
+ invalid_line = False
+ for i, tag in enumerate(tags):
+ if i != 0:
+ line.write('\t')
+ values = get_values(record, tag)
+ if len(values) < values_counter[tag]:
+ invalid_line = True
+ # Fill invalid record with 0
+ values += [0] * (values_counter[tag] - len(values))
+ line.write('\t'.join(str(t) for t in values))
+ if invalid_line:
+ logging.error('Invalid record found: ' + line.getvalue())
+ line.write('\n')
+ f.write(line.getvalue())
+ print 'Wrote: ' + filename
+
+
+def median(data):
+ """Calculates the median value from |data|."""
+ data = sorted(data)
+ n = len(data)
+ if n % 2 == 1:
+ return data[n / 2]
+ else:
+ n2 = n / 2
+ return (data[n2 - 1] + data[n2]) / 2.0
+
+
+def mean(data):
+ """Calculates the mean value from |data|."""
+ return float(sum(data)) / len(data)
+
+
+def stddev(data):
+ """Calculates the standard deviation value from |value|."""
+ m = mean(data)
+ return math.sqrt(sum((x - m) ** 2 for x in data) / len(data))
+
+
+def print_summary(record_list, end_tag):
+ """Prints the summary of |record_list|."""
+ # Filter out invalid data.
+ end_times = [get_last_value(record, end_tag) for record in record_list
+ if get_last_value(record, end_tag) != 0]
+ print 'mean: ', mean(end_times)
+ print 'median:', median(end_times)
+ print 'standard deviation:', stddev(end_times)
+
+
+def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
+ """Measures the boot time once."""
+ device.wait()
+ interval_adjuster.wait()
+ device.reboot()
+ print 'Rebooted the device'
+ record = {}
+ booted = False
+ while not booted:
+ device.wait()
+ # Stop the iteration if it does not finish within 120 seconds.
+ timeout = 120
+ t = WatchdogTimer(timeout)
+ p = subprocess.Popen(
+ ['adb', 'logcat', '-b', 'events', '-v', 'threadtime'],
+ stdout=subprocess.PIPE)
+ for line in readlines_unbuffered(p):
+ if t.is_timedout():
+ print '*** Timed out ***'
+ return record
+ m = event_tags_re.search(line)
+ if not m:
+ continue
+ tag = m.group('tag')
+ event_time = int(m.group('time'))
+ pid = m.group('pid')
+ record[(tag, pid)] = event_time
+ print 'Event log recorded: %s (%s) - %d ms' % (
+ tag, pid, event_time)
+ if tag == end_tag:
+ booted = True
+ t.cancel()
+ break
+ return record
+
+
+def parse_args():
+ """Parses the command line arguments."""
+ parser = argparse.ArgumentParser(
+ description=inspect.getdoc(sys.modules[__name__]),
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument('--iterations', type=int, default=5,
+ help='Number of times to repeat boot measurements.')
+ parser.add_argument('--interval', type=int,
+ help=('Duration between iterations. If this is not '
+ 'set explicitly, durations are determined '
+ 'adaptively based on CPUs temperature.'))
+ parser.add_argument('-o', '--output', help='File name of output data.')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Show verbose output.')
+ parser.add_argument('-s', '--serial', default=os.getenv('ANDROID_SERIAL'),
+ help='Adb device serial number.')
+ parser.add_argument('-t', '--tags', help='Specify the filename from which '
+ 'event tags are read. Every line contains one event '
+ 'tag and the last event tag is used to detect that '
+ 'the device has finished booting.')
+ parser.add_argument('--apk-dir', help='Specify the directory which contains '
+ 'APK files to be installed before measuring boot time.')
+ return parser.parse_args()
+
+
+def install_apks(device, apk_dir):
+ for apk in glob.glob(os.path.join(apk_dir, '*.apk')):
+ print 'Installing: ' + apk
+ device.install(apk, replace=True)
+
+
+def main():
+ args = parse_args()
+ if args.verbose:
+ logging.getLogger().setLevel(logging.INFO)
+
+ device = adb.get_device(args.serial)
+
+ if not args.output:
+ device.wait()
+ args.output = 'perf-%s-%s.tsv' % (
+ device.get_prop('ro.build.flavor'),
+ device.get_prop('ro.build.version.incremental'))
+ check_dm_verity_settings(device)
+
+ if args.apk_dir:
+ install_apks(device, args.apk_dir)
+
+ record_list = []
+ event_tags = filter_event_tags(read_event_tags(args.tags), device)
+ init_perf(device, args.output, record_list, event_tags)
+ interval_adjuster = IntervalAdjuster(args.interval, device)
+ event_tags_re = make_event_tags_re(event_tags)
+ end_tag = event_tags[-1]
+ for i in range(args.iterations):
+ print 'Run #%d ' % i
+ record = do_iteration(
+ device, interval_adjuster, event_tags_re, end_tag)
+ record_list.append(record)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/init/readme.txt b/init/readme.txt
index 5a758d7..d70c6f3 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -377,6 +377,29 @@
bootanimation ends at: 33790 31230 (-2560)
+Systrace
+--------
+Systrace [1] can be used for obtaining performance analysis reports during boot
+time on userdebug or eng builds.
+Here is an example of trace events of "wm" and "am" categories:
+
+ $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py wm am --boot
+
+This command will cause the device to reboot. After the device is rebooted and
+the boot sequence has finished, the trace report is obtained from the device
+and written as trace.html on the host by hitting Ctrl+C.
+
+LIMITATION
+Recording trace events is started after persistent properties are loaded, so
+the trace events that are emitted before that are not recorded. Several
+services such as vold, surfaceflinger, and servicemanager are affected by this
+limitation since they are started before persistent properties are loaded.
+Zygote initialization and the processes that are forked from the zygote are not
+affected.
+
+[1] http://developer.android.com/tools/help/systrace.html
+
+
Debugging init
--------------
By default, programs executed by init will drop stdout and stderr into
diff --git a/init/service.cpp b/init/service.cpp
new file mode 100644
index 0000000..a370d25
--- /dev/null
+++ b/init/service.cpp
@@ -0,0 +1,805 @@
+/*
+ * Copyright (C) 2015 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 "service.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <selinux/selinux.h>
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <cutils/android_reboot.h>
+#include <cutils/sockets.h>
+
+#include "action.h"
+#include "init.h"
+#include "init_parser.h"
+#include "keywords.h"
+#include "log.h"
+#include "property_service.h"
+#include "util.h"
+
+#define CRITICAL_CRASH_THRESHOLD 4 // if we crash >4 times ...
+#define CRITICAL_CRASH_WINDOW (4*60) // ... in 4 minutes, goto recovery
+
+SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
+}
+
+SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& socketcon)
+ : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) {
+}
+
+ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
+}
+
+ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
+ const std::string& value)
+ : name(name), value(value) {
+}
+
+Service::Service(const std::string& name, const std::string& classname,
+ const std::vector<std::string>& args)
+ : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
+ time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), seclabel_(""),
+ ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+ onrestart_.InitSingleTrigger("onrestart");
+}
+
+Service::Service(const std::string& name, const std::string& classname,
+ unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
+ const std::string& seclabel, const std::vector<std::string>& args)
+ : name_(name), classname_(classname), flags_(flags), pid_(0), time_started_(0),
+ time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid), supp_gids_(supp_gids),
+ seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+ onrestart_.InitSingleTrigger("onrestart");
+}
+
+void Service::NotifyStateChange(const std::string& new_state) const {
+ if (!properties_initialized()) {
+ // If properties aren't available yet, we can't set them.
+ return;
+ }
+
+ if ((flags_ & SVC_EXEC) != 0) {
+ // 'exec' commands don't have properties tracking their state.
+ return;
+ }
+
+ std::string prop_name = android::base::StringPrintf("init.svc.%s", name_.c_str());
+ if (prop_name.length() >= PROP_NAME_MAX) {
+ // If the property name would be too long, we can't set it.
+ ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
+ name_.c_str(), new_state.c_str());
+ return;
+ }
+
+ property_set(prop_name.c_str(), new_state.c_str());
+}
+
+bool Service::Reap() {
+ if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
+ NOTICE("Service '%s' (pid %d) killing any children in process group\n",
+ name_.c_str(), pid_);
+ kill(-pid_, SIGKILL);
+ }
+
+ // Remove any sockets we may have created.
+ for (const auto& si : sockets_) {
+ std::string tmp = android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s",
+ si.name.c_str());
+ unlink(tmp.c_str());
+ }
+
+ if (flags_ & SVC_EXEC) {
+ INFO("SVC_EXEC pid %d finished...\n", pid_);
+ return true;
+ }
+
+ pid_ = 0;
+ flags_ &= (~SVC_RUNNING);
+
+ // Oneshot processes go into the disabled state on exit,
+ // except when manually restarted.
+ if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+ flags_ |= SVC_DISABLED;
+ }
+
+ // Disabled and reset processes do not get restarted automatically.
+ if (flags_ & (SVC_DISABLED | SVC_RESET)) {
+ NotifyStateChange("stopped");
+ return false;
+ }
+
+ time_t now = gettime();
+ if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
+ if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) {
+ if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) {
+ ERROR("critical process '%s' exited %d times in %d minutes; "
+ "rebooting into recovery mode\n", name_.c_str(),
+ CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ return false;
+ }
+ } else {
+ time_crashed_ = now;
+ nr_crashed_ = 1;
+ }
+ }
+
+ flags_ &= (~SVC_RESTART);
+ flags_ |= SVC_RESTARTING;
+
+ // Execute all onrestart commands for this service.
+ onrestart_.ExecuteAllCommands();
+
+ NotifyStateChange("restarting");
+ return false;
+}
+
+void Service::DumpState() const {
+ INFO("service %s\n", name_.c_str());
+ INFO(" class '%s'\n", classname_.c_str());
+ INFO(" exec");
+ for (const auto& s : args_) {
+ INFO(" '%s'", s.c_str());
+ }
+ INFO("\n");
+ for (const auto& si : sockets_) {
+ INFO(" socket %s %s 0%o\n", si.name.c_str(), si.type.c_str(), si.perm);
+ }
+}
+
+bool Service::HandleLine(int kw, const std::vector<std::string>& args, std::string* err) {
+ std::vector<std::string> str_args;
+
+ ioprio_class_ = IoSchedClass_NONE;
+
+ switch (kw) {
+ case K_class:
+ if (args.size() != 2) {
+ *err = "class option requires a classname\n";
+ return false;
+ } else {
+ classname_ = args[1];
+ }
+ break;
+ case K_console:
+ flags_ |= SVC_CONSOLE;
+ break;
+ case K_disabled:
+ flags_ |= SVC_DISABLED;
+ flags_ |= SVC_RC_DISABLED;
+ break;
+ case K_ioprio:
+ if (args.size() != 3) {
+ *err = "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n";
+ return false;
+ } else {
+ ioprio_pri_ = std::stoul(args[2], 0, 8);
+
+ if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
+ *err = "priority value must be range 0 - 7\n";
+ return false;
+ }
+
+ if (args[1] == "rt") {
+ ioprio_class_ = IoSchedClass_RT;
+ } else if (args[1] == "be") {
+ ioprio_class_ = IoSchedClass_BE;
+ } else if (args[1] == "idle") {
+ ioprio_class_ = IoSchedClass_IDLE;
+ } else {
+ *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>\n";
+ return false;
+ }
+ }
+ break;
+ case K_group:
+ if (args.size() < 2) {
+ *err = "group option requires a group id\n";
+ return false;
+ } else if (args.size() > NR_SVC_SUPP_GIDS + 2) {
+ *err = android::base::StringPrintf("group option accepts at most %d supp. groups\n",
+ NR_SVC_SUPP_GIDS);
+ return false;
+ } else {
+ gid_ = decode_uid(args[1].c_str());
+ for (std::size_t n = 2; n < args.size(); n++) {
+ supp_gids_.push_back(decode_uid(args[n].c_str()));
+ }
+ }
+ break;
+ case K_keycodes:
+ if (args.size() < 2) {
+ *err = "keycodes option requires atleast one keycode\n";
+ return false;
+ } else {
+ for (std::size_t i = 1; i < args.size(); i++) {
+ keycodes_.push_back(std::stoi(args[i]));
+ }
+ }
+ break;
+ case K_oneshot:
+ flags_ |= SVC_ONESHOT;
+ break;
+ case K_onrestart:
+ if (args.size() < 2) {
+ return false;
+ }
+ str_args.assign(args.begin() + 1, args.end());
+ add_command_to_action(&onrestart_, str_args, "", 0, err);
+ break;
+ case K_critical:
+ flags_ |= SVC_CRITICAL;
+ break;
+ case K_setenv: { /* name value */
+ if (args.size() < 3) {
+ *err = "setenv option requires name and value arguments\n";
+ return false;
+ }
+
+ envvars_.push_back({args[1], args[2]});
+ break;
+ }
+ case K_socket: {/* name type perm [ uid gid context ] */
+ if (args.size() < 4) {
+ *err = "socket option requires name, type, perm arguments\n";
+ return false;
+ }
+ if (args[2] != "dgram" && args[2] != "stream" &&
+ args[2] != "seqpacket") {
+ *err = "socket type must be 'dgram', 'stream' or 'seqpacket'\n";
+ return false;
+ }
+
+ int perm = std::stoul(args[3], 0, 8);
+ uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+ gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+ std::string socketcon = args.size() > 6 ? args[6] : "";
+
+ sockets_.push_back({args[1], args[2], uid, gid, perm, socketcon});
+ break;
+ }
+ case K_user:
+ if (args.size() != 2) {
+ *err = "user option requires a user id\n";
+ return false;
+ } else {
+ uid_ = decode_uid(args[1].c_str());
+ }
+ break;
+ case K_seclabel:
+ if (args.size() != 2) {
+ *err = "seclabel option requires a label string\n";
+ return false;
+ } else {
+ seclabel_ = args[1];
+ }
+ break;
+ case K_writepid:
+ if (args.size() < 2) {
+ *err = "writepid option requires at least one filename\n";
+ return false;
+ }
+ writepid_files_.assign(args.begin() + 1, args.end());
+ break;
+
+ default:
+ *err = android::base::StringPrintf("invalid option '%s'\n", args[0].c_str());
+ return false;
+ }
+ return true;
+}
+
+bool Service::Start(const std::vector<std::string>& dynamic_args) {
+ // Starting a service removes it from the disabled or reset state and
+ // immediately takes it out of the restarting state if it was in there.
+ flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
+ time_started_ = 0;
+
+ // Running processes require no additional work --- if they're in the
+ // process of exiting, we've ensured that they will immediately restart
+ // on exit, unless they are ONESHOT.
+ if (flags_ & SVC_RUNNING) {
+ return false;
+ }
+
+ bool needs_console = (flags_ & SVC_CONSOLE);
+ if (needs_console && !have_console) {
+ ERROR("service '%s' requires console\n", name_.c_str());
+ flags_ |= SVC_DISABLED;
+ return false;
+ }
+
+ struct stat sb;
+ if (stat(args_[0].c_str(), &sb) == -1) {
+ ERROR("cannot find '%s' (%s), disabling '%s'\n",
+ args_[0].c_str(), strerror(errno), name_.c_str());
+ flags_ |= SVC_DISABLED;
+ return false;
+ }
+
+ if ((!(flags_ & SVC_ONESHOT)) && !dynamic_args.empty()) {
+ ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
+ args_[0].c_str());
+ flags_ |= SVC_DISABLED;
+ return false;
+ }
+
+ std::string scon;
+ if (!seclabel_.empty()) {
+ scon = seclabel_;
+ } else {
+ char* mycon = nullptr;
+ char* fcon = nullptr;
+
+ INFO("computing context for service '%s'\n", args_[0].c_str());
+ int rc = getcon(&mycon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", name_.c_str());
+ return false;
+ }
+
+ rc = getfilecon(args_[0].c_str(), &fcon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", name_.c_str());
+ free(mycon);
+ return false;
+ }
+
+ char* ret_scon = nullptr;
+ rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
+ &ret_scon);
+ if (rc == 0) {
+ scon = ret_scon;
+ free(ret_scon);
+ }
+ if (rc == 0 && scon == mycon) {
+ ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str());
+ free(mycon);
+ free(fcon);
+ return false;
+ }
+ free(mycon);
+ free(fcon);
+ if (rc < 0) {
+ ERROR("could not get context while starting '%s'\n", name_.c_str());
+ return false;
+ }
+ }
+
+ NOTICE("Starting service '%s'...\n", name_.c_str());
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ int fd, sz;
+
+ umask(077);
+ if (properties_initialized()) {
+ get_property_workspace(&fd, &sz);
+ std::string tmp = android::base::StringPrintf("%d,%d", dup(fd), sz);
+ add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
+ }
+
+ for (const auto& ei : envvars_) {
+ add_environment(ei.name.c_str(), ei.value.c_str());
+ }
+
+ for (const auto& si : sockets_) {
+ int socket_type = ((si.type == "stream" ? SOCK_STREAM :
+ (si.type == "dgram" ? SOCK_DGRAM :
+ SOCK_SEQPACKET)));
+ const char* socketcon =
+ !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
+
+ int s = create_socket(si.name.c_str(), socket_type, si.perm,
+ si.uid, si.gid, socketcon);
+ if (s >= 0) {
+ PublishSocket(si.name, s);
+ }
+ }
+
+ std::string pid_str = android::base::StringPrintf("%d", pid);
+ for (const auto& file : writepid_files_) {
+ if (!android::base::WriteStringToFile(pid_str, file)) {
+ ERROR("couldn't write %s to %s: %s\n",
+ pid_str.c_str(), file.c_str(), strerror(errno));
+ }
+ }
+
+ if (ioprio_class_ != IoSchedClass_NONE) {
+ if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
+ ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
+ getpid(), ioprio_class_, ioprio_pri_, strerror(errno));
+ }
+ }
+
+ if (needs_console) {
+ setsid();
+ OpenConsole();
+ } else {
+ ZapStdio();
+ }
+
+ setpgid(0, getpid());
+
+ // As requested, set our gid, supplemental gids, and uid.
+ if (gid_) {
+ if (setgid(gid_) != 0) {
+ ERROR("setgid failed: %s\n", strerror(errno));
+ _exit(127);
+ }
+ }
+ if (!supp_gids_.empty()) {
+ if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
+ ERROR("setgroups failed: %s\n", strerror(errno));
+ _exit(127);
+ }
+ }
+ if (uid_) {
+ if (setuid(uid_) != 0) {
+ ERROR("setuid failed: %s\n", strerror(errno));
+ _exit(127);
+ }
+ }
+ if (!seclabel_.empty()) {
+ if (setexeccon(seclabel_.c_str()) < 0) {
+ ERROR("cannot setexeccon('%s'): %s\n",
+ seclabel_.c_str(), strerror(errno));
+ _exit(127);
+ }
+ }
+
+ std::vector<char*> strs;
+ for (const auto& s : args_) {
+ strs.push_back(const_cast<char*>(s.c_str()));
+ }
+ for (const auto& s : dynamic_args) {
+ strs.push_back(const_cast<char*>(s.c_str()));
+ }
+ strs.push_back(nullptr);
+ if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
+ ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
+ }
+
+ _exit(127);
+ }
+
+ if (pid < 0) {
+ ERROR("failed to start '%s'\n", name_.c_str());
+ pid_ = 0;
+ return false;
+ }
+
+ time_started_ = gettime();
+ pid_ = pid;
+ flags_ |= SVC_RUNNING;
+
+ if ((flags_ & SVC_EXEC) != 0) {
+ INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
+ pid_, uid_, gid_, supp_gids_.size(),
+ !seclabel_.empty() ? seclabel_.c_str() : "default");
+ }
+
+ NotifyStateChange("running");
+ return true;
+}
+
+bool Service::Start() {
+ const std::vector<std::string> null_dynamic_args;
+ return Start(null_dynamic_args);
+}
+
+bool Service::StartIfNotDisabled() {
+ if (!(flags_ & SVC_DISABLED)) {
+ return Start();
+ } else {
+ flags_ |= SVC_DISABLED_START;
+ }
+ return true;
+}
+
+bool Service::Enable() {
+ flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
+ if (flags_ & SVC_DISABLED_START) {
+ return Start();
+ }
+ return true;
+}
+
+void Service::Reset() {
+ StopOrReset(SVC_RESET);
+}
+
+void Service::Stop() {
+ StopOrReset(SVC_DISABLED);
+}
+
+void Service::Restart() {
+ if (flags_ & SVC_RUNNING) {
+ /* Stop, wait, then start the service. */
+ StopOrReset(SVC_RESTART);
+ } else if (!(flags_ & SVC_RESTARTING)) {
+ /* Just start the service since it's not running. */
+ Start();
+ } /* else: Service is restarting anyways. */
+}
+
+void Service::RestartIfNeeded(time_t& process_needs_restart) {
+ time_t next_start_time = time_started_ + 5;
+
+ if (next_start_time <= gettime()) {
+ flags_ &= (~SVC_RESTARTING);
+ Start();
+ return;
+ }
+
+ if ((next_start_time < process_needs_restart) ||
+ (process_needs_restart == 0)) {
+ process_needs_restart = next_start_time;
+ }
+}
+
+/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
+void Service::StopOrReset(int how) {
+ /* The service is still SVC_RUNNING until its process exits, but if it has
+ * already exited it shoudn't attempt a restart yet. */
+ flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
+
+ if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
+ /* Hrm, an illegal flag. Default to SVC_DISABLED */
+ how = SVC_DISABLED;
+ }
+ /* if the service has not yet started, prevent
+ * it from auto-starting with its class
+ */
+ if (how == SVC_RESET) {
+ flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
+ } else {
+ flags_ |= how;
+ }
+
+ if (pid_) {
+ NOTICE("Service '%s' is being killed...\n", name_.c_str());
+ kill(-pid_, SIGKILL);
+ NotifyStateChange("stopping");
+ } else {
+ NotifyStateChange("stopped");
+ }
+}
+
+void Service::ZapStdio() const {
+ int fd;
+ fd = open("/dev/null", O_RDWR);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+}
+
+void Service::OpenConsole() const {
+ int fd;
+ if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
+ fd = open("/dev/null", O_RDWR);
+ }
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+}
+
+void Service::PublishSocket(const std::string& name, int fd) const {
+ std::string key = android::base::StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s",
+ name.c_str());
+ std::string val = android::base::StringPrintf("%d", fd);
+ add_environment(key.c_str(), val.c_str());
+
+ /* make sure we don't close-on-exec */
+ fcntl(fd, F_SETFD, 0);
+}
+
+int ServiceManager::exec_count_ = 0;
+
+ServiceManager::ServiceManager() {
+}
+
+ServiceManager& ServiceManager::GetInstance() {
+ static ServiceManager instance;
+ return instance;
+}
+
+Service* ServiceManager::AddNewService(const std::string& name,
+ const std::string& classname,
+ const std::vector<std::string>& args,
+ std::string* err) {
+ if (!IsValidName(name)) {
+ *err = android::base::StringPrintf("invalid service name '%s'\n", name.c_str());
+ return nullptr;
+ }
+
+ Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
+ if (svc) {
+ *err = android::base::StringPrintf("ignored duplicate definition of service '%s'\n",
+ name.c_str());
+ return nullptr;
+ }
+
+ std::unique_ptr<Service> svc_p(new Service(name, classname, args));
+ if (!svc_p) {
+ ERROR("Couldn't allocate service for service '%s'", name.c_str());
+ return nullptr;
+ }
+ svc = svc_p.get();
+ services_.push_back(std::move(svc_p));
+
+ return svc;
+}
+
+Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
+ // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
+ // SECLABEL can be a - to denote default
+ std::size_t command_arg = 1;
+ for (std::size_t i = 1; i < args.size(); ++i) {
+ if (args[i] == "--") {
+ command_arg = i + 1;
+ break;
+ }
+ }
+ if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
+ ERROR("exec called with too many supplementary group ids\n");
+ return nullptr;
+ }
+
+ if (command_arg >= args.size()) {
+ ERROR("exec called without command\n");
+ return nullptr;
+ }
+ std::vector<std::string> str_args(args.begin() + command_arg, args.end());
+
+ exec_count_++;
+ std::string name = android::base::StringPrintf("exec %d (%s)", exec_count_,
+ str_args[0].c_str());
+ unsigned flags = SVC_EXEC | SVC_ONESHOT;
+
+ std::string seclabel = "";
+ if (command_arg > 2 && args[1] != "-") {
+ seclabel = args[1];
+ }
+ uid_t uid = 0;
+ if (command_arg > 3) {
+ uid = decode_uid(args[2].c_str());
+ }
+ gid_t gid = 0;
+ std::vector<gid_t> supp_gids;
+ if (command_arg > 4) {
+ gid = decode_uid(args[3].c_str());
+ std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
+ for (size_t i = 0; i < nr_supp_gids; ++i) {
+ supp_gids.push_back(decode_uid(args[4 + i].c_str()));
+ }
+ }
+
+ std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid,
+ supp_gids, seclabel, str_args));
+ if (!svc_p) {
+ ERROR("Couldn't allocate service for exec of '%s'",
+ str_args[0].c_str());
+ return nullptr;
+ }
+ Service* svc = svc_p.get();
+ services_.push_back(std::move(svc_p));
+
+ return svc;
+}
+
+Service* ServiceManager::FindServiceByName(const std::string& name) const {
+ auto svc = std::find_if(services_.begin(), services_.end(),
+ [&name] (const std::unique_ptr<Service>& s) {
+ return name == s->name();
+ });
+ if (svc != services_.end()) {
+ return svc->get();
+ }
+ return nullptr;
+}
+
+Service* ServiceManager::FindServiceByPid(pid_t pid) const {
+ auto svc = std::find_if(services_.begin(), services_.end(),
+ [&pid] (const std::unique_ptr<Service>& s) {
+ return s->pid() == pid;
+ });
+ if (svc != services_.end()) {
+ return svc->get();
+ }
+ return nullptr;
+}
+
+Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
+ auto svc = std::find_if(services_.begin(), services_.end(),
+ [&keychord_id] (const std::unique_ptr<Service>& s) {
+ return s->keychord_id() == keychord_id;
+ });
+
+ if (svc != services_.end()) {
+ return svc->get();
+ }
+ return nullptr;
+}
+
+void ServiceManager::ForEachService(void (*func)(Service* svc)) const {
+ for (const auto& s : services_) {
+ func(s.get());
+ }
+}
+
+void ServiceManager::ForEachServiceInClass(const std::string& classname,
+ void (*func)(Service* svc)) const {
+ for (const auto& s : services_) {
+ if (classname == s->classname()) {
+ func(s.get());
+ }
+ }
+}
+
+void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
+ void (*func)(Service* svc)) const {
+ for (const auto& s : services_) {
+ if (s->flags() & matchflags) {
+ func(s.get());
+ }
+ }
+}
+
+void ServiceManager::RemoveService(const Service& svc)
+{
+ auto svc_it = std::find_if(services_.begin(), services_.end(),
+ [&svc] (const std::unique_ptr<Service>& s) {
+ return svc.name() == s->name();
+ });
+ if (svc_it == services_.end()) {
+ return;
+ }
+
+ services_.erase(svc_it);
+}
+
+bool ServiceManager::IsValidName(const std::string& name) const
+{
+ if (name.size() > 16) {
+ return false;
+ }
+ for (const auto& c : name) {
+ if (!isalnum(c) && (c != '_') && (c != '-')) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ServiceManager::DumpState() const
+{
+ for (const auto& s : services_) {
+ s->DumpState();
+ }
+ INFO("\n");
+}
diff --git a/init/service.h b/init/service.h
new file mode 100644
index 0000000..1ecf78a
--- /dev/null
+++ b/init/service.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 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 _INIT_SERVICE_H
+#define _INIT_SERVICE_H
+
+#include <sys/types.h>
+
+#include <cutils/iosched_policy.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "action.h"
+
+#define SVC_DISABLED 0x001 // do not autostart with class
+#define SVC_ONESHOT 0x002 // do not restart on exit
+#define SVC_RUNNING 0x004 // currently active
+#define SVC_RESTARTING 0x008 // waiting to restart
+#define SVC_CONSOLE 0x010 // requires console
+#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
+#define SVC_RESET 0x040 // Use when stopping a process,
+ // but not disabling so it can be restarted with its class.
+#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
+#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service.
+#define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time.
+#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'.
+
+#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
+
+class Action;
+class ServiceManager;
+
+struct SocketInfo {
+ SocketInfo();
+ SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& socketcon);
+ std::string name;
+ std::string type;
+ uid_t uid;
+ gid_t gid;
+ int perm;
+ std::string socketcon;
+};
+
+struct ServiceEnvironmentInfo {
+ ServiceEnvironmentInfo();
+ ServiceEnvironmentInfo(const std::string& name, const std::string& value);
+ std::string name;
+ std::string value;
+};
+
+class Service {
+public:
+ Service(const std::string& name, const std::string& classname,
+ const std::vector<std::string>& args);
+
+ Service(const std::string& name, const std::string& classname,
+ unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
+ const std::string& seclabel, const std::vector<std::string>& args);
+
+ bool HandleLine(int kw, const std::vector<std::string>& args, std::string* err);
+ bool Start(const std::vector<std::string>& dynamic_args);
+ bool Start();
+ bool StartIfNotDisabled();
+ bool Enable();
+ void Reset();
+ void Stop();
+ void Restart();
+ void RestartIfNeeded(time_t& process_needs_restart);
+ bool Reap();
+ void DumpState() const;
+
+ const std::string& name() const { return name_; }
+ const std::string& classname() const { return classname_; }
+ unsigned flags() const { return flags_; }
+ pid_t pid() const { return pid_; }
+ uid_t uid() const { return uid_; }
+ gid_t gid() const { return gid_; }
+ const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
+ const std::string& seclabel() const { return seclabel_; }
+ const std::vector<int>& keycodes() const { return keycodes_; }
+ int keychord_id() const { return keychord_id_; }
+ void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
+ const std::vector<std::string>& args() const { return args_; }
+
+private:
+ void NotifyStateChange(const std::string& new_state) const;
+ void StopOrReset(int how);
+ void ZapStdio() const;
+ void OpenConsole() const;
+ void PublishSocket(const std::string& name, int fd) const;
+
+ std::string name_;
+ std::string classname_;
+
+ unsigned flags_;
+ pid_t pid_;
+ time_t time_started_; // time of last start
+ time_t time_crashed_; // first crash within inspection window
+ int nr_crashed_; // number of times crashed within window
+
+ uid_t uid_;
+ gid_t gid_;
+ std::vector<gid_t> supp_gids_;
+
+ std::string seclabel_;
+
+ std::vector<SocketInfo> sockets_;
+ std::vector<ServiceEnvironmentInfo> envvars_;
+
+ Action onrestart_; // Commands to execute on restart.
+
+ std::vector<std::string> writepid_files_;
+
+ // keycodes for triggering this service via /dev/keychord
+ std::vector<int> keycodes_;
+ int keychord_id_;
+
+ IoSchedClass ioprio_class_;
+ int ioprio_pri_;
+
+ std::vector<std::string> args_;
+};
+
+class ServiceManager {
+public:
+ static ServiceManager& GetInstance();
+
+ Service* AddNewService(const std::string& name, const std::string& classname,
+ const std::vector<std::string>& args,
+ std::string* err);
+ Service* MakeExecOneshotService(const std::vector<std::string>& args);
+ Service* FindServiceByName(const std::string& name) const;
+ Service* FindServiceByPid(pid_t pid) const;
+ Service* FindServiceByKeychord(int keychord_id) const;
+ void ForEachService(void (*func)(Service* svc)) const;
+ void ForEachServiceInClass(const std::string& classname,
+ void (*func)(Service* svc)) const;
+ void ForEachServiceWithFlags(unsigned matchflags,
+ void (*func)(Service* svc)) const;
+ void RemoveService(const Service& svc);
+ void DumpState() const;
+private:
+ ServiceManager();
+
+ bool IsValidName(const std::string& name) const;
+
+ static int exec_count_; // Every service needs a unique name.
+ std::vector<std::unique_ptr<Service>> services_;
+};
+
+#endif
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 6893163..867abbc 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -28,13 +28,12 @@
#include <cutils/list.h>
#include <cutils/sockets.h>
+#include "action.h"
#include "init.h"
#include "log.h"
+#include "service.h"
#include "util.h"
-#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */
-#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery */
-
static int signal_write_fd = -1;
static int signal_read_fd = -1;
@@ -60,11 +59,11 @@
return false;
}
- service* svc = service_find_by_pid(pid);
+ Service* svc = ServiceManager::GetInstance().FindServiceByPid(pid);
std::string name;
if (svc) {
- name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
+ name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
} else {
name = android::base::StringPrintf("Untracked pid %d", pid);
}
@@ -75,77 +74,11 @@
return true;
}
- // TODO: all the code from here down should be a member function on service.
-
- if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
- NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
- kill(-pid, SIGKILL);
- }
-
- // Remove any sockets we may have created.
- for (socketinfo* si = svc->sockets; si; si = si->next) {
- char tmp[128];
- snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
- unlink(tmp);
- }
-
- if (svc->flags & SVC_EXEC) {
- INFO("SVC_EXEC pid %d finished...\n", svc->pid);
+ if (svc->Reap()) {
waiting_for_exec = false;
- list_remove(&svc->slist);
- free(svc->name);
- free(svc);
- return true;
+ ServiceManager::GetInstance().RemoveService(*svc);
}
- svc->pid = 0;
- svc->flags &= (~SVC_RUNNING);
-
- // Oneshot processes go into the disabled state on exit,
- // except when manually restarted.
- if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
- svc->flags |= SVC_DISABLED;
- }
-
- // Disabled and reset processes do not get restarted automatically.
- if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
- svc->NotifyStateChange("stopped");
- return true;
- }
-
- time_t now = gettime();
- if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
- if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
- if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
- ERROR("critical process '%s' exited %d times in %d minutes; "
- "rebooting into recovery mode\n", svc->name,
- CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- return true;
- }
- } else {
- svc->time_crashed = now;
- svc->nr_crashed = 1;
- }
- }
-
- svc->flags &= (~SVC_RESTART);
- svc->flags |= SVC_RESTARTING;
-
- // Execute all onrestart commands for this service.
- struct listnode* node;
- list_for_each(node, &svc->onrestart.commands) {
- command* cmd = node_to_item(node, struct command, clist);
- std::vector<std::string> arg_strs;
- if (expand_command_arguments(cmd->nargs, cmd->args, &arg_strs)) {
- std::vector<char*> args;
- for (auto& s : arg_strs) {
- args.push_back(&s[0]);
- }
- cmd->func(args.size(), &args[0]);
- }
- }
- svc->NotifyStateChange("restarting");
return true;
}
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
index 8946d3c..71bc94b 100644
--- a/libcutils/iosched_policy.c
+++ b/libcutils/iosched_policy.c
@@ -23,7 +23,7 @@
#include <cutils/iosched_policy.h>
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
#include <linux/ioprio.h>
#include <sys/syscall.h>
#define __android_unused
@@ -32,7 +32,7 @@
#endif
int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) {
return -1;
}
@@ -41,7 +41,7 @@
}
int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
int rc;
if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) {
diff --git a/libcutils/process_name.c b/libcutils/process_name.c
index cc931eb..5d28b6f 100644
--- a/libcutils/process_name.c
+++ b/libcutils/process_name.c
@@ -25,19 +25,19 @@
#include <unistd.h>
#include <cutils/process_name.h>
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
#include <cutils/properties.h>
#endif
#define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
static const char* process_name = "unknown";
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
static int running_in_emulator = -1;
#endif
void set_process_name(const char* new_name) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
char propBuf[PROPERTY_VALUE_MAX];
#endif
@@ -59,7 +59,7 @@
}
#endif
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
// If we know we are not running in the emulator, then return.
if (running_in_emulator == 0) {
return;
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c
index 6994904..2bc4226 100644
--- a/libcutils/record_stream.c
+++ b/libcutils/record_stream.c
@@ -22,7 +22,7 @@
#include <cutils/record_stream.h>
#include <string.h>
#include <stdint.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
#include <winsock2.h> /* for ntohl */
#else
#include <netinet/in.h>
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index dfc8777..103ff66 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -37,7 +37,7 @@
return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
}
-#if defined(HAVE_ANDROID_OS)
+#if defined(__ANDROID__)
#include <pthread.h>
#include <sched.h>
@@ -142,7 +142,7 @@
*/
static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
{
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
char pathBuf[32];
char lineBuf[256];
FILE *fp;
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
index 6c849de..7f0ccb8 100644
--- a/libcutils/socket_inaddr_any_server.c
+++ b/libcutils/socket_inaddr_any_server.c
@@ -20,7 +20,7 @@
#include <string.h>
#include <unistd.h>
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c
index 7b42daa..2526146 100644
--- a/libcutils/socket_local_client.c
+++ b/libcutils/socket_local_client.c
@@ -22,7 +22,7 @@
#include <cutils/sockets.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
int socket_local_client(const char *name, int namespaceId, int type)
{
@@ -30,7 +30,7 @@
return -1;
}
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
#include <sys/socket.h>
#include <sys/un.h>
@@ -165,4 +165,4 @@
return s;
}
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
index 60eb86b..c9acdad 100644
--- a/libcutils/socket_local_server.c
+++ b/libcutils/socket_local_server.c
@@ -23,7 +23,7 @@
#include <errno.h>
#include <stddef.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
int socket_local_server(const char *name, int namespaceId, int type)
{
@@ -31,7 +31,7 @@
return -1;
}
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
#include <sys/socket.h>
#include <sys/un.h>
@@ -123,4 +123,4 @@
return s;
}
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c
index 9aed7b7..e14cffb 100644
--- a/libcutils/socket_loopback_client.c
+++ b/libcutils/socket_loopback_client.c
@@ -20,7 +20,7 @@
#include <string.h>
#include <unistd.h>
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c
index 71afce7..b600e34 100644
--- a/libcutils/socket_loopback_server.c
+++ b/libcutils/socket_loopback_server.c
@@ -22,7 +22,7 @@
#define LISTEN_BACKLOG 4
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
diff --git a/libcutils/sockets.c b/libcutils/sockets.c
index 15ede2b..d438782 100644
--- a/libcutils/sockets.c
+++ b/libcutils/sockets.c
@@ -17,7 +17,7 @@
#include <cutils/sockets.h>
#include <log/log.h>
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
/* For the socket trust (credentials) check */
#include <private/android_filesystem_config.h>
#define __android_unused
@@ -27,7 +27,7 @@
bool socket_peer_is_trusted(int fd __android_unused)
{
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
struct ucred cr;
socklen_t len = sizeof(cr);
int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 9c4af30..cfc8a7a 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -37,7 +37,7 @@
/* branchless on many architectures. */
#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-#if (defined(USE_MINGW) || defined(HAVE_WINSOCK))
+#if defined(_WIN32)
#define WEAK static
#else
#define WEAK __attribute__((weak))
@@ -48,7 +48,7 @@
/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
int WEAK socket_local_client(const char *name, int namespaceId, int type)
{
@@ -56,7 +56,7 @@
return -ENOSYS;
}
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
#include <sys/socket.h>
#include <sys/un.h>
@@ -193,7 +193,7 @@
return s;
}
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
/* End of ../libcutils/socket_local_client.c */
#define logger_for_each(logger, logger_list) \
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index a9671a9..32a65ea 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -295,13 +295,13 @@
// so we save the extra file existence check.
char cpuinfo_path[1024];
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
#ifdef __LP64__
"64"
#endif // __LP64__
"/%s/cpuinfo", instruction_set);
-#else // !HAVE_ANDROID_OS
+#else // !__ANDROID__
// To be able to test on the host, we hardwire a relative path.
snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
#endif
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
index cec26ce..d3bbebe 100644
--- a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -32,8 +32,8 @@
TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
-#ifndef __APPLE__ // Mac OS does not support bind-mount.
-#ifndef HAVE_ANDROID_OS // Cannot write into the hard-wired location.
+#if !defined(__APPLE__) // Mac OS does not support bind-mount.
+#if !defined(__ANDROID__) // Cannot write into the hard-wired location.
// Try to create our mount namespace.
if (unshare(CLONE_NEWNS) != -1) {
// Create a dummy file.
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 7d2a5fb..956ed30 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -50,7 +50,7 @@
#define ALOGW printf
#endif
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
/* SIOCKILLADDR is an Android extension. */
#define SIOCKILLADDR 0x8939
#endif
@@ -596,7 +596,7 @@
int ifc_reset_connections(const char *ifname, const int reset_mask)
{
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
int result, success;
in_addr_t myaddr = 0;
struct ifreq ifr;
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 3323b82..28be60f 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -16,6 +16,7 @@
#include <utils/String8.h>
+#include <utils/Compat.h>
#include <utils/Log.h>
#include <utils/Unicode.h>
#include <utils/SharedBuffer.h>
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 64204a8..1fca2b2 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -19,7 +19,7 @@
* System clock functions.
*/
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
#include <linux/ioctl.h>
#include <linux/rtc.h>
#include <utils/Atomic.h>
@@ -107,7 +107,7 @@
*/
int64_t elapsedRealtimeNano()
{
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
struct timespec ts;
int result;
int64_t timestamp;
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index c3666e4..6dda6b5 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -45,7 +45,7 @@
#include <cutils/sched_policy.h>
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
# define __android_unused
#else
# define __android_unused __attribute__((__unused__))
@@ -132,7 +132,7 @@
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */
+#if defined(__ANDROID__) /* valgrind is rejecting RT-priority create reqs */
if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
// Now that the pthread_t has a method to find the associated
// android_thread_id_t (pid) from pthread_t, it would be possible to avoid
@@ -176,7 +176,7 @@
return 1;
}
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread)
{
return (pthread_t) thread;
@@ -302,12 +302,10 @@
gCreateThreadFn = func;
}
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
int androidSetThreadPriority(pid_t tid, int pri)
{
int rc = 0;
-
-#if !defined(_WIN32)
int lasterr = 0;
if (pri >= ANDROID_PRIORITY_BACKGROUND) {
@@ -325,17 +323,12 @@
} else {
errno = lasterr;
}
-#endif
return rc;
}
int androidGetThreadPriority(pid_t tid) {
-#if !defined(_WIN32)
return getpriority(PRIO_PROCESS, tid);
-#else
- return ANDROID_PRIORITY_NORMAL;
-#endif
}
#endif
@@ -658,7 +651,7 @@
mLock("Thread::mLock"),
mStatus(NO_ERROR),
mExitPending(false), mRunning(false)
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
, mTid(-1)
#endif
{
@@ -728,7 +721,7 @@
wp<Thread> weak(strong);
self->mHoldSelf.clear();
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
// this is very useful for debugging with gdb
self->mTid = gettid();
#endif
@@ -839,7 +832,7 @@
return mRunning;
}
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
pid_t Thread::getTid() const
{
// mTid is not defined until the child initializes it, and the caller may need it earlier
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index fb70e15..201bc41 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -23,7 +23,7 @@
#include <sys/time.h>
#include <time.h>
-#if defined(HAVE_ANDROID_OS)
+#if defined(__ANDROID__)
nsecs_t systemTime(int clock)
{
static const clockid_t clocks[] = {
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index fb876c9..6f4b721 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -18,7 +18,7 @@
#include <stddef.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
# undef nhtol
# undef htonl
# undef nhtos
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 0f5071b..b9e8973 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -218,18 +218,25 @@
}
// If we're using more than 256K of memory for log entries, prune
-// at least 10% of the log entries.
+// at least 10% of the log entries. For sizes above 1M, prune at
+// least 1% of the log entries.
//
// mLogElementsLock must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
size_t sizes = stats.sizes(id);
- if (sizes > log_buffer_size(id)) {
- size_t sizeOver90Percent = sizes - ((log_buffer_size(id) * 9) / 10);
- size_t elements = stats.elements(id);
- unsigned long pruneRows = elements * sizeOver90Percent / sizes;
- elements /= 10;
- if (pruneRows <= elements) {
- pruneRows = elements;
+ unsigned long maxSize = log_buffer_size(id);
+ if (sizes > maxSize) {
+ size_t sizeOver, minElements, elements = stats.elements(id);
+ if (maxSize > (4 * LOG_BUFFER_SIZE)) {
+ sizeOver = sizes - ((maxSize * 99) / 100);
+ minElements = elements / 100;
+ } else {
+ sizeOver = sizes - ((maxSize * 9) / 10);
+ minElements = elements / 10;
+ }
+ unsigned long pruneRows = elements * sizeOver / sizes;
+ if (pruneRows <= minElements) {
+ pruneRows = minElements;
}
prune(id, pruneRows);
}
diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc
deleted file mode 100644
index ea4a38c..0000000
--- a/metrics/uploader/system_profile_cache.cc
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "metrics/uploader/system_profile_cache.h"
-
-#include <string>
-#include <vector>
-
-#include "base/files/file_util.h"
-#include "base/guid.h"
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/sys_info.h"
-#include "metrics/persistent_integer.h"
-#include "metrics/uploader/metrics_log_base.h"
-#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
-#include "vboot/crossystem.h"
-
-namespace {
-
-const char kPersistentGUIDFile[] = "/var/lib/metrics/Sysinfo.GUID";
-const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId";
-const char kProductIdFieldName[] = "GOOGLE_METRICS_PRODUCT_ID";
-
-} // namespace
-
-std::string ChannelToString(
- const metrics::SystemProfileProto_Channel& channel) {
- switch (channel) {
- case metrics::SystemProfileProto::CHANNEL_STABLE:
- return "STABLE";
- case metrics::SystemProfileProto::CHANNEL_DEV:
- return "DEV";
- case metrics::SystemProfileProto::CHANNEL_BETA:
- return "BETA";
- case metrics::SystemProfileProto::CHANNEL_CANARY:
- return "CANARY";
- default:
- return "UNKNOWN";
- }
-}
-
-SystemProfileCache::SystemProfileCache()
- : initialized_(false),
- testing_(false),
- config_root_("/"),
- session_id_(new chromeos_metrics::PersistentInteger(
- kPersistentSessionIdFilename)) {
-}
-
-SystemProfileCache::SystemProfileCache(bool testing,
- const std::string& config_root)
- : initialized_(false),
- testing_(testing),
- config_root_(config_root),
- session_id_(new chromeos_metrics::PersistentInteger(
- kPersistentSessionIdFilename)) {
-}
-
-bool SystemProfileCache::Initialize() {
- CHECK(!initialized_)
- << "this should be called only once in the metrics_daemon lifetime.";
-
- std::string chromeos_version;
- std::string board;
- std::string build_type;
- if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_NAME",
- &profile_.os_name) ||
- !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION",
- &profile_.os_version) ||
- !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD", &board) ||
- !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE",
- &build_type) ||
- !GetChromeOSVersion(&chromeos_version) ||
- !GetHardwareId(&profile_.hardware_class)) {
- DLOG(ERROR) << "failing to initialize profile cache";
- return false;
- }
-
- std::string channel_string;
- base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &channel_string);
- profile_.channel = ProtoChannelFromString(channel_string);
-
- profile_.app_version = chromeos_version + " (" + build_type + ")" +
- ChannelToString(profile_.channel) + " " + board;
-
- // If the product id is not defined, use the default one from the protobuf.
- profile_.product_id = metrics::ChromeUserMetricsExtension::CHROME;
- if (GetProductId(&profile_.product_id)) {
- DLOG(INFO) << "Set the product id to " << profile_.product_id;
- }
-
- profile_.client_id =
- testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile);
-
- // Increment the session_id everytime we initialize this. If metrics_daemon
- // does not crash, this should correspond to the number of reboots of the
- // system.
- // TODO(bsimonnet): Change this to map to the number of time system-services
- // is started.
- session_id_->Add(1);
- profile_.session_id = static_cast<int32_t>(session_id_->Get());
-
- initialized_ = true;
- return initialized_;
-}
-
-bool SystemProfileCache::InitializeOrCheck() {
- return initialized_ || Initialize();
-}
-
-void SystemProfileCache::Populate(
- metrics::ChromeUserMetricsExtension* metrics_proto) {
- CHECK(metrics_proto);
- CHECK(InitializeOrCheck())
- << "failed to initialize system information.";
-
- // The client id is hashed before being sent.
- metrics_proto->set_client_id(
- metrics::MetricsLogBase::Hash(profile_.client_id));
- metrics_proto->set_session_id(profile_.session_id);
-
- // Sets the product id.
- metrics_proto->set_product(profile_.product_id);
-
- metrics::SystemProfileProto* profile_proto =
- metrics_proto->mutable_system_profile();
- profile_proto->mutable_hardware()->set_hardware_class(
- profile_.hardware_class);
- profile_proto->set_app_version(profile_.app_version);
- profile_proto->set_channel(profile_.channel);
-
- metrics::SystemProfileProto_OS* os = profile_proto->mutable_os();
- os->set_name(profile_.os_name);
- os->set_version(profile_.os_version);
-}
-
-std::string SystemProfileCache::GetPersistentGUID(const std::string& filename) {
- std::string guid;
- base::FilePath filepath(filename);
- if (!base::ReadFileToString(filepath, &guid)) {
- guid = base::GenerateGUID();
- // If we can't read or write the file, the guid will not be preserved during
- // the next reboot. Crash.
- CHECK(base::WriteFile(filepath, guid.c_str(), guid.size()));
- }
- return guid;
-}
-
-bool SystemProfileCache::GetChromeOSVersion(std::string* version) {
- if (testing_) {
- *version = "0.0.0.0";
- return true;
- }
-
- std::string milestone, build, branch, patch;
- unsigned tmp;
- if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_CHROME_MILESTONE",
- &milestone) &&
- base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_NUMBER",
- &build) &&
- base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BRANCH_NUMBER",
- &branch) &&
- base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_PATCH_NUMBER",
- &patch)) {
- // Convert to uint to ensure those fields are positive numbers.
- if (base::StringToUint(milestone, &tmp) &&
- base::StringToUint(build, &tmp) &&
- base::StringToUint(branch, &tmp) &&
- base::StringToUint(patch, &tmp)) {
- std::vector<std::string> parts = {milestone, build, branch, patch};
- *version = JoinString(parts, '.');
- return true;
- }
- DLOG(INFO) << "The milestone, build, branch or patch is not a positive "
- << "number.";
- return false;
- }
- DLOG(INFO) << "Field missing from /etc/lsb-release";
- return false;
-}
-
-bool SystemProfileCache::GetHardwareId(std::string* hwid) {
- CHECK(hwid);
-
- if (testing_) {
- // if we are in test mode, we do not call crossystem directly.
- DLOG(INFO) << "skipping hardware id";
- *hwid = "";
- return true;
- }
-
- char buffer[128];
- if (buffer != VbGetSystemPropertyString("hwid", buffer, sizeof(buffer))) {
- LOG(ERROR) << "error getting hwid";
- return false;
- }
-
- *hwid = std::string(buffer);
- return true;
-}
-
-bool SystemProfileCache::GetProductId(int* product_id) const {
- chromeos::OsReleaseReader reader;
- if (testing_) {
- base::FilePath root(config_root_);
- reader.LoadTestingOnly(root);
- } else {
- reader.Load();
- }
-
- std::string id;
- if (reader.GetString(kProductIdFieldName, &id)) {
- CHECK(base::StringToInt(id, product_id)) << "Failed to convert product_id "
- << id << " to int.";
- return true;
- }
- return false;
-}
-
-metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
- const std::string& channel) {
-
- if (channel == "stable-channel") {
- return metrics::SystemProfileProto::CHANNEL_STABLE;
- } else if (channel == "dev-channel") {
- return metrics::SystemProfileProto::CHANNEL_DEV;
- } else if (channel == "beta-channel") {
- return metrics::SystemProfileProto::CHANNEL_BETA;
- } else if (channel == "canary-channel") {
- return metrics::SystemProfileProto::CHANNEL_CANARY;
- }
-
- DLOG(INFO) << "unknown channel: " << channel;
- return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
-}
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
new file mode 100644
index 0000000..9edba6e
--- /dev/null
+++ b/metricsd/Android.mk
@@ -0,0 +1,116 @@
+# Copyright (C) 2015 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.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(HOST_OS),linux)
+
+metrics_cpp_extension := .cc
+libmetrics_sources := \
+ c_metrics_library.cc \
+ metrics_library.cc \
+ serialization/metric_sample.cc \
+ serialization/serialization_utils.cc
+
+metrics_client_sources := \
+ metrics_client.cc
+
+metrics_daemon_sources := \
+ metrics_daemon.cc \
+ metrics_daemon_main.cc \
+ persistent_integer.cc \
+ uploader/metrics_hashes.cc \
+ uploader/metrics_log_base.cc \
+ uploader/metrics_log.cc \
+ uploader/sender_http.cc \
+ uploader/system_profile_cache.cc \
+ uploader/upload_service.cc \
+ serialization/metric_sample.cc \
+ serialization/serialization_utils.cc
+
+metrics_CFLAGS := -Wall \
+ -D__BRILLO__ \
+ -Wno-char-subscripts \
+ -Wno-missing-field-initializers \
+ -Wno-unused-function \
+ -Wno-unused-parameter \
+ -Werror \
+ -fvisibility=default
+metrics_CPPFLAGS := -Wno-non-virtual-dtor \
+ -Wno-sign-promo \
+ -Wno-strict-aliasing \
+ -fvisibility=default
+metrics_includes := external/gtest/include \
+ $(LOCAL_PATH)/include
+metrics_shared_libraries := libchrome libchromeos
+
+# Shared library for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmetrics
+LOCAL_C_INCLUDES := $(metrics_includes)
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries)
+LOCAL_SRC_FILES := $(libmetrics_sources)
+include $(BUILD_SHARED_LIBRARY)
+
+# CLI client for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_client
+LOCAL_C_INCLUDES := $(metrics_includes)
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+ libmetrics
+LOCAL_SRC_FILES := $(metrics_client_sources)
+include $(BUILD_EXECUTABLE)
+
+# Protobuf library for metrics_daemon.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_daemon_protos
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+generated_sources_dir := $(call local-generated-sources-dir)
+LOCAL_EXPORT_C_INCLUDE_DIRS += \
+ $(generated_sources_dir)/proto/system/core/metricsd
+LOCAL_SRC_FILES := $(call all-proto-files-under,uploader/proto)
+LOCAL_STATIC_LIBRARIES := libprotobuf-cpp-lite
+include $(BUILD_STATIC_LIBRARY)
+
+# metrics daemon.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_daemon
+LOCAL_C_INCLUDES := $(metrics_includes) \
+ external/libchromeos
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+ libmetrics \
+ libprotobuf-cpp-lite \
+ libchromeos-http \
+ libchromeos-dbus \
+ libdbus
+LOCAL_SRC_FILES := $(metrics_daemon_sources)
+LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
+include $(BUILD_EXECUTABLE)
+
+endif # HOST_OS == linux
diff --git a/metricsd/MODULE_LICENSE_BSD b/metricsd/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/metricsd/MODULE_LICENSE_BSD
diff --git a/metricsd/NOTICE b/metricsd/NOTICE
new file mode 100644
index 0000000..b9e779f
--- /dev/null
+++ b/metricsd/NOTICE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium OS Authors. 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.
diff --git a/metrics/OWNERS b/metricsd/OWNERS
similarity index 100%
rename from metrics/OWNERS
rename to metricsd/OWNERS
diff --git a/metrics/README b/metricsd/README
similarity index 100%
rename from metrics/README
rename to metricsd/README
diff --git a/metrics/WATCHLISTS b/metricsd/WATCHLISTS
similarity index 100%
rename from metrics/WATCHLISTS
rename to metricsd/WATCHLISTS
diff --git a/metrics/c_metrics_library.cc b/metricsd/c_metrics_library.cc
similarity index 100%
rename from metrics/c_metrics_library.cc
rename to metricsd/c_metrics_library.cc
diff --git a/metricsd/constants.h b/metricsd/constants.h
new file mode 100644
index 0000000..d65e0e0
--- /dev/null
+++ b/metricsd/constants.h
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2015 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 METRICS_CONSTANTS_H_
+#define METRICS_CONSTANTS_H_
+
+namespace metrics {
+static const char kMetricsDirectory[] = "/data/misc/metrics/";
+static const char kMetricsEventsFilePath[] = "/data/misc/metrics/uma-events";
+static const char kMetricsGUIDFilePath[] = "/data/misc/metrics/Sysinfo.GUID";
+static const char kMetricsServer[] = "http://clients4.google.com/uma/v2";
+static const char kConsentFilePath[] = "/data/misc/metrics/enabled";
+static const char kDefaultVersion[] = "0.0.0.0";
+} // namespace metrics
+
+#endif // METRICS_CONSTANTS_H_
diff --git a/metrics/c_metrics_library.h b/metricsd/include/metrics/c_metrics_library.h
similarity index 100%
rename from metrics/c_metrics_library.h
rename to metricsd/include/metrics/c_metrics_library.h
diff --git a/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
similarity index 93%
rename from metrics/metrics_library.h
rename to metricsd/include/metrics/metrics_library.h
index a90f3e6..1c124d2 100644
--- a/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -14,8 +14,6 @@
#include <base/memory/scoped_ptr.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "policy/libpolicy.h"
-
class MetricsLibraryInterface {
public:
virtual void Init() = 0;
@@ -28,7 +26,7 @@
virtual ~MetricsLibraryInterface() {}
};
-// Library used to send metrics to both Autotest and Chrome/UMA.
+// Library used to send metrics to Chrome/UMA.
class MetricsLibrary : public MetricsLibraryInterface {
public:
MetricsLibrary();
@@ -109,9 +107,6 @@
// number in the histograms dashboard).
bool SendCrosEventToUMA(const std::string& event);
- // Sends to Autotest and returns true on success.
- static bool SendToAutotest(const std::string& name, int value);
-
private:
friend class CMetricsLibraryTest;
friend class MetricsLibraryTest;
@@ -130,9 +125,6 @@
char* buffer, int buffer_size,
bool* result);
- // This function is used by tests only to mock the device policies.
- void SetPolicyProvider(policy::PolicyProvider* provider);
-
// Time at which we last checked if metrics were enabled.
static time_t cached_enabled_time_;
@@ -142,8 +134,6 @@
std::string uma_events_file_;
std::string consent_file_;
- scoped_ptr<policy::PolicyProvider> policy_provider_;
-
DISALLOW_COPY_AND_ASSIGN(MetricsLibrary);
};
diff --git a/metrics/init/metrics_daemon.conf b/metricsd/init/metrics_daemon.conf
similarity index 100%
rename from metrics/init/metrics_daemon.conf
rename to metricsd/init/metrics_daemon.conf
diff --git a/metrics/init/metrics_library.conf b/metricsd/init/metrics_library.conf
similarity index 100%
rename from metrics/init/metrics_library.conf
rename to metricsd/init/metrics_library.conf
diff --git a/metrics/libmetrics-334380.gyp b/metricsd/libmetrics-334380.gyp
similarity index 100%
rename from metrics/libmetrics-334380.gyp
rename to metricsd/libmetrics-334380.gyp
diff --git a/metrics/libmetrics.gypi b/metricsd/libmetrics.gypi
similarity index 100%
rename from metrics/libmetrics.gypi
rename to metricsd/libmetrics.gypi
diff --git a/metrics/libmetrics.pc.in b/metricsd/libmetrics.pc.in
similarity index 100%
rename from metrics/libmetrics.pc.in
rename to metricsd/libmetrics.pc.in
diff --git a/metrics/make_tests.sh b/metricsd/make_tests.sh
similarity index 100%
rename from metrics/make_tests.sh
rename to metricsd/make_tests.sh
diff --git a/metrics/metrics.gyp b/metricsd/metrics.gyp
similarity index 100%
rename from metrics/metrics.gyp
rename to metricsd/metrics.gyp
diff --git a/metrics/metrics_client.cc b/metricsd/metrics_client.cc
similarity index 73%
rename from metrics/metrics_client.cc
rename to metricsd/metrics_client.cc
index bbe9dcd..b587e3a 100644
--- a/metrics/metrics_client.cc
+++ b/metricsd/metrics_client.cc
@@ -19,17 +19,15 @@
void ShowUsage() {
fprintf(stderr,
- "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n"
- " metrics_client [-ab] -e name sample max\n"
- " metrics_client [-ab] -s name sample\n"
- " metrics_client [-ab] -v event\n"
+ "Usage: metrics_client [-t] name sample min max nbuckets\n"
+ " metrics_client -e name sample max\n"
+ " metrics_client -s name sample\n"
+ " metrics_client -v event\n"
" metrics_client -u action\n"
" metrics_client [-cg]\n"
"\n"
- " default: send metric with integer values to Chrome only\n"
+ " default: send metric with integer values \n"
" |min| > 0, |min| <= sample < |max|\n"
- " -a: send metric (name/sample) to Autotest only\n"
- " -b: send metric to both Chrome and Autotest\n"
" -c: return exit status 0 if user consents to stats, 1 otherwise,\n"
" in guest mode always return 1\n"
" -e: send linear/enumeration histogram data\n"
@@ -64,9 +62,7 @@
static int SendStats(char* argv[],
int name_index,
enum Mode mode,
- bool secs_to_msecs,
- bool send_to_autotest,
- bool send_to_chrome) {
+ bool secs_to_msecs) {
const char* name = argv[name_index];
int sample;
if (secs_to_msecs) {
@@ -75,25 +71,18 @@
sample = ParseInt(argv[name_index + 1]);
}
- // Send metrics
- if (send_to_autotest) {
- MetricsLibrary::SendToAutotest(name, sample);
- }
-
- if (send_to_chrome) {
- MetricsLibrary metrics_lib;
- metrics_lib.Init();
- if (mode == kModeSendSparseSample) {
- metrics_lib.SendSparseToUMA(name, sample);
- } else if (mode == kModeSendEnumSample) {
- int max = ParseInt(argv[name_index + 2]);
- metrics_lib.SendEnumToUMA(name, sample, max);
- } else {
- int min = ParseInt(argv[name_index + 2]);
- int max = ParseInt(argv[name_index + 3]);
- int nbuckets = ParseInt(argv[name_index + 4]);
- metrics_lib.SendToUMA(name, sample, min, max, nbuckets);
- }
+ MetricsLibrary metrics_lib;
+ metrics_lib.Init();
+ if (mode == kModeSendSparseSample) {
+ metrics_lib.SendSparseToUMA(name, sample);
+ } else if (mode == kModeSendEnumSample) {
+ int max = ParseInt(argv[name_index + 2]);
+ metrics_lib.SendEnumToUMA(name, sample, max);
+ } else {
+ int min = ParseInt(argv[name_index + 2]);
+ int max = ParseInt(argv[name_index + 3]);
+ int nbuckets = ParseInt(argv[name_index + 4]);
+ metrics_lib.SendToUMA(name, sample, min, max, nbuckets);
}
return 0;
}
@@ -133,22 +122,12 @@
int main(int argc, char** argv) {
enum Mode mode = kModeSendSample;
- bool send_to_autotest = false;
- bool send_to_chrome = true;
bool secs_to_msecs = false;
// Parse arguments
int flag;
while ((flag = getopt(argc, argv, "abcegstuv")) != -1) {
switch (flag) {
- case 'a':
- send_to_autotest = true;
- send_to_chrome = false;
- break;
- case 'b':
- send_to_chrome = true;
- send_to_autotest = true;
- break;
case 'c':
mode = kModeHasConsent;
break;
@@ -203,9 +182,7 @@
return SendStats(argv,
arg_index,
mode,
- secs_to_msecs,
- send_to_autotest,
- send_to_chrome);
+ secs_to_msecs);
case kModeSendUserAction:
return SendUserAction(argv, arg_index);
case kModeSendCrosEvent:
diff --git a/metrics/metrics_daemon.cc b/metricsd/metrics_daemon.cc
similarity index 91%
rename from metrics/metrics_daemon.cc
rename to metricsd/metrics_daemon.cc
index 880e90c..f9061d5 100644
--- a/metrics/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/metrics_daemon.h"
+#include "metrics_daemon.h"
#include <fcntl.h>
#include <inttypes.h>
@@ -11,6 +11,7 @@
#include <sysexits.h>
#include <time.h>
+#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/hash.h>
@@ -20,9 +21,10 @@
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/sys_info.h>
-#include <chromeos/dbus/service_constants.h>
#include <dbus/dbus.h>
#include <dbus/message.h>
+
+#include "constants.h"
#include "uploader/upload_service.h"
using base::FilePath;
@@ -44,10 +46,6 @@
const char kCrashReporterMatchRule[] =
"type='signal',interface='%s',path='/',member='%s'";
-// Build type of an official build.
-// See src/third_party/chromiumos-overlay/chromeos/scripts/cros_set_lsb_release.
-const char kOfficialBuild[] = "Official Build";
-
const int kSecondsPerMinute = 60;
const int kMinutesPerHour = 60;
const int kHoursPerDay = 24;
@@ -199,28 +197,21 @@
if (version_hash_is_cached)
return cached_version_hash;
version_hash_is_cached = true;
- std::string version;
- if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", &version)) {
- cached_version_hash = base::Hash(version);
- } else if (testing_) {
+ std::string version = metrics::kDefaultVersion;
+ // The version might not be set for development devices. In this case, use the
+ // zero version.
+ base::SysInfo::GetLsbReleaseValue("BRILLO_VERSION", &version);
+ cached_version_hash = base::Hash(version);
+ if (testing_) {
cached_version_hash = 42; // return any plausible value for the hash
- } else {
- LOG(FATAL) << "could not find CHROMEOS_RELEASE_VERSION";
}
return cached_version_hash;
}
-bool MetricsDaemon::IsOnOfficialBuild() const {
- std::string build_type;
- return (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE",
- &build_type) &&
- build_type == kOfficialBuild);
-}
-
void MetricsDaemon::Init(bool testing,
bool uploader_active,
+ bool dbus_enabled,
MetricsLibraryInterface* metrics_lib,
- const string& diskstats_path,
const string& vmstats_path,
const string& scaling_max_freq_path,
const string& cpuinfo_max_freq_path,
@@ -230,6 +221,7 @@
const string& config_root) {
testing_ = testing;
uploader_active_ = uploader_active;
+ dbus_enabled_ = dbus_enabled;
config_root_ = config_root;
DCHECK(metrics_lib != nullptr);
metrics_lib_ = metrics_lib;
@@ -279,77 +271,58 @@
weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
version_cycle_.reset(new PersistentInteger("version.cycle"));
- diskstats_path_ = diskstats_path;
vmstats_path_ = vmstats_path;
scaling_max_freq_path_ = scaling_max_freq_path;
cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
-
- // If testing, initialize Stats Reporter without connecting DBus
- if (testing_)
- StatsReporterInit();
}
int MetricsDaemon::OnInit() {
- int return_code = chromeos::DBusDaemon::OnInit();
+ int return_code = dbus_enabled_ ? chromeos::DBusDaemon::OnInit() :
+ chromeos::Daemon::OnInit();
if (return_code != EX_OK)
return return_code;
- StatsReporterInit();
-
- // Start collecting meminfo stats.
- ScheduleMeminfoCallback(kMetricMeminfoInterval);
- memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0];
- ScheduleMemuseCallback(kMemuseIntervals[0]);
-
if (testing_)
return EX_OK;
- bus_->AssertOnDBusThread();
- CHECK(bus_->SetUpAsyncOperations());
+ if (dbus_enabled_) {
+ bus_->AssertOnDBusThread();
+ CHECK(bus_->SetUpAsyncOperations());
- if (bus_->is_connected()) {
- const std::string match_rule =
- base::StringPrintf(kCrashReporterMatchRule,
- kCrashReporterInterface,
- kCrashReporterUserCrashSignal);
+ if (bus_->is_connected()) {
+ const std::string match_rule =
+ base::StringPrintf(kCrashReporterMatchRule,
+ kCrashReporterInterface,
+ kCrashReporterUserCrashSignal);
- bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
+ bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
- DBusError error;
- dbus_error_init(&error);
- bus_->AddMatch(match_rule, &error);
+ DBusError error;
+ dbus_error_init(&error);
+ bus_->AddMatch(match_rule, &error);
- if (dbus_error_is_set(&error)) {
- LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
- << error.name << ": " << error.message;
- return EX_SOFTWARE;
+ if (dbus_error_is_set(&error)) {
+ LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
+ << error.name << ": " << error.message;
+ return EX_SOFTWARE;
+ }
+ } else {
+ LOG(ERROR) << "DBus isn't connected.";
+ return EX_UNAVAILABLE;
}
- } else {
- LOG(ERROR) << "DBus isn't connected.";
- return EX_UNAVAILABLE;
}
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
- base::Unretained(this)),
- base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
-
if (uploader_active_) {
- if (IsOnOfficialBuild()) {
- LOG(INFO) << "uploader enabled";
- upload_service_.reset(
- new UploadService(new SystemProfileCache(), metrics_lib_, server_));
- upload_service_->Init(upload_interval_, metrics_file_);
- } else {
- LOG(INFO) << "uploader disabled on non-official build";
- }
+ upload_service_.reset(
+ new UploadService(new SystemProfileCache(), metrics_lib_, server_));
+ upload_service_->Init(upload_interval_, metrics_file_);
}
return EX_OK;
}
void MetricsDaemon::OnShutdown(int* return_code) {
- if (!testing_ && bus_->is_connected()) {
+ if (!testing_ && dbus_enabled_ && bus_->is_connected()) {
const std::string match_rule =
base::StringPrintf(kCrashReporterMatchRule,
kCrashReporterInterface,
@@ -520,41 +493,6 @@
base::TimeDelta::FromSeconds(wait));
}
-bool MetricsDaemon::DiskStatsReadStats(uint64_t* read_sectors,
- uint64_t* write_sectors) {
- int nchars;
- int nitems;
- bool success = false;
- char line[200];
- if (diskstats_path_.empty()) {
- return false;
- }
- int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
- if (file < 0) {
- PLOG(WARNING) << "cannot open " << diskstats_path_;
- return false;
- }
- nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
- if (nchars < 0) {
- PLOG(WARNING) << "cannot read from " << diskstats_path_;
- return false;
- } else {
- LOG_IF(WARNING, nchars == sizeof(line))
- << "line too long in " << diskstats_path_;
- line[nchars] = '\0';
- nitems = sscanf(line, "%*d %*d %" PRIu64 " %*d %*d %*d %" PRIu64,
- read_sectors, write_sectors);
- if (nitems == 2) {
- success = true;
- } else {
- LOG(WARNING) << "found " << nitems << " items in "
- << diskstats_path_ << ", expected 2";
- }
- }
- IGNORE_EINTR(close(file));
- return success;
-}
-
bool MetricsDaemon::VmStatsParseStats(const char* stats,
struct VmstatRecord* record) {
// a mapping of string name to field in VmstatRecord and whether we found it
diff --git a/metrics/metrics_daemon.h b/metricsd/metrics_daemon.h
similarity index 98%
rename from metrics/metrics_daemon.h
rename to metricsd/metrics_daemon.h
index b1b2d11..ccac52a 100644
--- a/metrics/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -18,7 +18,7 @@
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "metrics/metrics_library.h"
-#include "metrics/persistent_integer.h"
+#include "persistent_integer.h"
#include "uploader/upload_service.h"
using chromeos_metrics::PersistentInteger;
@@ -31,8 +31,8 @@
// Initializes metrics class variables.
void Init(bool testing,
bool uploader_active,
+ bool dbus_enabled,
MetricsLibraryInterface* metrics_lib,
- const std::string& diskstats_path,
const std::string& vmstats_path,
const std::string& cpuinfo_max_freq_path,
const std::string& scaling_max_freq_path,
@@ -269,9 +269,6 @@
// to a unsigned 32-bit int.
uint32_t GetOsVersionHash();
- // Returns true if the system is using an official build.
- bool IsOnOfficialBuild() const;
-
// Updates stats, additionally sending them to UMA if enough time has elapsed
// since the last report.
void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time);
@@ -293,6 +290,10 @@
// Whether the uploader is enabled or disabled.
bool uploader_active_;
+ // Whether or not dbus should be used.
+ // If disabled, we will not collect the frequency of crashes.
+ bool dbus_enabled_;
+
// Root of the configuration files to use.
std::string config_root_;
@@ -356,7 +357,6 @@
scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
- std::string diskstats_path_;
std::string vmstats_path_;
std::string scaling_max_freq_path_;
std::string cpuinfo_max_freq_path_;
diff --git a/metrics/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
similarity index 69%
rename from metrics/metrics_daemon_main.cc
rename to metricsd/metrics_daemon_main.cc
index 1f64ef3..6c580ba 100644
--- a/metrics/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -8,40 +8,15 @@
#include <base/strings/string_util.h>
#include <chromeos/flag_helper.h>
#include <chromeos/syslog_logging.h>
-#include <rootdev/rootdev.h>
-#include "metrics/metrics_daemon.h"
+#include "constants.h"
+#include "metrics_daemon.h"
const char kScalingMaxFreqPath[] =
"/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq";
const char kCpuinfoMaxFreqPath[] =
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
-// Returns the path to the disk stats in the sysfs. Returns the null string if
-// it cannot find the disk stats file.
-static
-const std::string MetricsMainDiskStatsPath() {
- char dev_path_cstr[PATH_MAX];
- std::string dev_prefix = "/dev/";
- std::string dev_path;
- std::string dev_name;
-
- int ret = rootdev(dev_path_cstr, sizeof(dev_path_cstr), true, true);
- if (ret != 0) {
- LOG(WARNING) << "error " << ret << " determining root device";
- return "";
- }
- dev_path = dev_path_cstr;
- // Check that rootdev begins with "/dev/".
- if (!base::StartsWithASCII(dev_path, dev_prefix, false)) {
- LOG(WARNING) << "unexpected root device " << dev_path;
- return "";
- }
- // Get the device name, e.g. "sda" from "/dev/sda".
- dev_name = dev_path.substr(dev_prefix.length());
- return "/sys/class/block/" + dev_name + "/stat";
-}
-
int main(int argc, char** argv) {
DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)");
@@ -54,16 +29,19 @@
false,
"run the uploader once and exit");
+ // Enable dbus.
+ DEFINE_bool(withdbus, true, "Enable dbus");
+
// Upload Service flags.
DEFINE_int32(upload_interval_secs,
1800,
"Interval at which metrics_daemon sends the metrics. (needs "
"-uploader)");
DEFINE_string(server,
- "https://clients4.google.com/uma/v2",
+ metrics::kMetricsServer,
"Server to upload the metrics to. (needs -uploader)");
DEFINE_string(metrics_file,
- "/var/lib/metrics/uma-events",
+ metrics::kMetricsEventsFilePath,
"File to use as a proxy for uploading the metrics");
DEFINE_string(config_root,
"/", "Root of the configuration files (testing only)");
@@ -83,8 +61,8 @@
MetricsDaemon daemon;
daemon.Init(FLAGS_uploader_test,
FLAGS_uploader | FLAGS_uploader_test,
+ FLAGS_withdbus,
&metrics_lib,
- MetricsMainDiskStatsPath(),
"/proc/vmstat",
kScalingMaxFreqPath,
kCpuinfoMaxFreqPath,
diff --git a/metrics/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
similarity index 98%
rename from metrics/metrics_daemon_test.cc
rename to metricsd/metrics_daemon_test.cc
index 7dafbd6..5aa7ab8 100644
--- a/metrics/metrics_daemon_test.cc
+++ b/metricsd/metrics_daemon_test.cc
@@ -15,9 +15,9 @@
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
-#include "metrics/metrics_daemon.h"
-#include "metrics/metrics_library_mock.h"
-#include "metrics/persistent_integer_mock.h"
+#include "metrics_daemon.h"
+#include "metrics_library_mock.h"
+#include "persistent_integer_mock.h"
using base::FilePath;
using base::StringPrintf;
@@ -44,8 +44,6 @@
static const char kFakeVmStatsName[] = "fake-vm-stats";
static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq";
static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq";
-static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
-static const char kMetricsFilePath[] = "/var/lib/metrics/uma-events";
class MetricsDaemonTest : public testing::Test {
protected:
diff --git a/metrics/metrics_library.cc b/metricsd/metrics_library.cc
similarity index 71%
rename from metrics/metrics_library.cc
rename to metricsd/metrics_library.cc
index 70c8eac..f777f28 100644
--- a/metrics/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -13,14 +13,10 @@
#include <cstdio>
#include <cstring>
-#include "metrics/serialization/metric_sample.h"
-#include "metrics/serialization/serialization_utils.h"
+#include "constants.h"
+#include "serialization/metric_sample.h"
+#include "serialization/serialization_utils.h"
-#include "policy/device_policy.h"
-
-static const char kAutotestPath[] = "/var/log/metrics/autotest-events";
-static const char kUMAEventsPath[] = "/var/lib/metrics/uma-events";
-static const char kConsentFile[] = "/home/chronos/Consent To Send Stats";
static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
static const int kCrosEventHistogramMax = 100;
@@ -48,7 +44,7 @@
time_t MetricsLibrary::cached_enabled_time_ = 0;
bool MetricsLibrary::cached_enabled_ = false;
-MetricsLibrary::MetricsLibrary() : consent_file_(kConsentFile) {}
+MetricsLibrary::MetricsLibrary() : consent_file_(metrics::kConsentFilePath) {}
MetricsLibrary::~MetricsLibrary() {}
// We take buffer and buffer_size as parameters in order to simplify testing
@@ -123,47 +119,13 @@
time_t this_check_time = time(nullptr);
if (this_check_time != cached_enabled_time_) {
cached_enabled_time_ = this_check_time;
-
- if (!policy_provider_.get())
- policy_provider_.reset(new policy::PolicyProvider());
- policy_provider_->Reload();
- // We initialize with the default value which is false and will be preserved
- // if the policy is not set.
- bool enabled = false;
- bool has_policy = false;
- if (policy_provider_->device_policy_is_loaded()) {
- has_policy =
- policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled);
- }
- // If policy couldn't be loaded or the metrics policy is not set we should
- // still respect the consent file if it is present for migration purposes.
- // TODO(pastarmovj)
- if (!has_policy) {
- enabled = stat(consent_file_.c_str(), &stat_buffer) >= 0;
- }
-
- if (enabled && !IsGuestMode())
- cached_enabled_ = true;
- else
- cached_enabled_ = false;
+ cached_enabled_ = stat(consent_file_.c_str(), &stat_buffer) >= 0;
}
return cached_enabled_;
}
void MetricsLibrary::Init() {
- uma_events_file_ = kUMAEventsPath;
-}
-
-bool MetricsLibrary::SendToAutotest(const std::string& name, int value) {
- FILE* autotest_file = fopen(kAutotestPath, "a+");
- if (autotest_file == nullptr) {
- PLOG(ERROR) << kAutotestPath << ": fopen";
- return false;
- }
-
- fprintf(autotest_file, "%s=%d\n", name.c_str(), value);
- fclose(autotest_file);
- return true;
+ uma_events_file_ = metrics::kMetricsEventsFilePath;
}
bool MetricsLibrary::SendToUMA(const std::string& name,
@@ -174,34 +136,30 @@
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::HistogramSample(name, sample, min, max, nbuckets)
.get(),
- kUMAEventsPath);
+ metrics::kMetricsEventsFilePath);
}
bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
int max) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::LinearHistogramSample(name, sample, max).get(),
- kUMAEventsPath);
+ metrics::kMetricsEventsFilePath);
}
bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::SparseHistogramSample(name, sample).get(),
- kUMAEventsPath);
+ metrics::kMetricsEventsFilePath);
}
bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::UserActionSample(action).get(), kUMAEventsPath);
+ *metrics::MetricSample::UserActionSample(action).get(), metrics::kMetricsEventsFilePath);
}
bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) {
return metrics::SerializationUtils::WriteMetricToFile(
- *metrics::MetricSample::CrashSample(crash_kind).get(), kUMAEventsPath);
-}
-
-void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) {
- policy_provider_.reset(provider);
+ *metrics::MetricSample::CrashSample(crash_kind).get(), metrics::kMetricsEventsFilePath);
}
bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {
diff --git a/metrics/metrics_library_mock.h b/metricsd/metrics_library_mock.h
similarity index 100%
rename from metrics/metrics_library_mock.h
rename to metricsd/metrics_library_mock.h
diff --git a/metrics/metrics_library_test.cc b/metricsd/metrics_library_test.cc
similarity index 100%
rename from metrics/metrics_library_test.cc
rename to metricsd/metrics_library_test.cc
diff --git a/metrics/persistent_integer.cc b/metricsd/persistent_integer.cc
similarity index 91%
rename from metrics/persistent_integer.cc
rename to metricsd/persistent_integer.cc
index dd38f1e..0dcd52a 100644
--- a/metrics/persistent_integer.cc
+++ b/metricsd/persistent_integer.cc
@@ -2,21 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/persistent_integer.h"
+#include "persistent_integer.h"
#include <fcntl.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
+#include "constants.h"
#include "metrics/metrics_library.h"
-namespace {
-
-// The directory for the persistent storage.
-const char kBackingFilesDirectory[] = "/var/lib/metrics/";
-
-}
namespace chromeos_metrics {
@@ -31,7 +26,7 @@
if (testing_) {
backing_file_name_ = name_;
} else {
- backing_file_name_ = kBackingFilesDirectory + name_;
+ backing_file_name_ = metrics::kMetricsDirectory + name_;
}
}
diff --git a/metrics/persistent_integer.h b/metricsd/persistent_integer.h
similarity index 100%
rename from metrics/persistent_integer.h
rename to metricsd/persistent_integer.h
diff --git a/metrics/persistent_integer_mock.h b/metricsd/persistent_integer_mock.h
similarity index 93%
rename from metrics/persistent_integer_mock.h
rename to metricsd/persistent_integer_mock.h
index 2061e55..31bfc35 100644
--- a/metrics/persistent_integer_mock.h
+++ b/metricsd/persistent_integer_mock.h
@@ -9,7 +9,7 @@
#include <gmock/gmock.h>
-#include "metrics/persistent_integer.h"
+#include "persistent_integer.h"
namespace chromeos_metrics {
diff --git a/metrics/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
similarity index 97%
rename from metrics/persistent_integer_test.cc
rename to metricsd/persistent_integer_test.cc
index a56aede..4fccb72 100644
--- a/metrics/persistent_integer_test.cc
+++ b/metricsd/persistent_integer_test.cc
@@ -8,7 +8,7 @@
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
-#include "metrics/persistent_integer.h"
+#include "persistent_integer.h"
const char kBackingFileName[] = "1.pibakf";
const char kBackingFilePattern[] = "*.pibakf";
diff --git a/metrics/platform2_preinstall.sh b/metricsd/platform2_preinstall.sh
similarity index 100%
rename from metrics/platform2_preinstall.sh
rename to metricsd/platform2_preinstall.sh
diff --git a/metrics/serialization/metric_sample.cc b/metricsd/serialization/metric_sample.cc
similarity index 98%
rename from metrics/serialization/metric_sample.cc
rename to metricsd/serialization/metric_sample.cc
index 5447497..bc6583d 100644
--- a/metrics/serialization/metric_sample.cc
+++ b/metricsd/serialization/metric_sample.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/serialization/metric_sample.h"
+#include "serialization/metric_sample.h"
#include <string>
#include <vector>
diff --git a/metrics/serialization/metric_sample.h b/metricsd/serialization/metric_sample.h
similarity index 100%
rename from metrics/serialization/metric_sample.h
rename to metricsd/serialization/metric_sample.h
diff --git a/metrics/serialization/serialization_utils.cc b/metricsd/serialization/serialization_utils.cc
similarity index 98%
rename from metrics/serialization/serialization_utils.cc
rename to metricsd/serialization/serialization_utils.cc
index 9aa076a..d18dcd7 100644
--- a/metrics/serialization/serialization_utils.cc
+++ b/metricsd/serialization/serialization_utils.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/serialization/serialization_utils.h"
+#include "serialization/serialization_utils.h"
#include <sys/file.h>
@@ -17,7 +17,7 @@
#include "base/memory/scoped_vector.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "metrics/serialization/metric_sample.h"
+#include "serialization/metric_sample.h"
#define READ_WRITE_ALL_FILE_FLAGS \
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
diff --git a/metrics/serialization/serialization_utils.h b/metricsd/serialization/serialization_utils.h
similarity index 100%
rename from metrics/serialization/serialization_utils.h
rename to metricsd/serialization/serialization_utils.h
diff --git a/metrics/serialization/serialization_utils_unittest.cc b/metricsd/serialization/serialization_utils_unittest.cc
similarity index 98%
rename from metrics/serialization/serialization_utils_unittest.cc
rename to metricsd/serialization/serialization_utils_unittest.cc
index 34d76cf..fb802bc 100644
--- a/metrics/serialization/serialization_utils_unittest.cc
+++ b/metricsd/serialization/serialization_utils_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/serialization/serialization_utils.h"
+#include "serialization/serialization_utils.h"
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
@@ -10,7 +10,7 @@
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
-#include "metrics/serialization/metric_sample.h"
+#include "serialization/metric_sample.h"
namespace metrics {
namespace {
diff --git a/metrics/syslog_parser.sh b/metricsd/syslog_parser.sh
similarity index 100%
rename from metrics/syslog_parser.sh
rename to metricsd/syslog_parser.sh
diff --git a/metrics/timer.cc b/metricsd/timer.cc
similarity index 98%
rename from metrics/timer.cc
rename to metricsd/timer.cc
index 99f68fe..ce4bf67 100644
--- a/metrics/timer.cc
+++ b/metricsd/timer.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/timer.h"
+#include "timer.h"
#include <string>
diff --git a/metrics/timer.h b/metricsd/timer.h
similarity index 100%
rename from metrics/timer.h
rename to metricsd/timer.h
diff --git a/metrics/timer_mock.h b/metricsd/timer_mock.h
similarity index 97%
rename from metrics/timer_mock.h
rename to metricsd/timer_mock.h
index 2f2d0f4..ed76f12 100644
--- a/metrics/timer_mock.h
+++ b/metricsd/timer_mock.h
@@ -9,7 +9,7 @@
#include <gmock/gmock.h>
-#include "metrics/timer.h"
+#include "timer.h"
namespace chromeos_metrics {
diff --git a/metrics/timer_test.cc b/metricsd/timer_test.cc
similarity index 99%
rename from metrics/timer_test.cc
rename to metricsd/timer_test.cc
index ec6c6bd..b1689bf 100644
--- a/metrics/timer_test.cc
+++ b/metricsd/timer_test.cc
@@ -8,9 +8,9 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "metrics/metrics_library_mock.h"
-#include "metrics/timer.h"
-#include "metrics/timer_mock.h"
+#include "metrics_library_mock.h"
+#include "timer.h"
+#include "timer_mock.h"
using ::testing::_;
using ::testing::Return;
diff --git a/metrics/uploader/metrics_hashes.cc b/metricsd/uploader/metrics_hashes.cc
similarity index 95%
rename from metrics/uploader/metrics_hashes.cc
rename to metricsd/uploader/metrics_hashes.cc
index 87405a3..f9d0cfe 100644
--- a/metrics/uploader/metrics_hashes.cc
+++ b/metricsd/uploader/metrics_hashes.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/uploader/metrics_hashes.h"
+#include "uploader/metrics_hashes.h"
#include "base/logging.h"
#include "base/md5.h"
diff --git a/metrics/uploader/metrics_hashes.h b/metricsd/uploader/metrics_hashes.h
similarity index 100%
rename from metrics/uploader/metrics_hashes.h
rename to metricsd/uploader/metrics_hashes.h
diff --git a/metrics/uploader/metrics_hashes_unittest.cc b/metricsd/uploader/metrics_hashes_unittest.cc
similarity index 94%
rename from metrics/uploader/metrics_hashes_unittest.cc
rename to metricsd/uploader/metrics_hashes_unittest.cc
index f7e390f..8cdc7a9 100644
--- a/metrics/uploader/metrics_hashes_unittest.cc
+++ b/metricsd/uploader/metrics_hashes_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/uploader/metrics_hashes.h"
+#include "uploader/metrics_hashes.h"
#include <base/format_macros.h>
#include <base/macros.h>
diff --git a/metrics/uploader/metrics_log.cc b/metricsd/uploader/metrics_log.cc
similarity index 96%
rename from metrics/uploader/metrics_log.cc
rename to metricsd/uploader/metrics_log.cc
index 4d493b8..6f11f8a 100644
--- a/metrics/uploader/metrics_log.cc
+++ b/metricsd/uploader/metrics_log.cc
@@ -6,7 +6,7 @@
#include <string>
-#include "metrics/uploader/proto/system_profile.pb.h"
+#include "uploader/proto/system_profile.pb.h"
#include "uploader/system_profile_setter.h"
// We use default values for the MetricsLogBase constructor as the setter will
diff --git a/metrics/uploader/metrics_log.h b/metricsd/uploader/metrics_log.h
similarity index 96%
rename from metrics/uploader/metrics_log.h
rename to metricsd/uploader/metrics_log.h
index 5796325..a62798f 100644
--- a/metrics/uploader/metrics_log.h
+++ b/metricsd/uploader/metrics_log.h
@@ -9,7 +9,7 @@
#include <base/macros.h>
-#include "metrics/uploader/metrics_log_base.h"
+#include "uploader/metrics_log_base.h"
// This file defines a set of user experience metrics data recorded by
// the MetricsService. This is the unit of data that is sent to the server.
diff --git a/metrics/uploader/metrics_log_base.cc b/metricsd/uploader/metrics_log_base.cc
similarity index 94%
rename from metrics/uploader/metrics_log_base.cc
rename to metricsd/uploader/metrics_log_base.cc
index 7fe1ae1..3ae01e8 100644
--- a/metrics/uploader/metrics_log_base.cc
+++ b/metricsd/uploader/metrics_log_base.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/uploader/metrics_log_base.h"
+#include "uploader/metrics_log_base.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_samples.h"
-#include "metrics/uploader/metrics_hashes.h"
-#include "metrics/uploader/proto/histogram_event.pb.h"
-#include "metrics/uploader/proto/system_profile.pb.h"
-#include "metrics/uploader/proto/user_action_event.pb.h"
+#include "uploader/metrics_hashes.h"
+#include "uploader/proto/histogram_event.pb.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/proto/user_action_event.pb.h"
using base::Histogram;
using base::HistogramBase;
diff --git a/metrics/uploader/metrics_log_base.h b/metricsd/uploader/metrics_log_base.h
similarity index 97%
rename from metrics/uploader/metrics_log_base.h
rename to metricsd/uploader/metrics_log_base.h
index e871c0f..4173335 100644
--- a/metrics/uploader/metrics_log_base.h
+++ b/metricsd/uploader/metrics_log_base.h
@@ -13,7 +13,7 @@
#include "base/macros.h"
#include "base/metrics/histogram.h"
#include "base/time/time.h"
-#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
namespace base {
class HistogramSamples;
diff --git a/metrics/uploader/metrics_log_base_unittest.cc b/metricsd/uploader/metrics_log_base_unittest.cc
similarity index 97%
rename from metrics/uploader/metrics_log_base_unittest.cc
rename to metricsd/uploader/metrics_log_base_unittest.cc
index 5da428a..dc03f00 100644
--- a/metrics/uploader/metrics_log_base_unittest.cc
+++ b/metricsd/uploader/metrics_log_base_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/uploader/metrics_log_base.h"
+#include "uploader/metrics_log_base.h"
#include <string>
@@ -10,7 +10,7 @@
#include <base/metrics/sample_vector.h>
#include <gtest/gtest.h>
-#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
namespace metrics {
diff --git a/metrics/uploader/mock/mock_system_profile_setter.h b/metricsd/uploader/mock/mock_system_profile_setter.h
similarity index 100%
rename from metrics/uploader/mock/mock_system_profile_setter.h
rename to metricsd/uploader/mock/mock_system_profile_setter.h
diff --git a/metrics/uploader/mock/sender_mock.cc b/metricsd/uploader/mock/sender_mock.cc
similarity index 100%
rename from metrics/uploader/mock/sender_mock.cc
rename to metricsd/uploader/mock/sender_mock.cc
diff --git a/metrics/uploader/mock/sender_mock.h b/metricsd/uploader/mock/sender_mock.h
similarity index 95%
rename from metrics/uploader/mock/sender_mock.h
rename to metricsd/uploader/mock/sender_mock.h
index 159b645..0a15d61 100644
--- a/metrics/uploader/mock/sender_mock.h
+++ b/metricsd/uploader/mock/sender_mock.h
@@ -8,7 +8,7 @@
#include <string>
#include "base/compiler_specific.h"
-#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
#include "uploader/sender.h"
class SenderMock : public Sender {
diff --git a/metrics/uploader/proto/README b/metricsd/uploader/proto/README
similarity index 100%
rename from metrics/uploader/proto/README
rename to metricsd/uploader/proto/README
diff --git a/metrics/uploader/proto/chrome_user_metrics_extension.proto b/metricsd/uploader/proto/chrome_user_metrics_extension.proto
similarity index 91%
rename from metrics/uploader/proto/chrome_user_metrics_extension.proto
rename to metricsd/uploader/proto/chrome_user_metrics_extension.proto
index f712fc9..d4d4f24 100644
--- a/metrics/uploader/proto/chrome_user_metrics_extension.proto
+++ b/metricsd/uploader/proto/chrome_user_metrics_extension.proto
@@ -14,9 +14,9 @@
package metrics;
-import "histogram_event.proto";
-import "system_profile.proto";
-import "user_action_event.proto";
+import "system/core/metricsd/uploader/proto/histogram_event.proto";
+import "system/core/metricsd/uploader/proto/system_profile.proto";
+import "system/core/metricsd/uploader/proto/user_action_event.proto";
// Next tag: 13
message ChromeUserMetricsExtension {
diff --git a/metrics/uploader/proto/histogram_event.proto b/metricsd/uploader/proto/histogram_event.proto
similarity index 100%
rename from metrics/uploader/proto/histogram_event.proto
rename to metricsd/uploader/proto/histogram_event.proto
diff --git a/metrics/uploader/proto/system_profile.proto b/metricsd/uploader/proto/system_profile.proto
similarity index 100%
rename from metrics/uploader/proto/system_profile.proto
rename to metricsd/uploader/proto/system_profile.proto
diff --git a/metrics/uploader/proto/user_action_event.proto b/metricsd/uploader/proto/user_action_event.proto
similarity index 100%
rename from metrics/uploader/proto/user_action_event.proto
rename to metricsd/uploader/proto/user_action_event.proto
diff --git a/metrics/uploader/sender.h b/metricsd/uploader/sender.h
similarity index 100%
rename from metrics/uploader/sender.h
rename to metricsd/uploader/sender.h
diff --git a/metrics/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
similarity index 96%
rename from metrics/uploader/sender_http.cc
rename to metricsd/uploader/sender_http.cc
index 8488b66..a740310 100644
--- a/metrics/uploader/sender_http.cc
+++ b/metricsd/uploader/sender_http.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/uploader/sender_http.h"
+#include "uploader/sender_http.h"
#include <string>
diff --git a/metrics/uploader/sender_http.h b/metricsd/uploader/sender_http.h
similarity index 95%
rename from metrics/uploader/sender_http.h
rename to metricsd/uploader/sender_http.h
index 4880b28..380cad8 100644
--- a/metrics/uploader/sender_http.h
+++ b/metricsd/uploader/sender_http.h
@@ -9,7 +9,7 @@
#include <base/macros.h>
-#include "metrics/uploader/sender.h"
+#include "uploader/sender.h"
// Sender implemented using http_utils from libchromeos
class HttpSender : public Sender {
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
new file mode 100644
index 0000000..adbe0ae
--- /dev/null
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "uploader/system_profile_cache.h"
+
+#include <base/files/file_util.h>
+#include <base/guid.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/sys_info.h>
+#include <string>
+#include <vector>
+
+#include "constants.h"
+#include "persistent_integer.h"
+#include "uploader/metrics_log_base.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+
+namespace {
+
+const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId";
+
+} // namespace
+
+std::string ChannelToString(
+ const metrics::SystemProfileProto_Channel& channel) {
+ switch (channel) {
+ case metrics::SystemProfileProto::CHANNEL_STABLE:
+ return "STABLE";
+ case metrics::SystemProfileProto::CHANNEL_DEV:
+ return "DEV";
+ case metrics::SystemProfileProto::CHANNEL_BETA:
+ return "BETA";
+ case metrics::SystemProfileProto::CHANNEL_CANARY:
+ return "CANARY";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+SystemProfileCache::SystemProfileCache()
+ : initialized_(false),
+ testing_(false),
+ config_root_("/"),
+ session_id_(new chromeos_metrics::PersistentInteger(
+ kPersistentSessionIdFilename)) {
+}
+
+SystemProfileCache::SystemProfileCache(bool testing,
+ const std::string& config_root)
+ : initialized_(false),
+ testing_(testing),
+ config_root_(config_root),
+ session_id_(new chromeos_metrics::PersistentInteger(
+ kPersistentSessionIdFilename)) {
+}
+
+bool SystemProfileCache::Initialize() {
+ CHECK(!initialized_)
+ << "this should be called only once in the metrics_daemon lifetime.";
+
+ if (!base::SysInfo::GetLsbReleaseValue("BRILLO_BUILD_TARGET_ID",
+ &profile_.build_target_id)) {
+ LOG(ERROR) << "Could not initialize system profile.";
+ return false;
+ }
+
+ std::string channel;
+ if (!base::SysInfo::GetLsbReleaseValue("BRILLO_CHANNEL", &channel) ||
+ !base::SysInfo::GetLsbReleaseValue("BRILLO_VERSION", &profile_.version)) {
+ // If the channel or version is missing, the image is not official.
+ // In this case, set the channel to unknown and the version to 0.0.0.0 to
+ // avoid polluting the production data.
+ channel = "";
+ profile_.version = metrics::kDefaultVersion;
+
+ }
+ profile_.client_id =
+ testing_ ? "client_id_test" :
+ GetPersistentGUID(metrics::kMetricsGUIDFilePath);
+ profile_.hardware_class = "unknown";
+ profile_.channel = ProtoChannelFromString(channel);
+
+ // Increment the session_id everytime we initialize this. If metrics_daemon
+ // does not crash, this should correspond to the number of reboots of the
+ // system.
+ session_id_->Add(1);
+ profile_.session_id = static_cast<int32_t>(session_id_->Get());
+
+ initialized_ = true;
+ return initialized_;
+}
+
+bool SystemProfileCache::InitializeOrCheck() {
+ return initialized_ || Initialize();
+}
+
+void SystemProfileCache::Populate(
+ metrics::ChromeUserMetricsExtension* metrics_proto) {
+ CHECK(metrics_proto);
+ CHECK(InitializeOrCheck())
+ << "failed to initialize system information.";
+
+ // The client id is hashed before being sent.
+ metrics_proto->set_client_id(
+ metrics::MetricsLogBase::Hash(profile_.client_id));
+ metrics_proto->set_session_id(profile_.session_id);
+
+ // Sets the product id.
+ metrics_proto->set_product(9);
+
+ metrics::SystemProfileProto* profile_proto =
+ metrics_proto->mutable_system_profile();
+ profile_proto->mutable_hardware()->set_hardware_class(
+ profile_.hardware_class);
+ profile_proto->set_app_version(profile_.version);
+ profile_proto->set_channel(profile_.channel);
+ metrics::SystemProfileProto_BrilloDeviceData* device_data =
+ profile_proto->mutable_brillo();
+ device_data->set_build_target_id(profile_.build_target_id);
+}
+
+std::string SystemProfileCache::GetPersistentGUID(
+ const std::string& filename) {
+ std::string guid;
+ base::FilePath filepath(filename);
+ if (!base::ReadFileToString(filepath, &guid)) {
+ guid = base::GenerateGUID();
+ // If we can't read or write the file, the guid will not be preserved during
+ // the next reboot. Crash.
+ CHECK(base::WriteFile(filepath, guid.c_str(), guid.size()));
+ }
+ return guid;
+}
+
+metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
+ const std::string& channel) {
+ if (channel == "stable") {
+ return metrics::SystemProfileProto::CHANNEL_STABLE;
+ } else if (channel == "dev") {
+ return metrics::SystemProfileProto::CHANNEL_DEV;
+ } else if (channel == "beta") {
+ return metrics::SystemProfileProto::CHANNEL_BETA;
+ } else if (channel == "canary") {
+ return metrics::SystemProfileProto::CHANNEL_CANARY;
+ }
+
+ DLOG(INFO) << "unknown channel: " << channel;
+ return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
+}
diff --git a/metrics/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
similarity index 67%
rename from metrics/uploader/system_profile_cache.h
rename to metricsd/uploader/system_profile_cache.h
index e7a7337..b6ff337 100644
--- a/metrics/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -12,24 +12,21 @@
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
-#include "chromeos/osrelease_reader.h"
-#include "metrics/persistent_integer.h"
-#include "metrics/uploader/proto/system_profile.pb.h"
-#include "metrics/uploader/system_profile_setter.h"
+#include "persistent_integer.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_setter.h"
namespace metrics {
class ChromeUserMetricsExtension;
}
struct SystemProfile {
- std::string os_name;
- std::string os_version;
- metrics::SystemProfileProto::Channel channel;
- std::string app_version;
+ std::string version;
std::string hardware_class;
std::string client_id;
- int32_t session_id;
- int32_t product_id;
+ int session_id;
+ metrics::SystemProfileProto::Channel channel;
+ std::string build_target_id;
};
// Retrieves general system informations needed by the protobuf for context and
@@ -43,9 +40,9 @@
SystemProfileCache(bool testing, const std::string& config_root);
// Populates the ProfileSystem protobuf with system information.
- void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override;
+ void Populate(metrics::ChromeUserMetricsExtension* metrics_proto) override;
- // Converts a string representation of the channel (|channel|-channel) to a
+ // Converts a string representation of the channel to a
// SystemProfileProto_Channel
static metrics::SystemProfileProto_Channel ProtoChannelFromString(
const std::string& channel);
@@ -66,21 +63,6 @@
// Initializes |profile_| only if it has not been yet initialized.
bool InitializeOrCheck();
- // Gets the hardware ID using crossystem
- bool GetHardwareId(std::string* hwid);
-
- // Gets the product ID from the GOOGLE_METRICS_PRODUCT_ID field.
- bool GetProductId(int* product_id) const;
-
- // Generate the formatted chromeos version from the fields in
- // /etc/lsb-release. The format is A.B.C.D where A, B, C and D are positive
- // integer representing:
- // * the chrome milestone
- // * the build number
- // * the branch number
- // * the patch number
- bool GetChromeOSVersion(std::string* version);
-
bool initialized_;
bool testing_;
std::string config_root_;
diff --git a/metrics/uploader/system_profile_setter.h b/metricsd/uploader/system_profile_setter.h
similarity index 100%
rename from metrics/uploader/system_profile_setter.h
rename to metricsd/uploader/system_profile_setter.h
diff --git a/metrics/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
similarity index 95%
rename from metrics/uploader/upload_service.cc
rename to metricsd/uploader/upload_service.cc
index 92c9e10..3411004 100644
--- a/metrics/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "metrics/uploader/upload_service.h"
+#include "uploader/upload_service.h"
#include <string>
@@ -17,11 +17,11 @@
#include <base/metrics/statistics_recorder.h>
#include <base/sha1.h>
-#include "metrics/serialization/metric_sample.h"
-#include "metrics/serialization/serialization_utils.h"
-#include "metrics/uploader/metrics_log.h"
-#include "metrics/uploader/sender_http.h"
-#include "metrics/uploader/system_profile_cache.h"
+#include "serialization/metric_sample.h"
+#include "serialization/serialization_utils.h"
+#include "uploader/metrics_log.h"
+#include "uploader/sender_http.h"
+#include "uploader/system_profile_setter.h"
const int UploadService::kMaxFailedUpload = 10;
diff --git a/metrics/uploader/upload_service.h b/metricsd/uploader/upload_service.h
similarity index 97%
rename from metrics/uploader/upload_service.h
rename to metricsd/uploader/upload_service.h
index ebbb54f..c08fc1a 100644
--- a/metrics/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -12,9 +12,9 @@
#include "base/metrics/histogram_snapshot_manager.h"
#include "metrics/metrics_library.h"
-#include "metrics/uploader/metrics_log.h"
-#include "metrics/uploader/sender.h"
-#include "metrics/uploader/system_profile_cache.h"
+#include "uploader/metrics_log.h"
+#include "uploader/sender.h"
+#include "uploader/system_profile_cache.h"
namespace metrics {
class ChromeUserMetricsExtension;
diff --git a/metrics/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
similarity index 92%
rename from metrics/uploader/upload_service_test.cc
rename to metricsd/uploader/upload_service_test.cc
index ee17e15..efd0a56 100644
--- a/metrics/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -9,19 +9,16 @@
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/sys_info.h"
-#include "metrics/metrics_library_mock.h"
-#include "metrics/serialization/metric_sample.h"
-#include "metrics/uploader/metrics_log.h"
-#include "metrics/uploader/mock/mock_system_profile_setter.h"
-#include "metrics/uploader/mock/sender_mock.h"
-#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
-#include "metrics/uploader/proto/histogram_event.pb.h"
-#include "metrics/uploader/proto/system_profile.pb.h"
-#include "metrics/uploader/system_profile_cache.h"
-#include "metrics/uploader/upload_service.h"
-
-static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
-static const char kMetricsFilePath[] = "/var/run/metrics/uma-events";
+#include "metrics_library_mock.h"
+#include "serialization/metric_sample.h"
+#include "uploader/metrics_log.h"
+#include "uploader/mock/mock_system_profile_setter.h"
+#include "uploader/mock/sender_mock.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/proto/histogram_event.pb.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_cache.h"
+#include "uploader/upload_service.h"
class UploadServiceTest : public testing::Test {
protected:
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d6dad2d..30a2851 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -24,9 +24,18 @@
# Put it here instead of in init.rc module definition,
# because init.rc is conditionally included.
#
-# create some directories (some are mount points)
-LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data oem acct cache config storage mnt root)
+# create some directories (some are mount points) and symlinks
+local_post_install_cmd_base := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+ sbin dev proc sys system data oem acct cache config storage mnt root); \
+ ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
+ ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
+ ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
+ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+ LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base); mkdir -p $(TARGET_ROOT_OUT)/vendor
+else
+ LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base)
+endif
+local_post_install_cmd_base :=
include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f5febde..12999bd 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -259,6 +259,7 @@
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
mkdir /data/local 0751 root root
mkdir /data/misc/media 0700 media media
+ mkdir /data/misc/boottrace 0771 system shell
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc
index 50944e6..ed4629e 100644
--- a/rootdir/init.trace.rc
+++ b/rootdir/init.trace.rc
@@ -33,3 +33,11 @@
# Allow only the shell group to read and truncate the kernel trace.
chown root shell /sys/kernel/debug/tracing/trace
chmod 0660 /sys/kernel/debug/tracing/trace
+
+on property:persist.debug.atrace.boottrace=1
+ start boottrace
+
+# Run atrace with the categories written in a file
+service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories
+ disabled
+ oneshot
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 273b263..ae880c3 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -2,7 +2,7 @@
common_cflags := \
- -Werror -Wno-unused-parameter \
+ -Werror -Wno-unused-parameter -Wno-unused-const-variable \
-I$(LOCAL_PATH)/upstream-netbsd/include/ \
-include bsd-compatibility.h \
@@ -56,7 +56,6 @@
stop \
top \
uptime \
- watchprops \
ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
deleted file mode 100644
index cd62922..0000000
--- a/toolbox/watchprops.c
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <errno.h>
-
-#include <cutils/properties.h>
-#include <cutils/hashmap.h>
-
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-static int str_hash(void *key)
-{
- return hashmapHash(key, strlen(key));
-}
-
-static bool str_equals(void *keyA, void *keyB)
-{
- return strcmp(keyA, keyB) == 0;
-}
-
-static void announce(char *name, char *value)
-{
- unsigned char *x;
-
- for(x = (unsigned char *)value; *x; x++) {
- if((*x < 32) || (*x > 127)) *x = '.';
- }
-
- fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
-}
-
-static void add_to_watchlist(Hashmap *watchlist, const char *name,
- const prop_info *pi)
-{
- char *key = strdup(name);
- unsigned *value = malloc(sizeof(unsigned));
- if (!key || !value)
- exit(1);
-
- *value = __system_property_serial(pi);
- hashmapPut(watchlist, key, value);
-}
-
-static void populate_watchlist(const prop_info *pi, void *cookie)
-{
- Hashmap *watchlist = cookie;
- char name[PROP_NAME_MAX];
- char value_unused[PROP_VALUE_MAX];
-
- __system_property_read(pi, name, value_unused);
- add_to_watchlist(watchlist, name, pi);
-}
-
-static void update_watchlist(const prop_info *pi, void *cookie)
-{
- Hashmap *watchlist = cookie;
- char name[PROP_NAME_MAX];
- char value[PROP_VALUE_MAX];
- unsigned *serial;
-
- __system_property_read(pi, name, value);
- serial = hashmapGet(watchlist, name);
- if (!serial) {
- add_to_watchlist(watchlist, name, pi);
- announce(name, value);
- } else {
- unsigned tmp = __system_property_serial(pi);
- if (*serial != tmp) {
- *serial = tmp;
- announce(name, value);
- }
- }
-}
-
-int watchprops_main(int argc, char *argv[])
-{
- unsigned serial;
-
- Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals);
- if (!watchlist)
- exit(1);
-
- __system_property_foreach(populate_watchlist, watchlist);
-
- for(serial = 0;;) {
- serial = __system_property_wait_any(serial);
- __system_property_foreach(update_watchlist, watchlist);
- }
- return 0;
-}