Loading adb/SOCKET-ACTIVATION.txt 0 → 100644 +42 −0 Original line number Original line Diff line number Diff line adb can be configured to work with systemd-style socket activation, allowing the daemon to start automatically when the adb control port is forwarded across a network. You need two files, placed in the usual systemd service directories (e.g., ~/.config/systemd/user for a user service). adb.service: --- START adb.service CUT HERE --- [Unit] Description=adb After=adb.socket Requires=adb.socket [Service] Type=simple # FD 3 is part of the systemd interface ExecStart=/path/to/adb server nodaemon -L acceptfd:3 --- END adb.service CUT HERE --- --- START adb.socket CUT HERE --- [Unit] Description=adb PartOf=adb.service [Socket] ListenStream=127.0.0.1:5037 Accept=no [Install] WantedBy=sockets.target --- END adb.socket CUT HERE --- After installing the adb service, the adb server will be started automatically on any connection to 127.0.0.1:5037 (the default adb control port), even after adb kill-server kills the server. Other "superserver" launcher systems (like macOS launchd) can be configured analogously. The important part is that adb be started with "server" and "nodaemon" command line arguments and that the listen address (passed to -L) name a file descriptor that's ready to accept(2) connections and that's already bound to the desired address and listening. inetd-style pre-accepted sockets do _not_ work in this configuration: the file descriptor passed to acceptfd must be the serve socket, not the accepted connection socket. adb/client/adb_client.cpp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -222,7 +222,7 @@ std::optional<std::string> adb_get_server_executable_path() { int port; int port; std::string error; std::string error; if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) { if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) { LOG(FATAL) << "failed to parse server socket spec: " << error; return {}; } } return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port); return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port); Loading adb/client/commandline.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -107,6 +107,7 @@ static void help() { " localfilesystem:<unix domain socket name>\n" " localfilesystem:<unix domain socket name>\n" " dev:<character device name>\n" " dev:<character device name>\n" " jdwp:<process pid> (remote only)\n" " jdwp:<process pid> (remote only)\n" " acceptfd:<fd> (listen only)\n" " forward --remove LOCAL remove specific forward socket connection\n" " forward --remove LOCAL remove specific forward socket connection\n" " forward --remove-all remove all forward socket connections\n" " forward --remove-all remove all forward socket connections\n" " ppp TTY [PARAMETER...] run PPP over USB\n" " ppp TTY [PARAMETER...] run PPP over USB\n" Loading adb/socket_spec.cpp +47 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "socket_spec.h" #include "socket_spec.h" #include <limits> #include <string> #include <string> #include <string_view> #include <string_view> #include <unordered_map> #include <unordered_map> Loading @@ -28,10 +29,12 @@ #include <cutils/sockets.h> #include <cutils/sockets.h> #include "adb.h" #include "adb.h" #include "adb_utils.h" #include "sysdeps.h" #include "sysdeps.h" using namespace std::string_literals; using namespace std::string_literals; using android::base::ConsumePrefix; using android::base::StringPrintf; using android::base::StringPrintf; #if defined(__linux__) #if defined(__linux__) Loading Loading @@ -131,7 +134,7 @@ bool is_socket_spec(std::string_view spec) { return true; return true; } } } } return spec.starts_with("tcp:"); return spec.starts_with("tcp:") || spec.starts_with("acceptfd:"); } } bool is_local_socket_spec(std::string_view spec) { bool is_local_socket_spec(std::string_view spec) { Loading Loading @@ -235,6 +238,9 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std *error = "vsock is only supported on linux"; *error = "vsock is only supported on linux"; return false; return false; #endif // ADB_LINUX #endif // ADB_LINUX } else if (address.starts_with("acceptfd:")) { *error = "cannot connect to acceptfd"; return false; } } for (const auto& it : kLocalSocketTypes) { for (const auto& it : kLocalSocketTypes) { Loading Loading @@ -332,6 +338,46 @@ int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_ *error = "vsock is only supported on linux"; *error = "vsock is only supported on linux"; return -1; return -1; #endif // ADB_LINUX #endif // ADB_LINUX } else if (ConsumePrefix(&spec, "acceptfd:")) { #if ADB_WINDOWS *error = "socket activation not supported under Windows"; return -1; #else // We inherited the socket from some kind of launcher. It's already bound and // listening. Return a copy of the FD instead of the FD itself so we implement the // normal "listen" contract and can succeed more than once. unsigned int fd_u; if (!ParseUint(&fd_u, spec) || fd_u > std::numeric_limits<int>::max()) { *error = "invalid fd"; return -1; } int fd = static_cast<int>(fd_u); int flags = get_fd_flags(fd); if (flags < 0) { *error = android::base::StringPrintf("could not get flags of inherited fd %d: '%s'", fd, strerror(errno)); return -1; } if (flags & O_CLOEXEC) { *error = android::base::StringPrintf("fd %d was not inherited from parent", fd); return -1; } int dummy_sock_type; socklen_t dummy_sock_type_size = sizeof(dummy_sock_type); if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &dummy_sock_type, &dummy_sock_type_size)) { *error = android::base::StringPrintf("fd %d does not refer to a socket", fd); return -1; } int new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd < 0) { *error = android::base::StringPrintf("could not dup inherited fd %d: '%s'", fd, strerror(errno)); return -1; } return new_fd; #endif } } for (const auto& it : kLocalSocketTypes) { for (const auto& it : kLocalSocketTypes) { Loading adb/sysdeps.h +8 −1 Original line number Original line Diff line number Diff line Loading @@ -349,8 +349,15 @@ static __inline__ bool adb_is_separator(char c) { return c == '/'; return c == '/'; } } static __inline__ int get_fd_flags(borrowed_fd fd) { return fcntl(fd.get(), F_GETFD); } static __inline__ void close_on_exec(borrowed_fd fd) { static __inline__ void close_on_exec(borrowed_fd fd) { fcntl(fd.get(), F_SETFD, FD_CLOEXEC); int flags = get_fd_flags(fd); if (flags >= 0 && (flags & O_CLOEXEC) == 0) { fcntl(fd.get(), F_SETFD, flags | O_CLOEXEC); } } } // Open a file and return a file descriptor that may be used with unix_read(), // Open a file and return a file descriptor that may be used with unix_read(), Loading Loading
adb/SOCKET-ACTIVATION.txt 0 → 100644 +42 −0 Original line number Original line Diff line number Diff line adb can be configured to work with systemd-style socket activation, allowing the daemon to start automatically when the adb control port is forwarded across a network. You need two files, placed in the usual systemd service directories (e.g., ~/.config/systemd/user for a user service). adb.service: --- START adb.service CUT HERE --- [Unit] Description=adb After=adb.socket Requires=adb.socket [Service] Type=simple # FD 3 is part of the systemd interface ExecStart=/path/to/adb server nodaemon -L acceptfd:3 --- END adb.service CUT HERE --- --- START adb.socket CUT HERE --- [Unit] Description=adb PartOf=adb.service [Socket] ListenStream=127.0.0.1:5037 Accept=no [Install] WantedBy=sockets.target --- END adb.socket CUT HERE --- After installing the adb service, the adb server will be started automatically on any connection to 127.0.0.1:5037 (the default adb control port), even after adb kill-server kills the server. Other "superserver" launcher systems (like macOS launchd) can be configured analogously. The important part is that adb be started with "server" and "nodaemon" command line arguments and that the listen address (passed to -L) name a file descriptor that's ready to accept(2) connections and that's already bound to the desired address and listening. inetd-style pre-accepted sockets do _not_ work in this configuration: the file descriptor passed to acceptfd must be the serve socket, not the accepted connection socket.
adb/client/adb_client.cpp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -222,7 +222,7 @@ std::optional<std::string> adb_get_server_executable_path() { int port; int port; std::string error; std::string error; if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) { if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) { LOG(FATAL) << "failed to parse server socket spec: " << error; return {}; } } return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port); return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port); Loading
adb/client/commandline.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -107,6 +107,7 @@ static void help() { " localfilesystem:<unix domain socket name>\n" " localfilesystem:<unix domain socket name>\n" " dev:<character device name>\n" " dev:<character device name>\n" " jdwp:<process pid> (remote only)\n" " jdwp:<process pid> (remote only)\n" " acceptfd:<fd> (listen only)\n" " forward --remove LOCAL remove specific forward socket connection\n" " forward --remove LOCAL remove specific forward socket connection\n" " forward --remove-all remove all forward socket connections\n" " forward --remove-all remove all forward socket connections\n" " ppp TTY [PARAMETER...] run PPP over USB\n" " ppp TTY [PARAMETER...] run PPP over USB\n" Loading
adb/socket_spec.cpp +47 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "socket_spec.h" #include "socket_spec.h" #include <limits> #include <string> #include <string> #include <string_view> #include <string_view> #include <unordered_map> #include <unordered_map> Loading @@ -28,10 +29,12 @@ #include <cutils/sockets.h> #include <cutils/sockets.h> #include "adb.h" #include "adb.h" #include "adb_utils.h" #include "sysdeps.h" #include "sysdeps.h" using namespace std::string_literals; using namespace std::string_literals; using android::base::ConsumePrefix; using android::base::StringPrintf; using android::base::StringPrintf; #if defined(__linux__) #if defined(__linux__) Loading Loading @@ -131,7 +134,7 @@ bool is_socket_spec(std::string_view spec) { return true; return true; } } } } return spec.starts_with("tcp:"); return spec.starts_with("tcp:") || spec.starts_with("acceptfd:"); } } bool is_local_socket_spec(std::string_view spec) { bool is_local_socket_spec(std::string_view spec) { Loading Loading @@ -235,6 +238,9 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std *error = "vsock is only supported on linux"; *error = "vsock is only supported on linux"; return false; return false; #endif // ADB_LINUX #endif // ADB_LINUX } else if (address.starts_with("acceptfd:")) { *error = "cannot connect to acceptfd"; return false; } } for (const auto& it : kLocalSocketTypes) { for (const auto& it : kLocalSocketTypes) { Loading Loading @@ -332,6 +338,46 @@ int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_ *error = "vsock is only supported on linux"; *error = "vsock is only supported on linux"; return -1; return -1; #endif // ADB_LINUX #endif // ADB_LINUX } else if (ConsumePrefix(&spec, "acceptfd:")) { #if ADB_WINDOWS *error = "socket activation not supported under Windows"; return -1; #else // We inherited the socket from some kind of launcher. It's already bound and // listening. Return a copy of the FD instead of the FD itself so we implement the // normal "listen" contract and can succeed more than once. unsigned int fd_u; if (!ParseUint(&fd_u, spec) || fd_u > std::numeric_limits<int>::max()) { *error = "invalid fd"; return -1; } int fd = static_cast<int>(fd_u); int flags = get_fd_flags(fd); if (flags < 0) { *error = android::base::StringPrintf("could not get flags of inherited fd %d: '%s'", fd, strerror(errno)); return -1; } if (flags & O_CLOEXEC) { *error = android::base::StringPrintf("fd %d was not inherited from parent", fd); return -1; } int dummy_sock_type; socklen_t dummy_sock_type_size = sizeof(dummy_sock_type); if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &dummy_sock_type, &dummy_sock_type_size)) { *error = android::base::StringPrintf("fd %d does not refer to a socket", fd); return -1; } int new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd < 0) { *error = android::base::StringPrintf("could not dup inherited fd %d: '%s'", fd, strerror(errno)); return -1; } return new_fd; #endif } } for (const auto& it : kLocalSocketTypes) { for (const auto& it : kLocalSocketTypes) { Loading
adb/sysdeps.h +8 −1 Original line number Original line Diff line number Diff line Loading @@ -349,8 +349,15 @@ static __inline__ bool adb_is_separator(char c) { return c == '/'; return c == '/'; } } static __inline__ int get_fd_flags(borrowed_fd fd) { return fcntl(fd.get(), F_GETFD); } static __inline__ void close_on_exec(borrowed_fd fd) { static __inline__ void close_on_exec(borrowed_fd fd) { fcntl(fd.get(), F_SETFD, FD_CLOEXEC); int flags = get_fd_flags(fd); if (flags >= 0 && (flags & O_CLOEXEC) == 0) { fcntl(fd.get(), F_SETFD, flags | O_CLOEXEC); } } } // Open a file and return a file descriptor that may be used with unix_read(), // Open a file and return a file descriptor that may be used with unix_read(), Loading