Skip to content

Commit

Permalink
Get SSCSM mostly working on Android
Browse files Browse the repository at this point in the history
Log timestamps aren't working.
  • Loading branch information
TurkeyMcMac committed Jan 28, 2023
1 parent fdb4d03 commit 21c2496
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 4 deletions.
1 change: 1 addition & 0 deletions android/native/jni/Android.mk
Expand Up @@ -216,6 +216,7 @@ LOCAL_SRC_FILES := \
../../src/player.cpp \
../../src/porting.cpp \
../../src/porting_android.cpp \
../../src/process_sandbox.cpp \
../../src/profiler.cpp \
../../src/raycast.cpp \
../../src/reflowscan.cpp \
Expand Down
156 changes: 156 additions & 0 deletions src/porting_android.cpp
Expand Up @@ -32,6 +32,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream>
#include <exception>
#include <cstdlib>
#include <signal.h>
#include <set>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <vector>

#ifdef GPROF
#include "prof.h"
Expand All @@ -44,6 +52,8 @@ void android_main(android_app *app)
int retval = 0;
porting::app_global = app;

porting::selfExecInit();

Thread::setName("Main");

try {
Expand Down Expand Up @@ -136,6 +146,8 @@ void cleanupAndroid()
moncleanup();
#endif

porting::selfExecDestroy();

JavaVM *jvm = app_global->activity->vm;
jvm->DetachCurrentThread();
}
Expand Down Expand Up @@ -183,6 +195,150 @@ void initializePathsAndroid()
}
}

static int self_exec_socket = -1;
static pid_t self_exec_pid = 0;

int self_exec_spawned_proc(char *args, size_t args_size, int fd) noexcept
{
int argc = 0;
std::vector<char *> argv;
for (size_t i = 0; i < args_size; i++) {
void *next = memchr(args + i, 0, args_size - i);
if (next) {
argc++;
argv.push_back(args + i);
i = (char *)next - args;
} else {
break;
}
}
argv.push_back(nullptr);
if (fd >= 0 && fd != SELF_EXEC_SPAWN_FD && dup2(fd, SELF_EXEC_SPAWN_FD) < 0)
return EXIT_FAILURE;
return main(argc, argv.data());
}

void self_exec_spawner_proc(int socket) noexcept
{
porting::app_global = nullptr;
porting::jnienv = nullptr;

std::set<pid_t> children;
try {
for (;;) {
char buf[1024] = {};
struct iovec iov = {buf, sizeof(buf)};
char cmsg_buf alignas(cmsghdr) [CMSG_SPACE(sizeof(int))] = {};
msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buf;
msg.msg_controllen = CMSG_SPACE(sizeof(int));
ssize_t recv_size = recvmsg(socket, &msg, 0);
if (recv_size <= 0)
break;
if (recv_size == sizeof(pid_t) + 1 && buf[sizeof(pid_t)] == 'k') {
pid_t pid;
memcpy(&pid, buf, sizeof(pid));
if (children.find(pid) != children.end()) {
children.erase(pid);
kill(pid, SIGKILL);
waitpid(pid, nullptr, 0);
}
} else {
int fd = -1;
if (CMSG_FIRSTHDR(&msg))
memcpy(&fd, CMSG_DATA(CMSG_FIRSTHDR(&msg)), sizeof(fd));
pid_t pid = fork();
if (pid == 0) {
exit(self_exec_spawned_proc(buf, recv_size, fd));
} else if (pid > 0) {
children.insert(pid);
send(socket, &pid, sizeof(pid), MSG_EOR);
}
close(fd);
}
}
} catch (...) {
}
for (pid_t pid : children)
kill(pid, SIGKILL);
for (pid_t pid : children)
waitpid(pid, nullptr, 0);
}

bool selfExecInit() noexcept
{
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
pid_t pid = fork();
if (pid == 0) {
close(sockets[0]);
self_exec_spawner_proc(sockets[1]);
exit(EXIT_SUCCESS);
} else if (pid > 0) {
close(sockets[1]);
self_exec_socket = sockets[0];
self_exec_pid = pid;
return true;
}
close(sockets[0]);
close(sockets[1]);
}
return false;
}

bool selfExecDestroy() noexcept
{
shutdown(self_exec_socket, SHUT_RDWR);
close(self_exec_socket);
bool ok = waitpid(self_exec_pid, nullptr, 0) < 0;
self_exec_socket = -1;
self_exec_pid = 0;
return ok;
}

pid_t selfExecSpawn(const char *const argv[], int fd) noexcept
{
msghdr msg = {};
size_t argc;
for (argc = 0; argv[argc]; argc++)
;
if (argc < 1)
return -1;
std::unique_ptr<iovec[]> iov(new iovec[argc]);
for (size_t i = 0; i < argc; i++) {
iov[i].iov_base = (void *)argv[i];
iov[i].iov_len = strlen(argv[i]) + 1;
}
msg.msg_iov = iov.get();
msg.msg_iovlen = argc;
char cmsg_buf alignas(cmsghdr) [CMSG_SPACE(sizeof(fd))] = {};
if (fd >= 0) {
msg.msg_control = cmsg_buf;
msg.msg_controllen = CMSG_SPACE(sizeof(fd));
CMSG_FIRSTHDR(&msg)->cmsg_level = SOL_SOCKET;
CMSG_FIRSTHDR(&msg)->cmsg_type = SCM_RIGHTS;
CMSG_FIRSTHDR(&msg)->cmsg_len = CMSG_LEN(sizeof(fd));
memcpy(CMSG_DATA(CMSG_FIRSTHDR(&msg)), &fd, sizeof(fd));
}
if (sendmsg(self_exec_socket, &msg, MSG_EOR) < 0)
return -1;
pid_t pid = 0;
if (recv(self_exec_socket, &pid, sizeof(pid), 0) <= 0)
return -1;
return pid;
}

