2018-02-08 14:16:27 +00:00
|
|
|
#include "server.h"
|
2018-01-22 10:22:31 +00:00
|
|
|
|
2019-11-27 20:11:40 +00:00
|
|
|
#include <assert.h>
|
2018-01-22 10:22:31 +00:00
|
|
|
#include <errno.h>
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-15 21:59:21 +00:00
|
|
|
#include <inttypes.h>
|
2019-06-10 13:14:10 +00:00
|
|
|
#include <libgen.h>
|
2018-05-13 13:33:13 +00:00
|
|
|
#include <stdio.h>
|
2020-03-28 22:15:15 +00:00
|
|
|
#include <SDL2/SDL_thread.h>
|
2018-03-12 07:35:51 +00:00
|
|
|
#include <SDL2/SDL_timer.h>
|
2019-12-14 06:34:49 +00:00
|
|
|
#include <SDL2/SDL_platform.h>
|
2018-02-13 09:10:18 +00:00
|
|
|
|
2018-02-13 10:55:12 +00:00
|
|
|
#include "config.h"
|
2019-06-10 13:14:10 +00:00
|
|
|
#include "command.h"
|
2019-11-24 10:53:00 +00:00
|
|
|
#include "util/log.h"
|
|
|
|
#include "util/net.h"
|
2019-12-14 06:34:49 +00:00
|
|
|
#include "util/str_util.h"
|
2018-01-22 10:22:31 +00:00
|
|
|
|
2018-01-23 14:46:34 +00:00
|
|
|
#define SOCKET_NAME "scrcpy"
|
2019-10-30 22:40:10 +00:00
|
|
|
#define SERVER_FILENAME "scrcpy-server"
|
2018-01-23 14:46:34 +00:00
|
|
|
|
2019-06-12 09:14:08 +00:00
|
|
|
#define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FILENAME
|
2019-11-22 14:23:57 +00:00
|
|
|
#define DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
2018-02-28 14:13:56 +00:00
|
|
|
|
2019-12-14 17:13:56 +00:00
|
|
|
static char *
|
2019-03-02 19:09:56 +00:00
|
|
|
get_server_path(void) {
|
2019-12-14 06:34:49 +00:00
|
|
|
#ifdef __WINDOWS__
|
|
|
|
const wchar_t *server_path_env = _wgetenv(L"SCRCPY_SERVER_PATH");
|
|
|
|
#else
|
2019-06-10 13:14:10 +00:00
|
|
|
const char *server_path_env = getenv("SCRCPY_SERVER_PATH");
|
2019-12-14 06:34:49 +00:00
|
|
|
#endif
|
2019-06-10 13:14:10 +00:00
|
|
|
if (server_path_env) {
|
|
|
|
// if the envvar is set, use it
|
2019-12-14 06:34:49 +00:00
|
|
|
#ifdef __WINDOWS__
|
|
|
|
char *server_path = utf8_from_wide_char(server_path_env);
|
|
|
|
#else
|
2019-12-14 17:13:56 +00:00
|
|
|
char *server_path = SDL_strdup(server_path_env);
|
2019-12-14 06:34:49 +00:00
|
|
|
#endif
|
2019-12-14 17:13:56 +00:00
|
|
|
if (!server_path) {
|
|
|
|
LOGE("Could not allocate memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
LOGD("Using SCRCPY_SERVER_PATH: %s", server_path);
|
|
|
|
return server_path;
|
2019-06-10 13:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef PORTABLE
|
|
|
|
LOGD("Using server: " DEFAULT_SERVER_PATH);
|
2019-12-14 17:13:56 +00:00
|
|
|
char *server_path = SDL_strdup(DEFAULT_SERVER_PATH);
|
|
|
|
if (!server_path) {
|
|
|
|
LOGE("Could not allocate memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-06-10 13:14:10 +00:00
|
|
|
// the absolute path is hardcoded
|
2019-12-14 17:13:56 +00:00
|
|
|
return server_path;
|
2019-06-10 13:14:10 +00:00
|
|
|
#else
|
2019-12-14 17:13:56 +00:00
|
|
|
|
2019-10-30 22:40:10 +00:00
|
|
|
// use scrcpy-server in the same directory as the executable
|
2019-06-10 13:14:10 +00:00
|
|
|
char *executable_path = get_executable_path();
|
|
|
|
if (!executable_path) {
|
2019-06-23 18:49:38 +00:00
|
|
|
LOGE("Could not get executable path, "
|
2019-06-10 13:14:10 +00:00
|
|
|
"using " SERVER_FILENAME " from current directory");
|
|
|
|
// not found, use current directory
|
|
|
|
return SERVER_FILENAME;
|
|
|
|
}
|
|
|
|
char *dir = dirname(executable_path);
|
|
|
|
size_t dirlen = strlen(dir);
|
|
|
|
|
|
|
|
// sizeof(SERVER_FILENAME) gives statically the size including the null byte
|
|
|
|
size_t len = dirlen + 1 + sizeof(SERVER_FILENAME);
|
|
|
|
char *server_path = SDL_malloc(len);
|
2018-02-02 08:31:44 +00:00
|
|
|
if (!server_path) {
|
2019-06-23 18:49:38 +00:00
|
|
|
LOGE("Could not alloc server path string, "
|
2019-06-10 13:14:10 +00:00
|
|
|
"using " SERVER_FILENAME " from current directory");
|
|
|
|
SDL_free(executable_path);
|
|
|
|
return SERVER_FILENAME;
|
2018-01-23 14:46:34 +00:00
|
|
|
}
|
2019-06-10 13:14:10 +00:00
|
|
|
|
|
|
|
memcpy(server_path, dir, dirlen);
|
|
|
|
server_path[dirlen] = PATH_SEPARATOR;
|
|
|
|
memcpy(&server_path[dirlen + 1], SERVER_FILENAME, sizeof(SERVER_FILENAME));
|
|
|
|
// the final null byte has been copied with SERVER_FILENAME
|
|
|
|
|
|
|
|
SDL_free(executable_path);
|
|
|
|
|
|
|
|
LOGD("Using server (portable): %s", server_path);
|
2018-02-13 10:55:12 +00:00
|
|
|
return server_path;
|
2019-06-10 13:14:10 +00:00
|
|
|
#endif
|
2018-02-13 10:55:12 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
static bool
|
2019-03-02 19:09:56 +00:00
|
|
|
push_server(const char *serial) {
|
2019-12-14 17:13:56 +00:00
|
|
|
char *server_path = get_server_path();
|
|
|
|
if (!server_path) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-05 20:07:11 +00:00
|
|
|
if (!is_regular_file(server_path)) {
|
|
|
|
LOGE("'%s' does not exist or is not a regular file\n", server_path);
|
2019-12-14 17:13:56 +00:00
|
|
|
SDL_free(server_path);
|
2019-12-05 20:07:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
process_t process = adb_push(serial, server_path, DEVICE_SERVER_PATH);
|
2019-12-14 17:13:56 +00:00
|
|
|
SDL_free(server_path);
|
2018-02-08 14:16:27 +00:00
|
|
|
return process_check_success(process, "adb push");
|
2018-01-23 14:46:34 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
static bool
|
|
|
|
enable_tunnel_reverse(const char *serial, uint16_t local_port) {
|
2018-02-08 14:16:27 +00:00
|
|
|
process_t process = adb_reverse(serial, SOCKET_NAME, local_port);
|
|
|
|
return process_check_success(process, "adb reverse");
|
2018-01-23 14:46:34 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
static bool
|
2019-03-02 19:09:56 +00:00
|
|
|
disable_tunnel_reverse(const char *serial) {
|
2018-02-08 14:16:27 +00:00
|
|
|
process_t process = adb_reverse_remove(serial, SOCKET_NAME);
|
|
|
|
return process_check_success(process, "adb reverse --remove");
|
2018-01-23 14:46:34 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
static bool
|
|
|
|
enable_tunnel_forward(const char *serial, uint16_t local_port) {
|
2018-03-12 07:35:51 +00:00
|
|
|
process_t process = adb_forward(serial, local_port, SOCKET_NAME);
|
|
|
|
return process_check_success(process, "adb forward");
|
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
static bool
|
|
|
|
disable_tunnel_forward(const char *serial, uint16_t local_port) {
|
2018-03-12 07:35:51 +00:00
|
|
|
process_t process = adb_forward_remove(serial, local_port);
|
|
|
|
return process_check_success(process, "adb forward --remove");
|
|
|
|
}
|
|
|
|
|
2019-12-09 21:14:43 +00:00
|
|
|
static bool
|
|
|
|
disable_tunnel(struct server *server) {
|
|
|
|
if (server->tunnel_forward) {
|
|
|
|
return disable_tunnel_forward(server->serial, server->local_port);
|
|
|
|
}
|
|
|
|
return disable_tunnel_reverse(server->serial);
|
|
|
|
}
|
|
|
|
|
|
|
|
static socket_t
|
|
|
|
listen_on_port(uint16_t port) {
|
|
|
|
#define IPV4_LOCALHOST 0x7F000001
|
|
|
|
return net_listen(IPV4_LOCALHOST, port, 1);
|
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
static bool
|
2019-12-09 20:16:09 +00:00
|
|
|
enable_tunnel_reverse_any_port(struct server *server,
|
2020-06-19 20:04:06 +00:00
|
|
|
struct sc_port_range port_range) {
|
2019-12-09 20:16:09 +00:00
|
|
|
uint16_t port = port_range.first;
|
|
|
|
for (;;) {
|
|
|
|
if (!enable_tunnel_reverse(server->serial, port)) {
|
|
|
|
// the command itself failed, it will fail on any port
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-09 21:10:42 +00:00
|
|
|
// At the application level, the device part is "the server" because it
|
|
|
|
// serves video stream and control. However, at the network level, the
|
|
|
|
// client listens and the server connects to the client. That way, the
|
|
|
|
// client can listen before starting the server app, so there is no
|
|
|
|
// need to try to connect until the server socket is listening on the
|
|
|
|
// device.
|
2019-12-09 20:16:09 +00:00
|
|
|
server->server_socket = listen_on_port(port);
|
|
|
|
if (server->server_socket != INVALID_SOCKET) {
|
|
|
|
// success
|
|
|
|
server->local_port = port;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// failure, disable tunnel and try another port
|
|
|
|
if (!disable_tunnel_reverse(server->serial)) {
|
|
|
|
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check before incrementing to avoid overflow on port 65535
|
|
|
|
if (port < port_range.last) {
|
|
|
|
LOGW("Could not listen on port %" PRIu16", retrying on %" PRIu16,
|
2020-04-13 14:22:43 +00:00
|
|
|
port, (uint16_t) (port + 1));
|
2019-12-09 20:16:09 +00:00
|
|
|
port++;
|
|
|
|
continue;
|
2019-12-09 21:10:42 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 20:16:09 +00:00
|
|
|
if (port_range.first == port_range.last) {
|
|
|
|
LOGE("Could not listen on port %" PRIu16, port_range.first);
|
|
|
|
} else {
|
|
|
|
LOGE("Could not listen on any port in range %" PRIu16 ":%" PRIu16,
|
|
|
|
port_range.first, port_range.last);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
enable_tunnel_forward_any_port(struct server *server,
|
2020-06-19 20:04:06 +00:00
|
|
|
struct sc_port_range port_range) {
|
2019-12-09 20:16:09 +00:00
|
|
|
server->tunnel_forward = true;
|
|
|
|
uint16_t port = port_range.first;
|
|
|
|
for (;;) {
|
|
|
|
if (enable_tunnel_forward(server->serial, port)) {
|
|
|
|
// success
|
|
|
|
server->local_port = port;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port < port_range.last) {
|
|
|
|
LOGW("Could not forward port %" PRIu16", retrying on %" PRIu16,
|
|
|
|
port, port + 1);
|
|
|
|
port++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port_range.first == port_range.last) {
|
|
|
|
LOGE("Could not forward port %" PRIu16, port_range.first);
|
|
|
|
} else {
|
|
|
|
LOGE("Could not forward any port in range %" PRIu16 ":%" PRIu16,
|
|
|
|
port_range.first, port_range.last);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2020-06-19 20:04:06 +00:00
|
|
|
enable_tunnel_any_port(struct server *server, struct sc_port_range port_range,
|
2020-05-24 21:27:34 +00:00
|
|
|
bool force_adb_forward) {
|
|
|
|
if (!force_adb_forward) {
|
|
|
|
// Attempt to use "adb reverse"
|
|
|
|
if (enable_tunnel_reverse_any_port(server, port_range)) {
|
|
|
|
return true;
|
|
|
|
}
|
2018-03-12 07:35:51 +00:00
|
|
|
|
2020-05-24 21:27:34 +00:00
|
|
|
// if "adb reverse" does not work (e.g. over "adb connect"), it
|
|
|
|
// fallbacks to "adb forward", so the app socket is the client
|
|
|
|
|
|
|
|
LOGW("'adb reverse' failed, fallback to 'adb forward'");
|
|
|
|
}
|
2019-12-09 20:16:09 +00:00
|
|
|
|
|
|
|
return enable_tunnel_forward_any_port(server, port_range);
|
2018-03-12 07:35:51 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 19:51:40 +00:00
|
|
|
static const char *
|
|
|
|
log_level_to_server_string(enum sc_log_level level) {
|
|
|
|
switch (level) {
|
|
|
|
case SC_LOG_LEVEL_DEBUG:
|
|
|
|
return "debug";
|
|
|
|
case SC_LOG_LEVEL_INFO:
|
|
|
|
return "info";
|
|
|
|
case SC_LOG_LEVEL_WARN:
|
|
|
|
return "warn";
|
|
|
|
case SC_LOG_LEVEL_ERROR:
|
|
|
|
return "error";
|
|
|
|
default:
|
|
|
|
assert(!"unexpected log level");
|
|
|
|
return "(unknown)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:09:56 +00:00
|
|
|
static process_t
|
2019-06-04 21:59:55 +00:00
|
|
|
execute_server(struct server *server, const struct server_params *params) {
|
2018-02-01 11:18:06 +00:00
|
|
|
char max_size_string[6];
|
2018-02-01 15:36:50 +00:00
|
|
|
char bit_rate_string[11];
|
2019-11-17 21:07:19 +00:00
|
|
|
char max_fps_string[6];
|
2020-05-24 19:11:21 +00:00
|
|
|
char lock_video_orientation_string[5];
|
2020-02-24 11:16:38 +00:00
|
|
|
char display_id_string[6];
|
2019-06-04 21:59:55 +00:00
|
|
|
sprintf(max_size_string, "%"PRIu16, params->max_size);
|
|
|
|
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
|
2019-11-17 21:07:19 +00:00
|
|
|
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
|
2020-02-16 11:30:36 +00:00
|
|
|
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
|
2020-02-24 11:16:38 +00:00
|
|
|
sprintf(display_id_string, "%"PRIu16, params->display_id);
|
2018-01-22 10:22:31 +00:00
|
|
|
const char *const cmd[] = {
|
|
|
|
"shell",
|
2019-11-22 14:16:00 +00:00
|
|
|
"CLASSPATH=" DEVICE_SERVER_PATH,
|
2018-01-22 10:22:31 +00:00
|
|
|
"app_process",
|
2019-11-03 18:31:56 +00:00
|
|
|
#ifdef SERVER_DEBUGGER
|
|
|
|
# define SERVER_DEBUGGER_PORT "5005"
|
2020-03-19 18:15:43 +00:00
|
|
|
# ifdef SERVER_DEBUGGER_METHOD_NEW
|
|
|
|
/* Android 9 and above */
|
|
|
|
"-XjdwpProvider:internal -XjdwpOptions:transport=dt_socket,suspend=y,server=y,address="
|
|
|
|
# else
|
|
|
|
/* Android 8 and below */
|
2019-11-03 18:31:56 +00:00
|
|
|
"-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
|
2020-03-19 18:15:43 +00:00
|
|
|
# endif
|
2019-11-03 18:31:56 +00:00
|
|
|
SERVER_DEBUGGER_PORT,
|
|
|
|
#endif
|
2018-01-29 16:06:44 +00:00
|
|
|
"/", // unused
|
2018-02-28 13:57:18 +00:00
|
|
|
"com.genymobile.scrcpy.Server",
|
2019-11-10 12:23:58 +00:00
|
|
|
SCRCPY_VERSION,
|
2020-05-24 19:51:40 +00:00
|
|
|
log_level_to_server_string(params->log_level),
|
2018-02-01 11:18:06 +00:00
|
|
|
max_size_string,
|
2018-02-01 15:36:50 +00:00
|
|
|
bit_rate_string,
|
2019-11-17 21:07:19 +00:00
|
|
|
max_fps_string,
|
2020-02-16 11:30:36 +00:00
|
|
|
lock_video_orientation_string,
|
2019-06-04 21:59:55 +00:00
|
|
|
server->tunnel_forward ? "true" : "false",
|
|
|
|
params->crop ? params->crop : "-",
|
2019-07-30 23:55:32 +00:00
|
|
|
"true", // always send frame meta (packet boundaries + timestamp)
|
2019-06-04 19:31:46 +00:00
|
|
|
params->control ? "true" : "false",
|
2020-02-24 11:16:38 +00:00
|
|
|
display_id_string,
|
2020-05-01 21:49:37 +00:00
|
|
|
params->show_touches ? "true" : "false",
|
2020-05-01 23:54:48 +00:00
|
|
|
params->stay_awake ? "true" : "false",
|
2020-04-26 12:22:08 +00:00
|
|
|
params->codec_options ? params->codec_options : "-",
|
2018-01-22 10:22:31 +00:00
|
|
|
};
|
2019-11-03 18:31:56 +00:00
|
|
|
#ifdef SERVER_DEBUGGER
|
|
|
|
LOGI("Server debugger waiting for a client on device port "
|
|
|
|
SERVER_DEBUGGER_PORT "...");
|
|
|
|
// From the computer, run
|
|
|
|
// adb forward tcp:5005 tcp:5005
|
|
|
|
// Then, from Android Studio: Run > Debug > Edit configurations...
|
|
|
|
// On the left, click on '+', "Remote", with:
|
|
|
|
// Host: localhost
|
|
|
|
// Port: 5005
|
|
|
|
// Then click on "Debug"
|
|
|
|
#endif
|
2019-06-04 21:59:55 +00:00
|
|
|
return adb_execute(server->serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
|
2018-01-22 10:22:31 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 19:09:56 +00:00
|
|
|
static socket_t
|
2019-03-02 22:52:22 +00:00
|
|
|
connect_and_read_byte(uint16_t port) {
|
2018-03-12 07:35:51 +00:00
|
|
|
socket_t socket = net_connect(IPV4_LOCALHOST, port);
|
|
|
|
if (socket == INVALID_SOCKET) {
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
|
|
|
|
char byte;
|
|
|
|
// the connection may succeed even if the server behind the "adb tunnel"
|
|
|
|
// is not listening, so read one byte to detect a working connection
|
2019-05-28 19:03:44 +00:00
|
|
|
if (net_recv(socket, &byte, 1) != 1) {
|
2018-03-12 07:35:51 +00:00
|
|
|
// the server is not listening yet behind the adb tunnel
|
2019-06-20 10:15:45 +00:00
|
|
|
net_close(socket);
|
2018-03-12 07:35:51 +00:00
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:09:56 +00:00
|
|
|
static socket_t
|
2019-03-02 22:52:22 +00:00
|
|
|
connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
|
2018-03-12 07:35:51 +00:00
|
|
|
do {
|
|
|
|
LOGD("Remaining connection attempts: %d", (int) attempts);
|
|
|
|
socket_t socket = connect_and_read_byte(port);
|
|
|
|
if (socket != INVALID_SOCKET) {
|
|
|
|
// it worked!
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
if (attempts) {
|
|
|
|
SDL_Delay(delay);
|
|
|
|
}
|
|
|
|
} while (--attempts > 0);
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:09:56 +00:00
|
|
|
static void
|
2020-03-28 22:56:25 +00:00
|
|
|
close_socket(socket_t socket) {
|
|
|
|
assert(socket != INVALID_SOCKET);
|
|
|
|
net_shutdown(socket, SHUT_RDWR);
|
|
|
|
if (!net_close(socket)) {
|
2019-06-23 18:49:38 +00:00
|
|
|
LOGW("Could not close socket");
|
2018-02-16 13:55:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-02 19:09:56 +00:00
|
|
|
void
|
|
|
|
server_init(struct server *server) {
|
2018-02-08 14:16:27 +00:00
|
|
|
*server = (struct server) SERVER_INITIALIZER;
|
|
|
|
}
|
|
|
|
|
2020-03-28 22:15:15 +00:00
|
|
|
static int
|
|
|
|
run_wait_server(void *data) {
|
|
|
|
struct server *server = data;
|
|
|
|
cmd_simple_wait(server->process, NULL); // ignore exit code
|
2020-03-28 22:56:25 +00:00
|
|
|
// no need for synchronization, server_socket is initialized before this
|
|
|
|
// thread was created
|
|
|
|
if (server->server_socket != INVALID_SOCKET
|
2020-04-02 17:16:33 +00:00
|
|
|
&& !atomic_flag_test_and_set(&server->server_socket_closed)) {
|
2020-03-28 22:56:25 +00:00
|
|
|
// On Linux, accept() is unblocked by shutdown(), but on Windows, it is
|
|
|
|
// unblocked by closesocket(). Therefore, call both (close_socket()).
|
|
|
|
close_socket(server->server_socket);
|
|
|
|
}
|
2020-03-28 22:15:15 +00:00
|
|
|
LOGD("Server terminated");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
bool
|
2019-03-02 19:09:56 +00:00
|
|
|
server_start(struct server *server, const char *serial,
|
2019-06-04 21:59:55 +00:00
|
|
|
const struct server_params *params) {
|
2019-12-09 20:16:09 +00:00
|
|
|
server->port_range = params->port_range;
|
2018-03-12 07:35:51 +00:00
|
|
|
|
2018-03-12 09:19:12 +00:00
|
|
|
if (serial) {
|
|
|
|
server->serial = SDL_strdup(serial);
|
2018-05-26 12:20:05 +00:00
|
|
|
if (!server->serial) {
|
2019-03-02 22:52:22 +00:00
|
|
|
return false;
|
2018-05-26 12:20:05 +00:00
|
|
|
}
|
2018-03-12 09:19:12 +00:00
|
|
|
}
|
|
|
|
|
2018-02-08 14:16:27 +00:00
|
|
|
if (!push_server(serial)) {
|
2020-03-28 22:25:38 +00:00
|
|
|
goto error1;
|
2018-02-08 14:16:27 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 21:27:34 +00:00
|
|
|
if (!enable_tunnel_any_port(server, params->port_range,
|
|
|
|
params->force_adb_forward)) {
|
2020-03-28 22:25:38 +00:00
|
|
|
goto error1;
|
2018-02-08 14:16:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// server will connect to our server socket
|
2019-06-04 21:59:55 +00:00
|
|
|
server->process = execute_server(server, params);
|
2018-02-08 14:16:27 +00:00
|
|
|
if (server->process == PROCESS_NONE) {
|
2020-03-28 22:25:38 +00:00
|
|
|
goto error2;
|
2018-02-08 14:16:27 +00:00
|
|
|
}
|
|
|
|
|
2020-03-28 22:56:25 +00:00
|
|
|
// If the server process dies before connecting to the server socket, then
|
|
|
|
// the client will be stuck forever on accept(). To avoid the problem, we
|
|
|
|
// must be able to wake up the accept() call when the server dies. To keep
|
|
|
|
// things simple and multiplatform, just spawn a new thread waiting for the
|
|
|
|
// server process and calling shutdown()/close() on the server socket if
|
|
|
|
// necessary to wake up any accept() blocking call.
|
2020-03-28 22:15:15 +00:00
|
|
|
server->wait_server_thread =
|
|
|
|
SDL_CreateThread(run_wait_server, "wait-server", server);
|
|
|
|
if (!server->wait_server_thread) {
|
2020-03-28 23:49:08 +00:00
|
|
|
cmd_terminate(server->process);
|
2020-03-28 22:15:15 +00:00
|
|
|
cmd_simple_wait(server->process, NULL); // ignore exit code
|
|
|
|
goto error2;
|
|
|
|
}
|
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
server->tunnel_enabled = true;
|
2018-02-08 14:16:27 +00:00
|
|
|
|
2019-03-02 22:52:22 +00:00
|
|
|
return true;
|
2020-03-28 22:25:38 +00:00
|
|
|
|
|
|
|
error2:
|
|
|
|
if (!server->tunnel_forward) {
|
2020-04-02 17:16:33 +00:00
|
|
|
bool was_closed =
|
|
|
|
atomic_flag_test_and_set(&server->server_socket_closed);
|
|
|
|
// the thread is not started, the flag could not be already set
|
|
|
|
assert(!was_closed);
|
|
|
|
(void) was_closed;
|
2020-03-28 22:56:25 +00:00
|
|
|
close_socket(server->server_socket);
|
2020-03-28 22:25:38 +00:00
|
|
|
}
|
|
|
|
disable_tunnel(server);
|
|
|
|
error1:
|
|
|
|
SDL_free(server->serial);
|
|
|
|
return false;
|
2018-02-08 14:16:27 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 11:41:19 +00:00
|
|
|
bool
|
2019-03-02 19:09:56 +00:00
|
|
|
server_connect_to(struct server *server) {
|
2018-03-12 07:35:51 +00:00
|
|
|
if (!server->tunnel_forward) {
|
2019-05-28 19:03:54 +00:00
|
|
|
server->video_socket = net_accept(server->server_socket);
|
|
|
|
if (server->video_socket == INVALID_SOCKET) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
server->control_socket = net_accept(server->server_socket);
|
|
|
|
if (server->control_socket == INVALID_SOCKET) {
|
2019-11-15 08:44:24 +00:00
|
|
|
// the video_socket will be cleaned up on destroy
|
2019-05-28 19:02:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we don't need the server socket anymore
|
2020-04-02 17:16:33 +00:00
|
|
|
if (!atomic_flag_test_and_set(&server->server_socket_closed)) {
|
2020-03-28 22:56:25 +00:00
|
|
|
// close it from here
|
|
|
|
close_socket(server->server_socket);
|
|
|
|
// otherwise, it is closed by run_wait_server()
|
|
|
|
}
|
2018-03-12 07:35:51 +00:00
|
|
|
} else {
|
2019-03-02 22:52:22 +00:00
|
|
|
uint32_t attempts = 100;
|
|
|
|
uint32_t delay = 100; // ms
|
2019-05-28 19:03:54 +00:00
|
|
|
server->video_socket =
|
|
|
|
connect_to_server(server->local_port, attempts, delay);
|
|
|
|
if (server->video_socket == INVALID_SOCKET) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we know that the device is listening, we don't need several attempts
|
|
|
|
server->control_socket =
|
|
|
|
net_connect(IPV4_LOCALHOST, server->local_port);
|
|
|
|
if (server->control_socket == INVALID_SOCKET) {
|
2019-05-28 19:02:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-03-12 07:35:51 +00:00
|
|
|
}
|
2018-02-08 16:38:38 +00:00
|
|
|
|
|
|
|
// we don't need the adb tunnel anymore
|
2018-03-12 07:35:51 +00:00
|
|
|
disable_tunnel(server); // ignore failure
|
2019-03-02 22:52:22 +00:00
|
|
|
server->tunnel_enabled = false;
|
2018-02-08 16:38:38 +00:00
|
|
|
|
2019-05-28 11:41:19 +00:00
|
|
|
return true;
|
2018-02-08 14:16:27 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 19:09:56 +00:00
|
|
|
void
|
|
|
|
server_stop(struct server *server) {
|
2020-03-28 22:56:25 +00:00
|
|
|
if (server->server_socket != INVALID_SOCKET
|
2020-04-02 17:16:33 +00:00
|
|
|
&& !atomic_flag_test_and_set(&server->server_socket_closed)) {
|
2020-03-28 22:56:25 +00:00
|
|
|
close_socket(server->server_socket);
|
2019-05-30 09:40:12 +00:00
|
|
|
}
|
2019-05-28 19:03:54 +00:00
|
|
|
if (server->video_socket != INVALID_SOCKET) {
|
2020-03-28 22:56:25 +00:00
|
|
|
close_socket(server->video_socket);
|
2019-05-28 19:03:54 +00:00
|
|
|
}
|
|
|
|
if (server->control_socket != INVALID_SOCKET) {
|
2020-03-28 22:56:25 +00:00
|
|
|
close_socket(server->control_socket);
|
2019-05-30 09:40:12 +00:00
|
|
|
}
|
|
|
|
|
2019-11-27 20:11:40 +00:00
|
|
|
assert(server->process != PROCESS_NONE);
|
2018-02-16 10:11:07 +00:00
|
|
|
|
2020-03-28 23:49:08 +00:00
|
|
|
cmd_terminate(server->process);
|
2018-02-16 10:11:07 +00:00
|
|
|
|
2018-03-12 07:35:51 +00:00
|
|
|
if (server->tunnel_enabled) {
|
2018-02-08 14:16:27 +00:00
|
|
|
// ignore failure
|
2018-03-12 07:35:51 +00:00
|
|
|
disable_tunnel(server);
|
2018-02-08 14:16:27 +00:00
|
|
|
}
|
2020-03-28 22:15:15 +00:00
|
|
|
|
|
|
|
SDL_WaitThread(server->wait_server_thread, NULL);
|
2018-02-08 14:16:27 +00:00
|
|
|
}
|
2018-02-09 11:59:36 +00:00
|
|
|
|
2019-03-02 19:09:56 +00:00
|
|
|
void
|
|
|
|
server_destroy(struct server *server) {
|
2019-03-02 23:01:16 +00:00
|
|
|
SDL_free(server->serial);
|
2018-02-09 11:59:36 +00:00
|
|
|
}
|