Merge "Add fastbootd."
diff --git a/adb/Android.bp b/adb/Android.bp
index 2a9a579..906c5e3 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -277,27 +277,113 @@
},
}
+// libadbd_core contains the common sources to build libadbd and libadbd_services.
cc_library_static {
+ name: "libadbd_core",
+ defaults: ["adb_defaults"],
+ recovery_available: true,
+
+ // libminadbd wants both, as it's used to build native tests.
+ compile_multilib: "both",
+
+ srcs: libadb_srcs + libadb_posix_srcs + [
+ "daemon/auth.cpp",
+ "daemon/jdwp_service.cpp",
+ "daemon/usb.cpp",
+ ],
+
+ local_include_dirs: [
+ "daemon/include",
+ ],
+
+ static_libs: [
+ "libdiagnose_usb",
+ "libqemu_pipe",
+ ],
+
+ shared_libs: [
+ "libasyncio",
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libcutils",
+ "liblog",
+ ],
+}
+
+cc_library {
+ name: "libadbd_services",
+ defaults: ["adb_defaults"],
+ recovery_available: true,
+ compile_multilib: "both",
+
+ srcs: [
+ "daemon/file_sync_service.cpp",
+ "daemon/framebuffer_service.cpp",
+ "daemon/mdns.cpp",
+ "daemon/remount_service.cpp",
+ "daemon/services.cpp",
+ "daemon/set_verity_enable_state_service.cpp",
+ "daemon/shell_service.cpp",
+ "shell_service_protocol.cpp",
+ ],
+
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-Wno-deprecated-declarations",
+ ],
+
+ static_libs: [
+ "libadbd_core",
+ "libavb_user",
+ "libdiagnose_usb",
+ "libqemu_pipe",
+
+ // `daemon/shell_service.cpp` uses selinux_android_setcon(), which is not exposed by
+ // libselinux.
+ "libselinux",
+ ],
+
+ shared_libs: [
+ "libasyncio",
+ "libbase",
+ "libbootloader_message",
+ "libcrypto",
+ "libcrypto_utils",
+ "libcutils",
+ "libext4_utils",
+ "libfec",
+ "libfec_rs",
+ "libfs_mgr",
+ "liblog",
+ "libmdnssd",
+ ],
+}
+
+cc_library {
name: "libadbd",
defaults: ["adb_defaults"],
recovery_available: true,
- // libminadbd wants both, for some reason.
+ // Avoid getting duplicate symbol of android::build::GetBuildNumber().
+ use_version_lib: false,
+
+ // libminadbd wants both, as it's used to build native tests.
compile_multilib: "both",
- srcs: libadb_srcs + libadb_posix_srcs + [
- "daemon/auth.cpp",
- "daemon/usb.cpp",
- "daemon/jdwp_service.cpp",
+
+ // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
+ whole_static_libs: [
+ "libadbd_core",
],
- static_libs: [
+ shared_libs: [
+ "libadbd_services",
"libasyncio",
- "libbootloader_message",
- "libcrypto_utils",
- "libcrypto",
- "libdiagnose_usb",
- "libqemu_pipe",
"libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libcutils",
+ "liblog",
],
export_include_dirs: [
@@ -308,18 +394,10 @@
cc_binary {
name: "adbd",
defaults: ["adb_defaults"],
-
recovery_available: true,
srcs: [
"daemon/main.cpp",
- "daemon/mdns.cpp",
- "daemon/file_sync_service.cpp",
- "daemon/framebuffer_service.cpp",
- "daemon/remount_service.cpp",
- "daemon/set_verity_enable_state_service.cpp",
- "daemon/shell_service.cpp",
- "shell_service_protocol.cpp",
],
cflags: [
@@ -331,27 +409,16 @@
keep_symbols: true,
},
- static_libs: [
+ shared_libs: [
"libadbd",
- "libasyncio",
- "libavb_user",
- "libbootloader_message",
- "libcrypto_utils",
+ "libadbd_services",
+ "libbase",
+ "libcap",
"libcrypto",
- "libdiagnose_usb",
- "libfec",
- "libfec_rs",
- "libfs_mgr",
+ "libcutils",
"liblog",
- "libext4_utils",
- "libmdnssd",
"libminijail",
"libselinux",
- "libsquashfs_utils",
- "libqemu_pipe",
-
- "libbase",
- "libcutils",
],
}
@@ -359,6 +426,7 @@
name: "adbd_test",
defaults: ["adb_defaults"],
srcs: libadb_test_srcs + [
+ "daemon/services.cpp",
"daemon/shell_service.cpp",
"daemon/shell_service_test.cpp",
"shell_service_protocol.cpp",
@@ -368,6 +436,7 @@
static_libs: [
"libadbd",
"libbase",
+ "libbootloader_message",
"libcutils",
"libcrypto_utils",
"libcrypto",
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 19300f6..38c6f62 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -920,13 +920,45 @@
}
#endif /* ADB_HOST */
+bool handle_forward_request(const char* service, atransport* transport, int reply_fd) {
+ return handle_forward_request(service, [transport](std::string*) { return transport; },
+ reply_fd);
+}
+
// Try to handle a network forwarding request.
-// This returns 1 on success, 0 on failure, and -1 to indicate this is not
-// a forwarding-related request.
-int handle_forward_request(const char* service, atransport* transport, int reply_fd) {
+bool handle_forward_request(const char* service,
+ std::function<atransport*(std::string* error)> transport_acquirer,
+ int reply_fd) {
+ if (!strcmp(service, "list-forward")) {
+ // Create the list of forward redirections.
+ std::string listeners = format_listeners();
+#if ADB_HOST
+ SendOkay(reply_fd);
+#endif
+ SendProtocolString(reply_fd, listeners);
+ return true;
+ }
+
+ if (!strcmp(service, "killforward-all")) {
+ remove_all_listeners();
+#if ADB_HOST
+ /* On the host: 1st OKAY is connect, 2nd OKAY is status */
+ SendOkay(reply_fd);
+#endif
+ SendOkay(reply_fd);
+ return true;
+ }
+
if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
// killforward:local
// forward:(norebind:)?local;remote
+ std::string error;
+ atransport* transport = transport_acquirer(&error);
+ if (!transport) {
+ SendFail(reply_fd, error);
+ return true;
+ }
+
bool kill_forward = false;
bool no_rebind = false;
if (android::base::StartsWith(service, "killforward:")) {
@@ -946,17 +978,16 @@
// Check killforward: parameter format: '<local>'
if (pieces.size() != 1 || pieces[0].empty()) {
SendFail(reply_fd, android::base::StringPrintf("bad killforward: %s", service));
- return 1;
+ return true;
}
} else {
// Check forward: parameter format: '<local>;<remote>'
if (pieces.size() != 2 || pieces[0].empty() || pieces[1].empty() || pieces[1][0] == '*') {
SendFail(reply_fd, android::base::StringPrintf("bad forward: %s", service));
- return 1;
+ return true;
}
}
- std::string error;
InstallStatus r;
int resolved_tcp_port = 0;
if (kill_forward) {
@@ -977,7 +1008,7 @@
SendProtocolString(reply_fd, android::base::StringPrintf("%d", resolved_tcp_port));
}
- return 1;
+ return true;
}
std::string message;
@@ -996,9 +1027,10 @@
break;
}
SendFail(reply_fd, message);
- return 1;
+ return true;
}
- return 0;
+
+ return false;
}
#if ADB_HOST
@@ -1186,35 +1218,15 @@
return SendOkay(reply_fd, response);
}
- if (!strcmp(service, "list-forward")) {
- // Create the list of forward redirections.
- std::string listeners = format_listeners();
-#if ADB_HOST
- SendOkay(reply_fd);
-#endif
- return SendProtocolString(reply_fd, listeners);
+ if (handle_forward_request(service,
+ [=](std::string* error) {
+ return acquire_one_transport(type, serial, transport_id, nullptr,
+ error);
+ },
+ reply_fd)) {
+ return 0;
}
- if (!strcmp(service, "killforward-all")) {
- remove_all_listeners();
-#if ADB_HOST
- /* On the host: 1st OKAY is connect, 2nd OKAY is status */
- SendOkay(reply_fd);
-#endif
- SendOkay(reply_fd);
- return 1;
- }
-
- std::string error;
- atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
- if (!t) {
- SendFail(reply_fd, error);
- return 1;
- }
-
- int ret = handle_forward_request(service, t, reply_fd);
- if (ret >= 0)
- return ret - 1;
return -1;
}
diff --git a/adb/adb.h b/adb/adb.h
index 26b5fa1..e6af780 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -143,6 +143,10 @@
#endif
int service_to_fd(const char* name, atransport* transport);
+#if !ADB_HOST
+unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+#endif
+
#if ADB_HOST
asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
#endif
@@ -151,10 +155,13 @@
int init_jdwp(void);
asocket* create_jdwp_service_socket();
asocket* create_jdwp_tracker_service_socket();
-int create_jdwp_connection_fd(int jdwp_pid);
+unique_fd create_jdwp_connection_fd(int jdwp_pid);
#endif
-int handle_forward_request(const char* service, atransport* transport, int reply_fd);
+bool handle_forward_request(const char* service, atransport* transport, int reply_fd);
+bool handle_forward_request(const char* service,
+ std::function<atransport*(std::string* error)> transport_acquirer,
+ int reply_fd);
/* packet allocator */
apacket* get_apacket(void);
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index 346bb4b..fe98737 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -16,6 +16,8 @@
#define TRACE_TAG ADB
+#include "sysdeps.h"
+
#include "bugreport.h"
#include <string>
@@ -24,8 +26,6 @@
#include <android-base/file.h>
#include <android-base/strings.h>
-#include "sysdeps.h"
-
#include "adb_utils.h"
#include "client/file_sync_client.h"
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index a7a94e7..da273fd 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1614,9 +1614,9 @@
return bugreport.DoIt(argc, argv);
} else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
bool reverse = !strcmp(argv[0], "reverse");
- ++argv;
--argc;
if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
+ ++argv;
// Determine the <host-prefix> for this command.
std::string host_prefix;
@@ -1925,7 +1925,8 @@
for (int i = argc - 1; i >= 0; i--) {
const char* file = argv[i];
- if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+ if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+ android::base::EndsWithIgnoreCase(file, ".dm")) {
struct stat sb;
if (stat(file, &sb) != -1) total_size += sb.st_size;
first_apk = i;
@@ -1986,9 +1987,8 @@
}
std::string cmd = android::base::StringPrintf(
- "%s install-write -S %" PRIu64 " %d %d_%s -",
- install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
- android::base::Basename(file).c_str());
+ "%s install-write -S %" PRIu64 " %d %s -", install_cmd.c_str(),
+ static_cast<uint64_t>(sb.st_size), session_id, android::base::Basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 175e82e..b40faee 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -311,7 +311,7 @@
jdwp_process_list_updated();
}
-int create_jdwp_connection_fd(int pid) {
+unique_fd create_jdwp_connection_fd(int pid) {
D("looking for pid %d in JDWP process list", pid);
for (auto& proc : _jdwp_list) {
@@ -320,7 +320,7 @@
if (adb_socketpair(fds) < 0) {
D("%s: socket pair creation failed: %s", __FUNCTION__, strerror(errno));
- return -1;
+ return unique_fd{};
}
D("socketpair: (%d,%d)", fds[0], fds[1]);
@@ -329,11 +329,11 @@
fdevent_add(proc->fde, FDE_WRITE);
}
- return fds[0];
+ return unique_fd{fds[0]};
}
}
D("search failed !!");
- return -1;
+ return unique_fd{};
}
/** VM DEBUG CONTROL SOCKET
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 76a1452..0e79d82 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -92,12 +92,13 @@
return result;
}
-static bool fs_has_shared_blocks(const char* dev) {
+static bool fs_has_shared_blocks(const std::string& mount_point, const std::string& device) {
+ std::string path = mount_point + "/lost+found";
struct statfs fs;
- if (statfs(dev, &fs) == -1 || fs.f_type == EXT4_SUPER_MAGIC) {
+ if (statfs(path.c_str(), &fs) == -1 || fs.f_type != EXT4_SUPER_MAGIC) {
return false;
}
- unique_fd fd(unix_open(dev, O_RDONLY));
+ unique_fd fd(unix_open(device.c_str(), O_RDONLY));
if (fd < 0) {
return false;
}
@@ -216,7 +217,7 @@
}
void remount_service(unique_fd fd, const std::string& cmd) {
- bool user_requested_reboot = cmd != "-R";
+ bool user_requested_reboot = cmd == "-R";
if (getuid() != 0) {
WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
@@ -237,7 +238,7 @@
std::set<std::string> dedup;
for (const auto& partition : partitions) {
std::string dev = find_mount(partition.c_str(), partition == "/");
- if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) {
+ if (dev.empty() || !fs_has_shared_blocks(partition, dev)) {
continue;
}
if (can_unshare_blocks(fd.get(), dev.c_str())) {
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
new file mode 100644
index 0000000..dfcc52d
--- /dev/null
+++ b/adb/daemon/services.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define TRACE_TAG SERVICES
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <cutils/sockets.h>
+#include <log/log_properties.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "services.h"
+#include "socket_spec.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+#include "daemon/file_sync_service.h"
+#include "daemon/framebuffer_service.h"
+#include "daemon/remount_service.h"
+#include "daemon/set_verity_enable_state_service.h"
+#include "daemon/shell_service.h"
+
+void restart_root_service(unique_fd fd) {
+ if (getuid() == 0) {
+ WriteFdExactly(fd.get(), "adbd is already running as root\n");
+ return;
+ }
+ if (!__android_log_is_debuggable()) {
+ WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
+ return;
+ }
+
+ android::base::SetProperty("service.adb.root", "1");
+ WriteFdExactly(fd.get(), "restarting adbd as root\n");
+}
+
+void restart_unroot_service(unique_fd fd) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd.get(), "adbd not running as root\n");
+ return;
+ }
+ android::base::SetProperty("service.adb.root", "0");
+ WriteFdExactly(fd.get(), "restarting adbd as non root\n");
+}
+
+void restart_tcp_service(unique_fd fd, int port) {
+ if (port <= 0) {
+ WriteFdFmt(fd.get(), "invalid port %d\n", port);
+ return;
+ }
+
+ android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
+ WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
+}
+
+void restart_usb_service(unique_fd fd) {
+ android::base::SetProperty("service.adb.tcp.port", "0");
+ WriteFdExactly(fd.get(), "restarting in USB mode\n");
+}
+
+void reboot_service(unique_fd fd, const std::string& arg) {
+ std::string reboot_arg = arg;
+ bool auto_reboot = false;
+
+ if (reboot_arg == "sideload-auto-reboot") {
+ auto_reboot = true;
+ reboot_arg = "sideload";
+ }
+
+ // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
+ // in the command file.
+ if (reboot_arg == "sideload") {
+ if (getuid() != 0) {
+ WriteFdExactly(fd.get(), "'adb root' is required for 'adb reboot sideload'.\n");
+ return;
+ }
+
+ const std::vector<std::string> options = {auto_reboot ? "--sideload_auto_reboot"
+ : "--sideload"};
+ std::string err;
+ if (!write_bootloader_message(options, &err)) {
+ D("Failed to set bootloader message: %s", err.c_str());
+ return;
+ }
+
+ reboot_arg = "recovery";
+ }
+
+ sync();
+
+ if (reboot_arg.empty()) reboot_arg = "adb";
+ std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
+ if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+ WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
+ return;
+ }
+
+ // Don't return early. Give the reboot command time to take effect
+ // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+ while (true) {
+ pause();
+ }
+}
+
+void reconnect_service(unique_fd fd, atransport* t) {
+ WriteFdExactly(fd.get(), "done");
+ kick_transport(t);
+}
+
+unique_fd reverse_service(const char* command, atransport* transport) {
+ int s[2];
+ if (adb_socketpair(s)) {
+ PLOG(ERROR) << "cannot create service socket pair.";
+ return unique_fd{};
+ }
+ VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+ if (!handle_forward_request(command, transport, s[1])) {
+ SendFail(s[1], "not a reverse forwarding command");
+ }
+ adb_close(s[1]);
+ return unique_fd{s[0]};
+}
+
+// Shell service string can look like:
+// shell[,arg1,arg2,...]:[command]
+unique_fd ShellService(const std::string& args, const atransport* transport) {
+ size_t delimiter_index = args.find(':');
+ if (delimiter_index == std::string::npos) {
+ LOG(ERROR) << "No ':' found in shell service arguments: " << args;
+ return unique_fd{};
+ }
+
+ const std::string service_args = args.substr(0, delimiter_index);
+ const std::string command = args.substr(delimiter_index + 1);
+
+ // Defaults:
+ // PTY for interactive, raw for non-interactive.
+ // No protocol.
+ // $TERM set to "dumb".
+ SubprocessType type(command.empty() ? SubprocessType::kPty : SubprocessType::kRaw);
+ SubprocessProtocol protocol = SubprocessProtocol::kNone;
+ std::string terminal_type = "dumb";
+
+ for (const std::string& arg : android::base::Split(service_args, ",")) {
+ if (arg == kShellServiceArgRaw) {
+ type = SubprocessType::kRaw;
+ } else if (arg == kShellServiceArgPty) {
+ type = SubprocessType::kPty;
+ } else if (arg == kShellServiceArgShellProtocol) {
+ protocol = SubprocessProtocol::kShell;
+ } else if (android::base::StartsWith(arg, "TERM=")) {
+ terminal_type = arg.substr(5);
+ } else if (!arg.empty()) {
+ // This is not an error to allow for future expansion.
+ LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
+ }
+ }
+
+ return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+}
+
+unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
+ if (!strncmp("dev:", name, 4)) {
+ return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
+ } else if (!strncmp(name, "framebuffer:", 12)) {
+ return create_service_thread("fb", framebuffer_service);
+ } else if (!strncmp(name, "jdwp:", 5)) {
+ return create_jdwp_connection_fd(atoi(name + 5));
+ } else if (!strncmp(name, "shell", 5)) {
+ return ShellService(name + 5, transport);
+ } else if (!strncmp(name, "exec:", 5)) {
+ return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+ } else if (!strncmp(name, "sync:", 5)) {
+ return create_service_thread("sync", file_sync_service);
+ } else if (!strncmp(name, "remount:", 8)) {
+ std::string options(name + strlen("remount:"));
+ return create_service_thread("remount",
+ std::bind(remount_service, std::placeholders::_1, options));
+ } else if (!strncmp(name, "reboot:", 7)) {
+ std::string arg(name + strlen("reboot:"));
+ return create_service_thread("reboot",
+ std::bind(reboot_service, std::placeholders::_1, arg));
+ } else if (!strncmp(name, "root:", 5)) {
+ return create_service_thread("root", restart_root_service);
+ } else if (!strncmp(name, "unroot:", 7)) {
+ return create_service_thread("unroot", restart_unroot_service);
+ } else if (!strncmp(name, "backup:", 7)) {
+ return StartSubprocess(
+ android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
+ nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+ } else if (!strncmp(name, "restore:", 8)) {
+ return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
+ SubprocessProtocol::kNone);
+ } else if (!strncmp(name, "tcpip:", 6)) {
+ int port;
+ if (sscanf(name + 6, "%d", &port) != 1) {
+ return unique_fd{};
+ }
+ return create_service_thread("tcp",
+ std::bind(restart_tcp_service, std::placeholders::_1, port));
+ } else if (!strncmp(name, "usb:", 4)) {
+ return create_service_thread("usb", restart_usb_service);
+ } else if (!strncmp(name, "reverse:", 8)) {
+ return reverse_service(name + 8, transport);
+ } else if (!strncmp(name, "disable-verity:", 15)) {
+ return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+ std::placeholders::_1, false));
+ } else if (!strncmp(name, "enable-verity:", 15)) {
+ return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+ std::placeholders::_1, true));
+ } else if (!strcmp(name, "reconnect")) {
+ return create_service_thread(
+ "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
+ }
+ return unique_fd{};
+}
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 8c98c2d..2b6ec21 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -66,7 +66,7 @@
fec_verity_metadata metadata;
if (!fh.get_verity_metadata(metadata)) {
- WriteFdFmt(fd, "Couldn't find verity metadata!\n");
+ WriteFdExactly(fd, "Couldn't find verity metadata!\n");
return false;
}
@@ -109,12 +109,12 @@
bool verity_enabled;
if (is_avb_device_locked()) {
- WriteFdFmt(fd, "Device is locked. Please unlock the device first\n");
+ WriteFdExactly(fd, "Device is locked. Please unlock the device first\n");
return false;
}
if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
- WriteFdFmt(fd, "Error getting verity state. Try adb root first?\n");
+ WriteFdExactly(fd, "Error getting verity state. Try adb root first?\n");
return false;
}
@@ -124,7 +124,7 @@
}
if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
- WriteFdFmt(fd, "Error setting verity\n");
+ WriteFdExactly(fd, "Error setting verity\n");
return false;
}
@@ -150,7 +150,7 @@
}
if (!android::base::GetBoolProperty("ro.secure", false)) {
- WriteFdFmt(fd.get(), "verity not enabled - ENG build\n");
+ WriteFdExactly(fd.get(), "verity not enabled - ENG build\n");
return;
}
}
@@ -158,7 +158,7 @@
// Should never be possible to disable dm-verity on a USER build
// regardless of using AVB or VB1.0.
if (!__android_log_is_debuggable()) {
- WriteFdFmt(fd.get(), "verity cannot be disabled/enabled - USER build\n");
+ WriteFdExactly(fd.get(), "verity cannot be disabled/enabled - USER build\n");
return;
}
@@ -166,7 +166,7 @@
// Yep, the system is using AVB.
AvbOps* ops = avb_ops_user_new();
if (ops == nullptr) {
- WriteFdFmt(fd.get(), "Error getting AVB ops\n");
+ WriteFdExactly(fd.get(), "Error getting AVB ops\n");
return;
}
if (set_avb_verity_enabled_state(fd.get(), ops, enable)) {
@@ -179,7 +179,7 @@
// read all fstab entries at once from all sources
fstab = fs_mgr_read_fstab_default();
if (!fstab) {
- WriteFdFmt(fd.get(), "Failed to read fstab\nMaybe run adb root?\n");
+ WriteFdExactly(fd.get(), "Failed to read fstab\nMaybe run adb root?\n");
return;
}
@@ -195,6 +195,6 @@
}
if (any_changed) {
- WriteFdFmt(fd.get(), "Now reboot your device for settings to take effect\n");
+ WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
}
}
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index b042e5f..01097ac 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -716,38 +716,37 @@
} // namespace
// Create a pipe containing the error.
-static int ReportError(SubprocessProtocol protocol, const std::string& message) {
- int pipefd[2];
- if (pipe(pipefd) != 0) {
- LOG(ERROR) << "failed to create pipe to report error";
- return -1;
+static unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
+ unique_fd read, write;
+ if (!Pipe(&read, &write)) {
+ PLOG(ERROR) << "failed to create pipe to report error";
+ return unique_fd{};
}
std::string buf = android::base::StringPrintf("error: %s\n", message.c_str());
if (protocol == SubprocessProtocol::kShell) {
ShellProtocol::Id id = ShellProtocol::kIdStderr;
uint32_t length = buf.length();
- WriteFdExactly(pipefd[1], &id, sizeof(id));
- WriteFdExactly(pipefd[1], &length, sizeof(length));
+ WriteFdExactly(write.get(), &id, sizeof(id));
+ WriteFdExactly(write.get(), &length, sizeof(length));
}
- WriteFdExactly(pipefd[1], buf.data(), buf.length());
+ WriteFdExactly(write.get(), buf.data(), buf.length());
if (protocol == SubprocessProtocol::kShell) {
ShellProtocol::Id id = ShellProtocol::kIdExit;
uint32_t length = 1;
char exit_code = 126;
- WriteFdExactly(pipefd[1], &id, sizeof(id));
- WriteFdExactly(pipefd[1], &length, sizeof(length));
- WriteFdExactly(pipefd[1], &exit_code, sizeof(exit_code));
+ WriteFdExactly(write.get(), &id, sizeof(id));
+ WriteFdExactly(write.get(), &length, sizeof(length));
+ WriteFdExactly(write.get(), &exit_code, sizeof(exit_code));
}
- adb_close(pipefd[1]);
- return pipefd[0];
+ return read;
}
-int StartSubprocess(const char* name, const char* terminal_type,
- SubprocessType type, SubprocessProtocol protocol) {
+unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol) {
D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
type == SubprocessType::kRaw ? "raw" : "PTY",
protocol == SubprocessProtocol::kNone ? "none" : "shell",
@@ -774,5 +773,5 @@
return ReportError(protocol, error);
}
- return local_socket.release();
+ return local_socket;
}
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 3ffffa7..2a48923 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -16,6 +16,8 @@
#pragma once
+#include "adb_unique_fd.h"
+
enum class SubprocessType {
kPty,
kRaw,
@@ -30,5 +32,5 @@
// shell is started, otherwise |name| is executed non-interactively.
//
// Returns an open FD connected to the subprocess or -1 on failure.
-int StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
- SubprocessProtocol protocol);
+unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol);
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
index d073537..323bcec 100644
--- a/adb/daemon/shell_service_test.cpp
+++ b/adb/daemon/shell_service_test.cpp
@@ -55,7 +55,7 @@
static sighandler_t saved_sigpipe_handler_;
- int subprocess_fd_ = -1;
+ unique_fd subprocess_fd_;
};
sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
@@ -67,10 +67,6 @@
}
void ShellServiceTest::CleanupTestSubprocess() {
- if (subprocess_fd_ >= 0) {
- adb_close(subprocess_fd_);
- subprocess_fd_ = -1;
- }
}
namespace {
diff --git a/adb/services.cpp b/adb/services.cpp
index 639bb46..4b033bd 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -24,39 +24,15 @@
#include <stdlib.h>
#include <string.h>
-#ifndef _WIN32
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#endif
-
#include <thread>
-
-#include <android-base/file.h>
-#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/sockets.h>
-#if !ADB_HOST
-#include <android-base/properties.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
-#include <log/log_properties.h>
-#endif
-
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
-#if !ADB_HOST
-#include "daemon/file_sync_service.h"
-#include "daemon/framebuffer_service.h"
-#include "daemon/remount_service.h"
-#include "daemon/set_verity_enable_state_service.h"
-#include "daemon/shell_service.h"
-#endif
#include "services.h"
#include "socket_spec.h"
#include "sysdeps.h"
@@ -70,157 +46,7 @@
func(std::move(fd));
}
-#if !ADB_HOST
-
-void restart_root_service(unique_fd fd) {
- if (getuid() == 0) {
- WriteFdExactly(fd.get(), "adbd is already running as root\n");
- return;
- }
- if (!__android_log_is_debuggable()) {
- WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
- return;
- }
-
- android::base::SetProperty("service.adb.root", "1");
- WriteFdExactly(fd.get(), "restarting adbd as root\n");
-}
-
-void restart_unroot_service(unique_fd fd) {
- if (getuid() != 0) {
- WriteFdExactly(fd.get(), "adbd not running as root\n");
- return;
- }
- android::base::SetProperty("service.adb.root", "0");
- WriteFdExactly(fd.get(), "restarting adbd as non root\n");
-}
-
-void restart_tcp_service(unique_fd fd, int port) {
- if (port <= 0) {
- WriteFdFmt(fd.get(), "invalid port %d\n", port);
- return;
- }
-
- android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
- WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
-}
-
-void restart_usb_service(unique_fd fd) {
- android::base::SetProperty("service.adb.tcp.port", "0");
- WriteFdExactly(fd.get(), "restarting in USB mode\n");
-}
-
-bool reboot_service_impl(unique_fd fd, const std::string& arg) {
- std::string reboot_arg = arg;
- bool auto_reboot = false;
-
- if (reboot_arg == "sideload-auto-reboot") {
- auto_reboot = true;
- reboot_arg = "sideload";
- }
-
- // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
- // in the command file.
- if (reboot_arg == "sideload") {
- if (getuid() != 0) {
- WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
- return false;
- }
-
- const std::vector<std::string> options = {
- auto_reboot ? "--sideload_auto_reboot" : "--sideload"
- };
- std::string err;
- if (!write_bootloader_message(options, &err)) {
- D("Failed to set bootloader message: %s", err.c_str());
- return false;
- }
-
- reboot_arg = "recovery";
- }
-
- sync();
-
- if (reboot_arg.empty()) reboot_arg = "adb";
- std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
- if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
- WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
- return false;
- }
-
- return true;
-}
-
-void reboot_service(unique_fd fd, const std::string& arg) {
- if (!reboot_service_impl(std::move(fd), arg)) {
- return;
- }
- // Don't return early. Give the reboot command time to take effect
- // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
- while (true) {
- pause();
- }
-}
-
-void reconnect_service(unique_fd fd, atransport* t) {
- WriteFdExactly(fd, "done");
- kick_transport(t);
-}
-
-int reverse_service(const char* command, atransport* transport) {
- int s[2];
- if (adb_socketpair(s)) {
- PLOG(ERROR) << "cannot create service socket pair.";
- return -1;
- }
- VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
- if (handle_forward_request(command, transport, s[1]) < 0) {
- SendFail(s[1], "not a reverse forwarding command");
- }
- adb_close(s[1]);
- return s[0];
-}
-
-// Shell service string can look like:
-// shell[,arg1,arg2,...]:[command]
-int ShellService(const std::string& args, const atransport* transport) {
- size_t delimiter_index = args.find(':');
- if (delimiter_index == std::string::npos) {
- LOG(ERROR) << "No ':' found in shell service arguments: " << args;
- return -1;
- }
-
- const std::string service_args = args.substr(0, delimiter_index);
- const std::string command = args.substr(delimiter_index + 1);
-
- // Defaults:
- // PTY for interactive, raw for non-interactive.
- // No protocol.
- // $TERM set to "dumb".
- SubprocessType type(command.empty() ? SubprocessType::kPty
- : SubprocessType::kRaw);
- SubprocessProtocol protocol = SubprocessProtocol::kNone;
- std::string terminal_type = "dumb";
-
- for (const std::string& arg : android::base::Split(service_args, ",")) {
- if (arg == kShellServiceArgRaw) {
- type = SubprocessType::kRaw;
- } else if (arg == kShellServiceArgPty) {
- type = SubprocessType::kPty;
- } else if (arg == kShellServiceArgShellProtocol) {
- protocol = SubprocessProtocol::kShell;
- } else if (android::base::StartsWith(arg, "TERM=")) {
- terminal_type = arg.substr(5);
- } else if (!arg.empty()) {
- // This is not an error to allow for future expansion.
- LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
- }
- }
-
- return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
-}
-
-#endif // !ADB_HOST
+} // namespace
unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func) {
int s[2];
@@ -245,8 +71,6 @@
return unique_fd(s[0]);
}
-} // namespace
-
int service_to_fd(const char* name, atransport* transport) {
int ret = -1;
@@ -256,65 +80,12 @@
if (ret < 0) {
LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
}
+ } else {
#if !ADB_HOST
- } else if(!strncmp("dev:", name, 4)) {
- ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
- } else if (!strncmp(name, "framebuffer:", 12)) {
- ret = create_service_thread("fb", framebuffer_service).release();
- } else if (!strncmp(name, "jdwp:", 5)) {
- ret = create_jdwp_connection_fd(atoi(name + 5));
- } else if (!strncmp(name, "shell", 5)) {
- ret = ShellService(name + 5, transport);
- } else if (!strncmp(name, "exec:", 5)) {
- ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "sync:", 5)) {
- ret = create_service_thread("sync", file_sync_service).release();
- } else if (!strncmp(name, "remount:", 8)) {
- std::string options(name + strlen("remount:"));
- ret = create_service_thread("remount",
- std::bind(remount_service, std::placeholders::_1, options))
- .release();
- } else if (!strncmp(name, "reboot:", 7)) {
- std::string arg(name + strlen("reboot:"));
- ret = create_service_thread("reboot", std::bind(reboot_service, std::placeholders::_1, arg))
- .release();
- } else if (!strncmp(name, "root:", 5)) {
- ret = create_service_thread("root", restart_root_service).release();
- } else if (!strncmp(name, "unroot:", 7)) {
- ret = create_service_thread("unroot", restart_unroot_service).release();
- } else if (!strncmp(name, "backup:", 7)) {
- ret = StartSubprocess(
- android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
- nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "restore:", 8)) {
- ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
- SubprocessProtocol::kNone);
- } else if (!strncmp(name, "tcpip:", 6)) {
- int port;
- if (sscanf(name + 6, "%d", &port) != 1) {
- return -1;
- }
- ret = create_service_thread("tcp",
- std::bind(restart_tcp_service, std::placeholders::_1, port))
- .release();
- } else if (!strncmp(name, "usb:", 4)) {
- ret = create_service_thread("usb", restart_usb_service).release();
- } else if (!strncmp(name, "reverse:", 8)) {
- ret = reverse_service(name + 8, transport);
- } else if (!strncmp(name, "disable-verity:", 15)) {
- ret = create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
- std::placeholders::_1, false))
- .release();
- } else if (!strncmp(name, "enable-verity:", 15)) {
- ret = create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
- std::placeholders::_1, true))
- .release();
- } else if (!strcmp(name, "reconnect")) {
- ret = create_service_thread("reconnect",
- std::bind(reconnect_service, std::placeholders::_1, transport))
- .release();
+ ret = daemon_service_to_fd(name, transport).release();
#endif
}
+
if (ret >= 0) {
close_on_exec(ret);
}
diff --git a/adb/services.h b/adb/services.h
index 0428ca4..0ce25ba 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -17,8 +17,11 @@
#ifndef SERVICES_H_
#define SERVICES_H_
+#include "adb_unique_fd.h"
+
constexpr char kShellServiceArgRaw[] = "raw";
constexpr char kShellServiceArgPty[] = "pty";
constexpr char kShellServiceArgShellProtocol[] = "v2";
+unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func);
#endif // SERVICES_H_
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 793c283..3c74c75 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -50,6 +50,7 @@
#include "adb_trace.h"
#include "adb_utils.h"
#include "fdevent.h"
+#include "sysdeps/chrono.h"
static void register_transport(atransport* transport);
static void remove_transport(atransport* transport);
@@ -80,6 +81,7 @@
~ScopedAssumeLocked() RELEASE() {}
};
+#if ADB_HOST
// Tracks and handles atransport*s that are attempting reconnection.
class ReconnectHandler {
public:
@@ -102,12 +104,18 @@
// Tracks a reconnection attempt.
struct ReconnectAttempt {
atransport* transport;
- std::chrono::system_clock::time_point deadline;
+ std::chrono::steady_clock::time_point reconnect_time;
size_t attempts_left;
+
+ bool operator<(const ReconnectAttempt& rhs) const {
+ // std::priority_queue returns the largest element first, so we want attempts that have
+ // less time remaining (i.e. smaller time_points) to compare greater.
+ return reconnect_time > rhs.reconnect_time;
+ }
};
// Only retry for up to one minute.
- static constexpr const std::chrono::seconds kDefaultTimeout = std::chrono::seconds(10);
+ static constexpr const std::chrono::seconds kDefaultTimeout = 10s;
static constexpr const size_t kMaxAttempts = 6;
// Protects all members.
@@ -115,7 +123,7 @@
bool running_ GUARDED_BY(reconnect_mutex_) = true;
std::thread handler_thread_;
std::condition_variable reconnect_cv_;
- std::queue<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
+ std::priority_queue<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
DISALLOW_COPY_AND_ASSIGN(ReconnectHandler);
};
@@ -137,7 +145,7 @@
// Drain the queue to free all resources.
std::lock_guard<std::mutex> lock(reconnect_mutex_);
while (!reconnect_queue_.empty()) {
- ReconnectAttempt attempt = reconnect_queue_.front();
+ ReconnectAttempt attempt = reconnect_queue_.top();
reconnect_queue_.pop();
remove_transport(attempt.transport);
}
@@ -148,9 +156,10 @@
{
std::lock_guard<std::mutex> lock(reconnect_mutex_);
if (!running_) return;
- reconnect_queue_.emplace(ReconnectAttempt{
- transport, std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
- ReconnectHandler::kMaxAttempts});
+ // Arbitrary sleep to give adbd time to get ready, if we disconnected because it exited.
+ auto reconnect_time = std::chrono::steady_clock::now() + 250ms;
+ reconnect_queue_.emplace(
+ ReconnectAttempt{transport, reconnect_time, ReconnectHandler::kMaxAttempts});
}
reconnect_cv_.notify_one();
}
@@ -162,15 +171,27 @@
std::unique_lock<std::mutex> lock(reconnect_mutex_);
ScopedAssumeLocked assume_lock(reconnect_mutex_);
- auto deadline = std::chrono::time_point<std::chrono::system_clock>::max();
- if (!reconnect_queue_.empty()) deadline = reconnect_queue_.front().deadline;
- reconnect_cv_.wait_until(lock, deadline, [&]() REQUIRES(reconnect_mutex_) {
- return !running_ ||
- (!reconnect_queue_.empty() && reconnect_queue_.front().deadline < deadline);
- });
+ if (!reconnect_queue_.empty()) {
+ // FIXME: libstdc++ (used on Windows) implements condition_variable with
+ // system_clock as its clock, so we're probably hosed if the clock changes,
+ // even if we use steady_clock throughout. This problem goes away once we
+ // switch to libc++.
+ reconnect_cv_.wait_until(lock, reconnect_queue_.top().reconnect_time);
+ } else {
+ reconnect_cv_.wait(lock);
+ }
if (!running_) return;
- attempt = reconnect_queue_.front();
+ if (reconnect_queue_.empty()) continue;
+
+ // Go back to sleep in case |reconnect_cv_| woke up spuriously and we still
+ // have more time to wait for the current attempt.
+ auto now = std::chrono::steady_clock::now();
+ if (reconnect_queue_.top().reconnect_time > now) {
+ continue;
+ }
+
+ attempt = reconnect_queue_.top();
reconnect_queue_.pop();
if (attempt.transport->kicked()) {
D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str());
@@ -191,9 +212,9 @@
std::lock_guard<std::mutex> lock(reconnect_mutex_);
reconnect_queue_.emplace(ReconnectAttempt{
- attempt.transport,
- std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
- attempt.attempts_left - 1});
+ attempt.transport,
+ std::chrono::steady_clock::now() + ReconnectHandler::kDefaultTimeout,
+ attempt.attempts_left - 1});
continue;
}
@@ -204,6 +225,8 @@
static auto& reconnect_handler = *new ReconnectHandler();
+#endif
+
} // namespace
TransportId NextTransportId() {
@@ -677,9 +700,11 @@
update_transports();
}
+#if ADB_HOST
void init_reconnect_handler(void) {
reconnect_handler.Start();
}
+#endif
void init_transport_registration(void) {
int s[2];
@@ -698,7 +723,9 @@
}
void kick_all_transports() {
+#if ADB_HOST
reconnect_handler.Stop();
+#endif
// To avoid only writing part of a packet to a transport after exit, kick all transports.
std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto t : transport_list) {
@@ -736,13 +763,19 @@
t->ref_count--;
if (t->ref_count == 0) {
t->connection()->Stop();
+#if ADB_HOST
if (t->IsTcpDevice() && !t->kicked()) {
- D("transport: %s unref (attempting reconnection) %d", t->serial.c_str(), t->kicked());
+ D("transport: %s unref (attempting reconnection)", t->serial.c_str());
reconnect_handler.TrackTransport(t);
} else {
D("transport: %s unref (kicking and closing)", t->serial.c_str());
remove_transport(t);
}
+#else
+ D("transport: %s unref (kicking and closing)", t->serial.c_str());
+ remove_transport(t);
+#endif
+
} else {
D("transport: %s unref (count=%zu)", t->serial.c_str(), t->ref_count);
}
@@ -1157,17 +1190,11 @@
}
#endif // ADB_HOST
-int register_socket_transport(unique_fd s, const char* serial, int port, int local,
+int register_socket_transport(unique_fd s, std::string serial, int port, int local,
atransport::ReconnectCallback reconnect) {
atransport* t = new atransport(std::move(reconnect), kCsOffline);
- if (!serial) {
- char buf[32];
- snprintf(buf, sizeof(buf), "T-%p", t);
- serial = buf;
- }
-
- D("transport: %s init'ing for socket %d, on port %d", serial, s.get(), port);
+ D("transport: %s init'ing for socket %d, on port %d", serial.c_str(), s.get(), port);
if (init_socket_transport(t, std::move(s), port, local) < 0) {
delete t;
return -1;
@@ -1175,7 +1202,7 @@
std::unique_lock<std::recursive_mutex> lock(transport_lock);
for (const auto& transport : pending_list) {
- if (strcmp(serial, transport->serial.c_str()) == 0) {
+ if (serial == transport->serial) {
VLOG(TRANSPORT) << "socket transport " << transport->serial
<< " is already in pending_list and fails to register";
delete t;
@@ -1184,7 +1211,7 @@
}
for (const auto& transport : transport_list) {
- if (strcmp(serial, transport->serial.c_str()) == 0) {
+ if (serial == transport->serial) {
VLOG(TRANSPORT) << "socket transport " << transport->serial
<< " is already in transport_list and fails to register";
delete t;
@@ -1192,8 +1219,8 @@
}
}
+ t->serial = std::move(serial);
pending_list.push_front(t);
- t->serial = serial;
lock.unlock();
diff --git a/adb/transport.h b/adb/transport.h
index 1844ae8..4bfc2ce 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -364,7 +364,7 @@
void connect_device(const std::string& address, std::string* response);
/* cause new transports to be init'd and added to the list */
-int register_socket_transport(unique_fd s, const char* serial, int port, int local,
+int register_socket_transport(unique_fd s, std::string serial, int port, int local,
atransport::ReconnectCallback reconnect);
// This should only be used for transports with connection_state == kCsNoPerm.
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9398ceb..b20dee9 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -125,8 +125,7 @@
return init_socket_transport(t, std::move(fd), port, 0) >= 0;
};
- int ret =
- register_socket_transport(std::move(fd), serial.c_str(), port, 0, std::move(reconnect));
+ int ret = register_socket_transport(std::move(fd), serial, port, 0, std::move(reconnect));
if (ret < 0) {
if (ret == -EALREADY) {
*response = android::base::StringPrintf("already connected to %s", serial.c_str());
@@ -162,7 +161,7 @@
close_on_exec(fd.get());
disable_tcp_nagle(fd.get());
std::string serial = getEmulatorSerialString(console_port);
- if (register_socket_transport(std::move(fd), serial.c_str(), adb_port, 1,
+ if (register_socket_transport(std::move(fd), std::move(serial), adb_port, 1,
[](atransport*) { return false; }) == 0) {
return 0;
}
@@ -265,7 +264,7 @@
close_on_exec(fd.get());
disable_tcp_nagle(fd.get());
std::string serial = android::base::StringPrintf("host-%d", fd.get());
- register_socket_transport(std::move(fd), serial.c_str(), port, 1,
+ register_socket_transport(std::move(fd), std::move(serial), port, 1,
[](atransport*) { return false; });
}
}
@@ -362,7 +361,7 @@
* exchange. */
std::string serial = android::base::StringPrintf("host-%d", fd.get());
WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
- register_socket_transport(std::move(fd), serial.c_str(), port, 1,
+ register_socket_transport(std::move(fd), std::move(serial), port, 1,
[](atransport*) { return false; });
}
@@ -454,10 +453,6 @@
return it->second;
}
-std::string getEmulatorSerialString(int console_port) {
- return android::base::StringPrintf("emulator-%d", console_port);
-}
-
atransport* find_emulator_transport_by_adb_port(int adb_port) {
std::lock_guard<std::mutex> lock(local_transports_lock);
return find_emulator_transport_by_adb_port_locked(adb_port);
@@ -468,6 +463,10 @@
}
#endif
+std::string getEmulatorSerialString(int console_port) {
+ return android::base::StringPrintf("emulator-%d", console_port);
+}
+
int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
int fail = 0;
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index aea6ce6..f94cc25 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -447,11 +447,6 @@
private:
const std::unique_ptr<LogMessageData> data_;
- // TODO(b/35361699): remove these symbols once all prebuilds stop using it.
- LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, int error);
- static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
- const char* msg);
-
DISALLOW_COPY_AND_ASSIGN(LogMessage);
};
diff --git a/base/logging.cpp b/base/logging.cpp
index 35054ac..d60d91d 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -410,10 +410,6 @@
const char* tag, int error)
: data_(new LogMessageData(file, line, id, severity, tag, error)) {}
-LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
- int error)
- : LogMessage(file, line, id, severity, nullptr, error) {}
-
LogMessage::~LogMessage() {
// Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
if (!WOULD_LOG(data_->GetSeverity())) {
@@ -470,11 +466,6 @@
}
}
-void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
- const char* message) {
- LogLine(file, line, id, severity, nullptr, message);
-}
-
LogSeverity GetMinimumLogSeverity() {
return gMinimumLogSeverity;
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5aef85e..fb06d19 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -114,20 +114,26 @@
bool is_optional;
bool is_secondary;
} images[] = {
- // clang-format off
+ // clang-format off
{ "boot", "boot.img", "boot.sig", "boot", false, false },
{ nullptr, "boot_other.img", "boot.sig", "boot", true, true },
{ "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, false },
{ "dts", "dt.img", "dt.sig", "dts", true, false },
{ "odm", "odm.img", "odm.sig", "odm", true, false },
{ "product", "product.img", "product.sig", "product", true, false },
+ { "product-services",
+ "product-services.img",
+ "product-services.sig",
+ "product-services",
+ true, false },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, false },
+ { "super", "super.img", "super.sig", "super", true, false },
{ "system", "system.img", "system.sig", "system", false, false },
{ nullptr, "system_other.img", "system.sig", "system", true, true },
{ "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, false },
{ "vendor", "vendor.img", "vendor.sig", "vendor", true, false },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, true },
- // clang-format on
+ // clang-format on
};
static std::string find_item_given_name(const char* img_name) {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index c308420..55ca65d 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -134,7 +134,7 @@
return ret;
}
- std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:d:]]+)");
+ std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:xdigit:]]+)");
std::smatch sm;
for (auto& s : all) {
@@ -264,11 +264,16 @@
std::vector<std::string>* info) {
RetCode ret;
int dsize;
- if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize)) || dsize == 0) {
- error_ = "Upload request failed";
+ if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize))) {
+ error_ = "Upload request failed: " + error_;
return ret;
}
+ if (!dsize) {
+ error_ = "Upload request failed, device reports 0 bytes available";
+ return BAD_DEV_RESP;
+ }
+
std::vector<char> data;
data.resize(dsize);
@@ -462,6 +467,10 @@
}
RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
+ // ioctl on 0-length buffer causes freezing
+ if (!size) {
+ return BAD_ARG;
+ }
// Write the buffer
ssize_t tmp = transport->Write(buf, size);
@@ -517,7 +526,7 @@
// Now we need to send a multiple of chunk size
size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
- if (SendBuffer(data + total, nbytes)) {
+ if (nbytes && SendBuffer(data + total, nbytes)) { // Don't send a ZLP
error_ = ErrnoStr("Send failed in SparseWriteCallback()");
return -1;
}
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 9fdd317..e8711cb 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -103,7 +103,7 @@
/* HELPERS */
void SetInfoCallback(std::function<void(std::string&)> info);
- const std::string RCString(RetCode rc);
+ static const std::string RCString(RetCode rc);
std::string Error();
RetCode WaitForDisconnect();
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index bf69096..196321c 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -56,20 +56,21 @@
"liblog",
"libsquashfs_utils",
"libselinux",
+ "liblp",
],
static_libs: [
"libavb",
"libfstab",
"libdm",
- "liblp",
],
export_static_lib_headers: [
"libfstab",
"libdm",
+ ],
+ export_shared_lib_headers: [
"liblp",
],
whole_static_libs: [
- "liblp",
"liblogwrap",
"libdm",
"libfstab",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index b3df811..5f57182 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1052,6 +1052,9 @@
return FS_MGR_DOMNT_FAILED;
}
+ // Run fsck if needed
+ prepare_fs_for_mount(rec->blk_device, rec);
+
int ret = __mount(rec->blk_device, rec->mount_point, rec);
if (ret) {
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
@@ -1177,8 +1180,8 @@
{
int ret;
- ret = mount("tmpfs", n_name, "tmpfs",
- MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
+ ret = mount("tmpfs", n_name, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV | MS_NOEXEC,
+ CRYPTO_TMPFS_OPTIONS);
if (ret < 0) {
LERROR << "Cannot mount tmpfs filesystem at " << n_name;
return -1;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 05e03e1..aa68ceb 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -133,7 +133,11 @@
bool DestroyLogicalPartition(const std::string& name) {
DeviceMapper& dm = DeviceMapper::Instance();
- return dm.DeleteDevice(name);
+ if (!dm.DeleteDevice(name)) {
+ return false;
+ }
+ LINFO << "Unmapped logical partition " << name;
+ return true;
}
} // namespace fs_mgr
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a5b3fe8..8b46d64 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -811,7 +811,7 @@
std::string default_fstab;
// Use different fstab paths for normal boot and recovery boot, respectively
- if (access("/sbin/recovery", F_OK) == 0) {
+ if (access("/system/bin/recovery", F_OK) == 0) {
default_fstab = "/etc/recovery.fstab";
} else { // normal boot
default_fstab = get_fstab_path();
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 1434b21..89282db 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -14,7 +14,7 @@
// limitations under the License.
//
-cc_library_static {
+cc_library {
name: "liblp",
host_supported: true,
recovery_available: true,
@@ -29,17 +29,14 @@
"utility.cpp",
"writer.cpp",
],
- static_libs: [
+ shared_libs: [
"libbase",
"liblog",
"libcrypto",
"libcrypto_utils",
"libsparse",
- ],
- whole_static_libs: [
"libext2_uuid",
"libext4_utils",
- "libsparse",
"libz",
],
export_include_dirs: ["include"],
@@ -51,12 +48,9 @@
cppflags: [
"-Wno-unused-parameter",
],
- static_libs: [
- "libbase",
- "liblog",
- "libcrypto",
- "libcrypto_utils",
+ shared_libs: [
"liblp",
+ "libbase",
"libfs_mgr",
],
srcs: [
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 9d710f9..2015e4d 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -48,10 +48,20 @@
PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
return false;
}
- if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
+
+ int alignment_offset;
+ if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
return false;
}
+ int logical_block_size;
+ if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+ return false;
+ }
+
+ device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
+ device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
return true;
#else
(void)block_device;
@@ -74,6 +84,19 @@
void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+
+ if (LinearExtent* new_extent = extent->AsLinearExtent()) {
+ if (!extents_.empty() && extents_.back()->AsLinearExtent() &&
+ extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) {
+ // If the previous extent can be merged into this new one, do so
+ // to avoid creating unnecessary extents.
+ LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
+ extent = std::make_unique<LinearExtent>(
+ prev_extent->num_sectors() + new_extent->num_sectors(),
+ prev_extent->physical_sector());
+ extents_.pop_back();
+ }
+ }
extents_.push_back(std::move(extent));
}
@@ -82,11 +105,7 @@
extents_.clear();
}
-void Partition::ShrinkTo(uint64_t requested_size) {
- uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
- if (size_ <= aligned_size) {
- return;
- }
+void Partition::ShrinkTo(uint64_t aligned_size) {
if (aligned_size == 0) {
RemoveExtents();
return;
@@ -106,7 +125,7 @@
sectors_to_remove -= extent->num_sectors();
extents_.pop_back();
}
- DCHECK(size_ == requested_size);
+ DCHECK(size_ == aligned_size);
}
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
@@ -182,6 +201,7 @@
device_info_.alignment = geometry_.alignment;
device_info_.alignment_offset = geometry_.alignment_offset;
+ device_info_.logical_block_size = geometry_.logical_block_size;
return true;
}
@@ -205,6 +225,10 @@
LERROR << "Block device size must be a multiple of 512.";
return false;
}
+ if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) {
+ LERROR << "Logical block size must be a multiple of 512.";
+ return false;
+ }
if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
LERROR << "Alignment offset is not sector-aligned.";
return false;
@@ -248,6 +272,18 @@
return false;
}
+ // Finally, the size of the allocatable space must be a multiple of the
+ // logical block size. If we have no more free space after this
+ // computation, then we abort. Note that the last sector is inclusive,
+ // so we have to account for that.
+ uint64_t num_free_sectors = last_sector - first_sector + 1;
+ uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+ if (num_free_sectors < sectors_per_block) {
+ LERROR << "Not enough space to allocate any partition tables.";
+ return false;
+ }
+ last_sector = first_sector + (num_free_sectors / sectors_per_block) * sectors_per_block - 1;
+
geometry_.first_logical_sector = first_sector;
geometry_.last_logical_sector = last_sector;
geometry_.metadata_max_size = metadata_max_size;
@@ -255,6 +291,7 @@
geometry_.alignment = device_info_.alignment;
geometry_.alignment_offset = device_info_.alignment_offset;
geometry_.block_device_size = device_info_.size;
+ geometry_.logical_block_size = device_info.logical_block_size;
return true;
}
@@ -290,13 +327,7 @@
}
}
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
- // Align the space needed up to the nearest sector.
- uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
- if (partition->size() >= aligned_size) {
- return true;
- }
-
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
// Figure out how much we need to allocate.
uint64_t space_needed = aligned_size - partition->size();
uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
@@ -307,95 +338,101 @@
uint64_t end;
Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+ uint64_t length() const { return end - start; }
bool operator<(const Interval& other) const { return start < other.start; }
};
- std::vector<Interval> intervals;
- // Collect all extents in the partition table.
+ // Collect all extents in the partition table, then sort them by starting
+ // sector.
+ std::vector<Interval> extents;
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
if (!linear) {
continue;
}
- intervals.emplace_back(linear->physical_sector(),
- linear->physical_sector() + extent->num_sectors());
+ extents.emplace_back(linear->physical_sector(),
+ linear->physical_sector() + extent->num_sectors());
}
}
+ std::sort(extents.begin(), extents.end());
- // Sort extents by starting sector.
- std::sort(intervals.begin(), intervals.end());
+ // Convert the extent list into a list of gaps between the extents; i.e.,
+ // the list of ranges that are free on the disk.
+ std::vector<Interval> free_regions;
+ for (size_t i = 1; i < extents.size(); i++) {
+ const Interval& previous = extents[i - 1];
+ const Interval& current = extents[i];
+
+ uint64_t aligned = AlignSector(previous.end);
+ if (aligned >= current.start) {
+ // There is no gap between these two extents, try the next one.
+ // Note that we check with >= instead of >, since alignment may
+ // bump the ending sector past the beginning of the next extent.
+ continue;
+ }
+
+ // The new interval represents the free space starting at the end of
+ // the previous interval, and ending at the start of the next interval.
+ free_regions.emplace_back(aligned, current.start);
+ }
+
+ // Add a final interval representing the remainder of the free space.
+ uint64_t last_free_extent_start =
+ extents.empty() ? geometry_.first_logical_sector : extents.back().end;
+ last_free_extent_start = AlignSector(last_free_extent_start);
+ if (last_free_extent_start <= geometry_.last_logical_sector) {
+ free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1);
+ }
+
+ const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+ CHECK(sectors_needed % sectors_per_block == 0);
// Find gaps that we can use for new extents. Note we store new extents in a
// temporary vector, and only commit them if we are guaranteed enough free
// space.
std::vector<std::unique_ptr<LinearExtent>> new_extents;
- for (size_t i = 1; i < intervals.size(); i++) {
- const Interval& previous = intervals[i - 1];
- const Interval& current = intervals[i];
+ for (auto& region : free_regions) {
+ if (region.length() % sectors_per_block != 0) {
+ // This should never happen, because it would imply that we
+ // once allocated an extent that was not a multiple of the
+ // block size. That extent would be rejected by DM_TABLE_LOAD.
+ LERROR << "Region " << region.start << ".." << region.end
+ << " is not a multiple of the block size, " << sectors_per_block;
- if (previous.end >= current.start) {
- // There is no gap between these two extents, try the next one. Note that
- // extents may never overlap, but just for safety, we ignore them if they
- // do.
- DCHECK(previous.end == current.start);
- continue;
+ // If for some reason the final region is mis-sized we still want
+ // to be able to grow partitions. So just to be safe, round the
+ // region down to the nearest block.
+ region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block;
+ if (!region.length()) {
+ continue;
+ }
}
- uint64_t aligned = AlignSector(previous.end);
- if (aligned >= current.start) {
- // After alignment, this extent is not usable.
- continue;
- }
+ uint64_t sectors = std::min(sectors_needed, region.length());
+ CHECK(sectors % sectors_per_block == 0);
- // This gap is enough to hold the remainder of the space requested, so we
- // can allocate what we need and return.
- if (current.start - aligned >= sectors_needed) {
- auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
- sectors_needed -= extent->num_sectors();
- new_extents.push_back(std::move(extent));
+ auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+ new_extents.push_back(std::move(extent));
+ sectors_needed -= sectors;
+ if (!sectors_needed) {
break;
}
-
- // This gap is not big enough to fit the remainder of the space requested,
- // so consume the whole thing and keep looking for more.
- auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
- sectors_needed -= extent->num_sectors();
- new_extents.push_back(std::move(extent));
}
-
- // If we still have more to allocate, take it from the remaining free space
- // in the allocatable region.
if (sectors_needed) {
- uint64_t first_sector;
- if (intervals.empty()) {
- first_sector = geometry_.first_logical_sector;
- } else {
- first_sector = intervals.back().end;
- }
- DCHECK(first_sector <= geometry_.last_logical_sector);
-
- // Note: After alignment, |first_sector| may be > the last usable sector.
- first_sector = AlignSector(first_sector);
-
- // Note: the last usable sector is inclusive.
- if (first_sector > geometry_.last_logical_sector ||
- geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
- LERROR << "Not enough free space to expand partition: " << partition->name();
- return false;
- }
- auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
- new_extents.push_back(std::move(extent));
+ LERROR << "Not enough free space to expand partition: " << partition->name();
+ return false;
}
+ // Everything succeeded, so commit the new extents.
for (auto& extent : new_extents) {
partition->AddExtent(std::move(extent));
}
return true;
}
-void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
- partition->ShrinkTo(requested_size);
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
+ partition->ShrinkTo(aligned_size);
}
std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
@@ -455,6 +492,12 @@
void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
device_info_.size = device_info.size;
+ // Note that if the logical block size changes, we're probably in trouble:
+ // we could have already built extents that will only work on the previous
+ // size.
+ DCHECK(partitions_.empty() ||
+ device_info_.logical_block_size == device_info.logical_block_size);
+
// The kernel does not guarantee these values are present, so we only
// replace existing values if the new values are non-zero.
if (device_info.alignment) {
@@ -465,5 +508,23 @@
}
}
+bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
+ // Align the space needed up to the nearest sector.
+ uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size);
+ uint64_t old_size = partition->size();
+
+ if (aligned_size > old_size) {
+ if (!GrowPartition(partition, aligned_size)) {
+ return false;
+ }
+ } else if (aligned_size < partition->size()) {
+ ShrinkPartition(partition, aligned_size);
+ }
+
+ LINFO << "Partition " << partition->name() << " will resize from " << old_size << " bytes to "
+ << aligned_size << " bytes";
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index b610fd4..da9c8f3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -45,7 +45,7 @@
Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
- EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
EXPECT_EQ(system->size(), 65536);
ASSERT_EQ(system->extents().size(), 1);
@@ -55,24 +55,28 @@
// The first logical sector will be (4096+1024*2)/512 = 12.
EXPECT_EQ(extent->physical_sector(), 12);
- // Test growing to the same size.
- EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ // Test resizing to the same size.
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
EXPECT_EQ(system->size(), 65536);
EXPECT_EQ(system->extents().size(), 1);
EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
- // Test growing to a smaller size.
- EXPECT_EQ(builder->GrowPartition(system, 0), true);
- EXPECT_EQ(system->size(), 65536);
+ // Test resizing to a smaller size.
+ EXPECT_EQ(builder->ResizePartition(system, 0), true);
+ EXPECT_EQ(system->size(), 0);
+ EXPECT_EQ(system->extents().size(), 0);
+ // Test resizing to a greater size.
+ builder->ResizePartition(system, 131072);
+ EXPECT_EQ(system->size(), 131072);
EXPECT_EQ(system->extents().size(), 1);
- EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
- // Test shrinking to a greater size.
- builder->ShrinkPartition(system, 131072);
- EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 131072 / LP_SECTOR_SIZE);
+ // Test resizing again, that the extents are merged together.
+ builder->ResizePartition(system, 1024 * 256);
+ EXPECT_EQ(system->size(), 1024 * 256);
EXPECT_EQ(system->extents().size(), 1);
- EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), (1024 * 256) / LP_SECTOR_SIZE);
// Test shrinking within the same extent.
- builder->ShrinkPartition(system, 32768);
+ builder->ResizePartition(system, 32768);
EXPECT_EQ(system->size(), 32768);
EXPECT_EQ(system->extents().size(), 1);
extent = system->extents()[0]->AsLinearExtent();
@@ -81,7 +85,7 @@
EXPECT_EQ(extent->physical_sector(), 12);
// Test shrinking to 0.
- builder->ShrinkPartition(system, 0);
+ builder->ResizePartition(system, 0);
EXPECT_EQ(system->size(), 0);
EXPECT_EQ(system->extents().size(), 0);
}
@@ -92,12 +96,12 @@
// Test that we align up to one sector.
Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
- EXPECT_EQ(builder->GrowPartition(system, 10000), true);
- EXPECT_EQ(system->size(), 10240);
+ EXPECT_EQ(builder->ResizePartition(system, 10000), true);
+ EXPECT_EQ(system->size(), 12288);
EXPECT_EQ(system->extents().size(), 1);
- builder->ShrinkPartition(system, 9000);
- EXPECT_EQ(system->size(), 9216);
+ builder->ResizePartition(system, 7000);
+ EXPECT_EQ(system->size(), 8192);
EXPECT_EQ(system->extents().size(), 1);
}
@@ -121,13 +125,13 @@
TEST(liblp, InternalAlignment) {
// Test the metadata fitting within alignment.
- BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
+ BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
- EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
// Test a large alignment offset thrown in.
device_info.alignment_offset = 753664;
@@ -136,7 +140,7 @@
exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
- EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
// Alignment offset without alignment doesn't mean anything.
device_info.alignment = 0;
@@ -151,7 +155,7 @@
exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.first_logical_sector, 78);
- EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+ EXPECT_EQ(exported->geometry.last_logical_sector, 1973);
// Test a small alignment with no alignment offset.
device_info.alignment = 11 * 1024;
@@ -164,7 +168,7 @@
}
TEST(liblp, InternalPartitionAlignment) {
- BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
+ BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
Partition* a = builder->AddPartition("a", TEST_GUID, 0);
@@ -174,8 +178,8 @@
// Add a bunch of small extents to each, interleaving.
for (size_t i = 0; i < 10; i++) {
- ASSERT_TRUE(builder->GrowPartition(a, a->size() + 4096));
- ASSERT_TRUE(builder->GrowPartition(b, b->size() + 4096));
+ ASSERT_TRUE(builder->ResizePartition(a, a->size() + 4096));
+ ASSERT_TRUE(builder->ResizePartition(b, b->size() + 4096));
}
EXPECT_EQ(a->size(), 40960);
EXPECT_EQ(b->size(), 40960);
@@ -203,9 +207,9 @@
Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
- EXPECT_EQ(builder->GrowPartition(system, 1036288), true);
+ EXPECT_EQ(builder->ResizePartition(system, 1036288), true);
EXPECT_EQ(system->size(), 1036288);
- EXPECT_EQ(builder->GrowPartition(system, 1036289), false);
+ EXPECT_EQ(builder->ResizePartition(system, 1036289), false);
}
TEST(liblp, BuildComplex) {
@@ -215,9 +219,9 @@
Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
- EXPECT_EQ(builder->GrowPartition(system, 65536), true);
- EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
- EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+ EXPECT_EQ(builder->ResizePartition(system, 98304), true);
EXPECT_EQ(system->size(), 98304);
EXPECT_EQ(vendor->size(), 32768);
@@ -268,9 +272,9 @@
Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
- EXPECT_EQ(builder->GrowPartition(system, 65536), true);
- EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
- EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+ EXPECT_EQ(builder->ResizePartition(system, 98304), true);
unique_ptr<LpMetadata> exported = builder->Export();
EXPECT_NE(exported, nullptr);
@@ -323,9 +327,9 @@
Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
- EXPECT_EQ(builder->GrowPartition(system, 65536), true);
- EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
- EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+ EXPECT_EQ(builder->ResizePartition(system, 98304), true);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
@@ -382,7 +386,7 @@
static const size_t kMetadataSize = 64 * 1024;
// No space to store metadata + geometry.
- BlockDeviceInfo device_info(kDiskSize, 0, 0);
+ BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
@@ -391,8 +395,8 @@
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
- // Space for metadata + geometry + one free sector.
- device_info.size += LP_SECTOR_SIZE;
+ // Space for metadata + geometry + one free block.
+ device_info.size += device_info.logical_block_size;
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_NE(builder, nullptr);
@@ -425,19 +429,21 @@
ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
ASSERT_LE(device_info.alignment_offset, INT_MAX);
+ ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
// Having an alignment offset > alignment doesn't really make sense.
ASSERT_LT(device_info.alignment_offset, device_info.alignment);
}
TEST(liblp, UpdateBlockDeviceInfo) {
- BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
+ BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
EXPECT_EQ(builder->block_device_info().size, device_info.size);
EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+ EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size);
device_info.alignment = 0;
device_info.alignment_offset = 2048;
@@ -451,3 +457,27 @@
EXPECT_EQ(builder->block_device_info().alignment, 8192);
EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
}
+
+TEST(liblp, InvalidBlockSize) {
+ BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ EXPECT_EQ(builder, nullptr);
+}
+
+TEST(liblp, AlignedExtentSize) {
+ BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(partition, 512));
+ EXPECT_EQ(partition->size(), 4096);
+}
+
+TEST(liblp, AlignedFreeSpace) {
+ // Only one sector free - at least one block is required.
+ BlockDeviceInfo device_info(10240, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
+ ASSERT_EQ(builder, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 8bde313..a35cf8e 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -32,11 +32,16 @@
// By default, partitions are aligned on a 1MiB boundary.
static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+static const uint32_t kDefaultBlockSize = 4096;
struct BlockDeviceInfo {
- BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
- BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
- : size(size), alignment(alignment), alignment_offset(alignment_offset) {}
+ BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+ BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
+ uint32_t logical_block_size)
+ : size(size),
+ alignment(alignment),
+ alignment_offset(alignment_offset),
+ logical_block_size(logical_block_size) {}
// Size of the block device, in bytes.
uint64_t size;
// Optimal target alignment, in bytes. Partition extents will be aligned to
@@ -46,6 +51,8 @@
// |alignment_offset| on the target device is correctly aligned on its
// parent device. This value must be 0 or a multiple of 512.
uint32_t alignment_offset;
+ // Block size, for aligning extent sizes and partition sizes.
+ uint32_t logical_block_size;
};
// Abstraction around dm-targets that can be encoded into logical partition tables.
@@ -74,6 +81,7 @@
LinearExtent* AsLinearExtent() override { return this; }
uint64_t physical_sector() const { return physical_sector_; }
+ uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
private:
uint64_t physical_sector_;
@@ -88,6 +96,8 @@
};
class Partition final {
+ friend class MetadataBuilder;
+
public:
Partition(const std::string& name, const std::string& guid, uint32_t attributes);
@@ -97,10 +107,6 @@
// Remove all extents from this partition.
void RemoveExtents();
- // Remove and/or shrink extents until the partition is the requested size.
- // See MetadataBuilder::ShrinkPartition for more information.
- void ShrinkTo(uint64_t requested_size);
-
const std::string& name() const { return name_; }
uint32_t attributes() const { return attributes_; }
const std::string& guid() const { return guid_; }
@@ -108,6 +114,8 @@
uint64_t size() const { return size_; }
private:
+ void ShrinkTo(uint64_t aligned_size);
+
std::string name_;
std::string guid_;
std::vector<std::unique_ptr<Extent>> extents_;
@@ -144,7 +152,7 @@
// size. This is a convenience method for tests.
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
- BlockDeviceInfo device_info(blockdev_size, 0, 0);
+ BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
return New(device_info, metadata_max_size, metadata_slot_count);
}
@@ -162,29 +170,17 @@
// Find a partition by name. If no partition is found, nullptr is returned.
Partition* FindPartition(const std::string& name);
- // Grow a partition to the requested size. If the partition's size is already
- // greater or equal to the requested size, this will return true and the
- // partition table will not be changed. Otherwise, a greedy algorithm is
- // used to find free gaps in the partition table and allocate them for this
- // partition. If not enough space can be allocated, false is returned, and
- // the parition table will not be modified.
+ // Grow or shrink a partition to the requested size. This size will be
+ // rounded UP to the nearest block (512 bytes).
//
- // The size will be rounded UP to the nearest sector.
+ // When growing a partition, a greedy algorithm is used to find free gaps
+ // in the partition table and allocate them. If not enough space can be
+ // allocated, false is returned, and the parition table will not be
+ // modified.
//
// Note, this is an in-memory operation, and it does not alter the
// underlying filesystem or contents of the partition on disk.
- bool GrowPartition(Partition* partition, uint64_t requested_size);
-
- // Shrink a partition to the requested size. If the partition is already
- // smaller than the given size, this will return and the partition table
- // will not be changed. Otherwise, extents will be removed and/or shrunk
- // from the end of the partition until it is the requested size.
- //
- // The size will be rounded UP to the nearest sector.
- //
- // Note, this is an in-memory operation, and it does not alter the
- // underlying filesystem or contents of the partition on disk.
- void ShrinkPartition(Partition* partition, uint64_t requested_size);
+ bool ResizePartition(Partition* partition, uint64_t requested_size);
// Amount of space that can be allocated to logical partitions.
uint64_t AllocatableSpace() const;
@@ -198,7 +194,8 @@
MetadataBuilder();
bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
-
+ bool GrowPartition(Partition* partition, uint64_t aligned_size);
+ void ShrinkPartition(Partition* partition, uint64_t aligned_size);
uint64_t AlignSector(uint64_t sector);
LpMetadataGeometry geometry_;
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index e1323e1..52c80f7 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -136,6 +136,12 @@
* can be used to verify the geometry against a target device.
*/
uint64_t block_device_size;
+
+ /* 76: Logical block size of the super partition block device. This is the
+ * minimal alignment for partition and extent sizes, and it must be a
+ * multiple of LP_SECTOR_SIZE.
+ */
+ uint32_t logical_block_size;
} __attribute__((packed)) LpMetadataGeometry;
/* The logical partition metadata has a number of tables; they are described
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index bbbedc7..638f4b3 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -85,7 +85,7 @@
if (!system) {
return false;
}
- return builder->GrowPartition(system, 24 * 1024);
+ return builder->ResizePartition(system, 24 * 1024);
}
// Create a temporary disk and flash it with the default partition setup.
@@ -345,7 +345,7 @@
ASSERT_NE(partition, nullptr);
// Add one extent to any partition to fill up more space - we're at 508
// bytes after this, out of 512.
- ASSERT_TRUE(builder->GrowPartition(partition, 1024));
+ ASSERT_TRUE(builder->ResizePartition(partition, 1024));
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 4522275..6ef5124 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -26,6 +26,8 @@
#include "liblp/metadata_format.h"
#define LP_TAG "[liblp]"
+#define LWARN LOG(WARNING) << LP_TAG
+#define LINFO LOG(INFO) << LP_TAG
#define LERROR LOG(ERROR) << LP_TAG
#define PERROR PLOG(ERROR) << LP_TAG
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 092dbf1..7bf42ae 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -40,7 +40,8 @@
80000,
0,
0,
- 1024 * 1024};
+ 1024 * 1024,
+ 4096};
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 156319b..fc9d83f 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -304,7 +304,11 @@
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
return false;
}
- return FlashPartitionTable(fd, metadata, slot_number);
+ if (!FlashPartitionTable(fd, metadata, slot_number)) {
+ return false;
+ }
+ LWARN << "Flashed new logical partition geometry to " << block_device;
+ return true;
}
bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
@@ -314,7 +318,12 @@
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
return false;
}
- return UpdatePartitionTable(fd, metadata, slot_number);
+ if (!UpdatePartitionTable(fd, metadata, slot_number)) {
+ return false;
+ }
+ LINFO << "Updated logical partition table at slot " << slot_number << " on device "
+ << block_device;
+ return true;
}
bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index ea3d991..30f2cf3 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -21,77 +21,96 @@
#include "healthd_draw.h"
#define LOGE(x...) KLOG_ERROR("charger", x);
+#define LOGW(x...) KLOG_WARNING("charger", x);
#define LOGV(x...) KLOG_DEBUG("charger", x);
HealthdDraw::HealthdDraw(animation* anim)
: kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
- gr_init();
- gr_font_size(gr_sys_font(), &char_width_, &char_height_);
+ int ret = gr_init();
- screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
- screen_height_ = gr_fb_height();
+ if (ret < 0) {
+ LOGE("gr_init failed\n");
+ graphics_available = false;
+ return;
+ }
- int res;
- if (!anim->text_clock.font_file.empty() &&
- (res = gr_init_font(anim->text_clock.font_file.c_str(),
- &anim->text_clock.font)) < 0) {
- LOGE("Could not load time font (%d)\n", res);
- }
- if (!anim->text_percent.font_file.empty() &&
- (res = gr_init_font(anim->text_percent.font_file.c_str(),
- &anim->text_percent.font)) < 0) {
- LOGE("Could not load percent font (%d)\n", res);
- }
+ graphics_available = true;
+ sys_font = gr_sys_font();
+ if (sys_font == nullptr) {
+ LOGW("No system font, screen fallback text not available\n");
+ } else {
+ gr_font_size(sys_font, &char_width_, &char_height_);
+ }
+
+ screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
+ screen_height_ = gr_fb_height();
+
+ int res;
+ if (!anim->text_clock.font_file.empty() &&
+ (res = gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
+ LOGE("Could not load time font (%d)\n", res);
+ }
+ if (!anim->text_percent.font_file.empty() &&
+ (res = gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
+ LOGE("Could not load percent font (%d)\n", res);
+ }
}
HealthdDraw::~HealthdDraw() {}
void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
- clear_screen();
+ if (!graphics_available) return;
+ clear_screen();
- /* try to display *something* */
- if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
- draw_unknown(surf_unknown);
- else
- draw_battery(batt_anim);
- gr_flip();
+ /* try to display *something* */
+ if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
+ draw_unknown(surf_unknown);
+ else
+ draw_battery(batt_anim);
+ gr_flip();
}
-void HealthdDraw::blank_screen(bool blank) { gr_fb_blank(blank); }
+void HealthdDraw::blank_screen(bool blank) {
+ if (!graphics_available) return;
+ gr_fb_blank(blank);
+}
void HealthdDraw::clear_screen(void) {
- gr_color(0, 0, 0, 255);
- gr_clear();
+ if (!graphics_available) return;
+ gr_color(0, 0, 0, 255);
+ gr_clear();
}
int HealthdDraw::draw_surface_centered(GRSurface* surface) {
- int w = gr_get_width(surface);
- int h = gr_get_height(surface);
- int x = (screen_width_ - w) / 2 + kSplitOffset;
- int y = (screen_height_ - h) / 2;
+ if (!graphics_available) return 0;
- LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
- gr_blit(surface, 0, 0, w, h, x, y);
- if (kSplitScreen) {
- x += screen_width_ - 2 * kSplitOffset;
+ int w = gr_get_width(surface);
+ int h = gr_get_height(surface);
+ int x = (screen_width_ - w) / 2 + kSplitOffset;
+ int y = (screen_height_ - h) / 2;
+
LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
gr_blit(surface, 0, 0, w, h, x, y);
- }
+ if (kSplitScreen) {
+ x += screen_width_ - 2 * kSplitOffset;
+ LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
+ gr_blit(surface, 0, 0, w, h, x, y);
+ }
- return y + h;
+ return y + h;
}
int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
- int str_len_px = gr_measure(font, str);
+ if (!graphics_available) return 0;
+ int str_len_px = gr_measure(font, str);
- if (x < 0) x = (screen_width_ - str_len_px) / 2;
- if (y < 0) y = (screen_height_ - char_height_) / 2;
- gr_text(font, x + kSplitOffset, y, str, false /* bold */);
- if (kSplitScreen)
- gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
+ if (x < 0) x = (screen_width_ - str_len_px) / 2;
+ if (y < 0) y = (screen_height_ - char_height_) / 2;
+ gr_text(font, x + kSplitOffset, y, str, false /* bold */);
+ if (kSplitScreen) gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
- return y + char_height_;
+ return y + char_height_;
}
void HealthdDraw::determine_xy(const animation::text_field& field,
@@ -119,77 +138,80 @@
}
void HealthdDraw::draw_clock(const animation* anim) {
- static constexpr char CLOCK_FORMAT[] = "%H:%M";
- static constexpr int CLOCK_LENGTH = 6;
+ static constexpr char CLOCK_FORMAT[] = "%H:%M";
+ static constexpr int CLOCK_LENGTH = 6;
- const animation::text_field& field = anim->text_clock;
+ const animation::text_field& field = anim->text_clock;
- if (field.font == nullptr || field.font->char_width == 0 ||
- field.font->char_height == 0)
- return;
+ if (!graphics_available || field.font == nullptr || field.font->char_width == 0 ||
+ field.font->char_height == 0)
+ return;
- time_t rawtime;
- time(&rawtime);
- tm* time_info = localtime(&rawtime);
+ time_t rawtime;
+ time(&rawtime);
+ tm* time_info = localtime(&rawtime);
- char clock_str[CLOCK_LENGTH];
- size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
- if (length != CLOCK_LENGTH - 1) {
- LOGE("Could not format time\n");
- return;
- }
+ char clock_str[CLOCK_LENGTH];
+ size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
+ if (length != CLOCK_LENGTH - 1) {
+ LOGE("Could not format time\n");
+ return;
+ }
- int x, y;
- determine_xy(field, length, &x, &y);
+ int x, y;
+ determine_xy(field, length, &x, &y);
- LOGV("drawing clock %s %d %d\n", clock_str, x, y);
- gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
- draw_text(field.font, x, y, clock_str);
+ LOGV("drawing clock %s %d %d\n", clock_str, x, y);
+ gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+ draw_text(field.font, x, y, clock_str);
}
void HealthdDraw::draw_percent(const animation* anim) {
- int cur_level = anim->cur_level;
- if (anim->cur_status == BATTERY_STATUS_FULL) {
- cur_level = 100;
- }
+ if (!graphics_available) return;
+ int cur_level = anim->cur_level;
+ if (anim->cur_status == BATTERY_STATUS_FULL) {
+ cur_level = 100;
+ }
- if (cur_level <= 0) return;
+ if (cur_level <= 0) return;
- const animation::text_field& field = anim->text_percent;
- if (field.font == nullptr || field.font->char_width == 0 ||
- field.font->char_height == 0) {
- return;
- }
+ const animation::text_field& field = anim->text_percent;
+ if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
+ return;
+ }
- std::string str = base::StringPrintf("%d%%", cur_level);
+ std::string str = base::StringPrintf("%d%%", cur_level);
- int x, y;
- determine_xy(field, str.size(), &x, &y);
+ int x, y;
+ determine_xy(field, str.size(), &x, &y);
- LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
- gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
- draw_text(field.font, x, y, str.c_str());
+ LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
+ gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+ draw_text(field.font, x, y, str.c_str());
}
void HealthdDraw::draw_battery(const animation* anim) {
- const animation::frame& frame = anim->frames[anim->cur_frame];
+ if (!graphics_available) return;
+ const animation::frame& frame = anim->frames[anim->cur_frame];
- if (anim->num_frames != 0) {
- draw_surface_centered(frame.surface);
- LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame,
- frame.min_level, frame.disp_time);
- }
- draw_clock(anim);
- draw_percent(anim);
+ if (anim->num_frames != 0) {
+ draw_surface_centered(frame.surface);
+ LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame, frame.min_level,
+ frame.disp_time);
+ }
+ draw_clock(anim);
+ draw_percent(anim);
}
void HealthdDraw::draw_unknown(GRSurface* surf_unknown) {
int y;
if (surf_unknown) {
- draw_surface_centered(surf_unknown);
+ draw_surface_centered(surf_unknown);
+ } else if (sys_font) {
+ gr_color(0xa4, 0xc6, 0x39, 255);
+ y = draw_text(sys_font, -1, -1, "Charging!");
+ draw_text(sys_font, -1, y + 25, "?\?/100");
} else {
- gr_color(0xa4, 0xc6, 0x39, 255);
- y = draw_text(gr_sys_font(), -1, -1, "Charging!");
- draw_text(gr_sys_font(), -1, y + 25, "?\?/100");
+ LOGW("Charging, level unknown\n");
}
}
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 6a6ba76..7c847bd 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -70,6 +70,12 @@
const bool kSplitScreen;
// Pixels to offset graphics towards center split.
const int kSplitOffset;
+
+ // system text font, may be nullptr
+ const GRFont* sys_font;
+
+ // true if minui init'ed OK, false if minui init failed
+ bool graphics_available;
};
#endif // HEALTHD_DRAW_H
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 4f77e7a..f1fe5cd 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -307,6 +307,9 @@
disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
+ /* unblank the screen on first cycle and first frame */
+ if (batt_anim->cur_cycle == 0 && batt_anim->cur_frame == 0) healthd_draw->blank_screen(false);
+
/* animation starting, set up the animation */
if (batt_anim->cur_frame == 0) {
LOGV("[%" PRId64 "] animation starting\n", now);
@@ -330,9 +333,6 @@
}
}
- /* unblank the screen on first cycle */
- if (batt_anim->cur_cycle == 0) healthd_draw->blank_screen(false);
-
/* draw the new frame (@ cur_frame) */
healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);
@@ -632,7 +632,7 @@
ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
if (ret < 0) {
- LOGE("Cannot load custom battery_fail image. Reverting to built in.\n");
+ LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
if (ret < 0) {
LOGE("Cannot load built in battery_fail image\n");
diff --git a/init/Android.bp b/init/Android.bp
index 660d586..d42ab8a 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -107,6 +107,7 @@
"first_stage_mount.cpp",
"import_parser.cpp",
"init.cpp",
+ "init_first_stage.cpp",
"keychords.cpp",
"modalias_handler.cpp",
"parser.cpp",
@@ -115,6 +116,7 @@
"property_service.cpp",
"property_type.cpp",
"reboot.cpp",
+ "reboot_utils.cpp",
"security.cpp",
"selinux.cpp",
"service.cpp",
@@ -127,7 +129,6 @@
"ueventd.cpp",
"ueventd_parser.cpp",
"util.cpp",
- "watchdogd.cpp",
],
whole_static_libs: ["libcap"],
header_libs: ["bootimg_headers"],
@@ -157,7 +158,6 @@
srcs: ["main.cpp"],
symlinks: [
"sbin/ueventd",
- "sbin/watchdogd",
],
}
*/
diff --git a/init/Android.mk b/init/Android.mk
index a81a0f6..9d9d368 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -67,6 +67,7 @@
libprotobuf-cpp-lite \
libpropertyinfoserializer \
libpropertyinfoparser \
+ liblp \
shared_libs := \
libcutils \
@@ -97,7 +98,6 @@
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
- ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
LOCAL_SANITIZE := signed-integer-overflow
include $(BUILD_EXECUTABLE)
diff --git a/init/devices.cpp b/init/devices.cpp
index ed4a739..58c8b2e 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -34,7 +34,7 @@
#include "util.h"
#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#error "Do not include init.h in files used by ueventd; it will expose init's globals"
#endif
using android::base::Basename;
@@ -372,7 +372,7 @@
}
}
-void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+void DeviceHandler::HandleUevent(const Uevent& uevent) {
if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
FixupSysPermissions(uevent.path, uevent.subsystem);
}
@@ -418,6 +418,10 @@
HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
}
+void DeviceHandler::ColdbootDone() {
+ skip_restorecon_ = true;
+}
+
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
diff --git a/init/devices.h b/init/devices.h
index 0be660f..9d39eaa 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -29,6 +29,7 @@
#include <selinux/label.h>
#include "uevent.h"
+#include "uevent_handler.h"
namespace android {
namespace init {
@@ -105,7 +106,7 @@
std::string dir_name_ = "/dev";
};
-class DeviceHandler {
+class DeviceHandler : public UeventHandler {
public:
friend class DeviceHandlerTester;
@@ -113,11 +114,12 @@
DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
std::set<std::string> boot_devices, bool skip_restorecon);
+ virtual ~DeviceHandler() = default;
- void HandleDeviceEvent(const Uevent& uevent);
+ void HandleUevent(const Uevent& uevent) override;
+ void ColdbootDone() override;
std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
- void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
private:
bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 28bda34..740e82c 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -35,8 +35,6 @@
namespace android {
namespace init {
-std::vector<std::string> firmware_directories;
-
static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
int loading_fd, int data_fd) {
// Start transfer.
@@ -58,7 +56,10 @@
return access("/dev/.booting", F_OK) == 0;
}
-static void ProcessFirmwareEvent(const Uevent& uevent) {
+FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories)
+ : firmware_directories_(std::move(firmware_directories)) {}
+
+void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) {
int booting = IsBooting();
LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
@@ -80,7 +81,7 @@
}
try_loading_again:
- for (const auto& firmware_directory : firmware_directories) {
+ for (const auto& firmware_directory : firmware_directories_) {
std::string file = firmware_directory + uevent.firmware;
unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
struct stat sb;
@@ -104,7 +105,7 @@
write(loading_fd, "-1", 2);
}
-void HandleFirmwareEvent(const Uevent& uevent) {
+void FirmwareHandler::HandleUevent(const Uevent& uevent) {
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
// Loading the firmware in a child means we can do that in parallel...
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 6081511..3996096 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -21,13 +21,23 @@
#include <vector>
#include "uevent.h"
+#include "uevent_handler.h"
namespace android {
namespace init {
-extern std::vector<std::string> firmware_directories;
+class FirmwareHandler : public UeventHandler {
+ public:
+ explicit FirmwareHandler(std::vector<std::string> firmware_directories);
+ virtual ~FirmwareHandler() = default;
-void HandleFirmwareEvent(const Uevent& uevent);
+ void HandleUevent(const Uevent& uevent) override;
+
+ private:
+ void ProcessFirmwareEvent(const Uevent& uevent);
+
+ std::vector<std::string> firmware_directories_;
+};
} // namespace init
} // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 8ded873..41e8fff 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -116,7 +116,7 @@
}
static bool inline IsRecoveryMode() {
- return access("/sbin/recovery", F_OK) == 0;
+ return access("/system/bin/recovery", F_OK) == 0;
}
static inline bool IsDmLinearEnabled() {
@@ -206,7 +206,7 @@
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
- device_handler_->HandleDeviceEvent(uevent);
+ device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
@@ -273,7 +273,7 @@
lp_metadata_partition_ = links[0];
}
required_devices_partition_names_.erase(iter);
- device_handler_->HandleDeviceEvent(uevent);
+ device_handler_->HandleUevent(uevent);
if (required_devices_partition_names_.empty()) {
return ListenerAction::kStop;
} else {
@@ -310,7 +310,7 @@
auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
- device_handler_->HandleDeviceEvent(uevent);
+ device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 2352fc7..8866bdc 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -41,10 +41,9 @@
}
// selinux.h
-bool SelinuxHasVendorInit() {
- return true;
+int SelinuxGetVendorAndroidVersion() {
+ return 10000;
}
-
void SelabelInitialize() {}
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index f0e1f07..0af11f6 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -23,6 +23,9 @@
#include <string>
+// android/api-level.h
+#define __ANDROID_API_P__ 28
+
// sys/system_properties.h
#define PROP_VALUE_MAX 92
@@ -41,7 +44,7 @@
const std::string& source_context, const ucred& cr, std::string* error);
// selinux.h
-bool SelinuxHasVendorInit();
+int SelinuxGetVendorAndroidVersion();
void SelabelInitialize();
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
diff --git a/init/init.cpp b/init/init.cpp
index 6569871..ad80c98 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,15 +18,12 @@
#include <dirent.h>
#include <fcntl.h>
-#include <paths.h>
#include <pthread.h>
-#include <seccomp_policy.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/signalfd.h>
-#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
@@ -43,8 +40,6 @@
#include <cutils/android_reboot.h>
#include <keyutils.h>
#include <libavb/libavb.h>
-#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
#include "action_parser.h"
#include "epoll.h"
@@ -53,12 +48,12 @@
#include "keychords.h"
#include "property_service.h"
#include "reboot.h"
+#include "reboot_utils.h"
#include "security.h"
#include "selinux.h"
#include "sigchld_handler.h"
#include "ueventd.h"
#include "util.h"
-#include "watchdogd.h"
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -119,6 +114,9 @@
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
+ if (!parser.ParseConfig("/product-services/etc/init")) {
+ late_import_paths.emplace_back("/product-services/etc/init");
+ }
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
@@ -415,14 +413,6 @@
return Success();
}
-static void global_seccomp() {
- import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
- LOG(FATAL) << "Failed to globally enable seccomp!";
- }
- });
-}
-
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -439,40 +429,6 @@
}
}
-static void InstallRebootSignalHandlers() {
- // Instead of panic'ing the kernel as is the default behavior when init crashes,
- // we prefer to reboot to bootloader on development builds, as this will prevent
- // boot looping bad configurations and allow both developers and test farms to easily
- // recover.
- struct sigaction action;
- memset(&action, 0, sizeof(action));
- sigfillset(&action.sa_mask);
- action.sa_handler = [](int signal) {
- // These signal handlers are also caught for processes forked from init, however we do not
- // want them to trigger reboot, so we directly call _exit() for children processes here.
- if (getpid() != 1) {
- _exit(signal);
- }
-
- // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
- // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
- // and probably good enough given this is already an error case and only enabled for
- // development builds.
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
- };
- action.sa_flags = SA_RESTART;
- sigaction(SIGABRT, &action, nullptr);
- sigaction(SIGBUS, &action, nullptr);
- sigaction(SIGFPE, &action, nullptr);
- sigaction(SIGILL, &action, nullptr);
- sigaction(SIGSEGV, &action, nullptr);
-#if defined(SIGSTKFLT)
- sigaction(SIGSTKFLT, &action, nullptr);
-#endif
- sigaction(SIGSYS, &action, nullptr);
- sigaction(SIGTRAP, &action, nullptr);
-}
-
static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
if (siginfo.ssi_pid != 0) {
// Drop any userspace SIGTERM requests.
@@ -609,125 +565,27 @@
android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
}
+int first_stage_main(int argc, char** argv);
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
- if (!strcmp(basename(argv[0]), "watchdogd")) {
- return watchdogd_main(argc, argv);
- }
-
if (argc > 1 && !strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
+ if (getenv("INIT_SECOND_STAGE") == nullptr) {
+ return first_stage_main(argc, argv);
+ }
+
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
- bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
-
- if (is_first_stage) {
- boot_clock::time_point start_time = boot_clock::now();
-
- std::vector<std::pair<std::string, int>> errors;
-#define CHECKCALL(x) \
- if (x != 0) errors.emplace_back(#x " failed", errno);
-
- // Clear the umask.
- umask(0);
-
- CHECKCALL(clearenv());
- CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
- // Get the basic filesystem setup we need put together in the initramdisk
- // on / and then we'll let the rc file figure out the rest.
- CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
- CHECKCALL(mkdir("/dev/pts", 0755));
- CHECKCALL(mkdir("/dev/socket", 0755));
- CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
-#define MAKE_STR(x) __STRING(x)
- CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
-#undef MAKE_STR
- // Don't expose the raw commandline to unprivileged processes.
- CHECKCALL(chmod("/proc/cmdline", 0440));
- gid_t groups[] = { AID_READPROC };
- CHECKCALL(setgroups(arraysize(groups), groups));
- CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
- CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
-
- CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
-
- if constexpr (WORLD_WRITABLE_KMSG) {
- CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
- }
-
- CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
- CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
-
- // Mount staging areas for devices managed by vold
- // See storage config details at http://source.android.com/devices/storage/
- CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=1000"));
- // /mnt/vendor is used to mount vendor-specific partitions that can not be
- // part of the vendor partition, e.g. because they are mounted read-write.
- CHECKCALL(mkdir("/mnt/vendor", 0755));
- // /mnt/product is used to mount product-specific partitions that can not be
- // part of the product partition, e.g. because they are mounted read-write.
- CHECKCALL(mkdir("/mnt/product", 0755));
-
-#undef CHECKCALL
-
- // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
- // talk to the outside world...
- InitKernelLogging(argv);
-
- if (!errors.empty()) {
- for (const auto& [error_string, error_errno] : errors) {
- LOG(ERROR) << error_string << " " << strerror(error_errno);
- }
- LOG(FATAL) << "Init encountered errors starting first stage, aborting";
- }
-
- LOG(INFO) << "init first stage started!";
-
- if (!DoFirstStageMount()) {
- LOG(FATAL) << "Failed to mount required partitions early ...";
- }
-
- SetInitAvbVersionInRecovery();
-
- // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
- global_seccomp();
-
- // Set up SELinux, loading the SELinux policy.
- SelinuxSetupKernelLogging();
- SelinuxInitialize();
-
- // We're in the kernel domain, so re-exec init to transition to the init domain now
- // that the SELinux policy has been loaded.
- if (selinux_android_restorecon("/init", 0) == -1) {
- PLOG(FATAL) << "restorecon failed of /init failed";
- }
-
- setenv("INIT_SECOND_STAGE", "true", 1);
-
- static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
- uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
- setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
-
- char* path = argv[0];
- char* args[] = { path, nullptr };
- execv(path, args);
-
- // execv() only returns if an error happened, in which case we
- // panic and never fall through this conditional.
- PLOG(FATAL) << "execv(\"" << path << "\") failed";
- }
-
- // At this point we're in the second stage of init.
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
diff --git a/init/init.h b/init/init.h
index 6c82fa1..f244ad7 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,8 +31,8 @@
namespace android {
namespace init {
-// Note: These globals are *only* valid in init, so they should not be used in ueventd,
-// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
+// Note: These globals are *only* valid in init, so they should not be used in ueventd
+// or any files that may be included in ueventd, such as devices.cpp and util.cpp.
// TODO: Have an Init class and remove all globals.
extern std::string default_console;
extern std::vector<std::string> late_import_paths;
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
new file mode 100644
index 0000000..ef9ce81
--- /dev/null
+++ b/init/init_first_stage.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 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 <paths.h>
+#include <seccomp_policy.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
+#include "first_stage_mount.h"
+#include "reboot_utils.h"
+#include "selinux.h"
+#include "util.h"
+
+using android::base::boot_clock;
+
+namespace android {
+namespace init {
+
+static void GlobalSeccomp() {
+ import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
+ bool in_qemu) {
+ if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+ LOG(FATAL) << "Failed to globally enable seccomp!";
+ }
+ });
+}
+
+int first_stage_main(int argc, char** argv) {
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ InstallRebootSignalHandlers();
+ }
+
+ boot_clock::time_point start_time = boot_clock::now();
+
+ std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+ if (x != 0) errors.emplace_back(#x " failed", errno);
+
+ // Clear the umask.
+ umask(0);
+
+ CHECKCALL(clearenv());
+ CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
+ // Get the basic filesystem setup we need put together in the initramdisk
+ // on / and then we'll let the rc file figure out the rest.
+ CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+ CHECKCALL(mkdir("/dev/pts", 0755));
+ CHECKCALL(mkdir("/dev/socket", 0755));
+ CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+ CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
+ // Don't expose the raw commandline to unprivileged processes.
+ CHECKCALL(chmod("/proc/cmdline", 0440));
+ gid_t groups[] = {AID_READPROC};
+ CHECKCALL(setgroups(arraysize(groups), groups));
+ CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+ CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
+
+ CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
+
+ if constexpr (WORLD_WRITABLE_KMSG) {
+ CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
+ }
+
+ CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+ CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
+
+ // This is needed for log wrapper, which gets called before ueventd runs.
+ CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
+ CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
+
+ // Mount staging areas for devices managed by vold
+ // See storage config details at http://source.android.com/devices/storage/
+ CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=1000"));
+ // /mnt/vendor is used to mount vendor-specific partitions that can not be
+ // part of the vendor partition, e.g. because they are mounted read-write.
+ CHECKCALL(mkdir("/mnt/vendor", 0755));
+ // /mnt/product is used to mount product-specific partitions that can not be
+ // part of the product partition, e.g. because they are mounted read-write.
+ CHECKCALL(mkdir("/mnt/product", 0755));
+
+#undef CHECKCALL
+
+ // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
+ // talk to the outside world...
+ android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ });
+
+ if (!errors.empty()) {
+ for (const auto& [error_string, error_errno] : errors) {
+ LOG(ERROR) << error_string << " " << strerror(error_errno);
+ }
+ LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+ }
+
+ LOG(INFO) << "init first stage started!";
+
+ if (!DoFirstStageMount()) {
+ LOG(FATAL) << "Failed to mount required partitions early ...";
+ }
+
+ SetInitAvbVersionInRecovery();
+
+ // Does this need to be done in first stage init or can it be done later?
+ // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+ GlobalSeccomp();
+
+ // Set up SELinux, loading the SELinux policy.
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
+
+ // Unneeded? It's an ext4 file system so shouldn't it have the right domain already?
+ // We're in the kernel domain, so re-exec init to transition to the init domain now
+ // that the SELinux policy has been loaded.
+ if (selinux_android_restorecon("/init", 0) == -1) {
+ PLOG(FATAL) << "restorecon failed of /init failed";
+ }
+
+ setenv("INIT_SECOND_STAGE", "true", 1);
+
+ static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
+ uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
+ setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
+
+ const char* path = argv[0];
+ const char* args[] = {path, nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never fall through this conditional.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+ return 1;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index 1734a7e..1e0db57 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -139,7 +139,7 @@
return Insmod(dependencies[0], args);
}
-void ModaliasHandler::HandleModaliasEvent(const Uevent& uevent) {
+void ModaliasHandler::HandleUevent(const Uevent& uevent) {
if (uevent.modalias.empty()) return;
for (const auto& [alias, module] : module_aliases_) {
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
index e79da32..3247c86 100644
--- a/init/modalias_handler.h
+++ b/init/modalias_handler.h
@@ -16,22 +16,23 @@
#pragma once
-#include "result.h"
-#include "uevent.h"
-
#include <string>
#include <unordered_map>
#include <vector>
+#include "result.h"
+#include "uevent.h"
+#include "uevent_handler.h"
+
namespace android {
namespace init {
-class ModaliasHandler {
+class ModaliasHandler : public UeventHandler {
public:
ModaliasHandler();
- ~ModaliasHandler(){};
+ virtual ~ModaliasHandler() = default;
- void HandleModaliasEvent(const Uevent& uevent);
+ void HandleUevent(const Uevent& uevent) override;
private:
Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index d1c427d..cd2f630 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -16,6 +16,7 @@
#include "property_service.h"
+#include <android/api-level.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -576,7 +577,7 @@
size_t flen = 0;
const char* context = kInitContext.c_str();
- if (SelinuxHasVendorInit()) {
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
if (StartsWith(filename, path_prefix)) {
context = secontext;
@@ -691,6 +692,7 @@
}
}
load_properties_from_file("/product/build.prop", NULL);
+ load_properties_from_file("/product-services/build.prop", NULL);
load_properties_from_file("/odm/default.prop", NULL);
load_properties_from_file("/vendor/default.prop", NULL);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 6f6e39f..7401857 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,11 +20,9 @@
#include <fcntl.h>
#include <linux/fs.h>
#include <mntent.h>
-#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
-#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
@@ -51,9 +49,9 @@
#include <selinux/selinux.h>
#include "action_manager.h"
-#include "capabilities.h"
#include "init.h"
#include "property_service.h"
+#include "reboot_utils.h"
#include "service.h"
#include "sigchld_handler.h"
@@ -159,54 +157,6 @@
<< stat;
}
-bool IsRebootCapable() {
- if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
- PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
- return true;
- }
-
- ScopedCaps caps(cap_get_proc());
- if (!caps) {
- PLOG(WARNING) << "cap_get_proc() failed";
- return true;
- }
-
- cap_flag_value_t value = CAP_SET;
- if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
- PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
- return true;
- }
- return value == CAP_SET;
-}
-
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
- LOG(INFO) << "Reboot ending, jumping to kernel";
-
- if (!IsRebootCapable()) {
- // On systems where init does not have the capability of rebooting the
- // device, just exit cleanly.
- exit(0);
- }
-
- switch (cmd) {
- case ANDROID_RB_POWEROFF:
- reboot(RB_POWER_OFF);
- break;
-
- case ANDROID_RB_RESTART2:
- syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
- LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
- break;
-
- case ANDROID_RB_THERMOFF:
- reboot(RB_POWER_OFF);
- break;
- }
- // In normal case, reboot should not return.
- PLOG(ERROR) << "reboot call returned";
- abort();
-}
-
/* Find all read+write block devices and emulated devices in /proc/mounts
* and add them to correpsponding list.
*/
@@ -329,8 +279,15 @@
return stat;
}
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
- bool runFsck) {
+//* Reboot / shutdown the system.
+// cmd ANDROID_RB_* as defined in android_reboot.h
+// reason Reason string like "reboot", "shutdown,userrequested"
+// rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
+// empty string.
+// runFsck Whether to run fsck after umount is done.
+//
+static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+ bool runFsck) {
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
@@ -443,6 +400,7 @@
for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (!s->IsShutdownCritical()) s->Stop();
}
+ SubcontextTerminate();
ReapAnyOutstandingChildren();
// 3. send volume shutdown to vold
diff --git a/init/reboot.h b/init/reboot.h
index 1c58bd1..07dcb6e 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -22,26 +22,9 @@
namespace android {
namespace init {
-// This is a wrapper around the actual reboot calls. DoReboot() should be preferred in most cases.
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);
-
-/* Reboot / shutdown the system.
- * cmd ANDROID_RB_* as defined in android_reboot.h
- * reason Reason string like "reboot", "shutdown,userrequested"
- * rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
- * empty string.
- * runFsck Whether to run fsck after umount is done.
- */
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
- bool runFsck) __attribute__((__noreturn__));
-
// Parses and handles a setprop sys.powerctl message.
bool HandlePowerctlMessage(const std::string& command);
-// Determines whether the system is capable of rebooting. This is conservative,
-// so if any of the attempts to determine this fail, it will still return true.
-bool IsRebootCapable();
-
} // namespace init
} // namespace android
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
new file mode 100644
index 0000000..9610304
--- /dev/null
+++ b/init/reboot_utils.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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 <sys/capability.h>
+#include <sys/reboot.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+
+#include "capabilities.h"
+
+namespace android {
+namespace init {
+
+bool IsRebootCapable() {
+ if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
+ PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
+ return true;
+ }
+
+ ScopedCaps caps(cap_get_proc());
+ if (!caps) {
+ PLOG(WARNING) << "cap_get_proc() failed";
+ return true;
+ }
+
+ cap_flag_value_t value = CAP_SET;
+ if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
+ PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
+ return true;
+ }
+ return value == CAP_SET;
+}
+
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+ LOG(INFO) << "Reboot ending, jumping to kernel";
+
+ if (!IsRebootCapable()) {
+ // On systems where init does not have the capability of rebooting the
+ // device, just exit cleanly.
+ exit(0);
+ }
+
+ switch (cmd) {
+ case ANDROID_RB_POWEROFF:
+ reboot(RB_POWER_OFF);
+ break;
+
+ case ANDROID_RB_RESTART2:
+ syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
+ break;
+
+ case ANDROID_RB_THERMOFF:
+ reboot(RB_POWER_OFF);
+ break;
+ }
+ // In normal case, reboot should not return.
+ PLOG(ERROR) << "reboot call returned";
+ abort();
+}
+
+void InstallRebootSignalHandlers() {
+ // Instead of panic'ing the kernel as is the default behavior when init crashes,
+ // we prefer to reboot to bootloader on development builds, as this will prevent
+ // boot looping bad configurations and allow both developers and test farms to easily
+ // recover.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigfillset(&action.sa_mask);
+ action.sa_handler = [](int signal) {
+ // These signal handlers are also caught for processes forked from init, however we do not
+ // want them to trigger reboot, so we directly call _exit() for children processes here.
+ if (getpid() != 1) {
+ _exit(signal);
+ }
+
+ // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
+ // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
+ // and probably good enough given this is already an error case and only enabled for
+ // development builds.
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ };
+ action.sa_flags = SA_RESTART;
+ sigaction(SIGABRT, &action, nullptr);
+ sigaction(SIGBUS, &action, nullptr);
+ sigaction(SIGFPE, &action, nullptr);
+ sigaction(SIGILL, &action, nullptr);
+ sigaction(SIGSEGV, &action, nullptr);
+#if defined(SIGSTKFLT)
+ sigaction(SIGSTKFLT, &action, nullptr);
+#endif
+ sigaction(SIGSYS, &action, nullptr);
+ sigaction(SIGTRAP, &action, nullptr);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
new file mode 100644
index 0000000..073a16a
--- /dev/null
+++ b/init/reboot_utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace init {
+
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+bool IsRebootCapable();
+// This is a wrapper around the actual reboot calls.
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
+void InstallRebootSignalHandlers();
+
+} // namespace init
+} // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 94f206e..fd7e86f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -47,6 +47,7 @@
#include "selinux.h"
+#include <android/api-level.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
@@ -414,6 +415,8 @@
if constexpr (WORLD_WRITABLE_KMSG) {
selinux_android_restorecon("/dev/kmsg_debug", 0);
}
+ selinux_android_restorecon("/dev/null", 0);
+ selinux_android_restorecon("/dev/ptmx", 0);
selinux_android_restorecon("/dev/socket", 0);
selinux_android_restorecon("/dev/random", 0);
selinux_android_restorecon("/dev/urandom", 0);
@@ -470,29 +473,27 @@
selinux_set_callback(SELINUX_CB_LOG, cb);
}
-// This function checks whether the sepolicy supports vendor init.
-bool SelinuxHasVendorInit() {
+// This function returns the Android version with which the vendor SEPolicy was compiled.
+// It is used for version checks such as whether or not vendor_init should be used
+int SelinuxGetVendorAndroidVersion() {
if (!IsSplitPolicyDevice()) {
- // If this device does not split sepolicy files, vendor_init will be available in the latest
- // monolithic sepolicy file.
- return true;
+ // If this device does not split sepolicy files, it's not a Treble device and therefore,
+ // we assume it's always on the latest platform.
+ return __ANDROID_API_FUTURE__;
}
std::string version;
if (!GetVendorMappingVersion(&version)) {
- // Return true as the default if we failed to load the vendor sepolicy version.
- return true;
+ LOG(FATAL) << "Could not read vendor SELinux version";
}
int major_version;
std::string major_version_str(version, 0, version.find('.'));
if (!ParseInt(major_version_str, &major_version)) {
- PLOG(ERROR) << "Failed to parse the vendor sepolicy major version " << major_version_str;
- // Return true as the default if we failed to parse the major version.
- return true;
+ PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
}
- return major_version >= 28;
+ return major_version;
}
// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
diff --git a/init/selinux.h b/init/selinux.h
index 30069b5..c41d7f0 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -27,7 +27,7 @@
void SelinuxRestoreContext();
void SelinuxSetupKernelLogging();
-bool SelinuxHasVendorInit();
+int SelinuxGetVendorAndroidVersion();
void SelabelInitialize();
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
diff --git a/init/service.cpp b/init/service.cpp
index 4c2747e..d20e90a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -46,10 +46,12 @@
#include "util.h"
#if defined(__ANDROID__)
+#include <android/api-level.h>
#include <sys/system_properties.h>
#include "init.h"
#include "property_service.h"
+#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
@@ -1211,6 +1213,13 @@
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
+
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
+ if (str_args[0] == "/sbin/watchdogd") {
+ str_args[0] = "/system/bin/watchdogd";
+ }
+ }
+
service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
return Success();
}
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 267d530..c2a21d4 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -30,6 +30,7 @@
#include "util.h"
#if defined(__ANDROID__)
+#include <android/api-level.h>
#include "property_service.h"
#include "selinux.h"
#else
@@ -352,9 +353,10 @@
}
static std::vector<Subcontext> subcontexts;
+static bool shutting_down;
std::vector<Subcontext>* InitializeSubcontexts() {
- if (SelinuxHasVendorInit()) {
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
subcontexts.emplace_back(path_prefix, secontext);
}
@@ -365,12 +367,21 @@
bool SubcontextChildReap(pid_t pid) {
for (auto& subcontext : subcontexts) {
if (subcontext.pid() == pid) {
- subcontext.Restart();
+ if (!shutting_down) {
+ subcontext.Restart();
+ }
return true;
}
}
return false;
}
+void SubcontextTerminate() {
+ shutting_down = true;
+ for (auto& subcontext : subcontexts) {
+ kill(subcontext.pid(), SIGTERM);
+ }
+}
+
} // namespace init
} // namespace android
diff --git a/init/subcontext.h b/init/subcontext.h
index 22d7d43..628fd50 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -63,6 +63,7 @@
int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
std::vector<Subcontext>* InitializeSubcontexts();
bool SubcontextChildReap(pid_t pid);
+void SubcontextTerminate();
} // namespace init
} // namespace android
diff --git a/init/watchdogd.h b/init/uevent_handler.h
similarity index 71%
rename from init/watchdogd.h
rename to init/uevent_handler.h
index 73f77d5..75d1990 100644
--- a/init/watchdogd.h
+++ b/init/uevent_handler.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -14,15 +14,21 @@
* limitations under the License.
*/
-#ifndef _INIT_WATCHDOGD_H_
-#define _INIT_WATCHDOGD_H_
+#pragma once
+
+#include "uevent.h"
namespace android {
namespace init {
-int watchdogd_main(int argc, char **argv);
+class UeventHandler {
+ public:
+ virtual ~UeventHandler() = default;
+
+ virtual void HandleUevent(const Uevent& uevent) = 0;
+
+ virtual void ColdbootDone() {}
+};
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index e9d829b..95be6af 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -38,6 +38,7 @@
#include "firmware_handler.h"
#include "modalias_handler.h"
#include "selinux.h"
+#include "uevent_handler.h"
#include "uevent_listener.h"
#include "ueventd_parser.h"
#include "util.h"
@@ -107,11 +108,10 @@
class ColdBoot {
public:
- ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler,
- ModaliasHandler& modalias_handler)
+ ColdBoot(UeventListener& uevent_listener,
+ std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers)
: uevent_listener_(uevent_listener),
- device_handler_(device_handler),
- modalias_handler_(modalias_handler),
+ uevent_handlers_(uevent_handlers),
num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
void Run();
@@ -124,8 +124,7 @@
void WaitForSubProcesses();
UeventListener& uevent_listener_;
- DeviceHandler& device_handler_;
- ModaliasHandler& modalias_handler_;
+ std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
unsigned int num_handler_subprocesses_;
std::vector<Uevent> uevent_queue_;
@@ -136,16 +135,16 @@
void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
auto& uevent = uevent_queue_[i];
- device_handler_.HandleDeviceEvent(uevent);
- modalias_handler_.HandleModaliasEvent(uevent);
+
+ for (auto& uevent_handler : uevent_handlers_) {
+ uevent_handler->HandleUevent(uevent);
+ }
}
_exit(EXIT_SUCCESS);
}
void ColdBoot::RegenerateUevents() {
uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
- HandleFirmwareEvent(uevent);
-
uevent_queue_.emplace_back(std::move(uevent));
return ListenerAction::kContinue;
});
@@ -168,7 +167,6 @@
void ColdBoot::DoRestoreCon() {
selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
- device_handler_.set_skip_restorecon(false);
}
void ColdBoot::WaitForSubProcesses() {
@@ -234,8 +232,7 @@
SelinuxSetupKernelLogging();
SelabelInitialize();
- DeviceHandler device_handler;
- ModaliasHandler modalias_handler;
+ std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
UeventListener uevent_listener;
{
@@ -248,19 +245,27 @@
ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
"/ueventd." + hardware + ".rc"});
- device_handler = DeviceHandler{std::move(ueventd_configuration.dev_permissions),
- std::move(ueventd_configuration.sysfs_permissions),
- std::move(ueventd_configuration.subsystems),
- fs_mgr_get_boot_devices(), true};
+ uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
+ std::move(ueventd_configuration.dev_permissions),
+ std::move(ueventd_configuration.sysfs_permissions),
+ std::move(ueventd_configuration.subsystems), fs_mgr_get_boot_devices(), true));
+ uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
+ std::move(ueventd_configuration.firmware_directories)));
- firmware_directories = ueventd_configuration.firmware_directories;
+ if (ueventd_configuration.enable_modalias_handling) {
+ uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
+ }
}
if (access(COLDBOOT_DONE, F_OK) != 0) {
- ColdBoot cold_boot(uevent_listener, device_handler, modalias_handler);
+ ColdBoot cold_boot(uevent_listener, uevent_handlers);
cold_boot.Run();
}
+ for (auto& uevent_handler : uevent_handlers) {
+ uevent_handler->ColdbootDone();
+ }
+
// We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
signal(SIGCHLD, SIG_IGN);
// Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
@@ -268,10 +273,10 @@
while (waitpid(-1, nullptr, WNOHANG) > 0) {
}
- uevent_listener.Poll([&device_handler, &modalias_handler](const Uevent& uevent) {
- HandleFirmwareEvent(uevent);
- modalias_handler.HandleModaliasEvent(uevent);
- device_handler.HandleDeviceEvent(uevent);
+ uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
+ for (auto& uevent_handler : uevent_handlers) {
+ uevent_handler->HandleUevent(uevent);
+ }
return ListenerAction::kContinue;
});
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 54b0d16..677938e 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -84,6 +84,23 @@
return Success();
}
+Result<Success> ParseModaliasHandlingLine(std::vector<std::string>&& args,
+ bool* enable_modalias_handling) {
+ if (args.size() != 2) {
+ return Error() << "modalias_handling lines take exactly one parameter";
+ }
+
+ if (args[1] == "enabled") {
+ *enable_modalias_handling = true;
+ } else if (args[1] == "disabled") {
+ *enable_modalias_handling = false;
+ } else {
+ return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
+ }
+
+ return Success();
+}
+
class SubsystemParser : public SectionParser {
public:
SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
@@ -182,6 +199,9 @@
parser.AddSingleLineParser("firmware_directories",
std::bind(ParseFirmwareDirectoriesLine, _1,
&ueventd_configuration.firmware_directories));
+ parser.AddSingleLineParser("modalias_handling",
+ std::bind(ParseModaliasHandlingLine, _1,
+ &ueventd_configuration.enable_modalias_handling));
for (const auto& config : configs) {
parser.ParseConfig(config);
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 343d58b..7d30edf 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -30,6 +30,7 @@
std::vector<SysfsPermissions> sysfs_permissions;
std::vector<Permissions> dev_permissions;
std::vector<std::string> firmware_directories;
+ bool enable_modalias_handling = false;
};
UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
diff --git a/init/util.cpp b/init/util.cpp
index 7735717..105ac87 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -47,7 +47,7 @@
#endif
#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#error "Do not include init.h in files used by ueventd; it will expose init's globals"
#endif
using android::base::boot_clock;
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index a9cfce4..c564271 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -31,6 +31,7 @@
#include <deque>
#include <iterator>
+#include <memory>
#include <string>
#include <vector>
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 267b7b3..48def6f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -130,7 +130,7 @@
// Vendor entries should be done via a vendor or device specific config.fs.
// See https://source.android.com/devices/tech/config/filesystem#using-file-system-capabilities
static const struct fs_path_config android_files[] = {
- // clang-format off
+ // clang-format off
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
@@ -149,6 +149,7 @@
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_file + 1 },
{ 00600, AID_ROOT, AID_ROOT, 0, "product/build.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "product-services/build.prop" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
@@ -200,7 +201,7 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
- // clang-format on
+ // clang-format on
};
#ifndef __ANDROID_VNDK__
auto __for_testing_only__android_files = android_files;
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index bb1ce34..1b7c377 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -108,6 +108,13 @@
android_log_list_element android_log_read_next(android_log_context ctx);
android_log_list_element android_log_peek_next(android_log_context ctx);
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+ const char* msg, size_t len);
+
/* Finished with reader or writer context */
int android_log_destroy(android_log_context* ctx);
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
index cbd3091..9f74534 100644
--- a/liblog/include_vndk/log/log_event_list.h
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -63,6 +63,13 @@
/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
int android_log_write_list(android_log_context ctx, log_id_t id);
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+ const char* msg, size_t len);
+
/* Finished with reader or writer context */
int android_log_destroy(android_log_context* ctx);
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 66670fe..015c9cb 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -53,3 +53,9 @@
__android_log_is_loggable_len;
__android_log_is_debuggable; # vndk
};
+
+LIBLOG_Q {
+ global:
+ android_log_reset; #vndk
+ android_log_parser_reset; #vndk
+};
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index a59cb87..14002ce 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -45,14 +45,9 @@
uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
} android_log_context_internal;
-LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
- size_t needed, i;
- android_log_context_internal* context;
+static void init_context(android_log_context_internal* context, uint32_t tag) {
+ size_t needed;
- context = calloc(1, sizeof(android_log_context_internal));
- if (!context) {
- return NULL;
- }
context->tag = tag;
context->read_write_flag = kAndroidLoggerWrite;
needed = sizeof(uint8_t) + sizeof(uint8_t);
@@ -63,6 +58,24 @@
context->storage[context->pos + 0] = EVENT_TYPE_LIST;
context->list[0] = context->pos + 1;
context->pos += needed;
+}
+
+static void init_parser_context(android_log_context_internal* context,
+ const char* msg, size_t len) {
+ len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+ context->len = len;
+ memcpy(context->storage, msg, len);
+ context->read_write_flag = kAndroidLoggerRead;
+}
+
+LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
+ android_log_context_internal* context;
+
+ context = calloc(1, sizeof(android_log_context_internal));
+ if (!context) {
+ return NULL;
+ }
+ init_context(context, tag);
return (android_log_context)context;
}
@@ -76,10 +89,7 @@
if (!context) {
return NULL;
}
- len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
- context->len = len;
- memcpy(context->storage, msg, len);
- context->read_write_flag = kAndroidLoggerRead;
+ init_parser_context(context, msg, len);
return (android_log_context)context;
}
@@ -97,6 +107,38 @@
return 0;
}
+LIBLOG_ABI_PUBLIC int android_log_reset(android_log_context ctx) {
+ android_log_context_internal* context;
+ uint32_t tag;
+
+ context = (android_log_context_internal*)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ tag = context->tag;
+ memset(context, 0, sizeof(*context));
+ init_context(context, tag);
+
+ return 0;
+}
+
+LIBLOG_ABI_PUBLIC int android_log_parser_reset(android_log_context ctx,
+ const char* msg, size_t len) {
+ android_log_context_internal* context;
+
+ context = (android_log_context_internal*)ctx;
+ if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ memset(context, 0, sizeof(*context));
+ init_parser_context(context, msg, len);
+
+ return 0;
+}
+
+
LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
size_t needed;
android_log_context_internal* context;
diff --git a/libpixelflinger/fixed.cpp b/libpixelflinger/fixed.cpp
index 5094537..de6b479 100644
--- a/libpixelflinger/fixed.cpp
+++ b/libpixelflinger/fixed.cpp
@@ -70,17 +70,6 @@
// ------------------------------------------------------------------------
-GGLfixed gglFastDivx(GGLfixed n, GGLfixed d)
-{
- if ((d>>24) && ((d>>24)+1)) {
- n >>= 8;
- d >>= 8;
- }
- return gglMulx(n, gglRecip(d));
-}
-
-// ------------------------------------------------------------------------
-
static const GGLfixed ggl_sqrt_reciproc_approx_tab[8] = {
// 1/sqrt(x) with x = 1-N/16, N=[8...1]
0x16A09, 0x15555, 0x143D1, 0x134BF, 0x1279A, 0x11C01, 0x111AC, 0x10865
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 51e9e26..7f39e9b 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -86,7 +86,6 @@
GGLfixed gglPowx(GGLfixed x, GGLfixed y) CONST;
GGLfixed gglSqrtx(GGLfixed a) CONST;
GGLfixed gglSqrtRecipx(GGLfixed x) CONST;
-GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) CONST;
int32_t gglMulDivi(int32_t a, int32_t b, int32_t c);
int32_t gglRecipQNormalized(int32_t x, int* exponent);
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index ba7d8c4..2a59e35 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -63,6 +63,8 @@
*
* The original fences remain valid, and the caller is responsible for closing
* them.
+ *
+ * Available since API level 26.
*/
int32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);
@@ -70,6 +72,8 @@
* Retrieve detailed information about a sync file and its fences.
*
* The returned sync_file_info must be freed by calling sync_file_info_free().
+ *
+ * Available since API level 26.
*/
struct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);
@@ -78,6 +82,8 @@
*
* The returned array is owned by the parent sync file info, and has
* info->num_fences entries.
+ *
+ * Available since API level 26.
*/
static inline struct sync_fence_info* sync_get_fence_info(const struct sync_file_info* info) {
// This header should compile in C, but some C++ projects enable
@@ -88,7 +94,11 @@
#pragma GCC diagnostic pop
}
-/** Free a struct sync_file_info structure */
+/**
+ * Free a struct sync_file_info structure
+ *
+ * Available since API level 26.
+ */
void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
#endif /* __ANDROID_API__ >= 26 */
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index df5da65..be2145d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -190,6 +190,7 @@
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
+ "tests/RegsInfoTest.cpp",
"tests/RegsIterateTest.cpp",
"tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 2c00456..f59a472 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -167,15 +167,10 @@
return false;
}
- if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias)) {
- return false;
- }
-
- // We could still potentially unwind without the section header
- // information, so ignore any errors.
- if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
- log(0, "Malformed section header found, ignoring...");
- }
+ // If we have enough information that this is an elf file, then allow
+ // malformed program and section headers.
+ ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias);
+ ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
return true;
}
@@ -200,14 +195,12 @@
}
template <typename EhdrType, typename PhdrType>
-bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
+void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
uint64_t offset = ehdr.e_phoff;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
PhdrType phdr;
if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
- last_error_.code = ERROR_MEMORY_INVALID;
- last_error_.address = offset;
- return false;
+ return;
}
switch (phdr.p_type) {
@@ -242,11 +235,10 @@
break;
}
}
- return true;
}
template <typename EhdrType, typename ShdrType>
-bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
+void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
uint64_t offset = ehdr.e_shoff;
uint64_t sec_offset = 0;
uint64_t sec_size = 0;
@@ -267,9 +259,7 @@
offset += ehdr.e_shentsize;
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
- last_error_.code = ERROR_MEMORY_INVALID;
- last_error_.address = offset;
- return false;
+ return;
}
if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
@@ -277,18 +267,14 @@
// the string terminated names.
ShdrType str_shdr;
if (shdr.sh_link >= ehdr.e_shnum) {
- last_error_.code = ERROR_UNWIND_INFO;
- return false;
+ continue;
}
uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
- last_error_.code = ERROR_MEMORY_INVALID;
- last_error_.address = str_offset;
- return false;
+ continue;
}
if (str_shdr.sh_type != SHT_STRTAB) {
- last_error_.code = ERROR_UNWIND_INFO;
- return false;
+ continue;
}
symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
str_shdr.sh_offset, str_shdr.sh_size));
@@ -324,7 +310,6 @@
static_cast<uint64_t>(shdr.sh_offset)));
}
}
- return true;
}
template <typename DynType>
@@ -499,11 +484,13 @@
template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
-template bool ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, uint64_t*);
-template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, uint64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&,
+ uint64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&,
+ uint64_t*);
-template bool ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
-template bool ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
+template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
+template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index 47825f5..e6dd33c 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -25,11 +25,13 @@
template <typename AddressType>
struct RegsInfo {
+ static constexpr size_t MAX_REGISTERS = 64;
+
RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}
RegsImpl<AddressType>* regs = nullptr;
uint64_t saved_reg_map = 0;
- AddressType saved_regs[64];
+ AddressType saved_regs[MAX_REGISTERS];
inline AddressType Get(uint32_t reg) {
if (IsSaved(reg)) {
@@ -39,23 +41,23 @@
}
inline AddressType* Save(uint32_t reg) {
- if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
- // This should never happen as since all currently supported
- // architectures have the total number of registers < 64.
+ if (reg > MAX_REGISTERS) {
+ // This should never happen since all currently supported
+ // architectures have < 64 total registers.
abort();
}
- saved_reg_map |= 1 << reg;
+ saved_reg_map |= 1ULL << reg;
saved_regs[reg] = (*regs)[reg];
return &(*regs)[reg];
}
inline bool IsSaved(uint32_t reg) {
- if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
- // This should never happen as since all currently supported
- // architectures have the total number of registers < 64.
+ if (reg > MAX_REGISTERS) {
+ // This should never happen since all currently supported
+ // architectures have < 64 total registers.
abort();
}
- return saved_reg_map & (1 << reg);
+ return saved_reg_map & (1ULL << reg);
}
inline uint16_t Total() { return regs->total_regs(); }
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5c1210d..a45eba8 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -104,10 +104,10 @@
bool ReadAllHeaders(uint64_t* load_bias);
template <typename EhdrType, typename PhdrType>
- bool ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
+ void ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
template <typename EhdrType, typename ShdrType>
- bool ReadSectionHeaders(const EhdrType& ehdr);
+ void ReadSectionHeaders(const EhdrType& ehdr);
template <typename DynType>
bool GetSonameWithTemplate(std::string* soname);
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index aa6df84..9326bff 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -97,9 +97,15 @@
template <typename ElfType>
void InitHeadersDebugFrameFail();
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void InitProgramHeadersMalformed();
+
template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
void InitSectionHeadersMalformed();
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersMalformedSymData();
+
template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
void InitSectionHeaders(uint64_t entry_size);
@@ -677,6 +683,29 @@
InitHeadersDebugFrame<ElfInterface64Fake>();
}
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitProgramHeadersMalformed() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 3;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0U, load_bias);
+}
+
+TEST_F(ElfInterfaceTest, init_program_headers_malformed32) {
+ InitProgramHeadersMalformed<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_program_headers_malformed64) {
+ InitProgramHeadersMalformed<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>();
+}
+
template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
void ElfInterfaceTest::InitSectionHeadersMalformed() {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -700,6 +729,80 @@
InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
}
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformedSymData() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x1000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 5;
+ ehdr.e_shentsize = sizeof(Shdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 10;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 2;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for the entries.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0U, elf->debug_frame_offset());
+ EXPECT_EQ(0U, elf->debug_frame_size());
+ EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+ std::string name;
+ uint64_t name_offset;
+ ASSERT_FALSE(elf->GetFunctionName(0x90010, &name, &name_offset));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata32) {
+ InitSectionHeadersMalformedSymData<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata64) {
+ InitSectionHeadersMalformedSymData<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -708,7 +811,7 @@
Ehdr ehdr = {};
ehdr.e_shoff = offset;
- ehdr.e_shnum = 10;
+ ehdr.e_shnum = 5;
ehdr.e_shentsize = entry_size;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
@@ -795,7 +898,7 @@
Ehdr ehdr = {};
ehdr.e_shoff = offset;
- ehdr.e_shnum = 10;
+ ehdr.e_shnum = 6;
ehdr.e_shentsize = sizeof(Shdr);
ehdr.e_shstrndx = 2;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index f599503..861b82f 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -319,7 +319,7 @@
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
- ehdr.e_shnum = 4;
+ ehdr.e_shnum = 0;
memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
Elf* elf = info.GetElf(process_memory_, false);
@@ -341,7 +341,7 @@
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
- ehdr.e_shnum = 4;
+ ehdr.e_shnum = 0;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
Elf* elf = info.GetElf(process_memory_, false);
@@ -368,7 +368,7 @@
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
- ehdr.e_shnum = 4;
+ ehdr.e_shnum = 0;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
Elf* elf_in_threads[kNumConcurrentThreads];
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
new file mode 100644
index 0000000..052b5bf
--- /dev/null
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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 <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Regs.h>
+
+#include "RegsFake.h"
+#include "RegsInfo.h"
+
+namespace unwindstack {
+
+TEST(RegsInfoTest, single_uint32_t) {
+ RegsImplFake<uint32_t> regs(10);
+ RegsInfo<uint32_t> info(®s);
+
+ regs[1] = 0x100;
+ ASSERT_FALSE(info.IsSaved(1));
+ ASSERT_EQ(0x100U, info.Get(1));
+ ASSERT_EQ(10, info.Total());
+
+ uint32_t* value = info.Save(1);
+ ASSERT_EQ(value, ®s[1]);
+ regs[1] = 0x200;
+ ASSERT_TRUE(info.IsSaved(1));
+ ASSERT_EQ(0x100U, info.Get(1));
+ ASSERT_EQ(0x200U, regs[1]);
+}
+
+TEST(RegsInfoTest, single_uint64_t) {
+ RegsImplFake<uint64_t> regs(20);
+ RegsInfo<uint64_t> info(®s);
+
+ regs[3] = 0x300;
+ ASSERT_FALSE(info.IsSaved(3));
+ ASSERT_EQ(0x300U, info.Get(3));
+ ASSERT_EQ(20, info.Total());
+
+ uint64_t* value = info.Save(3);
+ ASSERT_EQ(value, ®s[3]);
+ regs[3] = 0x400;
+ ASSERT_TRUE(info.IsSaved(3));
+ ASSERT_EQ(0x300U, info.Get(3));
+ ASSERT_EQ(0x400U, regs[3]);
+}
+
+TEST(RegsInfoTest, all) {
+ RegsImplFake<uint64_t> regs(64);
+ RegsInfo<uint64_t> info(®s);
+
+ for (uint32_t i = 0; i < 64; i++) {
+ regs[i] = i * 0x100;
+ ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+ }
+
+ for (uint32_t i = 0; i < 64; i++) {
+ ASSERT_FALSE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+ uint64_t* reg = info.Save(i);
+ ASSERT_EQ(reg, ®s[i]) << "Reg " + std::to_string(i) + " failed.";
+ *reg = i * 0x1000 + 0x100;
+ ASSERT_EQ(i * 0x1000 + 0x100, regs[i]) << "Reg " + std::to_string(i) + " failed.";
+ }
+
+ for (uint32_t i = 0; i < 64; i++) {
+ ASSERT_TRUE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+ ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+ }
+}
+
+} // namespace unwindstack
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index bd6015e..2a83bd9 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,16 +16,15 @@
#define LOG_TAG "CallStack"
-#include <utils/CallStack.h>
-
-#include <memory>
-
#include <utils/Printer.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <backtrace/Backtrace.h>
+#define CALLSTACK_WEAK // Don't generate weak definitions.
+#include <utils/CallStack.h>
+
namespace android {
CallStack::CallStack() {
@@ -76,4 +75,26 @@
}
}
+// The following four functions may be used via weak symbol references from libutils.
+// Clients assume that if any of these symbols are available, then deleteStack() is.
+
+CallStack::CallStackUPtr CallStack::getCurrentInternal(int ignoreDepth) {
+ CallStack::CallStackUPtr stack(new CallStack());
+ stack->update(ignoreDepth + 1);
+ return stack;
+}
+
+void CallStack::logStackInternal(const char* logtag, const CallStack* stack,
+ android_LogPriority priority) {
+ stack->log(logtag, priority);
+}
+
+String8 CallStack::stackToStringInternal(const char* prefix, const CallStack* stack) {
+ return stack->toString(prefix);
+}
+
+void CallStack::deleteStack(CallStack* stack) {
+ delete stack;
+}
+
}; // namespace android
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 9074850..3f1e79a 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -17,30 +17,41 @@
#define LOG_TAG "RefBase"
// #define LOG_NDEBUG 0
+#include <memory>
+
#include <utils/RefBase.h>
#include <utils/CallStack.h>
+#include <utils/Mutex.h>
+
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
-// compile with refcounting debugging enabled
-#define DEBUG_REFS 0
+// Compile with refcounting debugging enabled.
+#define DEBUG_REFS 0
+
+// The following three are ignored unless DEBUG_REFS is set.
// whether ref-tracking is enabled by default, if not, trackMe(true, false)
// needs to be called explicitly
-#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
+#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
// whether callstack are collected (significantly slows things down)
-#define DEBUG_REFS_CALLSTACK_ENABLED 1
+#define DEBUG_REFS_CALLSTACK_ENABLED 1
// folder where stack traces are saved when DEBUG_REFS is enabled
// this folder needs to exist and be writable
-#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
+#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
// log all reference counting operations
-#define PRINT_REFS 0
+#define PRINT_REFS 0
+
+// Continue after logging a stack trace if ~RefBase discovers that reference
+// count has never been incremented. Normally we conspicuously crash in that
+// case.
+#define DEBUG_REFBASE_DESTRUCTION 1
// ---------------------------------------------------------------------------
@@ -184,7 +195,7 @@
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
- refs->stack.log(LOG_TAG);
+ CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
}
@@ -198,14 +209,14 @@
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
- refs->stack.log(LOG_TAG);
+ CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
}
}
if (dumpStack) {
ALOGE("above errors at:");
- CallStack stack(LOG_TAG);
+ CallStack::logStack(LOG_TAG);
}
}
@@ -279,7 +290,7 @@
this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
- write(rc, text.string(), text.length());
+ (void)write(rc, text.string(), text.length());
close(rc);
ALOGD("STACK TRACE for %p saved in %s", this, name);
}
@@ -294,7 +305,7 @@
ref_entry* next;
const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
- CallStack stack;
+ CallStack::CallStackUPtr stack;
#endif
int32_t ref;
};
@@ -311,7 +322,7 @@
ref->ref = mRef;
ref->id = id;
#if DEBUG_REFS_CALLSTACK_ENABLED
- ref->stack.update(2);
+ ref->stack = CallStack::getCurrent(2);
#endif
ref->next = *refs;
*refs = ref;
@@ -346,7 +357,7 @@
ref = ref->next;
}
- CallStack stack(LOG_TAG);
+ CallStack::logStack(LOG_TAG);
}
}
@@ -373,7 +384,7 @@
inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
- out->append(refs->stack.toString("\t\t"));
+ out->append(CallStack::stackToString("\t\t", refs->stack.get()));
#else
out->append("\t\t(call stacks disabled)");
#endif
@@ -700,16 +711,16 @@
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
- } else if (mRefs->mStrong.load(std::memory_order_relaxed)
- == INITIAL_STRONG_VALUE) {
+ } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
// We never acquired a strong reference on this object.
- LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0,
- "RefBase: Explicit destruction with non-zero weak "
- "reference count");
- // TODO: Always report if we get here. Currently MediaMetadataRetriever
- // C++ objects are inconsistently managed and sometimes get here.
- // There may be other cases, but we believe they should all be fixed.
- delete mRefs;
+#if DEBUG_REFBASE_DESTRUCTION
+ // Treating this as fatal is prone to causing boot loops. For debugging, it's
+ // better to treat as non-fatal.
+ ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
+ CallStack::logStack(LOG_TAG);
+#else
+ LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
+#endif
}
// For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = nullptr;
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 0c1b875..a7b1d10 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_CALLSTACK_H
#define ANDROID_CALLSTACK_H
+#include <memory>
+
#include <android/log.h>
#include <backtrace/backtrace_constants.h>
#include <utils/String8.h>
@@ -25,6 +27,11 @@
#include <stdint.h>
#include <sys/types.h>
+#ifndef CALLSTACK_WEAK
+#define CALLSTACK_WEAK __attribute__((weak))
+#endif
+#define ALWAYS_INLINE __attribute__((always_inline))
+
namespace android {
class Printer;
@@ -36,7 +43,7 @@
CallStack();
// Create a callstack with the current thread's stack trace.
// Immediately dump it to logcat using the given logtag.
- CallStack(const char* logtag, int32_t ignoreDepth=1);
+ CallStack(const char* logtag, int32_t ignoreDepth = 1);
~CallStack();
// Reset the stack frames (same as creating an empty call stack).
@@ -44,7 +51,7 @@
// Immediately collect the stack traces for the specified thread.
// The default is to dump the stack of the current call.
- void update(int32_t ignoreDepth=1, pid_t tid=BACKTRACE_CURRENT_THREAD);
+ void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
// Dump a stack trace to the log using the supplied logtag.
void log(const char* logtag,
@@ -63,7 +70,58 @@
// Get the count of stack frames that are in this call stack.
size_t size() const { return mFrameLines.size(); }
-private:
+ // DO NOT USE ANYTHING BELOW HERE. The following public members are expected
+ // to disappear again shortly, once a better replacement facility exists.
+ // The replacement facility will be incompatible!
+
+ // Debugging accesses to some basic functionality. These use weak symbols to
+ // avoid introducing a dependency on libutilscallstack. Such a dependency from
+ // libutils results in a cyclic build dependency. These routines can be called
+ // from within libutils. But if the actual library is unavailable, they do
+ // nothing.
+ //
+ // DO NOT USE THESE. They will disappear.
+ struct StackDeleter {
+ void operator()(CallStack* stack) { deleteStack(stack); }
+ };
+
+ typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;
+
+ // Return current call stack if possible, nullptr otherwise.
+ static CallStackUPtr ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
+ if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
+ ALOGW("CallStack::getCurrentInternal not linked, returning null");
+ return CallStackUPtr(nullptr);
+ } else {
+ return getCurrentInternal(ignoreDepth);
+ }
+ }
+ static void ALWAYS_INLINE logStack(const char* logtag, CallStack* stack = getCurrent().get(),
+ android_LogPriority priority = ANDROID_LOG_DEBUG) {
+ if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
+ logStackInternal(logtag, stack, priority);
+ } else {
+ ALOGW("CallStack::logStackInternal not linked");
+ }
+ }
+ static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
+ const CallStack* stack = getCurrent().get()) {
+ if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
+ return stackToStringInternal(prefix, stack);
+ } else {
+ return String8("<CallStack package not linked>");
+ }
+ }
+
+ private:
+ static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
+ static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
+ android_LogPriority priority);
+ static String8 CALLSTACK_WEAK stackToStringInternal(const char* prefix, const CallStack* stack);
+ // The deleter is only invoked on non-null pointers. Hence it will never be
+ // invoked if CallStack is not linked.
+ static void CALLSTACK_WEAK deleteStack(CallStack* stack);
+
Vector<String8> mFrameLines;
};
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 2efda86..9ac082f 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -7,17 +7,8 @@
"liblog",
],
local_include_dirs: ["include"],
- cflags: ["-Werror"],
-
+ cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
init_rc: ["lmkd.rc"],
-
- product_variables: {
- debuggable: {
- cflags: [
- "-DLMKD_TRACE_KILLS"
- ],
- },
- },
}
cc_library_static {
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index c2487d6..1a14be3 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -52,8 +52,8 @@
#else /* LMKD_TRACE_KILLS */
-#define TRACE_KILL_START(pid)
-#define TRACE_KILL_END()
+#define TRACE_KILL_START(pid) ((void)(pid))
+#define TRACE_KILL_END() ((void)0)
#endif /* LMKD_TRACE_KILLS */
@@ -111,6 +111,7 @@
static bool kill_heaviest_task;
static unsigned long kill_timeout_ms;
static bool use_minfree_levels;
+static bool per_app_memcg;
/* data required to handle events */
struct event_handler_info {
@@ -472,7 +473,7 @@
return;
}
- if (low_ram_device) {
+ if (per_app_memcg) {
if (params.oomadj >= 900) {
soft_limit_mult = 0;
} else if (params.oomadj >= 800) {
@@ -1481,6 +1482,8 @@
(unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
use_minfree_levels =
property_get_bool("ro.lmk.use_minfree_levels", false);
+ per_app_memcg =
+ property_get_bool("ro.config.per_app_memcg", low_ram_device);
if (!init()) {
if (!use_inkernel_interface) {
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 478b8d0..763dc79 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -93,6 +93,11 @@
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
endif
+ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product-services
+else
+ LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product-services $(TARGET_ROOT_OUT)/product-services
+endif
# For /odm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
@@ -202,6 +207,7 @@
$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
$$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
+ $$(hide) sed -i -e 's?%PRODUCTSERVICES%?$$(TARGET_COPY_OUT_PRODUCTSERVICES)?g' $$@
llndk_libraries_list :=
vndksp_libraries_list :=
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 42dc7ab..7d22a3a 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -8,6 +8,7 @@
dir.system = /system/bin/
dir.system = /system/xbin/
dir.system = /%PRODUCT%/bin/
+dir.system = /%PRODUCTSERVICES%/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -40,6 +41,7 @@
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCTSERVICES%/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -52,6 +54,7 @@
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
namespace.default.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
@@ -66,6 +69,9 @@
namespace.default.permitted.paths += /%PRODUCT%/framework
namespace.default.permitted.paths += /%PRODUCT%/app
namespace.default.permitted.paths += /%PRODUCT%/priv-app
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/framework
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/app
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
@@ -73,6 +79,8 @@
namespace.default.asan.search.paths += /system/${LIB}
namespace.default.asan.search.paths += /data/asan/product/${LIB}
namespace.default.asan.search.paths += /product/${LIB}
+namespace.default.asan.search.paths += /data/asan/product-services/${LIB}
+namespace.default.asan.search.paths += /product-services/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -92,6 +100,10 @@
namespace.default.asan.permitted.paths += /%PRODUCT%/framework
namespace.default.asan.permitted.paths += /%PRODUCT%/app
namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/framework
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/app
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
###############################################################################
@@ -328,6 +340,7 @@
namespace.system.search.paths = /system/${LIB}
namespace.system.search.paths += /%PRODUCT%/${LIB}
+namespace.system.search.paths += /%PRODUCTSERVICES%/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
@@ -346,3 +359,4 @@
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCTSERVICES%/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d3f038e..486d096 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -26,6 +26,7 @@
# Mount cgroup mount point for cpu accounting
mount cgroup none /acct nodev noexec nosuid cpuacct
+ chmod 0555 /acct
mkdir /acct/uid
# root memory control cgroup, used by lmkd
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 7834dd5..3d7521c 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -18,6 +18,7 @@
"newfs_msdos",
"reboot",
"sh",
+ "tcpdump",
"toolbox",
"toybox",
],
@@ -26,6 +27,7 @@
phony {
name: "shell_and_utilities_recovery",
required: [
+ "grep.recovery",
"sh.recovery",
"toolbox.recovery",
"toybox.recovery",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index e310e6b..d8cf867 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -193,14 +193,12 @@
Android Q
---------
-BSD: fsck\_msdos newfs\_msdos
+BSD: grep fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2
one-true-awk: awk
-PCRE: egrep fgrep grep
-
toolbox: getevent getprop
toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index fc51705..f08cf93 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -60,3 +60,39 @@
defaults: ["toolbox_defaults"],
srcs: ["r.c"],
}
+
+// We build BSD grep separately (but see http://b/111849261).
+cc_defaults {
+ name: "grep_common",
+ defaults: ["toolbox_defaults"],
+ srcs: [
+ "upstream-netbsd/usr.bin/grep/fastgrep.c",
+ "upstream-netbsd/usr.bin/grep/file.c",
+ "upstream-netbsd/usr.bin/grep/grep.c",
+ "upstream-netbsd/usr.bin/grep/queue.c",
+ "upstream-netbsd/usr.bin/grep/util.c",
+ ],
+ symlinks: [
+ "egrep",
+ "fgrep",
+ ],
+ sanitize: {
+ integer_overflow: false,
+ },
+}
+
+cc_binary {
+ name: "grep",
+ defaults: ["grep_common"],
+ recovery_available: true,
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+// when vendor_available is fully supported.
+cc_binary {
+ name: "grep_vendor",
+ stem: "grep",
+ vendor: true,
+ defaults: ["grep_common"],
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
new file mode 100644
index 0000000..2fcd864
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
@@ -0,0 +1,336 @@
+/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */
+/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * XXX: This file is a speed up for grep to cover the defects of the
+ * regex library. These optimizations should practically be implemented
+ * there keeping this code clean. This is a future TODO, but for the
+ * meantime, we need to use this workaround.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $");
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "grep.h"
+
+static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t);
+static inline void grep_revstr(unsigned char *, int);
+
+void
+fgrepcomp(fastgrep_t *fg, const char *pat)
+{
+ unsigned int i;
+
+ /* Initialize. */
+ fg->len = strlen(pat);
+ fg->bol = false;
+ fg->eol = false;
+ fg->reversed = false;
+
+ fg->pattern = (unsigned char *)grep_strdup(pat);
+
+ /* Preprocess pattern. */
+ for (i = 0; i <= UCHAR_MAX; i++)
+ fg->qsBc[i] = fg->len;
+ for (i = 1; i < fg->len; i++)
+ fg->qsBc[fg->pattern[i]] = fg->len - i;
+}
+
+/*
+ * Returns: -1 on failure, 0 on success
+ */
+int
+fastcomp(fastgrep_t *fg, const char *pat)
+{
+ unsigned int i;
+ int firstHalfDot = -1;
+ int firstLastHalfDot = -1;
+ int hasDot = 0;
+ int lastHalfDot = 0;
+ int shiftPatternLen;
+
+ /* Initialize. */
+ fg->len = strlen(pat);
+ fg->bol = false;
+ fg->eol = false;
+ fg->reversed = false;
+ fg->word = wflag;
+
+ /* Remove end-of-line character ('$'). */
+ if (fg->len > 0 && pat[fg->len - 1] == '$') {
+ fg->eol = true;
+ fg->len--;
+ }
+
+ /* Remove beginning-of-line character ('^'). */
+ if (pat[0] == '^') {
+ fg->bol = true;
+ fg->len--;
+ pat++;
+ }
+
+ if (fg->len >= 14 &&
+ memcmp(pat, "[[:<:]]", 7) == 0 &&
+ memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) {
+ fg->len -= 14;
+ pat += 7;
+ /* Word boundary is handled separately in util.c */
+ fg->word = true;
+ }
+
+ /*
+ * pat has been adjusted earlier to not include '^', '$' or
+ * the word match character classes at the beginning and ending
+ * of the string respectively.
+ */
+ fg->pattern = grep_malloc(fg->len + 1);
+ memcpy(fg->pattern, pat, fg->len);
+ fg->pattern[fg->len] = '\0';
+
+ /* Look for ways to cheat...er...avoid the full regex engine. */
+ for (i = 0; i < fg->len; i++) {
+ /* Can still cheat? */
+ if (fg->pattern[i] == '.') {
+ hasDot = i;
+ if (i < fg->len / 2) {
+ if (firstHalfDot < 0)
+ /* Closest dot to the beginning */
+ firstHalfDot = i;
+ } else {
+ /* Closest dot to the end of the pattern. */
+ lastHalfDot = i;
+ if (firstLastHalfDot < 0)
+ firstLastHalfDot = i;
+ }
+ } else {
+ /* Free memory and let others know this is empty. */
+ free(fg->pattern);
+ fg->pattern = NULL;
+ return (-1);
+ }
+ }
+
+ /*
+ * Determine if a reverse search would be faster based on the placement
+ * of the dots.
+ */
+ if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
+ ((lastHalfDot) && ((firstHalfDot < 0) ||
+ ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
+ !oflag && !color) {
+ fg->reversed = true;
+ hasDot = fg->len - (firstHalfDot < 0 ?
+ firstLastHalfDot : firstHalfDot) - 1;
+ grep_revstr(fg->pattern, fg->len);
+ }
+
+ /*
+ * Normal Quick Search would require a shift based on the position the
+ * next character after the comparison is within the pattern. With
+ * wildcards, the position of the last dot effects the maximum shift
+ * distance.
+ * The closer to the end the wild card is the slower the search. A
+ * reverse version of this algorithm would be useful for wildcards near
+ * the end of the string.
+ *
+ * Examples:
+ * Pattern Max shift
+ * ------- ---------
+ * this 5
+ * .his 4
+ * t.is 3
+ * th.s 2
+ * thi. 1
+ */
+
+ /* Adjust the shift based on location of the last dot ('.'). */
+ shiftPatternLen = fg->len - hasDot;
+
+ /* Preprocess pattern. */
+ for (i = 0; i <= (signed)UCHAR_MAX; i++)
+ fg->qsBc[i] = shiftPatternLen;
+ for (i = hasDot + 1; i < fg->len; i++) {
+ fg->qsBc[fg->pattern[i]] = fg->len - i;
+ }
+
+ /*
+ * Put pattern back to normal after pre-processing to allow for easy
+ * comparisons later.
+ */
+ if (fg->reversed)
+ grep_revstr(fg->pattern, fg->len);
+
+ return (0);
+}
+
+int
+grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch)
+{
+ unsigned int j;
+ int ret = REG_NOMATCH;
+
+ if (pmatch->rm_so == (ssize_t)len)
+ return (ret);
+
+ if (fg->bol && pmatch->rm_so != 0) {
+ pmatch->rm_so = len;
+ pmatch->rm_eo = len;
+ return (ret);
+ }
+
+ /* No point in going farther if we do not have enough data. */
+ if (len < fg->len)
+ return (ret);
+
+ /* Only try once at the beginning or ending of the line. */
+ if (fg->bol || fg->eol) {
+ /* Simple text comparison. */
+ /* Verify data is >= pattern length before searching on it. */
+ if (len >= fg->len) {
+ /* Determine where in data to start search at. */
+ j = fg->eol ? len - fg->len : 0;
+ if (!((fg->bol && fg->eol) && (len != fg->len)))
+ if (grep_cmp(fg->pattern, data + j,
+ fg->len) == -1) {
+ pmatch->rm_so = j;
+ pmatch->rm_eo = j + fg->len;
+ ret = 0;
+ }
+ }
+ } else if (fg->reversed) {
+ /* Quick Search algorithm. */
+ j = len;
+ do {
+ if (grep_cmp(fg->pattern, data + j - fg->len,
+ fg->len) == -1) {
+ pmatch->rm_so = j - fg->len;
+ pmatch->rm_eo = j;
+ ret = 0;
+ break;
+ }
+ /* Shift if within bounds, otherwise, we are done. */
+ if (j == fg->len)
+ break;
+ j -= fg->qsBc[data[j - fg->len - 1]];
+ } while (j >= fg->len);
+ } else {
+ /* Quick Search algorithm. */
+ j = pmatch->rm_so;
+ do {
+ if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
+ pmatch->rm_so = j;
+ pmatch->rm_eo = j + fg->len;
+ ret = 0;
+ break;
+ }
+
+ /* Shift if within bounds, otherwise, we are done. */
+ if (j + fg->len == len)
+ break;
+ else
+ j += fg->qsBc[data[j + fg->len]];
+ } while (j <= (len - fg->len));
+ }
+
+ return (ret);
+}
+
+/*
+ * Returns: i >= 0 on failure (position that it failed)
+ * -1 on success
+ */
+static inline int
+grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len)
+{
+ size_t size;
+ wchar_t *wdata, *wpat;
+ unsigned int i;
+
+ if (iflag) {
+ if ((size = mbstowcs(NULL, (const char *)data, 0)) ==
+ ((size_t) - 1))
+ return (-1);
+
+ wdata = grep_malloc(size * sizeof(wint_t));
+
+ if (mbstowcs(wdata, (const char *)data, size) ==
+ ((size_t) - 1))
+ return (-1);
+
+ if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
+ ((size_t) - 1))
+ return (-1);
+
+ wpat = grep_malloc(size * sizeof(wint_t));
+
+ if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1))
+ return (-1);
+ for (i = 0; i < len; i++) {
+ if ((towlower(wpat[i]) == towlower(wdata[i])) ||
+ ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
+ continue;
+ free(wpat);
+ free(wdata);
+ return (i);
+ }
+ } else {
+ for (i = 0; i < len; i++) {
+ if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) &&
+ pat[i] == '.'))
+ continue;
+ return (i);
+ }
+ }
+ return (-1);
+}
+
+static inline void
+grep_revstr(unsigned char *str, int len)
+{
+ int i;
+ char c;
+
+ for (i = 0; i < len / 2; i++) {
+ c = str[i];
+ str[i] = str[len - i - 1];
+ str[len - i - 1] = c;
+ }
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
new file mode 100644
index 0000000..cf4a0fa
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/file.c
@@ -0,0 +1,271 @@
+/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */
+/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */
+/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef __ANDROID__
+#include <bzlib.h>
+#endif
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+#ifndef __ANDROID__
+#include <zlib.h>
+#endif
+
+#include "grep.h"
+
+#define MAXBUFSIZ (32 * 1024)
+#define LNBUFBUMP 80
+
+#ifndef __ANDROID__
+static gzFile gzbufdesc;
+static BZFILE* bzbufdesc;
+#endif
+
+static unsigned char buffer[MAXBUFSIZ];
+static unsigned char *bufpos;
+static size_t bufrem;
+
+static unsigned char *lnbuf;
+static size_t lnbuflen;
+
+static inline int
+grep_refill(struct file *f)
+{
+ ssize_t nr;
+#ifndef __ANDROID__
+ int bzerr;
+#endif
+
+ bufpos = buffer;
+ bufrem = 0;
+
+#ifndef __ANDROID__
+ if (filebehave == FILE_GZIP)
+ nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
+ else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
+ nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
+ switch (bzerr) {
+ case BZ_OK:
+ case BZ_STREAM_END:
+ /* No problem, nr will be okay */
+ break;
+ case BZ_DATA_ERROR_MAGIC:
+ /*
+ * As opposed to gzread(), which simply returns the
+ * plain file data, if it is not in the correct
+ * compressed format, BZ2_bzRead() instead aborts.
+ *
+ * So, just restart at the beginning of the file again,
+ * and use plain reads from now on.
+ */
+ BZ2_bzReadClose(&bzerr, bzbufdesc);
+ bzbufdesc = NULL;
+ if (lseek(f->fd, 0, SEEK_SET) == -1)
+ return (-1);
+ nr = read(f->fd, buffer, MAXBUFSIZ);
+ break;
+ default:
+ /* Make sure we exit with an error */
+ nr = -1;
+ }
+ } else
+#endif
+ nr = read(f->fd, buffer, MAXBUFSIZ);
+
+ if (nr < 0)
+ return (-1);
+
+ bufrem = nr;
+ return (0);
+}
+
+static inline int
+grep_lnbufgrow(size_t newlen)
+{
+
+ if (lnbuflen < newlen) {
+ lnbuf = grep_realloc(lnbuf, newlen);
+ lnbuflen = newlen;
+ }
+
+ return (0);
+}
+
+char *
+grep_fgetln(struct file *f, size_t *lenp)
+{
+ unsigned char *p;
+ char *ret;
+ size_t len;
+ size_t off;
+ ptrdiff_t diff;
+
+ /* Fill the buffer, if necessary */
+ if (bufrem == 0 && grep_refill(f) != 0)
+ goto error;
+
+ if (bufrem == 0) {
+ /* Return zero length to indicate EOF */
+ *lenp = 0;
+ return ((char *)bufpos);
+ }
+
+ /* Look for a newline in the remaining part of the buffer */
+ if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
+ ++p; /* advance over newline */
+ ret = (char *)bufpos;
+ len = p - bufpos;
+ bufrem -= len;
+ bufpos = p;
+ *lenp = len;
+ return (ret);
+ }
+
+ /* We have to copy the current buffered data to the line buffer */
+ for (len = bufrem, off = 0; ; len += bufrem) {
+ /* Make sure there is room for more data */
+ if (grep_lnbufgrow(len + LNBUFBUMP))
+ goto error;
+ memcpy(lnbuf + off, bufpos, len - off);
+ off = len;
+ if (grep_refill(f) != 0)
+ goto error;
+ if (bufrem == 0)
+ /* EOF: return partial line */
+ break;
+ if ((p = memchr(bufpos, line_sep, bufrem)) == NULL)
+ continue;
+ /* got it: finish up the line (like code above) */
+ ++p;
+ diff = p - bufpos;
+ len += diff;
+ if (grep_lnbufgrow(len))
+ goto error;
+ memcpy(lnbuf + off, bufpos, diff);
+ bufrem -= diff;
+ bufpos = p;
+ break;
+ }
+ *lenp = len;
+ return ((char *)lnbuf);
+
+error:
+ *lenp = 0;
+ return (NULL);
+}
+
+static inline struct file *
+grep_file_init(struct file *f)
+{
+
+#ifndef __ANDROID__
+ if (filebehave == FILE_GZIP &&
+ (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
+ goto error;
+
+ if (filebehave == FILE_BZIP &&
+ (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
+ goto error;
+#endif
+
+ /* Fill read buffer, also catches errors early */
+ if (grep_refill(f) != 0)
+ goto error;
+
+ /* Check for binary stuff, if necessary */
+ if (!nulldataflag && binbehave != BINFILE_TEXT &&
+ memchr(bufpos, '\0', bufrem) != NULL)
+ f->binary = true;
+
+ return (f);
+error:
+ close(f->fd);
+ free(f);
+ return (NULL);
+}
+
+/*
+ * Opens a file for processing.
+ */
+struct file *
+grep_open(const char *path)
+{
+ struct file *f;
+
+ f = grep_malloc(sizeof *f);
+ memset(f, 0, sizeof *f);
+ if (path == NULL) {
+ /* Processing stdin implies --line-buffered. */
+ lbflag = true;
+ f->fd = STDIN_FILENO;
+ } else if ((f->fd = open(path, O_RDONLY)) == -1) {
+ free(f);
+ return (NULL);
+ }
+
+ return (grep_file_init(f));
+}
+
+/*
+ * Closes a file.
+ */
+void
+grep_close(struct file *f)
+{
+
+ close(f->fd);
+
+ /* Reset read buffer and line buffer */
+ bufpos = buffer;
+ bufrem = 0;
+
+ free(lnbuf);
+ lnbuf = NULL;
+ lnbuflen = 0;
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
new file mode 100644
index 0000000..1ea6ed3
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
@@ -0,0 +1,707 @@
+/* $NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $ */
+/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */
+/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <libgen.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "grep.h"
+
+#ifndef WITHOUT_NLS
+#include <nl_types.h>
+nl_catd catalog;
+#endif
+
+/*
+ * Default messags to use when NLS is disabled or no catalogue
+ * is found.
+ */
+const char *errstr[] = {
+ "",
+/* 1*/ "(standard input)",
+/* 2*/ "cannot read bzip2 compressed file",
+/* 3*/ "unknown %s option",
+/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
+/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
+/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
+/* 7*/ "\t[pattern] [file ...]\n",
+/* 8*/ "Binary file %s matches\n",
+/* 9*/ "%s (BSD grep) %s\n",
+};
+
+/* Flags passed to regcomp() and regexec() */
+int cflags = 0;
+int eflags = REG_STARTEND;
+
+/* Searching patterns */
+unsigned int patterns, pattern_sz;
+char **pattern;
+regex_t *r_pattern;
+fastgrep_t *fg_pattern;
+
+/* Filename exclusion/inclusion patterns */
+unsigned int fpatterns, fpattern_sz;
+unsigned int dpatterns, dpattern_sz;
+struct epat *dpattern, *fpattern;
+
+/* For regex errors */
+char re_error[RE_ERROR_BUF + 1];
+
+/* Command-line flags */
+unsigned long long Aflag; /* -A x: print x lines trailing each match */
+unsigned long long Bflag; /* -B x: print x lines leading each match */
+bool Hflag; /* -H: always print file name */
+bool Lflag; /* -L: only show names of files with no matches */
+bool bflag; /* -b: show block numbers for each match */
+bool cflag; /* -c: only show a count of matching lines */
+bool hflag; /* -h: don't print filename headers */
+bool iflag; /* -i: ignore case */
+bool lflag; /* -l: only show names of files with matches */
+bool mflag; /* -m x: stop reading the files after x matches */
+unsigned long long mcount; /* count for -m */
+bool nflag; /* -n: show line numbers in front of matching lines */
+bool oflag; /* -o: print only matching part */
+bool qflag; /* -q: quiet mode (don't output anything) */
+bool sflag; /* -s: silent mode (ignore errors) */
+bool vflag; /* -v: only show non-matching lines */
+bool wflag; /* -w: pattern must start and end on word boundaries */
+bool xflag; /* -x: pattern must match entire line */
+bool lbflag; /* --line-buffered */
+bool nullflag; /* --null */
+bool nulldataflag; /* --null-data */
+unsigned char line_sep = '\n'; /* 0 for --null-data */
+char *label; /* --label */
+const char *color; /* --color */
+int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
+int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
+int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
+int devbehave = DEV_READ; /* -D: handling of devices */
+int dirbehave = DIR_READ; /* -dRr: handling of directories */
+int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
+
+bool dexclude, dinclude; /* --exclude-dir and --include-dir */
+bool fexclude, finclude; /* --exclude and --include */
+
+enum {
+ BIN_OPT = CHAR_MAX + 1,
+ COLOR_OPT,
+ DECOMPRESS_OPT,
+ HELP_OPT,
+ MMAP_OPT,
+ LINEBUF_OPT,
+ LABEL_OPT,
+ R_EXCLUDE_OPT,
+ R_INCLUDE_OPT,
+ R_DEXCLUDE_OPT,
+ R_DINCLUDE_OPT
+};
+
+static inline const char *init_color(const char *);
+
+/* Housekeeping */
+int tail; /* lines left to print */
+bool notfound; /* file not found */
+
+extern char *__progname;
+
+/*
+ * Prints usage information and returns 2.
+ */
+__dead static void
+usage(void)
+{
+ fprintf(stderr, getstr(4), __progname);
+ fprintf(stderr, "%s", getstr(5));
+ fprintf(stderr, "%s", getstr(6));
+ fprintf(stderr, "%s", getstr(7));
+ exit(2);
+}
+
+static const char optstr[] =
+ "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
+
+struct option long_options[] =
+{
+ {"binary-files", required_argument, NULL, BIN_OPT},
+ {"decompress", no_argument, NULL, DECOMPRESS_OPT},
+ {"help", no_argument, NULL, HELP_OPT},
+ {"mmap", no_argument, NULL, MMAP_OPT},
+ {"line-buffered", no_argument, NULL, LINEBUF_OPT},
+ {"label", required_argument, NULL, LABEL_OPT},
+ {"color", optional_argument, NULL, COLOR_OPT},
+ {"colour", optional_argument, NULL, COLOR_OPT},
+ {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
+ {"include", required_argument, NULL, R_INCLUDE_OPT},
+ {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
+ {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
+ {"after-context", required_argument, NULL, 'A'},
+ {"text", no_argument, NULL, 'a'},
+ {"before-context", required_argument, NULL, 'B'},
+ {"byte-offset", no_argument, NULL, 'b'},
+ {"context", optional_argument, NULL, 'C'},
+ {"count", no_argument, NULL, 'c'},
+ {"devices", required_argument, NULL, 'D'},
+ {"directories", required_argument, NULL, 'd'},
+ {"extended-regexp", no_argument, NULL, 'E'},
+ {"regexp", required_argument, NULL, 'e'},
+ {"fixed-strings", no_argument, NULL, 'F'},
+ {"file", required_argument, NULL, 'f'},
+ {"basic-regexp", no_argument, NULL, 'G'},
+ {"no-filename", no_argument, NULL, 'h'},
+ {"with-filename", no_argument, NULL, 'H'},
+ {"ignore-case", no_argument, NULL, 'i'},
+ {"bz2decompress", no_argument, NULL, 'J'},
+ {"files-with-matches", no_argument, NULL, 'l'},
+ {"files-without-match", no_argument, NULL, 'L'},
+ {"max-count", required_argument, NULL, 'm'},
+ {"line-number", no_argument, NULL, 'n'},
+ {"only-matching", no_argument, NULL, 'o'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"silent", no_argument, NULL, 'q'},
+ {"recursive", no_argument, NULL, 'r'},
+ {"no-messages", no_argument, NULL, 's'},
+ {"binary", no_argument, NULL, 'U'},
+ {"unix-byte-offsets", no_argument, NULL, 'u'},
+ {"invert-match", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"word-regexp", no_argument, NULL, 'w'},
+ {"line-regexp", no_argument, NULL, 'x'},
+ {"null", no_argument, NULL, 'Z'},
+ {"null-data", no_argument, NULL, 'z'},
+ {NULL, no_argument, NULL, 0}
+};
+
+/*
+ * Adds a searching pattern to the internal array.
+ */
+static void
+add_pattern(char *pat, size_t len)
+{
+
+ /* TODO: Check for empty patterns and shortcut */
+
+ /* Increase size if necessary */
+ if (patterns == pattern_sz) {
+ pattern_sz *= 2;
+ pattern = grep_realloc(pattern, ++pattern_sz *
+ sizeof(*pattern));
+ }
+ if (len > 0 && pat[len - 1] == '\n')
+ --len;
+ /* pat may not be NUL-terminated */
+ pattern[patterns] = grep_malloc(len + 1);
+ memcpy(pattern[patterns], pat, len);
+ pattern[patterns][len] = '\0';
+ ++patterns;
+}
+
+/*
+ * Adds a file include/exclude pattern to the internal array.
+ */
+static void
+add_fpattern(const char *pat, int mode)
+{
+
+ /* Increase size if necessary */
+ if (fpatterns == fpattern_sz) {
+ fpattern_sz *= 2;
+ fpattern = grep_realloc(fpattern, ++fpattern_sz *
+ sizeof(struct epat));
+ }
+ fpattern[fpatterns].pat = grep_strdup(pat);
+ fpattern[fpatterns].mode = mode;
+ ++fpatterns;
+}
+
+/*
+ * Adds a directory include/exclude pattern to the internal array.
+ */
+static void
+add_dpattern(const char *pat, int mode)
+{
+
+ /* Increase size if necessary */
+ if (dpatterns == dpattern_sz) {
+ dpattern_sz *= 2;
+ dpattern = grep_realloc(dpattern, ++dpattern_sz *
+ sizeof(struct epat));
+ }
+ dpattern[dpatterns].pat = grep_strdup(pat);
+ dpattern[dpatterns].mode = mode;
+ ++dpatterns;
+}
+
+/*
+ * Reads searching patterns from a file and adds them with add_pattern().
+ */
+static void
+read_patterns(const char *fn)
+{
+ FILE *f;
+ char *line;
+ size_t len;
+ ssize_t rlen;
+
+ if ((f = fopen(fn, "r")) == NULL)
+ err(2, "%s", fn);
+ line = NULL;
+ len = 0;
+ while ((rlen = getline(&line, &len, f)) != -1)
+ add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
+ free(line);
+ if (ferror(f))
+ err(2, "%s", fn);
+ fclose(f);
+}
+
+static inline const char *
+init_color(const char *d)
+{
+ char *c;
+
+ c = getenv("GREP_COLOR");
+ return (c != NULL ? c : d);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char **aargv, **eargv, *eopts;
+ char *ep;
+ unsigned long long l;
+ unsigned int aargc, eargc, i, j;
+ int c, lastc, needpattern, newarg, prevoptind;
+
+ setlocale(LC_ALL, "");
+
+#ifndef WITHOUT_NLS
+ catalog = catopen("grep", NL_CAT_LOCALE);
+#endif
+
+ /* Check what is the program name of the binary. In this
+ way we can have all the funcionalities in one binary
+ without the need of scripting and using ugly hacks. */
+ switch (__progname[0]) {
+ case 'e':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'f':
+ grepbehave = GREP_FIXED;
+ break;
+ case 'g':
+ grepbehave = GREP_BASIC;
+ break;
+ case 'z':
+ filebehave = FILE_GZIP;
+ switch(__progname[1]) {
+ case 'e':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'f':
+ grepbehave = GREP_FIXED;
+ break;
+ case 'g':
+ grepbehave = GREP_BASIC;
+ break;
+ }
+ break;
+ }
+
+ lastc = '\0';
+ newarg = 1;
+ prevoptind = 1;
+ needpattern = 1;
+
+ eopts = getenv("GREP_OPTIONS");
+
+ /* support for extra arguments in GREP_OPTIONS */
+ eargc = 0;
+ if (eopts != NULL) {
+ char *str;
+
+ /* make an estimation of how many extra arguments we have */
+ for (j = 0; j < strlen(eopts); j++)
+ if (eopts[j] == ' ')
+ eargc++;
+
+ eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
+
+ eargc = 0;
+ /* parse extra arguments */
+ while ((str = strsep(&eopts, " ")) != NULL)
+ eargv[eargc++] = grep_strdup(str);
+
+ aargv = (char **)grep_calloc(eargc + argc + 1,
+ sizeof(char *));
+
+ aargv[0] = argv[0];
+ for (i = 0; i < eargc; i++)
+ aargv[i + 1] = eargv[i];
+ for (j = 1; j < (unsigned int)argc; j++, i++)
+ aargv[i + 1] = argv[j];
+
+ aargc = eargc + argc;
+ } else {
+ aargv = argv;
+ aargc = argc;
+ }
+
+ while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
+ -1)) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (newarg || !isdigit(lastc))
+ Aflag = 0;
+ else if (Aflag > LLONG_MAX / 10) {
+ errno = ERANGE;
+ err(2, NULL);
+ }
+ Aflag = Bflag = (Aflag * 10) + (c - '0');
+ break;
+ case 'C':
+ if (optarg == NULL) {
+ Aflag = Bflag = 2;
+ break;
+ }
+ /* FALLTHROUGH */
+ case 'A':
+ /* FALLTHROUGH */
+ case 'B':
+ errno = 0;
+ l = strtoull(optarg, &ep, 10);
+ if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
+ ((errno == EINVAL) && (l == 0)))
+ err(2, NULL);
+ else if (ep[0] != '\0') {
+ errno = EINVAL;
+ err(2, NULL);
+ }
+ if (c == 'A')
+ Aflag = l;
+ else if (c == 'B')
+ Bflag = l;
+ else
+ Aflag = Bflag = l;
+ break;
+ case 'a':
+ binbehave = BINFILE_TEXT;
+ break;
+ case 'b':
+ bflag = true;
+ break;
+ case 'c':
+ cflag = true;
+ break;
+ case 'D':
+ if (strcasecmp(optarg, "skip") == 0)
+ devbehave = DEV_SKIP;
+ else if (strcasecmp(optarg, "read") == 0)
+ devbehave = DEV_READ;
+ else
+ errx(2, getstr(3), "--devices");
+ break;
+ case 'd':
+ if (strcasecmp("recurse", optarg) == 0) {
+ Hflag = true;
+ dirbehave = DIR_RECURSE;
+ } else if (strcasecmp("skip", optarg) == 0)
+ dirbehave = DIR_SKIP;
+ else if (strcasecmp("read", optarg) == 0)
+ dirbehave = DIR_READ;
+ else
+ errx(2, getstr(3), "--directories");
+ break;
+ case 'E':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'e':
+ add_pattern(optarg, strlen(optarg));
+ needpattern = 0;
+ break;
+ case 'F':
+ grepbehave = GREP_FIXED;
+ break;
+ case 'f':
+ read_patterns(optarg);
+ needpattern = 0;
+ break;
+ case 'G':
+ grepbehave = GREP_BASIC;
+ break;
+ case 'H':
+ Hflag = true;
+ break;
+ case 'h':
+ Hflag = false;
+ hflag = true;
+ break;
+ case 'I':
+ binbehave = BINFILE_SKIP;
+ break;
+ case 'i':
+ case 'y':
+ iflag = true;
+ cflags |= REG_ICASE;
+ break;
+ case 'J':
+ filebehave = FILE_BZIP;
+ break;
+ case 'L':
+ lflag = false;
+ Lflag = true;
+ break;
+ case 'l':
+ Lflag = false;
+ lflag = true;
+ break;
+ case 'm':
+ mflag = true;
+ errno = 0;
+ mcount = strtoull(optarg, &ep, 10);
+ if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
+ ((errno == EINVAL) && (mcount == 0)))
+ err(2, NULL);
+ else if (ep[0] != '\0') {
+ errno = EINVAL;
+ err(2, NULL);
+ }
+ break;
+ case 'n':
+ nflag = true;
+ break;
+ case 'O':
+ linkbehave = LINK_EXPLICIT;
+ break;
+ case 'o':
+ oflag = true;
+ break;
+ case 'p':
+ linkbehave = LINK_SKIP;
+ break;
+ case 'q':
+ qflag = true;
+ break;
+ case 'S':
+ linkbehave = LINK_READ;
+ break;
+ case 'R':
+ case 'r':
+ dirbehave = DIR_RECURSE;
+ Hflag = true;
+ break;
+ case 's':
+ sflag = true;
+ break;
+ case 'U':
+ binbehave = BINFILE_BIN;
+ break;
+ case 'u':
+ case MMAP_OPT:
+ /* noop, compatibility */
+ break;
+ case 'V':
+ printf(getstr(9), __progname, VERSION);
+ exit(0);
+ case 'v':
+ vflag = true;
+ break;
+ case 'w':
+ wflag = true;
+ break;
+ case 'x':
+ xflag = true;
+ break;
+ case 'Z':
+ nullflag = true;
+ break;
+ case 'z':
+ nulldataflag = true;
+ line_sep = '\0';
+ break;
+ case BIN_OPT:
+ if (strcasecmp("binary", optarg) == 0)
+ binbehave = BINFILE_BIN;
+ else if (strcasecmp("without-match", optarg) == 0)
+ binbehave = BINFILE_SKIP;
+ else if (strcasecmp("text", optarg) == 0)
+ binbehave = BINFILE_TEXT;
+ else
+ errx(2, getstr(3), "--binary-files");
+ break;
+ case COLOR_OPT:
+ color = NULL;
+ if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
+ strcasecmp("tty", optarg) == 0 ||
+ strcasecmp("if-tty", optarg) == 0) {
+ char *term;
+
+ term = getenv("TERM");
+ if (isatty(STDOUT_FILENO) && term != NULL &&
+ strcasecmp(term, "dumb") != 0)
+ color = init_color("01;31");
+ } else if (strcasecmp("always", optarg) == 0 ||
+ strcasecmp("yes", optarg) == 0 ||
+ strcasecmp("force", optarg) == 0) {
+ color = init_color("01;31");
+ } else if (strcasecmp("never", optarg) != 0 &&
+ strcasecmp("none", optarg) != 0 &&
+ strcasecmp("no", optarg) != 0)
+ errx(2, getstr(3), "--color");
+ break;
+ case DECOMPRESS_OPT:
+ filebehave = FILE_GZIP;
+ break;
+ case LABEL_OPT:
+ label = optarg;
+ break;
+ case LINEBUF_OPT:
+ lbflag = true;
+ break;
+ case R_INCLUDE_OPT:
+ finclude = true;
+ add_fpattern(optarg, INCL_PAT);
+ break;
+ case R_EXCLUDE_OPT:
+ fexclude = true;
+ add_fpattern(optarg, EXCL_PAT);
+ break;
+ case R_DINCLUDE_OPT:
+ dinclude = true;
+ add_dpattern(optarg, INCL_PAT);
+ break;
+ case R_DEXCLUDE_OPT:
+ dexclude = true;
+ add_dpattern(optarg, EXCL_PAT);
+ break;
+ case HELP_OPT:
+ default:
+ usage();
+ }
+ lastc = c;
+ newarg = optind != prevoptind;
+ prevoptind = optind;
+ }
+ aargc -= optind;
+ aargv += optind;
+
+ /* Fail if we don't have any pattern */
+ if (aargc == 0 && needpattern)
+ usage();
+
+ /* Process patterns from command line */
+ if (aargc != 0 && needpattern) {
+ add_pattern(*aargv, strlen(*aargv));
+ --aargc;
+ ++aargv;
+ }
+
+ switch (grepbehave) {
+ case GREP_FIXED:
+ case GREP_BASIC:
+ break;
+ case GREP_EXTENDED:
+ cflags |= REG_EXTENDED;
+ break;
+ default:
+ /* NOTREACHED */
+ usage();
+ }
+
+ fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
+ r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
+/*
+ * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
+ * Optimizations should be done there.
+ */
+ /* Check if cheating is allowed (always is for fgrep). */
+ if (grepbehave == GREP_FIXED) {
+ for (i = 0; i < patterns; ++i)
+ fgrepcomp(&fg_pattern[i], pattern[i]);
+ } else {
+ for (i = 0; i < patterns; ++i) {
+ if (fastcomp(&fg_pattern[i], pattern[i])) {
+ /* Fall back to full regex library */
+ c = regcomp(&r_pattern[i], pattern[i], cflags);
+ if (c != 0) {
+ regerror(c, &r_pattern[i], re_error,
+ RE_ERROR_BUF);
+ errx(2, "%s", re_error);
+ }
+ }
+ }
+ }
+
+ if (lbflag)
+ setlinebuf(stdout);
+
+ if ((aargc == 0 || aargc == 1) && !Hflag)
+ hflag = true;
+
+ if (aargc == 0)
+ exit(!procfile("-"));
+
+ if (dirbehave == DIR_RECURSE)
+ c = grep_tree(aargv);
+ else
+ for (c = 0; aargc--; ++aargv) {
+ if ((finclude || fexclude) && !file_matching(*aargv))
+ continue;
+ c+= procfile(*aargv);
+ }
+
+#ifndef WITHOUT_NLS
+ catclose(catalog);
+#endif
+
+ /* Find out the correct return value according to the
+ results and the command line option. */
+ exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
new file mode 100644
index 0000000..fa2a3e3
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
@@ -0,0 +1,162 @@
+/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */
+/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */
+/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef __ANDROID__
+#include <bzlib.h>
+#endif
+#include <limits.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#ifndef __ANDROID__
+#include <zlib.h>
+#endif
+
+#ifdef WITHOUT_NLS
+#define getstr(n) errstr[n]
+#else
+#include <nl_types.h>
+
+extern nl_catd catalog;
+#define getstr(n) catgets(catalog, 1, n, errstr[n])
+#endif
+
+extern const char *errstr[];
+
+#define VERSION "2.5.1-FreeBSD"
+
+#define GREP_FIXED 0
+#define GREP_BASIC 1
+#define GREP_EXTENDED 2
+
+#define BINFILE_BIN 0
+#define BINFILE_SKIP 1
+#define BINFILE_TEXT 2
+
+#define FILE_STDIO 0
+#define FILE_GZIP 1
+#define FILE_BZIP 2
+
+#define DIR_READ 0
+#define DIR_SKIP 1
+#define DIR_RECURSE 2
+
+#define DEV_READ 0
+#define DEV_SKIP 1
+
+#define LINK_READ 0
+#define LINK_EXPLICIT 1
+#define LINK_SKIP 2
+
+#define EXCL_PAT 0
+#define INCL_PAT 1
+
+#define MAX_LINE_MATCHES 32
+
+struct file {
+ int fd;
+ bool binary;
+};
+
+struct str {
+ off_t off;
+ size_t len;
+ char *dat;
+ char *file;
+ int line_no;
+};
+
+struct epat {
+ char *pat;
+ int mode;
+};
+
+typedef struct {
+ size_t len;
+ unsigned char *pattern;
+ int qsBc[UCHAR_MAX + 1];
+ /* flags */
+ bool bol;
+ bool eol;
+ bool reversed;
+ bool word;
+} fastgrep_t;
+
+/* Flags passed to regcomp() and regexec() */
+extern int cflags, eflags;
+
+/* Command line flags */
+extern bool Eflag, Fflag, Gflag, Hflag, Lflag,
+ bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
+ qflag, sflag, vflag, wflag, xflag;
+extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag;
+extern unsigned char line_sep;
+extern unsigned long long Aflag, Bflag, mcount;
+extern char *label;
+extern const char *color;
+extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
+
+extern bool notfound;
+extern int tail;
+extern unsigned int dpatterns, fpatterns, patterns;
+extern char **pattern;
+extern struct epat *dpattern, *fpattern;
+extern regex_t *er_pattern, *r_pattern;
+extern fastgrep_t *fg_pattern;
+
+/* For regex errors */
+#define RE_ERROR_BUF 512
+extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */
+
+/* util.c */
+bool file_matching(const char *fname);
+int procfile(const char *fn);
+int grep_tree(char **argv);
+void *grep_malloc(size_t size);
+void *grep_calloc(size_t nmemb, size_t size);
+void *grep_realloc(void *ptr, size_t size);
+char *grep_strdup(const char *str);
+void printline(struct str *line, int sep, regmatch_t *matches, int m);
+
+/* queue.c */
+void enqueue(struct str *x);
+void printqueue(void);
+void clearqueue(void);
+
+/* file.c */
+void grep_close(struct file *f);
+struct file *grep_open(const char *path);
+char *grep_fgetln(struct file *f, size_t *len);
+
+/* fastgrep.c */
+int fastcomp(fastgrep_t *, const char *);
+void fgrepcomp(fastgrep_t *, const char *);
+int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *);
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/queue.c b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
new file mode 100644
index 0000000..e3c6be1
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
@@ -0,0 +1,116 @@
+/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */
+/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * A really poor man's queue. It does only what it has to and gets out of
+ * Dodge. It is used in place of <sys/queue.h> to get a better performance.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "grep.h"
+
+struct qentry {
+ STAILQ_ENTRY(qentry) list;
+ struct str data;
+};
+
+static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue);
+static unsigned long long count;
+
+static struct qentry *dequeue(void);
+
+void
+enqueue(struct str *x)
+{
+ struct qentry *item;
+
+ item = grep_malloc(sizeof(struct qentry));
+ item->data.dat = grep_malloc(sizeof(char) * x->len);
+ item->data.len = x->len;
+ item->data.line_no = x->line_no;
+ item->data.off = x->off;
+ memcpy(item->data.dat, x->dat, x->len);
+ item->data.file = x->file;
+
+ STAILQ_INSERT_TAIL(&queue, item, list);
+
+ if (++count > Bflag) {
+ item = dequeue();
+ free(item->data.dat);
+ free(item);
+ }
+}
+
+static struct qentry *
+dequeue(void)
+{
+ struct qentry *item;
+
+ item = STAILQ_FIRST(&queue);
+ if (item == NULL)
+ return (NULL);
+
+ STAILQ_REMOVE_HEAD(&queue, list);
+ --count;
+ return (item);
+}
+
+void
+printqueue(void)
+{
+ struct qentry *item;
+
+ while ((item = dequeue()) != NULL) {
+ printline(&item->data, '-', NULL, 0);
+ free(item->data.dat);
+ free(item);
+ }
+}
+
+void
+clearqueue(void)
+{
+ struct qentry *item;
+
+ while ((item = dequeue()) != NULL) {
+ free(item->data.dat);
+ free(item);
+ }
+}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
new file mode 100644
index 0000000..ecd948d
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/util.c
@@ -0,0 +1,499 @@
+/* $NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $ */
+/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */
+/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "grep.h"
+
+static bool first, first_global = true;
+static unsigned long long since_printed;
+
+static int procline(struct str *l, int);
+
+bool
+file_matching(const char *fname)
+{
+ char *fname_base, *fname_copy;
+ unsigned int i;
+ bool ret;
+
+ ret = finclude ? false : true;
+ fname_copy = grep_strdup(fname);
+ fname_base = basename(fname_copy);
+
+ for (i = 0; i < fpatterns; ++i) {
+ if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
+ fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
+ if (fpattern[i].mode == EXCL_PAT) {
+ free(fname_copy);
+ return (false);
+ } else
+ ret = true;
+ }
+ }
+ free(fname_copy);
+ return (ret);
+}
+
+static inline bool
+dir_matching(const char *dname)
+{
+ unsigned int i;
+ bool ret;
+
+ ret = dinclude ? false : true;
+
+ for (i = 0; i < dpatterns; ++i) {
+ if (dname != NULL &&
+ fnmatch(dname, dpattern[i].pat, 0) == 0) {
+ if (dpattern[i].mode == EXCL_PAT)
+ return (false);
+ else
+ ret = true;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Processes a directory when a recursive search is performed with
+ * the -R option. Each appropriate file is passed to procfile().
+ */
+int
+grep_tree(char **argv)
+{
+ FTS *fts;
+ FTSENT *p;
+ char *d, *dir = NULL;
+ int c, fts_flags;
+ bool ok;
+
+ c = fts_flags = 0;
+
+ switch(linkbehave) {
+ case LINK_EXPLICIT:
+ fts_flags = FTS_COMFOLLOW;
+ break;
+ case LINK_SKIP:
+ fts_flags = FTS_PHYSICAL;
+ break;
+ default:
+ fts_flags = FTS_LOGICAL;
+
+ }
+
+ fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
+
+ if (!(fts = fts_open(argv, fts_flags, NULL)))
+ err(2, "fts_open");
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_DNR:
+ /* FALLTHROUGH */
+ case FTS_ERR:
+ errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno));
+ break;
+ case FTS_D:
+ /* FALLTHROUGH */
+ case FTS_DP:
+ break;
+ case FTS_DC:
+ /* Print a warning for recursive directory loop */
+ warnx("warning: %s: recursive directory loop",
+ p->fts_path);
+ break;
+ default:
+ /* Check for file exclusion/inclusion */
+ ok = true;
+ if (dexclude || dinclude) {
+ if ((d = strrchr(p->fts_path, '/')) != NULL) {
+ dir = grep_malloc(sizeof(char) *
+ (d - p->fts_path + 1));
+ memcpy(dir, p->fts_path,
+ d - p->fts_path);
+ dir[d - p->fts_path] = '\0';
+ }
+ ok = dir_matching(dir);
+ free(dir);
+ dir = NULL;
+ }
+ if (fexclude || finclude)
+ ok &= file_matching(p->fts_path);
+
+ if (ok)
+ c += procfile(p->fts_path);
+ break;
+ }
+ }
+
+ fts_close(fts);
+ return (c);
+}
+
+/*
+ * Opens a file and processes it. Each file is processed line-by-line
+ * passing the lines to procline().
+ */
+int
+procfile(const char *fn)
+{
+ struct file *f;
+ struct stat sb;
+ struct str ln;
+ mode_t s;
+ int c, t;
+
+ if (mflag && (mcount <= 0))
+ return (0);
+
+ if (strcmp(fn, "-") == 0) {
+ fn = label != NULL ? label : getstr(1);
+ f = grep_open(NULL);
+ } else {
+ if (!stat(fn, &sb)) {
+ /* Check if we need to process the file */
+ s = sb.st_mode & S_IFMT;
+ if (s == S_IFDIR && dirbehave == DIR_SKIP)
+ return (0);
+ if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
+ || s == S_IFSOCK) && devbehave == DEV_SKIP)
+ return (0);
+ }
+ f = grep_open(fn);
+ }
+ if (f == NULL) {
+ if (!sflag)
+ warn("%s", fn);
+ if (errno == ENOENT)
+ notfound = true;
+ return (0);
+ }
+
+ ln.file = grep_malloc(strlen(fn) + 1);
+ strcpy(ln.file, fn);
+ ln.line_no = 0;
+ ln.len = 0;
+ tail = 0;
+ ln.off = -1;
+
+ for (first = true, c = 0; c == 0 || !(lflag || qflag); ) {
+ ln.off += ln.len + 1;
+ if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0)
+ break;
+ if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep)
+ --ln.len;
+ ln.line_no++;
+
+ /* Return if we need to skip a binary file */
+ if (f->binary && binbehave == BINFILE_SKIP) {
+ grep_close(f);
+ free(ln.file);
+ free(f);
+ return (0);
+ }
+ /* Process the file line-by-line */
+ t = procline(&ln, f->binary);
+ c += t;
+
+ /* Count the matches if we have a match limit */
+ if (mflag) {
+ mcount -= t;
+ if (mcount <= 0)
+ break;
+ }
+ }
+ if (Bflag > 0)
+ clearqueue();
+ grep_close(f);
+
+ if (cflag) {
+ if (!hflag)
+ printf("%s:", ln.file);
+ printf("%u%c", c, line_sep);
+ }
+ if (lflag && !qflag && c != 0)
+ printf("%s%c", fn, line_sep);
+ if (Lflag && !qflag && c == 0)
+ printf("%s%c", fn, line_sep);
+ if (c && !cflag && !lflag && !Lflag &&
+ binbehave == BINFILE_BIN && f->binary && !qflag)
+ printf(getstr(8), fn);
+
+ free(ln.file);
+ free(f);
+ return (c);
+}
+
+#define iswword(x) (iswalnum((x)) || (x) == L'_')
+
+/*
+ * Processes a line comparing it with the specified patterns. Each pattern
+ * is looped to be compared along with the full string, saving each and every
+ * match, which is necessary to colorize the output and to count the
+ * matches. The matching lines are passed to printline() to display the
+ * appropriate output.
+ */
+static int
+procline(struct str *l, int nottext)
+{
+ regmatch_t matches[MAX_LINE_MATCHES];
+ regmatch_t pmatch;
+ size_t st = 0;
+ unsigned int i;
+ int c = 0, m = 0, r = 0;
+
+ /* Loop to process the whole line */
+ while (st <= l->len) {
+ pmatch.rm_so = st;
+ pmatch.rm_eo = l->len;
+
+ /* Loop to compare with all the patterns */
+ for (i = 0; i < patterns; i++) {
+/*
+ * XXX: grep_search() is a workaround for speed up and should be
+ * removed in the future. See fastgrep.c.
+ */
+ if (fg_pattern[i].pattern) {
+ r = grep_search(&fg_pattern[i],
+ (unsigned char *)l->dat,
+ l->len, &pmatch);
+ r = (r == 0) ? 0 : REG_NOMATCH;
+ st = pmatch.rm_eo;
+ } else {
+ r = regexec(&r_pattern[i], l->dat, 1,
+ &pmatch, eflags);
+ r = (r == 0) ? 0 : REG_NOMATCH;
+ st = pmatch.rm_eo;
+ }
+ if (r == REG_NOMATCH)
+ continue;
+ /* Check for full match */
+ if (xflag &&
+ (pmatch.rm_so != 0 ||
+ (size_t)pmatch.rm_eo != l->len))
+ continue;
+ /* Check for whole word match */
+ if (fg_pattern[i].word && pmatch.rm_so != 0) {
+ wchar_t wbegin, wend;
+
+ wbegin = wend = L' ';
+ if (pmatch.rm_so != 0 &&
+ sscanf(&l->dat[pmatch.rm_so - 1],
+ "%lc", &wbegin) != 1)
+ continue;
+ if ((size_t)pmatch.rm_eo != l->len &&
+ sscanf(&l->dat[pmatch.rm_eo],
+ "%lc", &wend) != 1)
+ continue;
+ if (iswword(wbegin) || iswword(wend))
+ continue;
+ }
+ c = 1;
+ if (m < MAX_LINE_MATCHES)
+ matches[m++] = pmatch;
+ /* matches - skip further patterns */
+ if ((color != NULL && !oflag) || qflag || lflag)
+ break;
+ }
+
+ if (vflag) {
+ c = !c;
+ break;
+ }
+ /* One pass if we are not recording matches */
+ if ((color != NULL && !oflag) || qflag || lflag)
+ break;
+
+ if (st == (size_t)pmatch.rm_so)
+ break; /* No matches */
+ }
+
+ if (c && binbehave == BINFILE_BIN && nottext)
+ return (c); /* Binary file */
+
+ /* Dealing with the context */
+ if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
+ if (c) {
+ if ((Aflag || Bflag) && !first_global &&
+ (first || since_printed > Bflag))
+ printf("--\n");
+ tail = Aflag;
+ if (Bflag > 0)
+ printqueue();
+ printline(l, ':', matches, m);
+ } else {
+ printline(l, '-', matches, m);
+ tail--;
+ }
+ first = false;
+ first_global = false;
+ since_printed = 0;
+ } else {
+ if (Bflag)
+ enqueue(l);
+ since_printed++;
+ }
+ return (c);
+}
+
+/*
+ * Safe malloc() for internal use.
+ */
+void *
+grep_malloc(size_t size)
+{
+ void *ptr;
+
+ if ((ptr = malloc(size)) == NULL)
+ err(2, "malloc");
+ return (ptr);
+}
+
+/*
+ * Safe calloc() for internal use.
+ */
+void *
+grep_calloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if ((ptr = calloc(nmemb, size)) == NULL)
+ err(2, "calloc");
+ return (ptr);
+}
+
+/*
+ * Safe realloc() for internal use.
+ */
+void *
+grep_realloc(void *ptr, size_t size)
+{
+
+ if ((ptr = realloc(ptr, size)) == NULL)
+ err(2, "realloc");
+ return (ptr);
+}
+
+/*
+ * Safe strdup() for internal use.
+ */
+char *
+grep_strdup(const char *str)
+{
+ char *ret;
+
+ if ((ret = strdup(str)) == NULL)
+ err(2, "strdup");
+ return (ret);
+}
+
+/*
+ * Prints a matching line according to the command line options.
+ */
+void
+printline(struct str *line, int sep, regmatch_t *matches, int m)
+{
+ size_t a = 0;
+ int i, n = 0;
+
+ if (!hflag) {
+ if (nullflag == 0)
+ fputs(line->file, stdout);
+ else {
+ printf("%s", line->file);
+ putchar(0);
+ }
+ ++n;
+ }
+ if (nflag) {
+ if (n > 0)
+ putchar(sep);
+ printf("%d", line->line_no);
+ ++n;
+ }
+ if (bflag) {
+ if (n > 0)
+ putchar(sep);
+ printf("%lld", (long long)line->off);
+ ++n;
+ }
+ if (n)
+ putchar(sep);
+ /* --color and -o */
+ if ((oflag || color) && m > 0) {
+ for (i = 0; i < m; i++) {
+ if (!oflag)
+ fwrite(line->dat + a, matches[i].rm_so - a, 1,
+ stdout);
+ if (color)
+ fprintf(stdout, "\33[%sm\33[K", color);
+
+ fwrite(line->dat + matches[i].rm_so,
+ matches[i].rm_eo - matches[i].rm_so, 1,
+ stdout);
+ if (color)
+ fprintf(stdout, "\33[m\33[K");
+ a = matches[i].rm_eo;
+ if (oflag)
+ putchar('\n');
+ }
+ if (!oflag) {
+ if (line->len - a > 0)
+ fwrite(line->dat + a, line->len - a, 1, stdout);
+ putchar(line_sep);
+ }
+ } else {
+ fwrite(line->dat, line->len, 1, stdout);
+ putchar(line_sep);
+ }
+}
diff --git a/watchdogd/Android.bp b/watchdogd/Android.bp
new file mode 100644
index 0000000..0fbc33c
--- /dev/null
+++ b/watchdogd/Android.bp
@@ -0,0 +1,14 @@
+cc_binary {
+ name: "watchdogd",
+ recovery_available: true,
+ srcs: ["watchdogd.cpp"],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ shared_libs: ["libbase"],
+ sanitize: {
+ misc_undefined: ["signed-integer-overflow"],
+ },
+}
diff --git a/init/watchdogd.cpp b/watchdogd/watchdogd.cpp
similarity index 79%
rename from init/watchdogd.cpp
rename to watchdogd/watchdogd.cpp
index e03a2c3..5dc41e6 100644
--- a/init/watchdogd.cpp
+++ b/watchdogd/watchdogd.cpp
@@ -23,16 +23,9 @@
#include <android-base/logging.h>
-#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
-#endif
-
#define DEV_NAME "/dev/watchdog"
-namespace android {
-namespace init {
-
-int watchdogd_main(int argc, char **argv) {
+int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::KernelLogger);
int interval = 10;
@@ -43,7 +36,7 @@
LOG(INFO) << "watchdogd started (interval " << interval << ", margin " << margin << ")!";
- int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
+ int fd = open(DEV_NAME, O_RDWR | O_CLOEXEC);
if (fd == -1) {
PLOG(ERROR) << "Failed to open " << DEV_NAME;
return 1;
@@ -63,9 +56,8 @@
interval = 1;
}
LOG(WARNING) << "Adjusted interval to timeout returned by driver: "
- << "timeout " << timeout
- << ", interval " << interval
- << ", margin " << margin;
+ << "timeout " << timeout << ", interval " << interval << ", margin "
+ << margin;
}
}
@@ -74,6 +66,3 @@
sleep(interval);
}
}
-
-} // namespace init
-} // namespace android