Merge "adb: allow multiple args to push/pull."
diff --git a/adb_trace.cpp b/adb_trace.cpp
index 04b82f6..9586f7c 100644
--- a/adb_trace.cpp
+++ b/adb_trace.cpp
@@ -157,7 +157,7 @@
// Don't open log file if no tracing, since this will block
// the crypto unmount of /data
if (!get_trace_setting().empty()) {
- if (isatty(STDOUT_FILENO) == 0) {
+ if (unix_isatty(STDOUT_FILENO) == 0) {
start_device_log();
}
}
diff --git a/commandline.cpp b/commandline.cpp
index bb9b836..2e46b96 100644
--- a/commandline.cpp
+++ b/commandline.cpp
@@ -440,7 +440,7 @@
adb_thread_setname("stdin reader");
-#ifndef __WIN32
+#ifndef _WIN32
// Mask SIGTTIN in case we're in a backgrounded process
sigset_t sigset;
sigemptyset(&sigset);
@@ -1393,12 +1393,12 @@
// things like `adb shell < my_script.sh` work as expected.
// Otherwise leave |shell_type_arg| blank which uses PTY for
// interactive shells and raw for non-interactive.
- if (!isatty(STDIN_FILENO)) {
+ if (!unix_isatty(STDIN_FILENO)) {
shell_type_arg = kShellServiceArgRaw;
}
} else if (t_arg_count == 1) {
// A single -t arg isn't enough to override implicit -T.
- if (!isatty(STDIN_FILENO)) {
+ if (!unix_isatty(STDIN_FILENO)) {
fprintf(stderr,
"Remote PTY will not be allocated because stdin is not a terminal.\n"
"Use multiple -t options to force remote PTY allocation.\n");
diff --git a/fdevent.cpp b/fdevent.cpp
index ddd15a2..4458c85 100644
--- a/fdevent.cpp
+++ b/fdevent.cpp
@@ -72,6 +72,19 @@
// That's why we don't need a lock for fdevent.
static std::unordered_map<int, PollNode> g_poll_node_map;
static std::list<fdevent*> g_pending_list;
+static bool main_thread_valid;
+static pthread_t main_thread;
+
+static void check_main_thread() {
+ if (main_thread_valid) {
+ CHECK_NE(0, pthread_equal(main_thread, pthread_self()));
+ }
+}
+
+static void set_main_thread() {
+ main_thread_valid = true;
+ main_thread = pthread_self();
+}
static std::string dump_fde(const fdevent* fde) {
std::string state;
@@ -101,6 +114,7 @@
fdevent *fdevent_create(int fd, fd_func func, void *arg)
{
+ check_main_thread();
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
if(fde == 0) return 0;
fdevent_install(fde, fd, func, arg);
@@ -110,6 +124,7 @@
void fdevent_destroy(fdevent *fde)
{
+ check_main_thread();
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
@@ -119,6 +134,7 @@
}
void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+ check_main_thread();
CHECK_GE(fd, 0);
memset(fde, 0, sizeof(fdevent));
fde->state = FDE_ACTIVE;
@@ -137,6 +153,7 @@
}
void fdevent_remove(fdevent* fde) {
+ check_main_thread();
D("fdevent_remove %s", dump_fde(fde).c_str());
if (fde->state & FDE_ACTIVE) {
g_poll_node_map.erase(fde->fd);
@@ -171,6 +188,7 @@
}
void fdevent_set(fdevent* fde, unsigned events) {
+ check_main_thread();
events &= FDE_EVENTMASK;
if ((fde->state & FDE_EVENTMASK) == events) {
return;
@@ -190,10 +208,12 @@
}
void fdevent_add(fdevent* fde, unsigned events) {
+ check_main_thread();
fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
}
void fdevent_del(fdevent* fde, unsigned events) {
+ check_main_thread();
fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
}
@@ -335,6 +355,7 @@
void fdevent_loop()
{
+ set_main_thread();
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
@@ -359,4 +380,5 @@
void fdevent_reset() {
g_poll_node_map.clear();
g_pending_list.clear();
+ main_thread_valid = false;
}
diff --git a/file_sync_client.cpp b/file_sync_client.cpp
index bd6a63f..7a60580 100644
--- a/file_sync_client.cpp
+++ b/file_sync_client.cpp
@@ -187,6 +187,16 @@
line_printer_.Print(s, LinePrinter::ELIDE);
}
+ void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ std::string s;
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&s, fmt, ap);
+ va_end(ap);
+
+ Print(s);
+ }
+
void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
std::string s = "adb: error: ";
@@ -308,7 +318,7 @@
bytes_copied += ret;
int percentage = static_cast<int>(bytes_copied * 100 / total_size);
- sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
+ sc.Printf("%s: %d%%", rpath, percentage);
}
adb_close(lfd);
@@ -431,7 +441,7 @@
bytes_copied += msg.data.size;
int percentage = static_cast<int>(bytes_copied * 100 / size);
- sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
+ sc.Printf("%s: %d%%", rpath, percentage);
}
adb_close(lfd);
@@ -609,11 +619,9 @@
free(ci);
}
- sc.Print(android::base::StringPrintf("%s: %d file%s pushed. %d file%s skipped.%s\n",
- rpath,
- pushed, (pushed == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s",
- sc.TransferRate().c_str()));
+ sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath, pushed,
+ (pushed == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+ sc.TransferRate().c_str());
return true;
}
@@ -701,7 +709,7 @@
ci->next = *filelist;
*filelist = ci;
} else {
- args->sc->Print(android::base::StringPrintf("skipping special file '%s'\n", name));
+ args->sc->Printf("skipping special file '%s'\n", name);
}
}
@@ -767,7 +775,7 @@
while (ci) {
copyinfo* next = ci->next;
if (ci->flag == 0) {
- sc.Print(android::base::StringPrintf("pull: %s -> %s", ci->src, ci->dst));
+ sc.Printf("pull: %s -> %s", ci->src, ci->dst);
if (!sync_recv(sc, ci->src, ci->dst)) {
return false;
}
@@ -783,11 +791,9 @@
ci = next;
}
- sc.Print(android::base::StringPrintf("%s: %d file%s pulled. %d file%s skipped.%s\n",
- rpath,
- pulled, (pulled == 1) ? "" : "s",
- skipped, (skipped == 1) ? "" : "s",
- sc.TransferRate().c_str()));
+ sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath, pulled,
+ (pulled == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+ sc.TransferRate().c_str());
return true;
}
diff --git a/line_printer.cpp b/line_printer.cpp
index 81b3f0a..aa332f7 100644
--- a/line_printer.cpp
+++ b/line_printer.cpp
@@ -46,7 +46,7 @@
LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
#ifndef _WIN32
const char* term = getenv("TERM");
- smart_terminal_ = isatty(1) && term && string(term) != "dumb";
+ smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
#else
// Disable output buffer. It'd be nice to use line buffering but
// MSDN says: "For some systems, [_IOLBF] provides line
diff --git a/services.cpp b/services.cpp
index e24b470..19a6726 100644
--- a/services.cpp
+++ b/services.cpp
@@ -184,15 +184,18 @@
adb_close(fd);
}
-void reverse_service(int fd, void* arg)
-{
- const char* command = reinterpret_cast<const char*>(arg);
-
- if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
- SendFail(fd, "not a reverse forwarding command");
+int reverse_service(const char* command) {
+ int s[2];
+ if (adb_socketpair(s)) {
+ PLOG(ERROR) << "cannot create service socket pair.";
+ return -1;
}
- free(arg);
- adb_close(fd);
+ VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+ if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+ SendFail(s[1], "not a reverse forwarding command");
+ }
+ adb_close(s[1]);
+ return s[0];
}
// Shell service string can look like:
@@ -335,15 +338,7 @@
} else if(!strncmp(name, "usb:", 4)) {
ret = create_service_thread(restart_usb_service, NULL);
} else if (!strncmp(name, "reverse:", 8)) {
- char* cookie = strdup(name + 8);
- if (cookie == NULL) {
- ret = -1;
- } else {
- ret = create_service_thread(reverse_service, cookie);
- if (ret < 0) {
- free(cookie);
- }
- }
+ ret = reverse_service(name + 8);
} else if(!strncmp(name, "disable-verity:", 15)) {
ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
} else if(!strncmp(name, "enable-verity:", 15)) {
diff --git a/shell_service.cpp b/shell_service.cpp
index 544afce..be5921d 100644
--- a/shell_service.cpp
+++ b/shell_service.cpp
@@ -83,6 +83,7 @@
#include <errno.h>
#include <pty.h>
+#include <pwd.h>
#include <sys/select.h>
#include <termios.h>
@@ -281,6 +282,15 @@
parent_error_sfd.Reset();
close_on_exec(child_error_sfd.fd());
+ // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
+ passwd* pw = getpwuid(getuid());
+ if (pw != nullptr) {
+ setenv("HOME", pw->pw_dir, 1);
+ setenv("LOGNAME", pw->pw_name, 1);
+ setenv("SHELL", pw->pw_shell, 1);
+ setenv("USER", pw->pw_name, 1);
+ }
+
if (is_interactive()) {
execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr);
} else {
diff --git a/sysdeps.h b/sysdeps.h
index 51d09a6..1735627 100644
--- a/sysdeps.h
+++ b/sysdeps.h
@@ -181,6 +181,17 @@
extern int unix_open(const char* path, int options, ...);
#define open ___xxx_unix_open
+// Checks if |fd| corresponds to a console.
+// Standard Windows isatty() returns 1 for both console FDs and character
+// devices like NUL. unix_isatty() performs some extra checking to only match
+// console FDs.
+// |fd| must be a real file descriptor, meaning STDxx_FILENO or unix_open() FDs
+// will work but adb_open() FDs will not. Additionally the OS handle associated
+// with |fd| must have GENERIC_READ access (which console FDs have by default).
+// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
+// calling this function is unreliable and should not be used.
+int unix_isatty(int fd);
+#define isatty ___xxx_isatty
/* normally provided by <cutils/misc.h> */
extern void* load_file(const char* pathname, unsigned* psize);
@@ -551,6 +562,11 @@
#undef creat
#define creat ___xxx_creat
+static __inline__ int unix_isatty(int fd) {
+ return isatty(fd);
+}
+#define isatty ___xxx_isatty
+
// Helper for network_* functions.
inline int _fd_set_error_str(int fd, std::string* error) {
if (fd == -1) {
diff --git a/sysdeps_win32.cpp b/sysdeps_win32.cpp
index 14d1375..d2e6cdb 100644
--- a/sysdeps_win32.cpp
+++ b/sysdeps_win32.cpp
@@ -2499,10 +2499,52 @@
//
// Code organization:
//
+// * _get_console_handle() and unix_isatty() provide console information.
// * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
// * unix_read() detects console windows (as opposed to pipes, files, etc.).
// * _console_read() is the main code of the emulation.
+// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
+// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
+// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
+static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+ // First check isatty(); this is very fast and eliminates most non-console
+ // FDs, but returns 1 for both consoles and character devices like NUL.
+#pragma push_macro("isatty")
+#undef isatty
+ if (!isatty(fd)) {
+ return nullptr;
+ }
+#pragma pop_macro("isatty")
+
+ // To differentiate between character devices and consoles we need to get
+ // the underlying HANDLE and use GetConsoleMode(), which is what requires
+ // GENERIC_READ permissions.
+ const intptr_t intptr_handle = _get_osfhandle(fd);
+ if (intptr_handle == -1) {
+ return nullptr;
+ }
+ const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+ DWORD temp_mode = 0;
+ if (!GetConsoleMode(handle, mode ? mode : &temp_mode)) {
+ return nullptr;
+ }
+
+ return handle;
+}
+
+// Returns a console handle if |stream| is a console, otherwise returns nullptr.
+static HANDLE _get_console_handle(FILE* const stream) {
+ const int fd = fileno(stream);
+ if (fd < 0) {
+ return nullptr;
+ }
+ return _get_console_handle(fd);
+}
+
+int unix_isatty(int fd) {
+ return _get_console_handle(fd) ? 1 : 0;
+}
// Read an input record from the console; one that should be processed.
static bool _get_interesting_input_record_uncached(const HANDLE console,
@@ -3302,20 +3344,7 @@
void stdin_raw_init(const int fd) {
if (STDIN_FILENO == fd) {
- const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
- if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
- return;
- }
-
- if (GetFileType(in) != FILE_TYPE_CHAR) {
- // stdin might be a file or pipe.
- return;
- }
-
- if (!GetConsoleMode(in, &_old_console_mode)) {
- // If GetConsoleMode() fails, stdin is probably is not a console.
- return;
- }
+ const HANDLE in = _get_console_handle(fd, &_old_console_mode);
// Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
// calling the process Ctrl-C routine (configured by
@@ -3366,11 +3395,8 @@
} else {
// On older versions of Windows (definitely 7, definitely not 10),
// ReadConsole() with a size >= 31367 fails, so if |fd| is a console
- // we need to limit the read size. This may also catch devices like NUL,
- // but that is OK as we just want to avoid capping pipes and files which
- // don't need size limiting. This isatty() test is very simple and quick
- // and doesn't call the OS.
- if (isatty(fd) && len > 4096) {
+ // we need to limit the read size.
+ if (len > 4096 && unix_isatty(fd)) {
len = 4096;
}
// Just call into C Runtime which can read from pipes/files and which
@@ -3725,40 +3751,6 @@
return _wchmod(widen(path).c_str(), mode);
}
-// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
-static HANDLE _get_console_handle(FILE* const stream) {
- // Get a C Runtime file descriptor number from the FILE* structure.
- const int fd = fileno(stream);
- if (fd < 0) {
- return NULL;
- }
-
- // If it is not a "character device", it is probably a file and not a
- // console. Do this check early because it is probably cheap. Still do more
- // checks after this since there are devices that pass this test, but are
- // not a console, such as NUL, the Windows /dev/null equivalent (I think).
- if (!isatty(fd)) {
- return NULL;
- }
-
- // Given a C Runtime file descriptor number, get the underlying OS
- // file handle.
- const intptr_t osfh = _get_osfhandle(fd);
- if (osfh == -1) {
- return NULL;
- }
-
- const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
-
- DWORD old_mode = 0;
- if (!GetConsoleMode(h, &old_mode)) {
- return NULL;
- }
-
- // If GetConsoleMode() was successful, assume this is a console.
- return h;
-}
-
// Internal helper function to write UTF-8 bytes to a console. Returns -1
// on error.
static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
diff --git a/sysdeps_win32_test.cpp b/sysdeps_win32_test.cpp
index 66d1ba8..55b5eb4 100755
--- a/sysdeps_win32_test.cpp
+++ b/sysdeps_win32_test.cpp
@@ -18,6 +18,8 @@
#include "sysdeps.h"
+#include "base/test_utils.h"
+
TEST(sysdeps_win32, adb_getenv) {
// Insert all test env vars before first call to adb_getenv() which will
// read the env var block only once.
@@ -93,3 +95,45 @@
// adb_strerror() returns.
TestAdbStrError(ECONNRESET, "Connection reset by peer");
}
+
+TEST(sysdeps_win32, unix_isatty) {
+ // stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
+ // so that we can test this even if stdin/stdout have been redirected. Read
+ // permissions are required for unix_isatty().
+ int conin_fd = unix_open("CONIN$", O_RDONLY);
+ int conout_fd = unix_open("CONOUT$", O_RDWR);
+ for (const int fd : {conin_fd, conout_fd}) {
+ EXPECT_TRUE(fd >= 0);
+ EXPECT_EQ(1, unix_isatty(fd));
+ EXPECT_EQ(0, unix_close(fd));
+ }
+
+ // nul returns 1 from isatty(), make sure unix_isatty() corrects that.
+ for (auto flags : {O_RDONLY, O_RDWR}) {
+ int nul_fd = unix_open("nul", flags);
+ EXPECT_TRUE(nul_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(nul_fd));
+ EXPECT_EQ(0, unix_close(nul_fd));
+ }
+
+ // Check a real file, both read-write and read-only.
+ TemporaryFile temp_file;
+ EXPECT_TRUE(temp_file.fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file.fd));
+
+ int temp_file_ro_fd = unix_open(temp_file.path, O_RDONLY);
+ EXPECT_TRUE(temp_file_ro_fd >= 0);
+ EXPECT_EQ(0, unix_isatty(temp_file_ro_fd));
+ EXPECT_EQ(0, unix_close(temp_file_ro_fd));
+
+ // Check a real OS pipe.
+ int pipe_fds[2];
+ EXPECT_EQ(0, _pipe(pipe_fds, 64, _O_BINARY));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[0]));
+ EXPECT_EQ(0, unix_isatty(pipe_fds[1]));
+ EXPECT_EQ(0, _close(pipe_fds[0]));
+ EXPECT_EQ(0, _close(pipe_fds[1]));
+
+ // Make sure an invalid FD is handled correctly.
+ EXPECT_EQ(0, unix_isatty(-1));
+}