bool selfExecKill(pid_t pid) noexcept
{
char buf[sizeof(pid) + 1];
memcpy(buf, &pid, sizeof(pid));
buf[sizeof(pid)] = 'k';
return send(self_exec_socket, buf, sizeof(buf), MSG_EOR) >= 0;
}


void showInputDialog(const std::string &acceptButton, const std::string &hint,
const std::string &current, int editType)
{
Expand Down
14 changes: 14 additions & 0 deletions src/porting_android.h
Expand Up @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <android/log.h>

#include <string>
#include <unistd.h>

namespace porting {
// java app
Expand All @@ -41,6 +42,19 @@ void initAndroid();

void cleanupAndroid();

// A special subsystem is needed to simulate self-exec-ing since an executable
// is not present on Android.

bool selfExecInit() noexcept;

bool selfExecDestroy() noexcept;

#define SELF_EXEC_SPAWN_FD 3

pid_t selfExecSpawn(const char *const argv[], int fd) noexcept;

bool selfExecKill(pid_t pid) noexcept;

/**
* Initializes path_* variables for Android
* @param env Android JNI environment
Expand Down
22 changes: 19 additions & 3 deletions src/process_sandbox.cpp
Expand Up @@ -96,6 +96,20 @@ bool start_sandbox()
#define SYSCALL_ARG(n) (offsetof(seccomp_data, args) + (n) * 8 + 4)
#endif

#if defined(__NR_mmap) || defined(__NR_mmap2)
# define HAS_MMAP 1
# if defined(__NR_mmap)
# define NR_MMAP_1 __NR_mmap
# if defined(__NR_mmap2)
# define NR_MMAP_2 __NR_mmap2
# endif
# else
# define NR_MMAP_1 __NR_mmap2
# endif
#else
# define HAS_MMAP 0
#endif

bool start_sandbox()
{
close_resources();
Expand All @@ -117,15 +131,17 @@ bool start_sandbox()
// Allow clock_getres
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_clock_getres, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
#if HAS_MMAP
// Allow mmap/mmap2 with MAP_ANONYMOUS
#if defined(__NR_mmap2)
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap2, 1, 0),
#if defined(NR_MMAP_2)
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, NR_MMAP_2, 1, 0),
#endif
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap, 0, 4),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, NR_MMAP_1, 0, 4),
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SYSCALL_ARG(3)),
BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, MAP_ANONYMOUS, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS),
#endif
// Allow mprotect
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mprotect, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
Expand Down
26 changes: 25 additions & 1 deletion src/script/csm/csm_controller.cpp
Expand Up @@ -38,12 +38,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <spawn.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#if defined(__ANDROID__)
#include "porting_android.h"
#include <sys/syscall.h>
#else
#include <spawn.h>
#endif
#endif // !defined(_WIN32)

Expand Down Expand Up @@ -149,12 +151,16 @@ bool CSMController::start()
CloseHandle(m_ipc_shm);
error_shm:
#else
#if !defined(__ANDROID__)
char exe_path[PATH_MAX];
#endif

std::string shm_str;

const char *argv[] = {"minetest", "--csm", client_path.c_str(), nullptr, nullptr};
#if !defined(__ANDROID__)
char *const envp[] = {nullptr};
#endif

int shm = -1;

Expand All @@ -172,7 +178,11 @@ bool CSMController::start()
#endif
if (shm == -1)
goto error_shm;
#if defined(__ANDROID__)
shm_str = std::to_string(SELF_EXEC_SPAWN_FD);
#else
shm_str = std::to_string(shm);
#endif
argv[3] = shm_str.c_str();

{
Expand All @@ -196,19 +206,29 @@ bool CSMController::start()
goto error_make_ipc;
}

#if !defined(__ANDROID__)
if (!porting::getCurrentExecPath(exe_path, sizeof(exe_path)))
goto error_exe_path;
#endif

#if defined(__ANDROID__)
m_script_pid = porting::selfExecSpawn(argv, shm);
if (m_script_pid < 0)
goto error_spawn;
#else
if (posix_spawn(&m_script_pid, exe_path,
nullptr, nullptr, (char *const *)argv, envp) != 0)
goto error_spawn;
#endif

close(shm);
return true;

error_spawn:
m_script_pid = 0;
#if !defined(__ANDROID__)
error_exe_path:
#endif
error_make_ipc:
m_ipc_shared->~IPCChannelShared();
munmap(m_ipc_shared, sizeof(IPCChannelShared));
Expand Down Expand Up @@ -236,9 +256,13 @@ void CSMController::stop()
UnmapViewOfFile(m_ipc_shared);
CloseHandle(m_ipc_sem_a);
CloseHandle(m_ipc_sem_b);
#else
#if defined(__ANDROID__)
porting::selfExecKill(m_script_pid);
#else
kill(m_script_pid, SIGKILL);
waitpid(m_script_pid, nullptr, 0);
#endif
m_script_pid = 0;
m_ipc_shared->~IPCChannelShared();
munmap(m_ipc_shared, sizeof(IPCChannelShared));
Expand Down

0 comments on commit 21c2496

Please sign in to comment.