Kill adb's ScopedFd for unique_fd.

It's the adb unique_fd, but at least it's the same API...

Change-Id: If5d6642457e145c450374fde6f1358e5ac386a96
This commit is contained in:
Elliott Hughes 2016-05-27 17:51:24 -07:00
parent b42e4a6b63
commit 2ce86e527b
2 changed files with 80 additions and 124 deletions

View File

@ -66,43 +66,4 @@ struct AdbCloser {
using unique_fd = android::base::unique_fd_impl<AdbCloser>;
// TODO: switch remaining users over to unique_fd...
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);
};
#endif

View File

@ -136,14 +136,14 @@ std::string ReadAll(int fd) {
}
// Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) {
bool CreateSocketpair(unique_fd* fd1, unique_fd* 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]);
fd1->reset(sockets[0]);
fd2->reset(sockets[1]);
return true;
}
@ -155,7 +155,7 @@ class Subprocess {
const std::string& command() const { return command_; }
int local_socket_fd() const { return local_socket_sfd_.fd(); }
int local_socket_fd() const { return local_socket_sfd_; }
pid_t pid() const { return pid_; }
@ -165,19 +165,19 @@ class Subprocess {
private:
// Opens the file at |pts_name|.
int OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd);
int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
static void ThreadHandler(void* userdata);
void PassDataStreams();
void WaitForExit();
ScopedFd* SelectLoop(fd_set* master_read_set_ptr,
fd_set* master_write_set_ptr);
unique_fd* SelectLoop(fd_set* master_read_set_ptr,
fd_set* master_write_set_ptr);
// Input/output stream handlers. Success returns nullptr, failure returns
// a pointer to the failed FD.
ScopedFd* PassInput();
ScopedFd* PassOutput(ScopedFd* sfd, ShellProtocol::Id id);
unique_fd* PassInput();
unique_fd* PassOutput(unique_fd* sfd, ShellProtocol::Id id);
const std::string command_;
const std::string terminal_type_;
@ -185,10 +185,10 @@ class Subprocess {
SubprocessType type_;
SubprocessProtocol protocol_;
pid_t pid_ = -1;
ScopedFd local_socket_sfd_;
unique_fd local_socket_sfd_;
// Shell protocol variables.
ScopedFd stdinout_sfd_, stderr_sfd_, protocol_sfd_;
unique_fd stdinout_sfd_, stderr_sfd_, protocol_sfd_;
std::unique_ptr<ShellProtocol> input_, output_;
size_t input_bytes_left_ = 0;
@ -220,8 +220,8 @@ Subprocess::~Subprocess() {
}
bool Subprocess::ForkAndExec(std::string* error) {
ScopedFd child_stdinout_sfd, child_stderr_sfd;
ScopedFd parent_error_sfd, child_error_sfd;
unique_fd child_stdinout_sfd, child_stderr_sfd;
unique_fd parent_error_sfd, child_error_sfd;
char pts_name[PATH_MAX];
if (command_.empty()) {
@ -285,7 +285,7 @@ bool Subprocess::ForkAndExec(std::string* error) {
int fd;
pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
if (pid_ > 0) {
stdinout_sfd_.Reset(fd);
stdinout_sfd_.reset(fd);
}
} else {
if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
@ -313,40 +313,39 @@ bool Subprocess::ForkAndExec(std::string* error) {
init_subproc_child();
if (type_ == SubprocessType::kPty) {
child_stdinout_sfd.Reset(OpenPtyChildFd(pts_name, &child_error_sfd));
child_stdinout_sfd.reset(OpenPtyChildFd(pts_name, &child_error_sfd));
}
dup2(child_stdinout_sfd.fd(), STDIN_FILENO);
dup2(child_stdinout_sfd.fd(), STDOUT_FILENO);
dup2(child_stderr_sfd.valid() ? child_stderr_sfd.fd() : child_stdinout_sfd.fd(),
STDERR_FILENO);
dup2(child_stdinout_sfd, STDIN_FILENO);
dup2(child_stdinout_sfd, STDOUT_FILENO);
dup2(child_stderr_sfd != -1 ? child_stderr_sfd : child_stdinout_sfd, STDERR_FILENO);
// exec doesn't trigger destructors, close the FDs manually.
stdinout_sfd_.Reset();
stderr_sfd_.Reset();
child_stdinout_sfd.Reset();
child_stderr_sfd.Reset();
parent_error_sfd.Reset();
close_on_exec(child_error_sfd.fd());
stdinout_sfd_.reset(-1);
stderr_sfd_.reset(-1);
child_stdinout_sfd.reset(-1);
child_stderr_sfd.reset(-1);
parent_error_sfd.reset(-1);
close_on_exec(child_error_sfd);
if (command_.empty()) {
execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
} else {
execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
}
WriteFdExactly(child_error_sfd.fd(), "exec '" _PATH_BSHELL "' failed: ");
WriteFdExactly(child_error_sfd.fd(), strerror(errno));
child_error_sfd.Reset();
WriteFdExactly(child_error_sfd, "exec '" _PATH_BSHELL "' failed: ");
WriteFdExactly(child_error_sfd, strerror(errno));
child_error_sfd.reset(-1);
_Exit(1);
}
// Subprocess parent.
D("subprocess parent: stdin/stdout FD = %d, stderr FD = %d",
stdinout_sfd_.fd(), stderr_sfd_.fd());
stdinout_sfd_.get(), stderr_sfd_.get());
// Wait to make sure the subprocess exec'd without error.
child_error_sfd.Reset();
std::string error_message = ReadAll(parent_error_sfd.fd());
child_error_sfd.reset(-1);
std::string error_message = ReadAll(parent_error_sfd);
if (!error_message.empty()) {
*error = error_message;
return false;
@ -356,7 +355,7 @@ bool Subprocess::ForkAndExec(std::string* error) {
if (protocol_ == SubprocessProtocol::kNone) {
// No protocol: all streams pass through the stdinout FD and hook
// directly into the local socket for raw data transfer.
local_socket_sfd_.Reset(stdinout_sfd_.Release());
local_socket_sfd_.reset(stdinout_sfd_.release());
} else {
// Shell protocol: create another socketpair to intercept data.
if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
@ -365,10 +364,10 @@ bool Subprocess::ForkAndExec(std::string* error) {
kill(pid_, SIGKILL);
return false;
}
D("protocol FD = %d", protocol_sfd_.fd());
D("protocol FD = %d", protocol_sfd_.get());
input_.reset(new ShellProtocol(protocol_sfd_.fd()));
output_.reset(new ShellProtocol(protocol_sfd_.fd()));
input_.reset(new ShellProtocol(protocol_sfd_));
output_.reset(new ShellProtocol(protocol_sfd_));
if (!input_ || !output_) {
*error = "failed to allocate shell protocol objects";
kill(pid_, SIGKILL);
@ -379,7 +378,7 @@ bool Subprocess::ForkAndExec(std::string* error) {
// 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_.fd(), stderr_sfd_.fd()}) {
for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
if (fd >= 0) {
if (!set_file_block_mode(fd, false)) {
*error = android::base::StringPrintf(
@ -402,7 +401,7 @@ bool Subprocess::ForkAndExec(std::string* error) {
return true;
}
int Subprocess::OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd) {
int Subprocess::OpenPtyChildFd(const char* pts_name, unique_fd* 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
@ -410,7 +409,7 @@ int Subprocess::OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd) {
const char* messages[] = {"child failed to open pseudo-term slave ",
pts_name, ": ", strerror(errno)};
for (const char* message : messages) {
WriteFdExactly(error_sfd->fd(), message);
WriteFdExactly(*error_sfd, message);
}
abort();
}
@ -419,16 +418,16 @@ int Subprocess::OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd) {
termios tattr;
if (tcgetattr(child_fd, &tattr) == -1) {
int saved_errno = errno;
WriteFdExactly(error_sfd->fd(), "tcgetattr failed: ");
WriteFdExactly(error_sfd->fd(), strerror(saved_errno));
WriteFdExactly(*error_sfd, "tcgetattr failed: ");
WriteFdExactly(*error_sfd, strerror(saved_errno));
abort();
}
cfmakeraw(&tattr);
if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
int saved_errno = errno;
WriteFdExactly(error_sfd->fd(), "tcsetattr failed: ");
WriteFdExactly(error_sfd->fd(), strerror(saved_errno));
WriteFdExactly(*error_sfd, "tcsetattr failed: ");
WriteFdExactly(*error_sfd, strerror(saved_errno));
abort();
}
}
@ -449,7 +448,7 @@ void Subprocess::ThreadHandler(void* userdata) {
}
void Subprocess::PassDataStreams() {
if (!protocol_sfd_.valid()) {
if (protocol_sfd_ == -1) {
return;
}
@ -457,21 +456,20 @@ void Subprocess::PassDataStreams() {
fd_set master_read_set, master_write_set;
FD_ZERO(&master_read_set);
FD_ZERO(&master_write_set);
for (ScopedFd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
if (sfd->valid()) {
FD_SET(sfd->fd(), &master_read_set);
for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
if (*sfd != -1) {
FD_SET(*sfd, &master_read_set);
}
}
// Pass data until the protocol FD or both the subprocess pipes die, at
// which point we can't pass any more data.
while (protocol_sfd_.valid() &&
(stdinout_sfd_.valid() || stderr_sfd_.valid())) {
ScopedFd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
while (protocol_sfd_ != -1 && (stdinout_sfd_ != -1 || stderr_sfd_ != -1)) {
unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
if (dead_sfd) {
D("closing FD %d", dead_sfd->fd());
FD_CLR(dead_sfd->fd(), &master_read_set);
FD_CLR(dead_sfd->fd(), &master_write_set);
D("closing FD %d", dead_sfd->get());
FD_CLR(*dead_sfd, &master_read_set);
FD_CLR(*dead_sfd, &master_write_set);
if (dead_sfd == &protocol_sfd_) {
// Using SIGHUP is a decent general way to indicate that the
// controlling process is going away. If specific signals are
@ -480,25 +478,24 @@ void Subprocess::PassDataStreams() {
D("protocol FD died, sending SIGHUP to pid %d", pid_);
kill(pid_, SIGHUP);
}
dead_sfd->Reset();
dead_sfd->reset(-1);
}
}
}
namespace {
inline bool ValidAndInSet(const ScopedFd& sfd, fd_set* set) {
return sfd.valid() && FD_ISSET(sfd.fd(), set);
inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
return sfd != -1 && FD_ISSET(sfd, set);
}
} // namespace
ScopedFd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
fd_set* master_write_set_ptr) {
unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
fd_set* master_write_set_ptr) {
fd_set read_set, write_set;
int select_n = std::max(std::max(protocol_sfd_.fd(), stdinout_sfd_.fd()),
stderr_sfd_.fd()) + 1;
ScopedFd* dead_sfd = nullptr;
int select_n = std::max(std::max(protocol_sfd_, stdinout_sfd_), stderr_sfd_) + 1;
unique_fd* dead_sfd = nullptr;
// Keep calling select() and passing data until an FD closes/errors.
while (!dead_sfd) {
@ -509,8 +506,8 @@ ScopedFd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
continue;
} else {
PLOG(ERROR) << "select failed, closing subprocess pipes";
stdinout_sfd_.Reset();
stderr_sfd_.Reset();
stdinout_sfd_.reset(-1);
stderr_sfd_.reset(-1);
return nullptr;
}
}
@ -530,8 +527,8 @@ ScopedFd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
dead_sfd = PassInput();
// If we didn't finish writing, block on stdin write.
if (input_bytes_left_) {
FD_CLR(protocol_sfd_.fd(), master_read_set_ptr);
FD_SET(stdinout_sfd_.fd(), master_write_set_ptr);
FD_CLR(protocol_sfd_, master_read_set_ptr);
FD_SET(stdinout_sfd_, master_write_set_ptr);
}
}
@ -540,8 +537,8 @@ ScopedFd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
dead_sfd = PassInput();
// If we finished writing, go back to blocking on protocol read.
if (!input_bytes_left_) {
FD_SET(protocol_sfd_.fd(), master_read_set_ptr);
FD_CLR(stdinout_sfd_.fd(), master_write_set_ptr);
FD_SET(protocol_sfd_, master_read_set_ptr);
FD_CLR(stdinout_sfd_, master_write_set_ptr);
}
}
} // while (!dead_sfd)
@ -549,19 +546,18 @@ ScopedFd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
return dead_sfd;
}
ScopedFd* Subprocess::PassInput() {
unique_fd* Subprocess::PassInput() {
// Only read a new packet if we've finished writing the last one.
if (!input_bytes_left_) {
if (!input_->Read()) {
// Read() uses ReadFdExactly() which sets errno to 0 on EOF.
if (errno != 0) {
PLOG(ERROR) << "error reading protocol FD "
<< protocol_sfd_.fd();
PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
}
return &protocol_sfd_;
}
if (stdinout_sfd_.valid()) {
if (stdinout_sfd_ != -1) {
switch (input_->id()) {
case ShellProtocol::kIdWindowSizeChange:
int rows, cols, x_pixels, y_pixels;
@ -572,7 +568,7 @@ ScopedFd* Subprocess::PassInput() {
ws.ws_col = cols;
ws.ws_xpixel = x_pixels;
ws.ws_ypixel = y_pixels;
ioctl(stdinout_sfd_.fd(), TIOCSWINSZ, &ws);
ioctl(stdinout_sfd_, TIOCSWINSZ, &ws);
}
break;
case ShellProtocol::kIdStdin:
@ -580,11 +576,11 @@ ScopedFd* Subprocess::PassInput() {
break;
case ShellProtocol::kIdCloseStdin:
if (type_ == SubprocessType::kRaw) {
if (adb_shutdown(stdinout_sfd_.fd(), SHUT_WR) == 0) {
if (adb_shutdown(stdinout_sfd_, SHUT_WR) == 0) {
return nullptr;
}
PLOG(ERROR) << "failed to shutdown writes to FD "
<< stdinout_sfd_.fd();
<< stdinout_sfd_;
return &stdinout_sfd_;
} else {
// PTYs can't close just input, so rather than close the
@ -593,7 +589,7 @@ ScopedFd* Subprocess::PassInput() {
// non-interactively which is rare and unsupported.
// If necessary, the client can manually close the shell
// with `exit` or by killing the adb client process.
D("can't close input for PTY FD %d", stdinout_sfd_.fd());
D("can't close input for PTY FD %d", stdinout_sfd_.get());
}
break;
}
@ -602,11 +598,10 @@ ScopedFd* Subprocess::PassInput() {
if (input_bytes_left_ > 0) {
int index = input_->data_length() - input_bytes_left_;
int bytes = adb_write(stdinout_sfd_.fd(), input_->data() + index,
input_bytes_left_);
int bytes = adb_write(stdinout_sfd_, input_->data() + index, input_bytes_left_);
if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
if (bytes < 0) {
PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_.fd();
PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_;
}
// stdin is done, mark this packet as finished and we'll just start
// dumping any further data received from the protocol FD.
@ -620,20 +615,20 @@ ScopedFd* Subprocess::PassInput() {
return nullptr;
}
ScopedFd* Subprocess::PassOutput(ScopedFd* sfd, ShellProtocol::Id id) {
int bytes = adb_read(sfd->fd(), output_->data(), output_->data_capacity());
unique_fd* Subprocess::PassOutput(unique_fd* sfd, ShellProtocol::Id id) {
int bytes = adb_read(*sfd, output_->data(), output_->data_capacity());
if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
// read() returns EIO if a PTY closes; don't report this as an error,
// it just means the subprocess completed.
if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
PLOG(ERROR) << "error reading output FD " << sfd->fd();
PLOG(ERROR) << "error reading output FD " << *sfd;
}
return sfd;
}
if (bytes > 0 && !output_->Write(id, bytes)) {
if (errno != 0) {
PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_.fd();
PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
}
return &protocol_sfd_;
}
@ -665,25 +660,25 @@ void Subprocess::WaitForExit() {
}
// If we have an open protocol FD send an exit packet.
if (protocol_sfd_.valid()) {
if (protocol_sfd_ != -1) {
output_->data()[0] = exit_code;
if (output_->Write(ShellProtocol::kIdExit, 1)) {
D("wrote the exit code packet: %d", exit_code);
} else {
PLOG(ERROR) << "failed to write the exit code packet";
}
protocol_sfd_.Reset();
protocol_sfd_.reset(-1);
}
// Pass the local socket FD to the shell cleanup fdevent.
if (SHELL_EXIT_NOTIFY_FD >= 0) {
int fd = local_socket_sfd_.fd();
int fd = local_socket_sfd_;
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();
static_cast<void>(local_socket_sfd_.release());
} else {
PLOG(ERROR) << "failed to write fd " << fd
<< " to SHELL_EXIT_NOTIFY_FD (" << SHELL_EXIT_NOTIFY_FD