diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp index 4cf3a743a..9fa827dd2 100644 --- a/adb/client/adb_client.cpp +++ b/adb/client/adb_client.cpp @@ -31,10 +31,12 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -214,15 +216,26 @@ int adb_connect(std::string_view service, std::string* error) { return adb_connect(nullptr, service, error); } -int adb_connect(TransportId* transport, std::string_view service, std::string* error) { - // first query the adb server's version +#if defined(__linux__) +std::optional adb_get_server_executable_path() { + int port; + std::string error; + if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) { + LOG(FATAL) << "failed to parse server socket spec: " << error; + } + + return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port); +} +#endif + +static bool __adb_check_server_version(std::string* error) { unique_fd fd(_adb_connect("host:version", nullptr, error)); - LOG(DEBUG) << "adb_connect: service: " << service; - if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) { + bool local = is_local_socket_spec(__adb_server_socket_spec); + if (fd == -2 && !local) { fprintf(stderr, "* cannot start server on remote host\n"); // error is the original network connection error - return fd; + return false; } else if (fd == -2) { fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec); start_server: @@ -232,7 +245,7 @@ int adb_connect(TransportId* transport, std::string_view service, std::string* e // return a generic error string about the overall adb_connect() // that the caller requested. *error = "cannot connect to daemon"; - return -1; + return false; } else { fprintf(stderr, "* daemon started successfully\n"); } @@ -254,18 +267,39 @@ int adb_connect(TransportId* transport, std::string_view service, std::string* e if (sscanf(&version_string[0], "%04x", &version) != 1) { *error = android::base::StringPrintf("cannot parse version string: %s", version_string.c_str()); - return -1; + return false; } } else { // If fd is -1 check for "unknown host service" which would // indicate a version of adb that does not support the // version command, in which case we should fall-through to kill it. if (*error != "unknown host service") { - return fd; + return false; } } if (version != ADB_SERVER_VERSION) { +#if defined(__linux__) + if (version > ADB_SERVER_VERSION && local) { + // Try to re-exec the existing adb server's binary. + constexpr const char* adb_reexeced = "adb (re-execed)"; + if (strcmp(adb_reexeced, *__adb_argv) != 0) { + __adb_argv[0] = adb_reexeced; + std::optional server_path_path = adb_get_server_executable_path(); + std::string server_path; + if (server_path_path && + android::base::ReadFileToString(*server_path_path, &server_path)) { + if (execve(server_path.c_str(), const_cast(__adb_argv), + const_cast(__adb_envp)) == -1) { + LOG(ERROR) << "failed to exec newer version at " << server_path; + } + + // Fall-through to restarting the server. + } + } + } +#endif + fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n", version, ADB_SERVER_VERSION); adb_kill_server(); @@ -273,12 +307,36 @@ int adb_connect(TransportId* transport, std::string_view service, std::string* e } } + return true; +} + +bool adb_check_server_version(std::string* error) { + // Only check the version once per process, since this isn't atomic anyway. + static std::once_flag once; + static bool result; + static std::string* err; + std::call_once(once, []() { + err = new std::string(); + result = __adb_check_server_version(err); + }); + *error = *err; + return result; +} + +int adb_connect(TransportId* transport, std::string_view service, std::string* error) { + LOG(DEBUG) << "adb_connect: service: " << service; + + // Query the adb server's version. + if (!adb_check_server_version(error)) { + return -1; + } + // if the command is start-server, we are done. if (service == "host:start-server") { return 0; } - fd.reset(_adb_connect(service, transport, error)); + unique_fd fd(_adb_connect(service, transport, error)); if (fd == -1) { D("_adb_connect error: %s", error->c_str()); } else if(fd == -2) { diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h index 0a7378770..8d32c93b4 100644 --- a/adb/client/adb_client.h +++ b/adb/client/adb_client.h @@ -20,8 +20,14 @@ #include "sysdeps.h" #include "transport.h" +#include #include +// Explicitly check the adb server version. +// All of the commands below do this implicitly. +// Only the first invocation of this function will check the server version. +bool adb_check_server_version(std::string* _Nonnull error); + // Connect to adb, connect to the named service, and return a valid fd for // interacting with that service upon success or a negative number on failure. int adb_connect(std::string_view service, std::string* _Nonnull error); @@ -65,3 +71,13 @@ std::string format_host_command(const char* _Nonnull command); // Get the feature set of the current preferred transport. bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error); + +#if defined(__linux__) +// Get the path of a file containing the path to the server executable, if the socket spec set via +// adb_set_socket_spec is a local one. +std::optional adb_get_server_executable_path(); +#endif + +// Globally acccesible argv/envp, for the purpose of re-execing adb. +extern const char* _Nullable * _Nullable __adb_argv; +extern const char* _Nullable * _Nullable __adb_envp; diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp index 3d5d9db32..bb30ae570 100644 --- a/adb/client/commandline.cpp +++ b/adb/client/commandline.cpp @@ -1303,9 +1303,9 @@ static void parse_push_pull_args(const char** arg, int narg, std::vector 0) { - if (!strcmp(argv[0],"server")) { + if (!strcmp(argv[0], "server")) { is_server = true; - } else if (!strcmp(argv[0],"nodaemon")) { + } else if (!strcmp(argv[0], "nodaemon")) { no_daemon = true; } else if (!strcmp(argv[0], "fork-server")) { /* this is a special flag used only when the ADB client launches the ADB Server */ @@ -1433,11 +1433,11 @@ int adb_commandline(int argc, const char** argv) { if (*id != '\0') { error_exit("invalid transport id"); } - } else if (!strcmp(argv[0],"-d")) { + } else if (!strcmp(argv[0], "-d")) { transport_type = kTransportUsb; - } else if (!strcmp(argv[0],"-e")) { + } else if (!strcmp(argv[0], "-e")) { transport_type = kTransportLocal; - } else if (!strcmp(argv[0],"-a")) { + } else if (!strcmp(argv[0], "-a")) { gListenAll = 1; } else if (!strncmp(argv[0], "-H", 2)) { if (argv[0][2] == '\0') { @@ -1569,6 +1569,10 @@ int adb_commandline(int argc, const char** argv) { } std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt); + std::string error; + if (!adb_check_server_version(&error)) { + error_exit("failed to check server version: %s", error.c_str()); + } printf("List of devices attached\n"); return adb_query_command(query); } diff --git a/adb/client/main.cpp b/adb/client/main.cpp index 2ee81a994..0c5c28f1b 100644 --- a/adb/client/main.cpp +++ b/adb/client/main.cpp @@ -32,12 +32,16 @@ #include "adb.h" #include "adb_auth.h" +#include "adb_client.h" #include "adb_listeners.h" #include "adb_utils.h" #include "commandline.h" #include "sysdeps/chrono.h" #include "transport.h" +const char** __adb_argv; +const char** __adb_envp; + static void setup_daemon_logging() { const std::string log_file_path(GetLogFilePath()); int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640); @@ -191,13 +195,29 @@ int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply notify_thread.detach(); } +#if defined(__linux__) + // Write our location to .android/adb.$PORT, so that older clients can exec us. + std::string path; + if (!android::base::Readlink("/proc/self/exe", &path)) { + PLOG(ERROR) << "failed to readlink /proc/self/exe"; + } + + std::optional server_executable_path = adb_get_server_executable_path(); + if (server_executable_path) { + if (!android::base::WriteStringToFile(path, *server_executable_path)) { + PLOG(ERROR) << "failed to write server path to " << path; + } + } +#endif + D("Event loop starting"); fdevent_loop(); - return 0; } -int main(int argc, char** argv) { +int main(int argc, char* argv[], char* envp[]) { + __adb_argv = const_cast(argv); + __adb_envp = const_cast(envp); adb_trace_init(argv); return adb_commandline(argc - 1, const_cast(argv + 1)); } diff --git a/adb/socket_spec.h b/adb/socket_spec.h index 687d751f6..7cc2fac57 100644 --- a/adb/socket_spec.h +++ b/adb/socket_spec.h @@ -29,6 +29,5 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std std::string* error); int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr); -// Exposed for testing. bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port, std::string* serial, std::string* error);