Loading adb/Android.mk +11 −1 Original line number Diff line number Diff line Loading @@ -131,6 +131,8 @@ LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS) LOCAL_SRC_FILES := \ $(LIBADB_TEST_SRCS) \ $(LIBADB_TEST_linux_SRCS) \ shell_service_protocol.cpp \ shell_service_protocol_test.cpp \ LOCAL_SANITIZE := $(adb_target_sanitize) LOCAL_STATIC_LIBRARIES := libadbd Loading @@ -145,7 +147,12 @@ LOCAL_MODULE := adb_test LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS) LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS) LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS) LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp LOCAL_SRC_FILES := \ $(LIBADB_TEST_SRCS) \ services.cpp \ shell_service_protocol.cpp \ shell_service_protocol_test.cpp \ LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS) LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS) LOCAL_SANITIZE := $(adb_host_sanitize) Loading Loading @@ -201,6 +208,7 @@ LOCAL_SRC_FILES := \ adb_client.cpp \ services.cpp \ file_sync_client.cpp \ shell_service_protocol.cpp \ LOCAL_CFLAGS += \ $(ADB_COMMON_CFLAGS) \ Loading Loading @@ -248,6 +256,8 @@ LOCAL_SRC_FILES := \ framebuffer_service.cpp \ remount_service.cpp \ set_verity_enable_state_service.cpp \ shell_service.cpp \ shell_service_protocol.cpp \ LOCAL_CFLAGS := \ $(ADB_COMMON_CFLAGS) \ Loading adb/adb.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -176,7 +176,8 @@ static void setup_trace_mask() { {"transport", TRACE_TRANSPORT}, {"jdwp", TRACE_JDWP}, {"services", TRACE_SERVICES}, {"auth", TRACE_AUTH}}; {"auth", TRACE_AUTH}, {"shell", TRACE_SHELL}}; std::vector<std::string> elements = android::base::Split(trace_setting, " "); for (const auto& elem : elements) { Loading adb/adb_trace.h +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ enum AdbTrace { TRACE_SERVICES, TRACE_AUTH, TRACE_FDEVENT, TRACE_SHELL }; extern int adb_trace_mask; Loading adb/services.cpp +8 −222 Original line number Diff line number Diff line Loading @@ -24,11 +24,6 @@ #include <stdlib.h> #include <string.h> #if !ADB_HOST #include <pty.h> #include <termios.h> #endif #ifndef _WIN32 #include <netdb.h> #include <netinet/in.h> Loading @@ -51,6 +46,7 @@ #include "adb_utils.h" #include "file_sync_service.h" #include "remount_service.h" #include "shell_service.h" #include "transport.h" struct stinfo { Loading @@ -59,11 +55,6 @@ struct stinfo { void *cookie; }; enum class SubprocessType { kPty, kRaw, }; void *service_bootstrap_func(void *x) { stinfo* sti = reinterpret_cast<stinfo*>(x); Loading Loading @@ -234,211 +225,6 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) return s[0]; } #if !ADB_HOST static void init_subproc_child() { setsid(); // Set OOM score adjustment to prevent killing int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC); if (fd >= 0) { adb_write(fd, "0", 1); adb_close(fd); } else { D("adb: unable to update oom_score_adj"); } } #if !ADB_HOST static int create_subproc_pty(const char* cmd, const char* arg0, const char* arg1, pid_t* pid) { D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)", cmd, arg0, arg1); char pts_name[PATH_MAX]; int ptm; *pid = forkpty(&ptm, pts_name, nullptr, nullptr); if (*pid == -1) { printf("- fork failed: %s -\n", strerror(errno)); unix_close(ptm); return -1; } if (*pid == 0) { init_subproc_child(); int pts = unix_open(pts_name, O_RDWR | O_CLOEXEC); if (pts == -1) { fprintf(stderr, "child failed to open pseudo-term slave %s: %s\n", pts_name, strerror(errno)); unix_close(ptm); exit(-1); } // arg0 is "-c" in batch mode and "-" in interactive mode. if (strcmp(arg0, "-c") == 0) { termios tattr; if (tcgetattr(pts, &tattr) == -1) { fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); unix_close(pts); unix_close(ptm); exit(-1); } cfmakeraw(&tattr); if (tcsetattr(pts, TCSADRAIN, &tattr) == -1) { fprintf(stderr, "tcsetattr failed: %s\n", strerror(errno)); unix_close(pts); unix_close(ptm); exit(-1); } } dup2(pts, STDIN_FILENO); dup2(pts, STDOUT_FILENO); dup2(pts, STDERR_FILENO); unix_close(pts); unix_close(ptm); execl(cmd, cmd, arg0, arg1, nullptr); fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", cmd, strerror(errno), errno); exit(-1); } else { return ptm; } } #endif // !ADB_HOST static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) { D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)", cmd, arg0, arg1); #if defined(_WIN32) fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); return -1; #else // 0 is parent socket, 1 is child socket int sv[2]; if (adb_socketpair(sv) < 0) { printf("[ cannot create socket pair - %s ]\n", strerror(errno)); return -1; } D("socketpair: (%d,%d)", sv[0], sv[1]); *pid = fork(); if (*pid < 0) { printf("- fork failed: %s -\n", strerror(errno)); adb_close(sv[0]); adb_close(sv[1]); return -1; } if (*pid == 0) { adb_close(sv[0]); init_subproc_child(); dup2(sv[1], STDIN_FILENO); dup2(sv[1], STDOUT_FILENO); dup2(sv[1], STDERR_FILENO); adb_close(sv[1]); execl(cmd, cmd, arg0, arg1, NULL); fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", cmd, strerror(errno), errno); exit(-1); } else { adb_close(sv[1]); return sv[0]; } #endif /* !defined(_WIN32) */ } #endif /* !ABD_HOST */ #if ADB_HOST #define SHELL_COMMAND "/bin/sh" #else #define SHELL_COMMAND "/system/bin/sh" #endif #if !ADB_HOST static void subproc_waiter_service(int fd, void *cookie) { pid_t pid = (pid_t) (uintptr_t) cookie; D("entered. fd=%d of pid=%d", fd, pid); while (true) { int status; pid_t p = waitpid(pid, &status, 0); if (p == pid) { D("fd=%d, post waitpid(pid=%d) status=%04x", fd, p, status); if (WIFSIGNALED(status)) { D("*** Killed by signal %d", WTERMSIG(status)); break; } else if (!WIFEXITED(status)) { D("*** Didn't exit!!. status %d", status); break; } else if (WEXITSTATUS(status) >= 0) { D("*** Exit code %d", WEXITSTATUS(status)); break; } } } D("shell exited fd=%d of pid=%d err=%d", fd, pid, errno); if (SHELL_EXIT_NOTIFY_FD >=0) { int res; res = WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)) ? 0 : -1; D("notified shell exit via fd=%d for pid=%d res=%d errno=%d", SHELL_EXIT_NOTIFY_FD, pid, res, errno); } } // Starts a subprocess and spawns a thread to wait for the subprocess to finish // and trigger the necessary cleanup. // // |name| is the command to execute in the subprocess; empty string will start // an interactive session. // |type| selects between a PTY or raw subprocess. // // Returns an open file descriptor tied to the subprocess stdin/stdout/stderr. static int create_subproc_thread(const char *name, SubprocessType type) { const char *arg0, *arg1; if (*name == '\0') { arg0 = "-"; arg1 = nullptr; } else { arg0 = "-c"; arg1 = name; } pid_t pid = -1; int ret_fd; if (type == SubprocessType::kPty) { ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid); } else { ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid); } D("create_subproc ret_fd=%d pid=%d", ret_fd, pid); stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo))); if(sti == 0) fatal("cannot allocate stinfo"); sti->func = subproc_waiter_service; sti->cookie = (void*) (uintptr_t) pid; sti->fd = ret_fd; if (!adb_thread_create(service_bootstrap_func, sti)) { free(sti); adb_close(ret_fd); fprintf(stderr, "cannot create service thread\n"); return -1; } D("service thread started, fd=%d pid=%d", ret_fd, pid); return ret_fd; } #endif int service_to_fd(const char* name) { int ret = -1; Loading Loading @@ -483,13 +269,13 @@ int service_to_fd(const char* name) { const char* args = name + 6; if (*args) { // Non-interactive session uses a raw subprocess. ret = create_subproc_thread(args, SubprocessType::kRaw); ret = StartSubprocess(args, SubprocessType::kRaw); } else { // Interactive session uses a PTY subprocess. ret = create_subproc_thread(args, SubprocessType::kPty); ret = StartSubprocess(args, SubprocessType::kPty); } } else if(!strncmp(name, "exec:", 5)) { ret = create_subproc_thread(name + 5, SubprocessType::kRaw); ret = StartSubprocess(name + 5, SubprocessType::kRaw); } else if(!strncmp(name, "sync:", 5)) { ret = create_service_thread(file_sync_service, NULL); } else if(!strncmp(name, "remount:", 8)) { Loading @@ -503,11 +289,11 @@ int service_to_fd(const char* name) { } else if(!strncmp(name, "unroot:", 7)) { ret = create_service_thread(restart_unroot_service, NULL); } else if(!strncmp(name, "backup:", 7)) { ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s", ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(), SubprocessType::kRaw); } else if(!strncmp(name, "restore:", 8)) { ret = create_subproc_thread("/system/bin/bu restore", SubprocessType::kRaw); ret = StartSubprocess("/system/bin/bu restore", SubprocessType::kRaw); } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) != 1) { Loading adb/shell_service.cpp 0 → 100644 +329 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define TRACE_TAG TRACE_SHELL #include "shell_service.h" #if !ADB_HOST #include <errno.h> #include <pty.h> #include <termios.h> #include <base/logging.h> #include <base/stringprintf.h> #include <paths.h> #include "adb.h" #include "adb_io.h" #include "adb_trace.h" #include "sysdeps.h" namespace { void init_subproc_child() { setsid(); // Set OOM score adjustment to prevent killing int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC); if (fd >= 0) { adb_write(fd, "0", 1); adb_close(fd); } else { D("adb: unable to update oom_score_adj"); } } // Reads from |fd| until close or failure. std::string ReadAll(int fd) { char buffer[512]; std::string received; while (1) { int bytes = adb_read(fd, buffer, sizeof(buffer)); if (bytes <= 0) { break; } received.append(buffer, bytes); } return received; } // Helper to automatically close an FD when it goes out of scope. class ScopedFd { public: ScopedFd() {} ~ScopedFd() { Reset(); } void Reset(int fd=-1) { if (fd != fd_) { if (valid()) { adb_close(fd_); } fd_ = fd; } } int Release() { int temp = fd_; fd_ = -1; return temp; } bool valid() const { return fd_ >= 0; } int fd() const { return fd_; } private: int fd_ = -1; DISALLOW_COPY_AND_ASSIGN(ScopedFd); }; // Creates a socketpair and saves the endpoints to |fd1| and |fd2|. bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) { int sockets[2]; if (adb_socketpair(sockets) < 0) { PLOG(ERROR) << "cannot create socket pair"; return false; } fd1->Reset(sockets[0]); fd2->Reset(sockets[1]); return true; } class Subprocess { public: Subprocess(const std::string& command, SubprocessType type); ~Subprocess(); const std::string& command() const { return command_; } bool is_interactive() const { return command_.empty(); } int local_socket_fd() const { return local_socket_sfd_.fd(); } pid_t pid() const { return pid_; } // Sets up FDs, forks a subprocess, starts the subprocess manager thread, // and exec's the child. Returns false on failure. bool ForkAndExec(); private: // Opens the file at |pts_name|. int OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd); static void* ThreadHandler(void* userdata); void WaitForExit(); const std::string command_; SubprocessType type_; pid_t pid_ = -1; ScopedFd local_socket_sfd_; DISALLOW_COPY_AND_ASSIGN(Subprocess); }; Subprocess::Subprocess(const std::string& command, SubprocessType type) : command_(command), type_(type) { } Subprocess::~Subprocess() { } bool Subprocess::ForkAndExec() { ScopedFd parent_sfd, child_sfd, parent_error_sfd, child_error_sfd; char pts_name[PATH_MAX]; // Create a socketpair for the fork() child to report any errors back to // the parent. Since we use threads, logging directly from the child could // create a race condition. if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) { LOG(ERROR) << "failed to create pipe for subprocess error reporting"; } if (type_ == SubprocessType::kPty) { int fd; pid_ = forkpty(&fd, pts_name, nullptr, nullptr); parent_sfd.Reset(fd); } else { if (!CreateSocketpair(&parent_sfd, &child_sfd)) { return false; } pid_ = fork(); } if (pid_ == -1) { PLOG(ERROR) << "fork failed"; return false; } if (pid_ == 0) { // Subprocess child. init_subproc_child(); if (type_ == SubprocessType::kPty) { child_sfd.Reset(OpenPtyChildFd(pts_name, &child_error_sfd)); } dup2(child_sfd.fd(), STDIN_FILENO); dup2(child_sfd.fd(), STDOUT_FILENO); dup2(child_sfd.fd(), STDERR_FILENO); // exec doesn't trigger destructors, close the FDs manually. parent_sfd.Reset(); child_sfd.Reset(); parent_error_sfd.Reset(); close_on_exec(child_error_sfd.fd()); if (is_interactive()) { execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr); } else { execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr); } WriteFdExactly(child_error_sfd.fd(), "exec '" _PATH_BSHELL "' failed"); child_error_sfd.Reset(); exit(-1); } // Subprocess parent. D("subprocess parent: subprocess FD = %d", parent_sfd.fd()); // Wait to make sure the subprocess exec'd without error. child_error_sfd.Reset(); std::string error_message = ReadAll(parent_error_sfd.fd()); if (!error_message.empty()) { LOG(ERROR) << error_message; return false; } local_socket_sfd_.Reset(parent_sfd.Release()); if (!adb_thread_create(ThreadHandler, this)) { PLOG(ERROR) << "failed to create subprocess thread"; return false; } return true; } int Subprocess::OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd) { int child_fd = adb_open(pts_name, O_RDWR | O_CLOEXEC); if (child_fd == -1) { // Don't use WriteFdFmt; since we're in the fork() child we don't want // to allocate any heap memory to avoid race conditions. const char* messages[] = {"child failed to open pseudo-term slave ", pts_name, ": ", strerror(errno)}; for (const char* message : messages) { WriteFdExactly(error_sfd->fd(), message); } exit(-1); } if (!is_interactive()) { termios tattr; if (tcgetattr(child_fd, &tattr) == -1) { WriteFdExactly(error_sfd->fd(), "tcgetattr failed"); exit(-1); } cfmakeraw(&tattr); if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) { WriteFdExactly(error_sfd->fd(), "tcsetattr failed"); exit(-1); } } return child_fd; } void* Subprocess::ThreadHandler(void* userdata) { Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata); adb_thread_setname(android::base::StringPrintf( "shell srvc %d", subprocess->local_socket_fd())); subprocess->WaitForExit(); D("deleting Subprocess"); delete subprocess; return nullptr; } void Subprocess::WaitForExit() { D("waiting for pid %d", pid_); while (true) { int status; if (pid_ == waitpid(pid_, &status, 0)) { D("post waitpid (pid=%d) status=%04x", pid_, status); if (WIFSIGNALED(status)) { D("subprocess killed by signal %d", WTERMSIG(status)); break; } else if (!WIFEXITED(status)) { D("subprocess didn't exit"); break; } else if (WEXITSTATUS(status) >= 0) { D("subprocess exit code = %d", WEXITSTATUS(status)); break; } } } // Pass the local socket FD to the shell cleanup fdevent. if (SHELL_EXIT_NOTIFY_FD >= 0) { int fd = local_socket_sfd_.fd(); if (WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd))) { D("passed fd %d to SHELL_EXIT_NOTIFY_FD (%d) for pid %d", fd, SHELL_EXIT_NOTIFY_FD, pid_); // The shell exit fdevent now owns the FD and will close it once // the last bit of data flushes through. local_socket_sfd_.Release(); } else { PLOG(ERROR) << "failed to write fd " << fd << " to SHELL_EXIT_NOTIFY_FD (" << SHELL_EXIT_NOTIFY_FD << ") for pid " << pid_; } } } } // namespace int StartSubprocess(const char *name, SubprocessType type) { D("starting %s subprocess: '%s'", type == SubprocessType::kRaw ? "raw" : "PTY", name); Subprocess* subprocess = new Subprocess(name, type); if (!subprocess) { LOG(ERROR) << "failed to allocate new subprocess"; return -1; } if (!subprocess->ForkAndExec()) { LOG(ERROR) << "failed to start subprocess"; delete subprocess; return -1; } D("subprocess creation successful: local_socket_fd=%d, pid=%d", subprocess->local_socket_fd(), subprocess->pid()); return subprocess->local_socket_fd(); } #endif // !ADB_HOST Loading
adb/Android.mk +11 −1 Original line number Diff line number Diff line Loading @@ -131,6 +131,8 @@ LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS) LOCAL_SRC_FILES := \ $(LIBADB_TEST_SRCS) \ $(LIBADB_TEST_linux_SRCS) \ shell_service_protocol.cpp \ shell_service_protocol_test.cpp \ LOCAL_SANITIZE := $(adb_target_sanitize) LOCAL_STATIC_LIBRARIES := libadbd Loading @@ -145,7 +147,12 @@ LOCAL_MODULE := adb_test LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS) LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS) LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS) LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp LOCAL_SRC_FILES := \ $(LIBADB_TEST_SRCS) \ services.cpp \ shell_service_protocol.cpp \ shell_service_protocol_test.cpp \ LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS) LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS) LOCAL_SANITIZE := $(adb_host_sanitize) Loading Loading @@ -201,6 +208,7 @@ LOCAL_SRC_FILES := \ adb_client.cpp \ services.cpp \ file_sync_client.cpp \ shell_service_protocol.cpp \ LOCAL_CFLAGS += \ $(ADB_COMMON_CFLAGS) \ Loading Loading @@ -248,6 +256,8 @@ LOCAL_SRC_FILES := \ framebuffer_service.cpp \ remount_service.cpp \ set_verity_enable_state_service.cpp \ shell_service.cpp \ shell_service_protocol.cpp \ LOCAL_CFLAGS := \ $(ADB_COMMON_CFLAGS) \ Loading
adb/adb.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -176,7 +176,8 @@ static void setup_trace_mask() { {"transport", TRACE_TRANSPORT}, {"jdwp", TRACE_JDWP}, {"services", TRACE_SERVICES}, {"auth", TRACE_AUTH}}; {"auth", TRACE_AUTH}, {"shell", TRACE_SHELL}}; std::vector<std::string> elements = android::base::Split(trace_setting, " "); for (const auto& elem : elements) { Loading
adb/adb_trace.h +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ enum AdbTrace { TRACE_SERVICES, TRACE_AUTH, TRACE_FDEVENT, TRACE_SHELL }; extern int adb_trace_mask; Loading
adb/services.cpp +8 −222 Original line number Diff line number Diff line Loading @@ -24,11 +24,6 @@ #include <stdlib.h> #include <string.h> #if !ADB_HOST #include <pty.h> #include <termios.h> #endif #ifndef _WIN32 #include <netdb.h> #include <netinet/in.h> Loading @@ -51,6 +46,7 @@ #include "adb_utils.h" #include "file_sync_service.h" #include "remount_service.h" #include "shell_service.h" #include "transport.h" struct stinfo { Loading @@ -59,11 +55,6 @@ struct stinfo { void *cookie; }; enum class SubprocessType { kPty, kRaw, }; void *service_bootstrap_func(void *x) { stinfo* sti = reinterpret_cast<stinfo*>(x); Loading Loading @@ -234,211 +225,6 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) return s[0]; } #if !ADB_HOST static void init_subproc_child() { setsid(); // Set OOM score adjustment to prevent killing int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC); if (fd >= 0) { adb_write(fd, "0", 1); adb_close(fd); } else { D("adb: unable to update oom_score_adj"); } } #if !ADB_HOST static int create_subproc_pty(const char* cmd, const char* arg0, const char* arg1, pid_t* pid) { D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)", cmd, arg0, arg1); char pts_name[PATH_MAX]; int ptm; *pid = forkpty(&ptm, pts_name, nullptr, nullptr); if (*pid == -1) { printf("- fork failed: %s -\n", strerror(errno)); unix_close(ptm); return -1; } if (*pid == 0) { init_subproc_child(); int pts = unix_open(pts_name, O_RDWR | O_CLOEXEC); if (pts == -1) { fprintf(stderr, "child failed to open pseudo-term slave %s: %s\n", pts_name, strerror(errno)); unix_close(ptm); exit(-1); } // arg0 is "-c" in batch mode and "-" in interactive mode. if (strcmp(arg0, "-c") == 0) { termios tattr; if (tcgetattr(pts, &tattr) == -1) { fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); unix_close(pts); unix_close(ptm); exit(-1); } cfmakeraw(&tattr); if (tcsetattr(pts, TCSADRAIN, &tattr) == -1) { fprintf(stderr, "tcsetattr failed: %s\n", strerror(errno)); unix_close(pts); unix_close(ptm); exit(-1); } } dup2(pts, STDIN_FILENO); dup2(pts, STDOUT_FILENO); dup2(pts, STDERR_FILENO); unix_close(pts); unix_close(ptm); execl(cmd, cmd, arg0, arg1, nullptr); fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", cmd, strerror(errno), errno); exit(-1); } else { return ptm; } } #endif // !ADB_HOST static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) { D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)", cmd, arg0, arg1); #if defined(_WIN32) fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); return -1; #else // 0 is parent socket, 1 is child socket int sv[2]; if (adb_socketpair(sv) < 0) { printf("[ cannot create socket pair - %s ]\n", strerror(errno)); return -1; } D("socketpair: (%d,%d)", sv[0], sv[1]); *pid = fork(); if (*pid < 0) { printf("- fork failed: %s -\n", strerror(errno)); adb_close(sv[0]); adb_close(sv[1]); return -1; } if (*pid == 0) { adb_close(sv[0]); init_subproc_child(); dup2(sv[1], STDIN_FILENO); dup2(sv[1], STDOUT_FILENO); dup2(sv[1], STDERR_FILENO); adb_close(sv[1]); execl(cmd, cmd, arg0, arg1, NULL); fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", cmd, strerror(errno), errno); exit(-1); } else { adb_close(sv[1]); return sv[0]; } #endif /* !defined(_WIN32) */ } #endif /* !ABD_HOST */ #if ADB_HOST #define SHELL_COMMAND "/bin/sh" #else #define SHELL_COMMAND "/system/bin/sh" #endif #if !ADB_HOST static void subproc_waiter_service(int fd, void *cookie) { pid_t pid = (pid_t) (uintptr_t) cookie; D("entered. fd=%d of pid=%d", fd, pid); while (true) { int status; pid_t p = waitpid(pid, &status, 0); if (p == pid) { D("fd=%d, post waitpid(pid=%d) status=%04x", fd, p, status); if (WIFSIGNALED(status)) { D("*** Killed by signal %d", WTERMSIG(status)); break; } else if (!WIFEXITED(status)) { D("*** Didn't exit!!. status %d", status); break; } else if (WEXITSTATUS(status) >= 0) { D("*** Exit code %d", WEXITSTATUS(status)); break; } } } D("shell exited fd=%d of pid=%d err=%d", fd, pid, errno); if (SHELL_EXIT_NOTIFY_FD >=0) { int res; res = WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)) ? 0 : -1; D("notified shell exit via fd=%d for pid=%d res=%d errno=%d", SHELL_EXIT_NOTIFY_FD, pid, res, errno); } } // Starts a subprocess and spawns a thread to wait for the subprocess to finish // and trigger the necessary cleanup. // // |name| is the command to execute in the subprocess; empty string will start // an interactive session. // |type| selects between a PTY or raw subprocess. // // Returns an open file descriptor tied to the subprocess stdin/stdout/stderr. static int create_subproc_thread(const char *name, SubprocessType type) { const char *arg0, *arg1; if (*name == '\0') { arg0 = "-"; arg1 = nullptr; } else { arg0 = "-c"; arg1 = name; } pid_t pid = -1; int ret_fd; if (type == SubprocessType::kPty) { ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid); } else { ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid); } D("create_subproc ret_fd=%d pid=%d", ret_fd, pid); stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo))); if(sti == 0) fatal("cannot allocate stinfo"); sti->func = subproc_waiter_service; sti->cookie = (void*) (uintptr_t) pid; sti->fd = ret_fd; if (!adb_thread_create(service_bootstrap_func, sti)) { free(sti); adb_close(ret_fd); fprintf(stderr, "cannot create service thread\n"); return -1; } D("service thread started, fd=%d pid=%d", ret_fd, pid); return ret_fd; } #endif int service_to_fd(const char* name) { int ret = -1; Loading Loading @@ -483,13 +269,13 @@ int service_to_fd(const char* name) { const char* args = name + 6; if (*args) { // Non-interactive session uses a raw subprocess. ret = create_subproc_thread(args, SubprocessType::kRaw); ret = StartSubprocess(args, SubprocessType::kRaw); } else { // Interactive session uses a PTY subprocess. ret = create_subproc_thread(args, SubprocessType::kPty); ret = StartSubprocess(args, SubprocessType::kPty); } } else if(!strncmp(name, "exec:", 5)) { ret = create_subproc_thread(name + 5, SubprocessType::kRaw); ret = StartSubprocess(name + 5, SubprocessType::kRaw); } else if(!strncmp(name, "sync:", 5)) { ret = create_service_thread(file_sync_service, NULL); } else if(!strncmp(name, "remount:", 8)) { Loading @@ -503,11 +289,11 @@ int service_to_fd(const char* name) { } else if(!strncmp(name, "unroot:", 7)) { ret = create_service_thread(restart_unroot_service, NULL); } else if(!strncmp(name, "backup:", 7)) { ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s", ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(), SubprocessType::kRaw); } else if(!strncmp(name, "restore:", 8)) { ret = create_subproc_thread("/system/bin/bu restore", SubprocessType::kRaw); ret = StartSubprocess("/system/bin/bu restore", SubprocessType::kRaw); } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) != 1) { Loading
adb/shell_service.cpp 0 → 100644 +329 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define TRACE_TAG TRACE_SHELL #include "shell_service.h" #if !ADB_HOST #include <errno.h> #include <pty.h> #include <termios.h> #include <base/logging.h> #include <base/stringprintf.h> #include <paths.h> #include "adb.h" #include "adb_io.h" #include "adb_trace.h" #include "sysdeps.h" namespace { void init_subproc_child() { setsid(); // Set OOM score adjustment to prevent killing int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC); if (fd >= 0) { adb_write(fd, "0", 1); adb_close(fd); } else { D("adb: unable to update oom_score_adj"); } } // Reads from |fd| until close or failure. std::string ReadAll(int fd) { char buffer[512]; std::string received; while (1) { int bytes = adb_read(fd, buffer, sizeof(buffer)); if (bytes <= 0) { break; } received.append(buffer, bytes); } return received; } // Helper to automatically close an FD when it goes out of scope. class ScopedFd { public: ScopedFd() {} ~ScopedFd() { Reset(); } void Reset(int fd=-1) { if (fd != fd_) { if (valid()) { adb_close(fd_); } fd_ = fd; } } int Release() { int temp = fd_; fd_ = -1; return temp; } bool valid() const { return fd_ >= 0; } int fd() const { return fd_; } private: int fd_ = -1; DISALLOW_COPY_AND_ASSIGN(ScopedFd); }; // Creates a socketpair and saves the endpoints to |fd1| and |fd2|. bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) { int sockets[2]; if (adb_socketpair(sockets) < 0) { PLOG(ERROR) << "cannot create socket pair"; return false; } fd1->Reset(sockets[0]); fd2->Reset(sockets[1]); return true; } class Subprocess { public: Subprocess(const std::string& command, SubprocessType type); ~Subprocess(); const std::string& command() const { return command_; } bool is_interactive() const { return command_.empty(); } int local_socket_fd() const { return local_socket_sfd_.fd(); } pid_t pid() const { return pid_; } // Sets up FDs, forks a subprocess, starts the subprocess manager thread, // and exec's the child. Returns false on failure. bool ForkAndExec(); private: // Opens the file at |pts_name|. int OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd); static void* ThreadHandler(void* userdata); void WaitForExit(); const std::string command_; SubprocessType type_; pid_t pid_ = -1; ScopedFd local_socket_sfd_; DISALLOW_COPY_AND_ASSIGN(Subprocess); }; Subprocess::Subprocess(const std::string& command, SubprocessType type) : command_(command), type_(type) { } Subprocess::~Subprocess() { } bool Subprocess::ForkAndExec() { ScopedFd parent_sfd, child_sfd, parent_error_sfd, child_error_sfd; char pts_name[PATH_MAX]; // Create a socketpair for the fork() child to report any errors back to // the parent. Since we use threads, logging directly from the child could // create a race condition. if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) { LOG(ERROR) << "failed to create pipe for subprocess error reporting"; } if (type_ == SubprocessType::kPty) { int fd; pid_ = forkpty(&fd, pts_name, nullptr, nullptr); parent_sfd.Reset(fd); } else { if (!CreateSocketpair(&parent_sfd, &child_sfd)) { return false; } pid_ = fork(); } if (pid_ == -1) { PLOG(ERROR) << "fork failed"; return false; } if (pid_ == 0) { // Subprocess child. init_subproc_child(); if (type_ == SubprocessType::kPty) { child_sfd.Reset(OpenPtyChildFd(pts_name, &child_error_sfd)); } dup2(child_sfd.fd(), STDIN_FILENO); dup2(child_sfd.fd(), STDOUT_FILENO); dup2(child_sfd.fd(), STDERR_FILENO); // exec doesn't trigger destructors, close the FDs manually. parent_sfd.Reset(); child_sfd.Reset(); parent_error_sfd.Reset(); close_on_exec(child_error_sfd.fd()); if (is_interactive()) { execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr); } else { execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr); } WriteFdExactly(child_error_sfd.fd(), "exec '" _PATH_BSHELL "' failed"); child_error_sfd.Reset(); exit(-1); } // Subprocess parent. D("subprocess parent: subprocess FD = %d", parent_sfd.fd()); // Wait to make sure the subprocess exec'd without error. child_error_sfd.Reset(); std::string error_message = ReadAll(parent_error_sfd.fd()); if (!error_message.empty()) { LOG(ERROR) << error_message; return false; } local_socket_sfd_.Reset(parent_sfd.Release()); if (!adb_thread_create(ThreadHandler, this)) { PLOG(ERROR) << "failed to create subprocess thread"; return false; } return true; } int Subprocess::OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd) { int child_fd = adb_open(pts_name, O_RDWR | O_CLOEXEC); if (child_fd == -1) { // Don't use WriteFdFmt; since we're in the fork() child we don't want // to allocate any heap memory to avoid race conditions. const char* messages[] = {"child failed to open pseudo-term slave ", pts_name, ": ", strerror(errno)}; for (const char* message : messages) { WriteFdExactly(error_sfd->fd(), message); } exit(-1); } if (!is_interactive()) { termios tattr; if (tcgetattr(child_fd, &tattr) == -1) { WriteFdExactly(error_sfd->fd(), "tcgetattr failed"); exit(-1); } cfmakeraw(&tattr); if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) { WriteFdExactly(error_sfd->fd(), "tcsetattr failed"); exit(-1); } } return child_fd; } void* Subprocess::ThreadHandler(void* userdata) { Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata); adb_thread_setname(android::base::StringPrintf( "shell srvc %d", subprocess->local_socket_fd())); subprocess->WaitForExit(); D("deleting Subprocess"); delete subprocess; return nullptr; } void Subprocess::WaitForExit() { D("waiting for pid %d", pid_); while (true) { int status; if (pid_ == waitpid(pid_, &status, 0)) { D("post waitpid (pid=%d) status=%04x", pid_, status); if (WIFSIGNALED(status)) { D("subprocess killed by signal %d", WTERMSIG(status)); break; } else if (!WIFEXITED(status)) { D("subprocess didn't exit"); break; } else if (WEXITSTATUS(status) >= 0) { D("subprocess exit code = %d", WEXITSTATUS(status)); break; } } } // Pass the local socket FD to the shell cleanup fdevent. if (SHELL_EXIT_NOTIFY_FD >= 0) { int fd = local_socket_sfd_.fd(); if (WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd))) { D("passed fd %d to SHELL_EXIT_NOTIFY_FD (%d) for pid %d", fd, SHELL_EXIT_NOTIFY_FD, pid_); // The shell exit fdevent now owns the FD and will close it once // the last bit of data flushes through. local_socket_sfd_.Release(); } else { PLOG(ERROR) << "failed to write fd " << fd << " to SHELL_EXIT_NOTIFY_FD (" << SHELL_EXIT_NOTIFY_FD << ") for pid " << pid_; } } } } // namespace int StartSubprocess(const char *name, SubprocessType type) { D("starting %s subprocess: '%s'", type == SubprocessType::kRaw ? "raw" : "PTY", name); Subprocess* subprocess = new Subprocess(name, type); if (!subprocess) { LOG(ERROR) << "failed to allocate new subprocess"; return -1; } if (!subprocess->ForkAndExec()) { LOG(ERROR) << "failed to start subprocess"; delete subprocess; return -1; } D("subprocess creation successful: local_socket_fd=%d, pid=%d", subprocess->local_socket_fd(), subprocess->pid()); return subprocess->local_socket_fd(); } #endif // !ADB_HOST