adb: fix subprocess termination for legacy shell.
http://r.android.com/166419 changed `adb shell` behavior to not
allocate a remote PTY for non-interactive commands, but adbd relied on
having a PTY to properly terminate the subprocess.
One impact of this is that when using older versions of adb or passing
the -x flag, `adb screenrecord` wasn't properly terminating and closing
out the video file.
This CL restores the old behavior for legacy shell connections: always
use a PTY, but put it in raw mode if the client is doing local PTY
input/output processing itself.
Bug: http://b/26742824
Change-Id: I9ee630c0ff0d2d6a0db367387af7123deea79676
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index e092dc4..b9a22dc 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -210,6 +210,7 @@
const std::string command_;
const std::string terminal_type_;
+ bool make_pty_raw_ = false;
SubprocessType type_;
SubprocessProtocol protocol_;
pid_t pid_ = -1;
@@ -229,6 +230,18 @@
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;
+ }
}
Subprocess::~Subprocess() {
@@ -408,6 +421,24 @@
exit(-1);
}
+ if (make_pty_raw_) {
+ 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));
+ exit(-1);
+ }
+
+ 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));
+ exit(-1);
+ }
+ }
+
return child_fd;
}