diff options
| author | Elliott Hughes <enh@google.com> | 2015-11-04 13:36:32 -0800 |
|---|---|---|
| committer | Elliott Hughes <enh@google.com> | 2015-11-09 13:57:47 -0800 |
| commit | 28416d6d33267f92768116e269966d61eac01db9 (patch) | |
| tree | 8b360fbbca77635aeb3e88e49dda3d6f75de2197 | |
| parent | 892f0e93007dc912f18a09fd281c73adf762277a (diff) | |
| download | core-28416d6d33267f92768116e269966d61eac01db9.tar.gz core-28416d6d33267f92768116e269966d61eac01db9.tar.bz2 core-28416d6d33267f92768116e269966d61eac01db9.zip | |
Fix adb shell escape handling.
Start of input is, by definition, the start of a line. Give the states
names to make the error clearer. We should also swallow the escape
character until/unless it's proven not to have been part of an escape
sequence. Finally, implement an ssh(1)-like -e option so the user can
choose -- or, more importantly, disable -- escape character handling.
Change-Id: I28a91db47e15f3e7de96303c0b9a98d302b9c92e
| -rw-r--r-- | adb/commandline.cpp | 71 |
1 files changed, 45 insertions, 26 deletions
diff --git a/adb/commandline.cpp b/adb/commandline.cpp index 6970a1840..dfa5ef212 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp @@ -467,6 +467,7 @@ struct StdinReadArgs { int stdin_fd, write_fd; bool raw_stdin; std::unique_ptr<ShellProtocol> protocol; + char escape_char; }; // Loops to read from stdin and push the data to the given FD. @@ -474,7 +475,6 @@ struct StdinReadArgs { // will take ownership of the object and delete it when finished. static void* stdin_read_thread_loop(void* x) { std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x)); - int state = 0; #if !defined(_WIN32) // Mask SIGTTIN in case we're in a backgrounded process. @@ -496,7 +496,7 @@ static void* stdin_read_thread_loop(void* x) { // Set up the initial window size. send_window_size_change(args->stdin_fd, args->protocol); - char raw_buffer[1024]; + char raw_buffer[BUFSIZ]; char* buffer_ptr = raw_buffer; size_t buffer_size = sizeof(raw_buffer); if (args->protocol != nullptr) { @@ -504,6 +504,14 @@ static void* stdin_read_thread_loop(void* x) { buffer_size = args->protocol->data_capacity(); } + // If we need to parse escape sequences, make life easy. + if (args->raw_stdin && args->escape_char != '\0') { + buffer_size = 1; + } + + enum EscapeState { kMidFlow, kStartOfLine, kInEscape }; + EscapeState state = kStartOfLine; + while (true) { // Use unix_read() rather than adb_read() for stdin. D("stdin_read_thread_loop(): pre unix_read(fdi=%d,...)", args->stdin_fd); @@ -529,36 +537,36 @@ static void* stdin_read_thread_loop(void* x) { } break; } - // If we made stdin raw, check input for the "~." escape sequence. In + // If we made stdin raw, check input for escape sequences. In // this situation signals like Ctrl+C are sent remotely rather than // interpreted locally so this provides an emergency out if the remote // process starts ignoring the signal. SSH also does this, see the // "escape characters" section on the ssh man page for more info. - if (args->raw_stdin) { - for (int n = 0; n < r; n++) { - switch (buffer_ptr[n]) { - case '\n': - state = 1; - break; - case '\r': - state = 1; - break; - case '~': - if (state == 1) { - state++; - } else { - state = 0; - } - break; - case '.': - if (state == 2) { - fprintf(stderr,"\r\n* disconnect *\r\n"); + if (args->raw_stdin && args->escape_char != '\0') { + char ch = buffer_ptr[0]; + if (ch == args->escape_char) { + if (state == kStartOfLine) { + state = kInEscape; + // Swallow the escape character. + continue; + } else { + state = kMidFlow; + } + } else { + if (state == kInEscape) { + if (ch == '.') { + fprintf(stderr,"\r\n[ disconnected ]\r\n"); stdin_raw_restore(); exit(0); + } else { + // We swallowed an escape character that wasn't part of + // a valid escape sequence; time to cough it up. + buffer_ptr[0] = args->escape_char; + buffer_ptr[1] = ch; + ++r; } - default: - state = 0; } + state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow; } } if (args->protocol) { @@ -603,6 +611,7 @@ static std::string ShellServiceString(bool use_shell_protocol, // 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); @@ -628,6 +637,7 @@ static int RemoteShell(bool use_shell_protocol, const std::string& type_arg, args->stdin_fd = STDIN_FILENO; args->write_fd = fd; args->raw_stdin = raw_stdin; + args->escape_char = escape_char; if (use_shell_protocol) { args->protocol.reset(new ShellProtocol(args->write_fd)); } @@ -689,8 +699,17 @@ static int adb_shell(int argc, const char** argv, --argc; ++argv; int t_arg_count = 0; + char escape_char = '~'; while (argc) { - if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) { + if (!strcmp(argv[0], "-e")) { + if (argc < 2 || !(strlen(argv[1]) == 1 || strcmp(argv[1], "none") == 0)) { + fprintf(stderr, "error: -e requires a single-character argument or 'none'\n"); + return 1; + } + escape_char = (strcmp(argv[1], "none") == 0) ? 0 : argv[1][0]; + argc -= 2; + argv += 2; + } else if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) { if (!CanUseFeature(features, kFeatureShell2)) { fprintf(stderr, "error: target doesn't support PTY args -Tt\n"); return 1; @@ -746,7 +765,7 @@ static int adb_shell(int argc, const char** argv, command = android::base::Join(std::vector<const char*>(argv, argv + argc), ' '); } - return RemoteShell(use_shell_protocol, shell_type_arg, command); + return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command); } static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz, |
