Merge "ueventd.rc: Move device-specific entries to /vendor/ueventd.rc."
diff --git a/adb/Android.bp b/adb/Android.bp
index 6234af1..36bfad4 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -355,6 +355,7 @@
compile_multilib: "both",
srcs: [
+ "daemon/abb_service.cpp",
"daemon/file_sync_service.cpp",
"daemon/framebuffer_service.cpp",
"daemon/mdns.cpp",
@@ -391,6 +392,14 @@
"libmdnssd",
"libselinux",
],
+
+ target: {
+ recovery: {
+ exclude_srcs: [
+ "daemon/abb_service.cpp",
+ ],
+ },
+ },
}
cc_library {
@@ -455,6 +464,40 @@
],
}
+cc_binary {
+ name: "abb",
+
+ defaults: ["adb_defaults"],
+ recovery_available: false,
+
+ srcs: [
+ "daemon/abb.cpp",
+ ],
+
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-Wno-deprecated-declarations",
+ ],
+
+ strip: {
+ keep_symbols: true,
+ },
+
+ static_libs: [
+ "libadbd_core",
+ "libadbd_services",
+ "libcmd",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ "libselinux",
+ ],
+}
+
cc_test {
name: "adbd_test",
defaults: ["adb_defaults"],
diff --git a/adb/adb.h b/adb/adb.h
index 47ea0e8..d79cd2d 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -153,6 +153,10 @@
#endif
#if !ADB_HOST
+unique_fd execute_binder_command(std::string_view command);
+#endif
+
+#if !ADB_HOST
int init_jdwp(void);
asocket* create_jdwp_service_socket();
asocket* create_jdwp_tracker_service_socket();
@@ -202,6 +206,9 @@
#define CHUNK_SIZE (64 * 1024)
+// Argument delimeter for adb abb command.
+#define ABB_ARG_DELIMETER ('\0')
+
#if !ADB_HOST
#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index e6fefda..605d27d 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -20,6 +20,11 @@
#include <unistd.h>
+#if !ADB_HOST
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
#include <thread>
#include <android-base/stringprintf.h>
@@ -182,3 +187,79 @@
return false;
}
}
+
+#if defined(__linux__)
+bool SendFileDescriptor(int socket_fd, int fd) {
+ struct msghdr msg;
+ struct iovec iov;
+ char dummy = '!';
+ union {
+ cmsghdr cm;
+ char buffer[CMSG_SPACE(sizeof(int))];
+ } cm_un;
+
+ iov.iov_base = &dummy;
+ iov.iov_len = 1;
+ msg.msg_name = nullptr;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cm_un.buffer;
+ msg.msg_controllen = sizeof(cm_un.buffer);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ ((int*)CMSG_DATA(cmsg))[0] = fd;
+
+ int ret = TEMP_FAILURE_RETRY(sendmsg(socket_fd, &msg, 0));
+ if (ret < 0) {
+ D("sending file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
+ return false;
+ }
+
+ D("sent file descriptor %d to via socket %d", fd, socket_fd);
+ return true;
+}
+
+bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error) {
+ char dummy = '!';
+ union {
+ cmsghdr cm;
+ char buffer[CMSG_SPACE(sizeof(int))];
+ } cm_un;
+
+ iovec iov;
+ iov.iov_base = &dummy;
+ iov.iov_len = 1;
+
+ msghdr msg;
+ msg.msg_name = nullptr;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cm_un.buffer;
+ msg.msg_controllen = sizeof(cm_un.buffer);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ ((int*)(CMSG_DATA(cmsg)))[0] = -1;
+
+ int rc = TEMP_FAILURE_RETRY(recvmsg(socket_fd, &msg, 0));
+ if (rc <= 0) {
+ *error = perror_str("receiving file descriptor via socket failed");
+ D("receiving file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
+ return false;
+ }
+
+ fd->reset(((int*)(CMSG_DATA(cmsg)))[0]);
+ D("received file descriptor %d to via socket %d", fd->get(), socket_fd);
+
+ return true;
+}
+#endif
diff --git a/adb/adb_io.h b/adb/adb_io.h
index aa550af..2ccaa32 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -21,6 +21,8 @@
#include <string>
+#include "adb_unique_fd.h"
+
// Sends the protocol "OKAY" message.
bool SendOkay(int fd);
@@ -73,4 +75,12 @@
// Same as above, but formats the string to send.
bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+#if !ADB_HOST
+// Sends an FD via Unix domain socket.
+bool SendFileDescriptor(int socket_fd, int fd);
+
+// Receives an FD via Unix domain socket.
+bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error);
+#endif
+
#endif /* ADB_IO_H */
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 8676214..ce51b1c 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -579,12 +579,8 @@
//
// On success returns the remote exit code if |use_shell_protocol| is true,
// 0 otherwise. On failure returns 1.
-static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
- char escape_char,
- const std::string& command) {
- std::string service_string = ShellServiceString(use_shell_protocol,
- type_arg, command);
-
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg, char escape_char,
+ bool empty_command, const std::string& service_string) {
// Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
// Use |use_shell_protocol| to determine whether to allow a command longer than that.
if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
@@ -595,8 +591,7 @@
// Make local stdin raw if the device allocates a PTY, which happens if:
// 1. We are explicitly asking for a PTY shell, or
// 2. We don't specify shell type and are starting an interactive session.
- bool raw_stdin = (type_arg == kShellServiceArgPty ||
- (type_arg.empty() && command.empty()));
+ bool raw_stdin = (type_arg == kShellServiceArgPty || (type_arg.empty() && empty_command));
std::string error;
int fd = adb_connect(service_string, &error);
@@ -756,7 +751,29 @@
command = android::base::Join(std::vector<const char*>(argv + optind, argv + argc), ' ');
}
- return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
+ std::string service_string = ShellServiceString(use_shell_protocol, shell_type_arg, command);
+ return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command.empty(),
+ service_string);
+}
+
+static int adb_abb(int argc, const char** argv) {
+ // Defaults.
+ constexpr char escape_char = '~'; // -e
+ constexpr bool use_shell_protocol = true;
+ constexpr auto shell_type_arg = kShellServiceArgRaw;
+ constexpr bool empty_command = false;
+
+ std::string service_string("abb:");
+ for (auto i = optind; i < argc; ++i) {
+ service_string.append(argv[i]);
+ service_string.push_back(ABB_ARG_DELIMETER);
+ }
+
+ D("abb -e 0x%x [%*.s]\n", escape_char, static_cast<int>(service_string.size()),
+ service_string.data());
+
+ return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, empty_command,
+ service_string);
}
static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
@@ -1546,14 +1563,13 @@
std::string query = android::base::StringPrintf("host:disconnect:%s",
(argc == 2) ? argv[1] : "");
return adb_query_command(query);
- }
- else if (!strcmp(argv[0], "emu")) {
+ } else if (!strcmp(argv[0], "abb")) {
+ return adb_abb(argc, argv);
+ } else if (!strcmp(argv[0], "emu")) {
return adb_send_emulator_command(argc, argv, serial);
- }
- else if (!strcmp(argv[0], "shell")) {
+ } else if (!strcmp(argv[0], "shell")) {
return adb_shell(argc, argv);
- }
- else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
+ } else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
if (argc < 2) error_exit("usage: adb %s command", argv[0]);
@@ -1581,11 +1597,9 @@
adb_close(fd);
return 0;
- }
- else if (!strcmp(argv[0], "kill-server")) {
+ } else if (!strcmp(argv[0], "kill-server")) {
return adb_kill_server() ? 0 : 1;
- }
- else if (!strcmp(argv[0], "sideload")) {
+ } else if (!strcmp(argv[0], "sideload")) {
if (argc != 2) error_exit("sideload requires an argument");
if (adb_sideload_host(argv[1])) {
return 1;
@@ -1695,8 +1709,7 @@
else if (!strcmp(argv[0], "ls")) {
if (argc != 2) error_exit("ls requires an argument");
return do_sync_ls(argv[1]) ? 0 : 1;
- }
- else if (!strcmp(argv[0], "push")) {
+ } else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
bool sync = false;
std::vector<const char*> srcs;
@@ -1705,8 +1718,7 @@
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
if (srcs.empty() || !dst) error_exit("push requires an argument");
return do_sync_push(srcs, dst, sync) ? 0 : 1;
- }
- else if (!strcmp(argv[0], "pull")) {
+ } else if (!strcmp(argv[0], "pull")) {
bool copy_attrs = false;
std::vector<const char*> srcs;
const char* dst = ".";
@@ -1714,20 +1726,16 @@
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
if (srcs.empty()) error_exit("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
- }
- else if (!strcmp(argv[0], "install")) {
+ } else if (!strcmp(argv[0], "install")) {
if (argc < 2) error_exit("install requires an argument");
return install_app(argc, argv);
- }
- else if (!strcmp(argv[0], "install-multiple")) {
+ } else if (!strcmp(argv[0], "install-multiple")) {
if (argc < 2) error_exit("install-multiple requires an argument");
return install_multiple_app(argc, argv);
- }
- else if (!strcmp(argv[0], "uninstall")) {
+ } else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) error_exit("uninstall requires an argument");
return uninstall_app(argc, argv);
- }
- else if (!strcmp(argv[0], "sync")) {
+ } else if (!strcmp(argv[0], "sync")) {
std::string src;
bool list_only = false;
if (argc < 2) {
@@ -1757,34 +1765,28 @@
return 0;
}
/* passthrough commands */
- else if (!strcmp(argv[0],"get-state") ||
- !strcmp(argv[0],"get-serialno") ||
- !strcmp(argv[0],"get-devpath"))
- {
+ else if (!strcmp(argv[0], "get-state") || !strcmp(argv[0], "get-serialno") ||
+ !strcmp(argv[0], "get-devpath")) {
return adb_query_command(format_host_command(argv[0]));
}
/* other commands */
- else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
+ else if (!strcmp(argv[0], "logcat") || !strcmp(argv[0], "lolcat") ||
+ !strcmp(argv[0], "longcat")) {
return logcat(argc, argv);
- }
- else if (!strcmp(argv[0],"ppp")) {
+ } else if (!strcmp(argv[0], "ppp")) {
return ppp(argc, argv);
- }
- else if (!strcmp(argv[0], "start-server")) {
+ } else if (!strcmp(argv[0], "start-server")) {
std::string error;
const int result = adb_connect("host:start-server", &error);
if (result < 0) {
fprintf(stderr, "error: %s\n", error.c_str());
}
return result;
- }
- else if (!strcmp(argv[0], "backup")) {
+ } else if (!strcmp(argv[0], "backup")) {
return backup(argc, argv);
- }
- else if (!strcmp(argv[0], "restore")) {
+ } else if (!strcmp(argv[0], "restore")) {
return restore(argc, argv);
- }
- else if (!strcmp(argv[0], "keygen")) {
+ } else if (!strcmp(argv[0], "keygen")) {
if (argc != 2) error_exit("keygen requires an argument");
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
new file mode 100644
index 0000000..f69babe
--- /dev/null
+++ b/adb/daemon/abb.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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 "adb.h"
+#include "adb_io.h"
+#include "shell_service.h"
+
+#include "cmd.h"
+
+#include <sys/wait.h>
+
+namespace {
+
+class AdbFdTextOutput : public android::TextOutput {
+ public:
+ explicit AdbFdTextOutput(int fd) : mFD(fd) {}
+
+ private:
+ android::status_t print(const char* txt, size_t len) override {
+ return WriteFdExactly(mFD, txt, len) ? android::OK : -errno;
+ }
+ void moveIndent(int delta) override { /*not implemented*/
+ }
+
+ void pushBundle() override { /*not implemented*/
+ }
+ void popBundle() override { /*not implemented*/
+ }
+
+ private:
+ int mFD;
+};
+
+std::vector<std::string_view> parseCmdArgs(std::string_view args) {
+ std::vector<std::string_view> argv;
+
+ char delim = ABB_ARG_DELIMETER;
+ size_t size = args.size();
+ size_t base = 0;
+ while (base < size) {
+ size_t found;
+ for (found = base; found < size && args[found] && args[found] != delim; ++found)
+ ;
+ if (found > base) {
+ argv.emplace_back(args.substr(base, found - base));
+ }
+ base = found + 1;
+ }
+
+ return argv;
+}
+
+} // namespace
+
+static int execCmd(std::string_view args, int in, int out, int err) {
+ AdbFdTextOutput oin(out);
+ AdbFdTextOutput oerr(err);
+ return cmdMain(parseCmdArgs(args), oin, oerr, in, out, err, RunMode::kLibrary);
+}
+
+int main(int argc, char* const argv[]) {
+ signal(SIGPIPE, SIG_IGN);
+
+ int fd = STDIN_FILENO;
+ std::string data;
+ while (true) {
+ std::string error;
+ if (!ReadProtocolString(fd, &data, &error)) {
+ PLOG(ERROR) << "Failed to read message: " << error;
+ break;
+ }
+
+ auto result = StartCommandInProcess(std::move(data), &execCmd);
+ if (!SendFileDescriptor(fd, result)) {
+ PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
+ break;
+ }
+ }
+}
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
new file mode 100644
index 0000000..817aea1
--- /dev/null
+++ b/adb/daemon/abb_service.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "shell_service.h"
+
+namespace {
+
+struct AbbProcess;
+static auto& abbp = *new std::unique_ptr<AbbProcess>(std::make_unique<AbbProcess>());
+
+struct AbbProcess {
+ unique_fd sendCommand(std::string_view command);
+
+ private:
+ static unique_fd startAbbProcess(unique_fd* error_fd);
+
+ static constexpr auto kRetries = 2;
+ static constexpr auto kErrorProtocol = SubprocessProtocol::kShell;
+
+ std::mutex locker_;
+ unique_fd socket_fd_;
+};
+
+unique_fd AbbProcess::sendCommand(std::string_view command) {
+ std::unique_lock lock{locker_};
+
+ for (int i = 0; i < kRetries; ++i) {
+ unique_fd error_fd;
+ if (socket_fd_ == -1) {
+ socket_fd_ = startAbbProcess(&error_fd);
+ }
+ if (socket_fd_ == -1) {
+ LOG(ERROR) << "failed to start abb process";
+ return error_fd;
+ }
+
+ if (!SendProtocolString(socket_fd_, std::string(command))) {
+ PLOG(ERROR) << "failed to send command to abb";
+ socket_fd_.reset();
+ continue;
+ }
+
+ unique_fd fd;
+ std::string error;
+ if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
+ LOG(ERROR) << "failed to receive FD from abb: " << error;
+ socket_fd_.reset();
+ continue;
+ }
+
+ return fd;
+ }
+
+ LOG(ERROR) << "abb is unavailable";
+ socket_fd_.reset();
+ return ReportError(kErrorProtocol, "abb is unavailable");
+}
+
+unique_fd AbbProcess::startAbbProcess(unique_fd* error_fd) {
+ constexpr auto abb_process_type = SubprocessType::kRaw;
+ constexpr auto abb_protocol = SubprocessProtocol::kNone;
+ constexpr auto make_pty_raw = false;
+ return StartSubprocess("abb", "dumb", abb_process_type, abb_protocol, make_pty_raw,
+ kErrorProtocol, error_fd);
+}
+
+} // namespace
+
+unique_fd execute_binder_command(std::string_view command) {
+ return abbp->sendCommand(command);
+}
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 1363976..f02cc13 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -237,30 +237,7 @@
CHECK(!proc->out_fds.empty());
int fd = proc->out_fds.back().get();
- struct cmsghdr* cmsg;
- struct msghdr msg;
- struct iovec iov;
- char dummy = '!';
- char buffer[sizeof(struct cmsghdr) + sizeof(int)];
-
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = buffer;
- msg.msg_controllen = sizeof(buffer);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = msg.msg_controllen;
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)CMSG_DATA(cmsg))[0] = fd;
-
- int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
- if (ret < 0) {
+ if (!SendFileDescriptor(socket, fd)) {
D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
goto CloseProcess;
}
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 3693997..5ae210f 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -328,6 +328,13 @@
}
unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+#ifndef __ANDROID_RECOVERY__
+ if (name.starts_with("abb:")) {
+ name.remove_prefix(strlen("abb:"));
+ return execute_binder_command(name);
+ }
+#endif
+
if (name.starts_with("dev:")) {
name.remove_prefix(strlen("dev:"));
return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 595d5c6..455595f 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -141,7 +141,7 @@
class Subprocess {
public:
Subprocess(std::string command, const char* terminal_type, SubprocessType type,
- SubprocessProtocol protocol);
+ SubprocessProtocol protocol, bool make_pty_raw);
~Subprocess();
const std::string& command() const { return command_; }
@@ -154,6 +154,10 @@
// and exec's the child. Returns false and sets error on failure.
bool ForkAndExec(std::string* _Nonnull error);
+ // Sets up FDs, starts a thread executing command and the manager thread,
+ // Returns false and sets error on failure.
+ bool ExecInProcess(Command command, std::string* _Nonnull error);
+
// Start the subprocess manager thread. Consumes the subprocess, regardless of success.
// Returns false and sets error on failure.
static bool StartThread(std::unique_ptr<Subprocess> subprocess,
@@ -177,9 +181,9 @@
const std::string command_;
const std::string terminal_type_;
- bool make_pty_raw_ = false;
SubprocessType type_;
SubprocessProtocol protocol_;
+ bool make_pty_raw_;
pid_t pid_ = -1;
unique_fd local_socket_sfd_;
@@ -192,24 +196,12 @@
};
Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
- SubprocessProtocol protocol)
+ SubprocessProtocol protocol, bool make_pty_raw)
: command_(std::move(command)),
terminal_type_(terminal_type ? terminal_type : ""),
type_(type),
- protocol_(protocol) {
- // If we aren't using the shell protocol we must allocate a PTY to properly close the
- // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
- // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
- // e.g. screenrecord, will never notice the broken pipe and terminate.
- // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
- // with select() and will send SIGHUP manually to the child process.
- if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
- // Disable PTY input/output processing since the client is expecting raw data.
- D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
- type_ = SubprocessType::kPty;
- make_pty_raw_ = true;
- }
-}
+ protocol_(protocol),
+ make_pty_raw_(make_pty_raw) {}
Subprocess::~Subprocess() {
WaitForExit();
@@ -430,6 +422,67 @@
return true;
}
+bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) {
+ unique_fd child_stdinout_sfd, child_stderr_sfd;
+
+ CHECK(type_ == SubprocessType::kRaw);
+ CHECK(protocol_ == SubprocessProtocol::kShell);
+
+ __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+
+ if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
+ strerror(errno));
+ return false;
+ }
+ // Raw subprocess + shell protocol allows for splitting stderr.
+ if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
+ strerror(errno));
+ return false;
+ }
+
+ D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
+ stderr_sfd_.get());
+
+ // Required for shell protocol: create another socketpair to intercept data.
+ if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
+ *error = android::base::StringPrintf("failed to create socketpair to intercept data: %s",
+ strerror(errno));
+ return false;
+ }
+ D("protocol FD = %d", protocol_sfd_.get());
+
+ input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+ output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+ if (!input_ || !output_) {
+ *error = "failed to allocate shell protocol objects";
+ return false;
+ }
+
+ // Don't let reads/writes to the subprocess block our thread. This isn't
+ // likely but could happen under unusual circumstances, such as if we
+ // write a ton of data to stdin but the subprocess never reads it and
+ // the pipe fills up.
+ for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
+ if (fd >= 0) {
+ if (!set_file_block_mode(fd, false)) {
+ *error = android::base::StringPrintf("failed to set non-blocking mode for fd %d",
+ fd);
+ return false;
+ }
+ }
+ }
+
+ std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
+ command = std::move(command),
+ args = command_]() { command(args, inout_sfd, inout_sfd, err_sfd); })
+ .detach();
+
+ D("execinprocess: completed");
+ return true;
+}
+
bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
Subprocess* raw = subprocess.release();
std::thread(ThreadHandler, raw).detach();
@@ -512,7 +565,9 @@
// needed (e.g. SIGINT), pass those through the shell protocol
// and only fall back on this for unexpected closures.
D("protocol FD died, sending SIGHUP to pid %d", pid_);
- kill(pid_, SIGHUP);
+ if (pid_ != -1) {
+ kill(pid_, SIGHUP);
+ }
// We also need to close the pipes connected to the child process
// so that if it ignores SIGHUP and continues to write data it
@@ -682,7 +737,7 @@
int exit_code = 1;
D("waiting for pid %d", pid_);
- while (true) {
+ while (pid_ != -1) {
int status;
if (pid_ == waitpid(pid_, &status, 0)) {
D("post waitpid (pid=%d) status=%04x", pid_, status);
@@ -716,7 +771,7 @@
} // namespace
// Create a pipe containing the error.
-static unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
+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";
@@ -747,20 +802,49 @@
unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol) {
+ // If we aren't using the shell protocol we must allocate a PTY to properly close the
+ // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
+ // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
+ // e.g. screenrecord, will never notice the broken pipe and terminate.
+ // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
+ // with select() and will send SIGHUP manually to the child process.
+ bool make_pty_raw = false;
+ if (protocol == SubprocessProtocol::kNone && type == SubprocessType::kRaw) {
+ // Disable PTY input/output processing since the client is expecting raw data.
+ D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
+ type = SubprocessType::kPty;
+ make_pty_raw = true;
+ }
+
+ unique_fd error_fd;
+ unique_fd fd = StartSubprocess(std::move(name), terminal_type, type, protocol, make_pty_raw,
+ protocol, &error_fd);
+ if (fd == -1) {
+ return error_fd;
+ }
+ return fd;
+}
+
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol, bool make_pty_raw,
+ SubprocessProtocol error_protocol, unique_fd* error_fd) {
D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
type == SubprocessType::kRaw ? "raw" : "PTY",
protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
- auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol);
+ auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+ make_pty_raw);
if (!subprocess) {
LOG(ERROR) << "failed to allocate new subprocess";
- return ReportError(protocol, "failed to allocate new subprocess");
+ *error_fd = ReportError(error_protocol, "failed to allocate new subprocess");
+ return {};
}
std::string error;
if (!subprocess->ForkAndExec(&error)) {
LOG(ERROR) << "failed to start subprocess: " << error;
- return ReportError(protocol, error);
+ *error_fd = ReportError(error_protocol, error);
+ return {};
}
unique_fd local_socket(subprocess->ReleaseLocalSocket());
@@ -769,6 +853,40 @@
if (!Subprocess::StartThread(std::move(subprocess), &error)) {
LOG(ERROR) << "failed to start subprocess management thread: " << error;
+ *error_fd = ReportError(error_protocol, error);
+ return {};
+ }
+
+ return local_socket;
+}
+
+unique_fd StartCommandInProcess(std::string name, Command command) {
+ LOG(INFO) << "StartCommandInProcess(" << dump_hex(name.data(), name.size()) << ")";
+
+ constexpr auto terminal_type = "";
+ constexpr auto type = SubprocessType::kRaw;
+ constexpr auto protocol = SubprocessProtocol::kShell;
+ constexpr auto make_pty_raw = false;
+
+ auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+ make_pty_raw);
+ if (!subprocess) {
+ LOG(ERROR) << "failed to allocate new subprocess";
+ return ReportError(protocol, "failed to allocate new subprocess");
+ }
+
+ std::string error;
+ if (!subprocess->ExecInProcess(std::move(command), &error)) {
+ LOG(ERROR) << "failed to start subprocess: " << error;
+ return ReportError(protocol, error);
+ }
+
+ unique_fd local_socket(subprocess->ReleaseLocalSocket());
+ D("inprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
+ subprocess->pid());
+
+ if (!Subprocess::StartThread(std::move(subprocess), &error)) {
+ LOG(ERROR) << "failed to start inprocess management thread: " << error;
return ReportError(protocol, error);
}
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 421d61f..fc66377 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -20,6 +20,8 @@
#include "adb_unique_fd.h"
+#include <string_view>
+
enum class SubprocessType {
kPty,
kRaw,
@@ -36,3 +38,18 @@
// Returns an open FD connected to the subprocess or -1 on failure.
unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol);
+
+// The same as above but with more fined grained control and custom error handling.
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol, bool make_pty_raw,
+ SubprocessProtocol error_protocol, unique_fd* error_fd);
+
+// Executes |command| in a separate thread.
+// Sets up in/out and error streams to emulate shell-like behavior.
+//
+// Returns an open FD connected to the thread or -1 on failure.
+using Command = int(std::string_view args, int in, int out, int err);
+unique_fd StartCommandInProcess(std::string name, Command command);
+
+// Create a pipe containing the error.
+unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index f44ff41..32a5d21 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -114,7 +114,7 @@
const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
- static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs"};
+ static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
return false;
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 097e07f..4a0d4b5 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -165,23 +165,13 @@
return true;
}
-static bool GetBlockDeviceParams(int bdev_fd, const std::string& bdev_path, uint64_t* blocksz,
- uint64_t* bdev_size) {
- // TODO: For some reason, the block device ioctl require the argument to be initialized
- // to zero even if its the out parameter for the given ioctl cmd.
- uint64_t blksz = 0;
- if (ioctl(bdev_fd, BLKBSZGET, &blksz)) {
- PLOG(ERROR) << "Failed to get block size for: " << bdev_path;
- return false;
- }
-
+static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
uint64_t size_in_bytes = 0;
if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
return false;
}
- *blocksz = blksz;
*bdev_size = size_in_bytes;
return true;
@@ -197,22 +187,20 @@
return sb.st_size;
}
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t blocksz,
+static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
uint32_t* fs_type) {
- // Check if the size aligned to the block size of the block device.
- // We need this to be true in order to be able to write the file using FIEMAP.
- if (file_size % blocksz) {
- LOG(ERROR) << "File size " << file_size << " is not aligned to block size " << blocksz
- << " for file " << file_path;
- return false;
- }
-
struct statfs64 sfs;
if (statfs64(file_path.c_str(), &sfs)) {
PLOG(ERROR) << "Failed to read file system status at: " << file_path;
return false;
}
+ if (file_size % sfs.f_bsize) {
+ LOG(ERROR) << "File size " << file_size << " is not aligned to optimal block size "
+ << sfs.f_bsize << " for file " << file_path;
+ return false;
+ }
+
// Check if the filesystem is of supported types.
// Only ext4 and f2fs are tested and supported.
if ((sfs.f_type != EXT4_SUPER_MAGIC) && (sfs.f_type != F2FS_SUPER_MAGIC)) {
@@ -226,6 +214,7 @@
return false;
}
+ *blocksz = sfs.f_bsize;
*fs_type = sfs.f_type;
return true;
}
@@ -303,7 +292,7 @@
uint32_t pin_status = 1;
int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
- if (error) {
+ if (error < 0) {
if ((errno == ENOTTY) || (errno == ENOTSUP)) {
PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
} else {
@@ -316,7 +305,7 @@
}
#if 0
-static bool PinFileStatus(int file_fd, const std::string& file_path, uint32_t fs_type) {
+static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
if (fs_type == EXT4_SUPER_MAGIC) {
// No pinning necessary for ext4. The blocks, once allocated, are expected
// to be fixed.
@@ -339,9 +328,10 @@
#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
#endif
- uint32_t pin_status;
- int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &pin_status);
- if (error) {
+ // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
+ uint32_t moved_blocks_nr;
+ int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
+ if (error < 0) {
if ((errno == ENOTTY) || (errno == ENOTSUP)) {
PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
} else {
@@ -350,7 +340,10 @@
return false;
}
- return !!pin_status;
+ if (moved_blocks_nr) {
+ LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
+ }
+ return moved_blocks_nr == 0;
}
#endif
@@ -447,7 +440,7 @@
std::string bdev_path;
if (!FileToBlockDevicePath(abs_path, &bdev_path)) {
LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
- cleanup(file_path, create);
+ cleanup(abs_path, create);
return nullptr;
}
@@ -459,9 +452,9 @@
return nullptr;
}
- uint64_t blocksz, bdevsz;
- if (!GetBlockDeviceParams(bdev_fd, bdev_path, &blocksz, &bdevsz)) {
- LOG(ERROR) << "Failed to get block device params for: " << bdev_path;
+ uint64_t bdevsz;
+ if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
+ LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
cleanup(file_path, create);
return nullptr;
}
@@ -474,23 +467,24 @@
}
}
+ uint64_t blocksz;
uint32_t fs_type;
- if (!PerformFileChecks(abs_path, file_size, blocksz, &fs_type)) {
+ if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
- cleanup(file_path, create);
+ cleanup(abs_path, create);
return nullptr;
}
if (create) {
if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) {
- unlink(abs_path.c_str());
+ cleanup(abs_path, create);
return nullptr;
}
}
// f2fs may move the file blocks around.
if (!PinFile(file_fd, file_path, fs_type)) {
- cleanup(file_path, create);
+ cleanup(abs_path, create);
LOG(ERROR) << "Failed to pin the file in storage";
return nullptr;
}
@@ -499,7 +493,7 @@
FiemapUniquePtr fmap(new FiemapWriter());
if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
- cleanup(file_path, create);
+ cleanup(abs_path, create);
return nullptr;
}
@@ -543,9 +537,10 @@
<< " for block size " << block_size_;
return false;
}
+
#if 0
// TODO(b/122138114): check why this fails.
- if (!PinFileStatus(file_fd_, file_path_, fs_type_)) {
+ if (!IsFilePinned(file_fd_, file_path_, fs_type_)) {
LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
return false;
}
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index 6653ada..6dff0e8 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -40,11 +40,22 @@
using unique_fd = android::base::unique_fd;
using LoopDevice = android::dm::LoopDevice;
-std::string testfile = "";
std::string testbdev = "";
uint64_t testfile_size = 536870912; // default of 512MiB
-TEST(FiemapWriter, CreateImpossiblyLargeFile) {
+class FiemapWriterTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ testfile = ::android::base::StringPrintf("%s/testdata/%s", exec_dir.c_str(), tinfo->name());
+ }
+
+ // name of the file we use for testing
+ std::string testfile;
+};
+
+TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
// Try creating a file of size ~100TB but aligned to
// 512 byte to make sure block alignment tests don't
// fail.
@@ -54,7 +65,7 @@
EXPECT_EQ(errno, ENOENT);
}
-TEST(FiemapWriter, CreateUnalignedFile) {
+TEST_F(FiemapWriterTest, CreateUnalignedFile) {
// Try creating a file of size 4097 bytes which is guaranteed
// to be unaligned to all known block sizes. The creation must
// fail.
@@ -64,7 +75,7 @@
EXPECT_EQ(errno, ENOENT);
}
-TEST(FiemapWriter, CheckFilePath) {
+TEST_F(FiemapWriterTest, CheckFilePath) {
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
ASSERT_NE(fptr, nullptr);
EXPECT_EQ(fptr->size(), 4096);
@@ -72,20 +83,20 @@
EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
}
-TEST(FiemapWriter, CheckBlockDevicePath) {
+TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
EXPECT_EQ(fptr->size(), 4096);
EXPECT_EQ(fptr->bdev_path(), testbdev);
}
-TEST(FiemapWriter, CheckFileCreated) {
+TEST_F(FiemapWriterTest, CheckFileCreated) {
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
ASSERT_NE(fptr, nullptr);
unique_fd fd(open(testfile.c_str(), O_RDONLY));
EXPECT_GT(fd, -1);
}
-TEST(FiemapWriter, CheckFileSizeActual) {
+TEST_F(FiemapWriterTest, CheckFileSizeActual) {
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
ASSERT_NE(fptr, nullptr);
@@ -94,13 +105,13 @@
EXPECT_EQ(sb.st_size, testfile_size);
}
-TEST(FiemapWriter, CheckFileExtents) {
+TEST_F(FiemapWriterTest, CheckFileExtents) {
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
ASSERT_NE(fptr, nullptr);
EXPECT_GT(fptr->extents().size(), 0);
}
-TEST(FiemapWriter, CheckWriteError) {
+TEST_F(FiemapWriterTest, CheckWriteError) {
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
ASSERT_NE(fptr, nullptr);
@@ -339,18 +350,17 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- if (argc <= 2) {
+ if (argc <= 1) {
cerr << "Filepath with its bdev path must be provided as follows:" << endl;
- cerr << " $ fiemap_writer_test <path to file> </dev/block/XXXX" << endl;
+ cerr << " $ fiemap_writer_test </dev/block/XXXX" << endl;
cerr << " where, /dev/block/XXX is the block device where the file resides" << endl;
exit(EXIT_FAILURE);
}
::android::base::InitLogging(argv, ::android::base::StderrLogger);
- testfile = argv[1];
- testbdev = argv[2];
- if (argc > 3) {
- testfile_size = strtoull(argv[3], NULL, 0);
+ testbdev = argv[1];
+ if (argc > 2) {
+ testfile_size = strtoull(argv[2], NULL, 0);
if (testfile_size == ULLONG_MAX) {
testfile_size = 512 * 1024 * 1024;
}
diff --git a/fs_mgr/libfs_avb/tests/Android.bp b/fs_mgr/libfs_avb/tests/Android.bp
index 4c02237..24e1d76 100644
--- a/fs_mgr/libfs_avb/tests/Android.bp
+++ b/fs_mgr/libfs_avb/tests/Android.bp
@@ -32,4 +32,9 @@
srcs: [
"fs_avb_unittest_util.cpp",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 37afb98..4291212 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -89,6 +89,7 @@
"socket_inaddr_any_server_windows.cpp",
"socket_network_client_windows.cpp",
"sockets_windows.cpp",
+ "trace-host.cpp",
],
enabled: true,
diff --git a/llkd/README.md b/llkd/README.md
index 43bb94a..191f988 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -165,9 +165,14 @@
NB: false is a very very very unlikely process to want to blacklist.
#### ro.llk.blacklist.parent
-default 0,2,adbd (kernel, [kthreadd] and adbd).
+default 0,2,adbd&[setsid] (kernel, [kthreadd] and adbd *only for zombie setsid*).
Do not watch processes that have this parent.
-A parent process can be comm, cmdline or pid reference.
+An ampersand (*&*) separator is used to specify that the parent is ignored
+only in combination with the target child process.
+Ampersand was selected because it is never part of a process name,
+however a setprop in the shell requires it to be escaped or quoted;
+init rc file where this is normally specified does not have this issue.
+A parent or target processes can be specified as comm, cmdline or pid reference.
#### ro.llk.blacklist.uid
default *empty* or false, comma separated list of uid numbers or names.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 7b7dbf9..3586ca1 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -56,11 +56,7 @@
#define LLK_BLACKLIST_PROCESS_DEFAULT \
"0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
#define LLK_BLACKLIST_PARENT_PROPERTY "ro.llk.blacklist.parent"
-#ifdef __PTRACE_ENABLED__ // defined if userdebug build
-#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd],adbd"
-#else
-#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd]"
-#endif
+#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd],adbd&[setsid]"
#define LLK_BLACKLIST_UID_PROPERTY "ro.llk.blacklist.uid"
#define LLK_BLACKLIST_UID_DEFAULT ""
#define LLK_BLACKLIST_STACK_PROPERTY "ro.llk.blacklist.process.stack"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 3a593ec..3c295b5 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -108,6 +108,9 @@
// list of parent pids, comm or cmdline names to skip. default:
// kernel pid (0), [kthreadd] (2), or ourselves, enforced and implied
std::unordered_set<std::string> llkBlacklistParent;
+// list of parent and target processes to skip. default:
+// adbd *and* [setsid]
+std::unordered_map<std::string, std::unordered_set<std::string>> llkBlacklistParentAndChild;
// list of uids, and uid names, to skip, default nothing
std::unordered_set<std::string> llkBlacklistUid;
#ifdef __PTRACE_ENABLED__
@@ -624,6 +627,19 @@
return ret;
}
+std::string llkFormat(
+ const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist,
+ bool leading_comma = false) {
+ std::string ret;
+ for (const auto& entry : blacklist) {
+ for (const auto& target : entry.second) {
+ if (leading_comma || !ret.empty()) ret += ",";
+ ret += entry.first + "&" + target;
+ }
+ }
+ return ret;
+}
+
// This function parses the properties as a list, incorporating the supplied
// default. A leading comma separator means preserve the defaults and add
// entries (with an optional leading + sign), or removes entries with a leading
@@ -691,6 +707,27 @@
return false;
}
+const std::unordered_set<std::string>& llkSkipName(
+ const std::string& name,
+ const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist) {
+ static const std::unordered_set<std::string> empty;
+ if (name.empty() || blacklist.empty()) return empty;
+ auto found = blacklist.find(name);
+ if (found == blacklist.end()) return empty;
+ return found->second;
+}
+
+bool llkSkipPproc(proc* pprocp, proc* procp,
+ const std::unordered_map<std::string, std::unordered_set<std::string>>&
+ blacklist = llkBlacklistParentAndChild) {
+ if (!pprocp || !procp || blacklist.empty()) return false;
+ if (llkSkipProc(procp, llkSkipName(std::to_string(pprocp->pid), blacklist))) return true;
+ if (llkSkipProc(procp, llkSkipName(pprocp->getComm(), blacklist))) return true;
+ if (llkSkipProc(procp, llkSkipName(pprocp->getCmdline(), blacklist))) return true;
+ return llkSkipProc(procp,
+ llkSkipName(android::base::Basename(pprocp->getCmdline()), blacklist));
+}
+
bool llkSkipPid(pid_t pid) {
return llkSkipName(std::to_string(pid), llkBlacklistProcess);
}
@@ -875,7 +912,8 @@
<< LLK_BLACKLIST_STACK_PROPERTY "=" << llkFormat(llkBlacklistStack) << "\n"
#endif
<< LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
- << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent) << "\n"
+ << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent)
+ << llkFormat(llkBlacklistParentAndChild, true) << "\n"
<< LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
}
@@ -1050,7 +1088,8 @@
break;
}
- if (llkSkipName(procp->getComm())) {
+ auto process_comm = procp->getComm();
+ if (llkSkipName(process_comm)) {
continue;
}
if (llkSkipName(procp->getCmdline())) {
@@ -1065,6 +1104,7 @@
pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
}
if (pprocp) {
+ if (llkSkipPproc(pprocp, procp)) break;
if (llkSkipProc(pprocp, llkBlacklistParent)) break;
} else {
if (llkSkipName(std::to_string(ppid), llkBlacklistParent)) break;
@@ -1084,7 +1124,7 @@
stuck = true;
} else if (procp->count != 0ms) {
LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
- << pid << "->" << tid << ' ' << procp->getComm();
+ << pid << "->" << tid << ' ' << process_comm;
}
}
if (!stuck) continue;
@@ -1092,7 +1132,7 @@
if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
if (procp->count != 0ms) {
LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
- << pid << "->" << tid << ' ' << procp->getComm();
+ << pid << "->" << tid << ' ' << process_comm;
}
continue;
}
@@ -1120,7 +1160,7 @@
break;
}
LOG(WARNING) << "Z " << llkFormat(procp->count) << ' ' << ppid << "->"
- << pid << "->" << tid << ' ' << procp->getComm() << " [kill]";
+ << pid << "->" << tid << ' ' << process_comm << " [kill]";
if ((llkKillOneProcess(pprocp, procp) >= 0) ||
(llkKillOneProcess(ppid, procp) >= 0)) {
continue;
@@ -1137,7 +1177,7 @@
// kernel (worse).
default:
LOG(WARNING) << state << ' ' << llkFormat(procp->count) << ' ' << pid
- << "->" << tid << ' ' << procp->getComm() << " [kill]";
+ << "->" << tid << ' ' << process_comm << " [kill]";
if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||
(llkKillOneProcess(pid, state, tid) >= 0) ||
(llkKillOneProcess(procp, procp) >= 0) ||
@@ -1150,7 +1190,7 @@
// We are here because we have confirmed kernel live-lock
const auto message = state + " "s + llkFormat(procp->count) + " " +
std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
- std::to_string(tid) + " " + procp->getComm() + " [panic]";
+ std::to_string(tid) + " " + process_comm + " [panic]";
llkPanicKernel(dump, tid,
(state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
message);
@@ -1274,6 +1314,26 @@
llkBlacklistParent = llkSplit(LLK_BLACKLIST_PARENT_PROPERTY,
std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
"," LLK_BLACKLIST_PARENT_DEFAULT);
+ // derive llkBlacklistParentAndChild by moving entries with '&' from above
+ for (auto it = llkBlacklistParent.begin(); it != llkBlacklistParent.end();) {
+ auto pos = it->find('&');
+ if (pos == std::string::npos) {
+ ++it;
+ continue;
+ }
+ auto parent = it->substr(0, pos);
+ auto child = it->substr(pos + 1);
+ it = llkBlacklistParent.erase(it);
+
+ auto found = llkBlacklistParentAndChild.find(parent);
+ if (found == llkBlacklistParentAndChild.end()) {
+ llkBlacklistParentAndChild.emplace(std::make_pair(
+ std::move(parent), std::unordered_set<std::string>({std::move(child)})));
+ } else {
+ found->second.emplace(std::move(child));
+ }
+ }
+
llkBlacklistUid = llkSplit(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT);
// internal watchdog
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index d738935..96079cc 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -17,6 +17,7 @@
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -333,3 +334,37 @@
unlink(stack_pipe_file);
}
+
+// b/120983740
+TEST(llkd, adbd_and_setsid) {
+ if (checkKill("kernel_panic,sysrq,livelock,zombie")) {
+ return;
+ }
+ const auto period = llkdSleepPeriod('S');
+
+ // expect llkd.zombie to trigger, but not for adbd&[setsid]
+ // Create a Persistent Zombie setsid Process
+ pid_t child_pid = fork();
+ ASSERT_LE(0, child_pid);
+ if (!child_pid) {
+ prctl(PR_SET_NAME, "adbd");
+ auto zombie_pid = fork();
+ ASSERT_LE(0, zombie_pid);
+ if (!zombie_pid) {
+ prctl(PR_SET_NAME, "setsid");
+ sleep(1);
+ exit(0);
+ }
+ sleep(period.count());
+ exit(42);
+ }
+
+ // Reverse of waitForPid, do _not_ expect kill
+ int wstatus;
+ ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ if (WIFEXITED(wstatus)) {
+ EXPECT_EQ(42, WEXITSTATUS(wstatus));
+ }
+ ASSERT_FALSE(WIFSIGNALED(wstatus)) << "[ INFO ] signo=" << WTERMSIG(wstatus);
+}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 49ae960..5601e53 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -334,11 +334,9 @@
###############################################################################
namespace.runtime.isolated = true
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = system,default
-namespace.runtime.link.system.shared_libs = %LLNDK_LIBRARIES%
-namespace.runtime.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.runtime.links = system
# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
-namespace.runtime.link.default.allow_all_shared_libs = true
+namespace.runtime.link.system.allow_all_shared_libs = true
###############################################################################
# "vndk" namespace