diff --git a/project/.sandbox/kinda-safe-godot/.gitignore b/project/.sandbox/kinda-safe-godot/.gitignore new file mode 100644 index 0000000..4ed7f3f --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/.gitignore @@ -0,0 +1,3 @@ +fakechroot_enviroment/root/home/* +fakechroot_enviroment/root/GATES-FILES/game +game \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/README.md b/project/.sandbox/kinda-safe-godot/README.md new file mode 100644 index 0000000..4e31618 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/README.md @@ -0,0 +1,38 @@ +# Kinda-Safe-Godot + +## Sandbox and File Isolation for Godot + +Kinda-Safe-Godot provides a sandboxed environment with file isolation for running Godot games. Although extensive efforts have been made to prevent sandbox escapes, it is essential to acknowledge that no system can guarantee absolute security. + +The sandboxed environment utilizes symbolic links to expose specific directories on your computer. This method may inadvertently leak some information, such as installed programs and resource usage. + +Running a bash environment inside the sandbox is not possible due to restricted syscalls. + +## Purpose + +The development of Kinda-Safe-Godot was primarily motivated by the [gates](https://flathub.org/apps/io.itch.nordup.TheGates) project. While a typical approach would involve creating a container image or using Flatpak, these solutions introduce significant dependencies, potentially hindering casual users from accessing the game. + +Instead of using this project, I recommend building a Flatpak, which provides finer controls and ensures compatibility across various systems. + +## Usage + +1. Execute the "runner/build.sh" script. +2. Export your game as a single file bundle and rename its executable file to "game". +3. Move the game executable to the main directory. +4. Run the "run_game.sh" script. + +## Generating the List of Syscalls + +To generate the list of syscalls, we suggest using the "strace" tool: + +``` +strace ./{game} 2> /dev/stdout | sed 's/\([^()]*\).*/\1/' > syscalls.txt +``` + +Once you have the "syscalls.txt" file, you can sort and deduplicate the entries: + +``` +cat syscalls.txt | sort | uniq +``` + +You may need to remove any garbage data. diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/fakechroot.sh b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/fakechroot.sh new file mode 100755 index 0000000..a4729f4 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/fakechroot.sh @@ -0,0 +1,263 @@ +#!/usr/bin/sh + +# fakechroot +# +# Script which sets fake chroot environment +# +# (c) 2011, 2013 Piotr Roszatycki , LGPL + + +FAKECHROOT_VERSION=2.20.1 + + +fakechroot_die () { + echo "$@" 1>&2 + exit 1 +} + + +fakechroot_usage () { + fakechroot_die "Usage: + fakechroot [-l|--lib fakechrootlib] + [-d|--elfloader ldso] + [-s|--use-system-libs] + [-e|--environment type] + [-c|--config-dir directory] + [-b|--bindir directory] + [--] [command] + fakechroot -v|--version + fakechroot -h|--help" +} + + +fakechroot_next_cmd () { + if [ "$1" = "fakeroot" ]; then + shift + # skip the options + while [ $# -gt 0 ]; do + case "$1" in + -h|-v) + break + ;; + -u|--unknown-is-real) + shift + ;; + -l|--lib|--faked|-s|-i|-b) + shift 2 + ;; + --) + shift + break + ;; + *) + break + ;; + esac + done + fi + + if [ -n "$1" -a "$1" != "-v" -a "$1" != "-h" ]; then + fakechroot_environment=`basename -- "$1"` + fi +} + + +if [ "$FAKECHROOT" = "true" ]; then + fakechroot_die "fakechroot: nested operation is not supported" +fi + + +# fakechroot doesn't work with CDPATH correctly +unset CDPATH + + +# Default settings +fakechroot_lib=libfakechroot.so +fakechroot_paths=$(pwd)/fakechroot_enviroment/fakechroot/ +fakechroot_sysconfdir=/etc/fakechroot +fakechroot_confdir= +fakechroot_environment= +fakechroot_bindir= + +if [ "$fakechroot_paths" = "no" ]; then + fakechroot_paths= +fi + +if command -v which >/dev/null; then + fakechroot_echo=`which echo` + fakechroot_echo=${fakechroot_echo:-/bin/echo} +else + fakechroot_echo=/bin/echo +fi + + +# Get options +fakechroot_getopttest=`getopt --version` +case $fakechroot_getopttest in + getopt*) + # GNU getopt + fakechroot_opts=`getopt -q -l lib: -l elfloader: -l use-system-libs -l config-dir: -l environment: -l bindir: -l version -l help -- +l:d:sc:e:b:vh "$@"` + ;; + *) + # POSIX getopt ? + fakechroot_opts=`getopt l:d:sc:e:b:vh "$@"` + ;; +esac + +if [ "$?" -ne 0 ]; then + fakechroot_usage +fi + +eval set -- "$fakechroot_opts" + +while [ $# -gt 0 ]; do + fakechroot_opt=$1 + shift + case "$fakechroot_opt" in + -h|--help) + fakechroot_usage + ;; + -v|--version) + echo "fakechroot version $FAKECHROOT_VERSION" + exit 0 + ;; + -l|--lib) + fakechroot_lib=`eval echo "$1"` + fakechroot_paths= + shift + ;; + -d|--elfloader) + FAKECHROOT_ELFLOADER=$1 + export FAKECHROOT_ELFLOADER + shift + ;; + -s|--use-system-libs) + fakechroot_paths="${fakechroot_paths:+$fakechroot_paths:}/usr/lib:/lib" + ;; + -c|--config-dir) + fakechroot_confdir=$1 + shift + ;; + -e|--environment) + fakechroot_environment=$1 + shift + ;; + -b|--bindir) + fakechroot_bindir=$1 + shift + ;; + --) + break + ;; + esac +done + +if [ -z "$fakechroot_environment" ]; then + fakechroot_next_cmd "$@" +fi + + +# Autodetect if dynamic linker supports --argv0 option +if [ -n "$FAKECHROOT_ELFLOADER" ]; then + fakechroot_detect=`$FAKECHROOT_ELFLOADER --argv0 echo $fakechroot_echo yes 2>&1` + if [ "$fakechroot_detect" = yes ]; then + FAKECHROOT_ELFLOADER_OPT_ARGV0="--argv0" + export FAKECHROOT_ELFLOADER_OPT_ARGV0 + fi +fi + + +# Swap libfakechroot and libfakeroot in LD_PRELOAD if needed +# libfakeroot must come first +# an alternate fakeroot library may be given +# in the FAKEROOT_ALT_LIB environment variable +if [ -n "$FAKEROOT_ALT_LIB" ]; then + lib_libfakeroot="$FAKEROOT_ALT_LIB" +else + lib_libfakeroot="libfakeroot-sysv.so" +fi + +for preload in $(echo "$LD_PRELOAD" | tr ':' ' '); do + case "$preload" in + "$lib_libfakeroot") + lib_libfakeroot_to_preload="$preload" + ;; + *) + lib_to_preload="${lib_to_preload:+${lib_to_preload}:}$preload" + ;; + esac +done + + +# Make sure the preload is available +fakechroot_paths="$fakechroot_paths${LD_LIBRARY_PATH:+${fakechroot_paths:+:}$LD_LIBRARY_PATH}" +fakechroot_lib="${lib_libfakeroot_to_preload:+${lib_libfakeroot_to_preload}:}$fakechroot_lib${lib_to_preload:+:$lib_to_preload}" + +fakechroot_detect=`LD_LIBRARY_PATH="$fakechroot_paths" LD_PRELOAD="$fakechroot_lib" FAKECHROOT_DETECT=1 $fakechroot_echo 2>&1` +case "$fakechroot_detect" in + fakechroot*) + fakechroot_libfound=yes + ;; + *) + fakechroot_libfound=no +esac + +if [ $fakechroot_libfound = no ]; then + fakechroot_die "fakechroot: preload library not found, aborting." +fi + + +# Additional environment setting from configuration file +if [ "$fakechroot_environment" != "none" ]; then + for fakechroot_e in "$fakechroot_environment" "${fakechroot_environment%.*}" default; do + for fakechroot_d in "$fakechroot_confdir" "$HOME/.fakechroot" "$fakechroot_sysconfdir"; do + fakechroot_f="$fakechroot_d/$fakechroot_e.env" + if [ -f "$fakechroot_f" ]; then + . "$fakechroot_f" + break 2 + fi + done + done +fi + + +# Check if substituted command is called +fakechroot_cmd=`command -v "$1"` + +fakechroot_cmd_wrapper=` + IFS=: + for fakechroot_cmd_subst in $FAKECHROOT_CMD_SUBST; do + case "$fakechroot_cmd_subst" in + "$fakechroot_cmd="*) + echo "${fakechroot_cmd_subst#*=}" + break 2 + ;; + esac + done +` + +# Set FAKECHROOT_CMD_ORIG if wrapped +if [ -n "$fakechroot_cmd_wrapper" ]; then + FAKECHROOT_CMD_ORIG="$fakechroot_cmd" + export FAKECHROOT_CMD_ORIG +fi + +fakechroot_cmd=${fakechroot_cmd_wrapper:-$1} + + +# Execute command +if [ -z "$*" ]; then + LD_LIBRARY_PATH="$fakechroot_paths" LD_PRELOAD="$fakechroot_lib" ${SHELL:-/bin/sh} + exit $? +else + if [ -n "$fakechroot_cmd" ]; then + # Call substituted command + shift + LD_LIBRARY_PATH="$fakechroot_paths" LD_PRELOAD="$fakechroot_lib" "$fakechroot_cmd" "$@" + exit $? + else + # Call original command + LD_LIBRARY_PATH="$fakechroot_paths" LD_PRELOAD="$fakechroot_lib" "$@" + exit $? + fi +fi diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/GATES-FILES/launch.sh b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/GATES-FILES/launch.sh new file mode 100755 index 0000000..b1fa7c2 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/GATES-FILES/launch.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chmod +x /GATES-FILES/game +/GATES-FILES/game $@ diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/dev b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/dev new file mode 120000 index 0000000..052abb2 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/dev @@ -0,0 +1 @@ +/dev \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/etc/fonts b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/etc/fonts new file mode 120000 index 0000000..846c025 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/etc/fonts @@ -0,0 +1 @@ +/etc/fonts/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/lib b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/lib new file mode 120000 index 0000000..dc55552 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/lib @@ -0,0 +1 @@ +/lib \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/lib64 b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/lib64 new file mode 120000 index 0000000..867b313 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/lib64 @@ -0,0 +1 @@ +/lib64 \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/run b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/run new file mode 120000 index 0000000..e4bfa79 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/run @@ -0,0 +1 @@ +/run/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/tmp b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/tmp new file mode 120000 index 0000000..8986f9d --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/tmp @@ -0,0 +1 @@ +/tmp/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/lib b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/lib new file mode 120000 index 0000000..6c1836e --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/lib @@ -0,0 +1 @@ +/usr/lib \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/lib64 b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/lib64 new file mode 120000 index 0000000..4875bfa --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/lib64 @@ -0,0 +1 @@ +/usr/lib64/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/X11 b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/X11 new file mode 120000 index 0000000..8d636f3 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/X11 @@ -0,0 +1 @@ +/usr/share/X11/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/alsa b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/alsa new file mode 120000 index 0000000..da93d10 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/alsa @@ -0,0 +1 @@ +/usr/share/alsa \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/locale b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/locale new file mode 120000 index 0000000..c9ad9c0 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/locale @@ -0,0 +1 @@ +/usr/share/locale/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/vulkan b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/vulkan new file mode 120000 index 0000000..6042501 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/usr/share/vulkan @@ -0,0 +1 @@ +/usr/share/vulkan/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/var/lib/alsa b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/var/lib/alsa new file mode 120000 index 0000000..5bae28e --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/fakechroot_enviroment/root/var/lib/alsa @@ -0,0 +1 @@ +/var/lib/alsa/ \ No newline at end of file diff --git a/project/.sandbox/kinda-safe-godot/list_child_processes.sh b/project/.sandbox/kinda-safe-godot/list_child_processes.sh new file mode 100755 index 0000000..f40bf9c --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/list_child_processes.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +extract_child_pid() { + echo "$(ps --ppid $1)" | grep -oE '^[[:space:]]*[0-9]+' | awk '{print $1}' +} + +pid=$1 +while [[ -n "$pid" ]]; do + pid=$(extract_child_pid "$pid") + if [[ -n "$pid" ]]; then + echo "$pid" + fi +done diff --git a/project/.sandbox/kinda-safe-godot/run_game.sh b/project/.sandbox/kinda-safe-godot/run_game.sh new file mode 100755 index 0000000..4055ac0 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/run_game.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd $1 +sh ./fakechroot_enviroment/fakechroot.sh chroot ./fakechroot_enviroment/root /bin/sh /GATES-FILES/launch.sh ${@:2} +rm ./fakechroot_enviroment/root/GATES-FILES/game diff --git a/project/.sandbox/kinda-safe-godot/zip.sh b/project/.sandbox/kinda-safe-godot/zip.sh new file mode 100755 index 0000000..a732b20 --- /dev/null +++ b/project/.sandbox/kinda-safe-godot/zip.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +zip=../sandbox/sandbox_env.zip + +rm $zip +zip -ry $zip fakechroot_enviroment run_game.sh list_child_processes.sh \ No newline at end of file diff --git a/project/project.godot b/project/project.godot index 8b23c7d..03d8810 100644 --- a/project/project.godot +++ b/project/project.godot @@ -12,6 +12,7 @@ config_version=5 config/name="TheGates" config/description="Building new Internet" +config/tags=PackedStringArray("thegates") run/main_scene="res://scenes/app.tscn" config/features=PackedStringArray("4.1") run/max_fps=60 diff --git a/project/resources/sandbox_env.tres b/project/resources/sandbox_env.tres new file mode 100644 index 0000000..f3a2c61 --- /dev/null +++ b/project/resources/sandbox_env.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="SandboxEnv" load_steps=2 format=3 uid="uid://bo6qgr210aamc"] + +[ext_resource type="Script" path="res://scripts/sandbox/sandbox_env.gd" id="1_2dvtt"] + +[resource] +script = ExtResource("1_2dvtt") +zip = "sandbox/sandbox_env.zip" +the_gates_folder = "fakechroot_enviroment/root/GATES-FILES" +the_gates_folder_abs = "/GATES-FILES" +snbx_exe_name = "game" +start_sh = "run_game.sh" +subprocesses_sh = "list_child_processes.sh" diff --git a/project/resources/sandbox_executable.tres b/project/resources/sandbox_executable.tres index 8eafb6d..630c5c2 100644 --- a/project/resources/sandbox_executable.tres +++ b/project/resources/sandbox_executable.tres @@ -1,6 +1,6 @@ [gd_resource type="Resource" script_class="SandboxExecutable" load_steps=2 format=3 uid="uid://cmb7xvbue74qa"] -[ext_resource type="Script" path="res://scripts/loading/sandbox_executable.gd" id="1_q0dqh"] +[ext_resource type="Script" path="res://scripts/sandbox/sandbox_executable.gd" id="1_q0dqh"] [resource] script = ExtResource("1_q0dqh") diff --git a/project/scenes/menu_body/world.tscn b/project/scenes/menu_body/world.tscn index 7bd4d7d..5a48a77 100644 --- a/project/scenes/menu_body/world.tscn +++ b/project/scenes/menu_body/world.tscn @@ -1,13 +1,14 @@ -[gd_scene load_steps=22 format=3 uid="uid://kywrsqro3d5i"] +[gd_scene load_steps=23 format=3 uid="uid://kywrsqro3d5i"] [ext_resource type="Script" path="res://scripts/loading/gate_loader.gd" id="1_uxhy6"] [ext_resource type="Resource" uid="uid://b1xvdym0qh6td" path="res://resources/gate_events.res" id="2_q7cvi"] -[ext_resource type="Script" path="res://scripts/loading/sandbox_manager.gd" id="3_0cpfc"] [ext_resource type="Resource" uid="uid://cmb7xvbue74qa" path="res://resources/sandbox_executable.tres" id="4_shus3"] [ext_resource type="Script" path="res://scripts/sandbox/render_result.gd" id="5_nlg2s"] [ext_resource type="Resource" uid="uid://l1quiaghft2f" path="res://resources/command_events.res" id="6_18mgg"] +[ext_resource type="Script" path="res://scripts/sandbox/sandbox_manager.gd" id="6_368sj"] [ext_resource type="Texture2D" uid="uid://cykx425p6ylwr" path="res://textures/background.png" id="7_52jgh"] [ext_resource type="Script" path="res://scripts/sandbox/input_sync.gd" id="8_1blsi"] +[ext_resource type="Resource" uid="uid://bo6qgr210aamc" path="res://resources/sandbox_env.tres" id="8_a6dvr"] [ext_resource type="Resource" uid="uid://crjhix0osmtnf" path="res://resources/ui_events.res" id="9_ir58h"] [ext_resource type="Script" path="res://scripts/sandbox/command_sync.gd" id="10_cqo55"] [ext_resource type="Script" path="res://scripts/ui/world/world_ui.gd" id="12_jdwjt"] @@ -281,10 +282,11 @@ gate_events = ExtResource("2_q7cvi") connect_timeout = 10.0 [node name="SandboxManager" type="Node" parent="." node_paths=PackedStringArray("render_result")] -script = ExtResource("3_0cpfc") +script = ExtResource("6_368sj") gate_events = ExtResource("2_q7cvi") render_result = NodePath("../HBoxContainer/WorldCanvas/RenderResult") snbx_executable = ExtResource("4_shus3") +snbx_env = ExtResource("8_a6dvr") [node name="HBoxContainer" type="HBoxContainer" parent="."] custom_minimum_size = Vector2(0, 700) diff --git a/project/scripts/loading/sandbox_manager.gd b/project/scripts/loading/sandbox_manager.gd deleted file mode 100644 index d48d024..0000000 --- a/project/scripts/loading/sandbox_manager.gd +++ /dev/null @@ -1,39 +0,0 @@ -extends Node -class_name SandboxManager - -@export var gate_events: GateEvents -@export var render_result: RenderResult -@export var snbx_executable: SandboxExecutable - -var sandbox_pid: int - - -func _ready() -> void: - gate_events.gate_loaded.connect(create_process) - - -func create_process(gate: Gate) -> void: - if not snbx_executable.exists(): - Debug.logerr("Sandbox executable not found at " + snbx_executable.path); return - - var pack_file = ProjectSettings.globalize_path(gate.resource_pack) - var shared_libs = ProjectSettings.globalize_path(gate.shared_libs_dir) - var args = [ - "--main-pack", pack_file, - "--gdext-libs-dir", shared_libs, - "--resolution", "%dx%d" % [render_result.width, render_result.height] - ] - Debug.logclr(snbx_executable.path + " " + " ".join(args), Color.DARK_VIOLET) - sandbox_pid = OS.create_process(snbx_executable.path, args) - - gate_events.gate_entered_emit() - - -func kill_process() -> void: - if OS.is_process_running(sandbox_pid): - OS.kill(sandbox_pid) - Debug.logclr("Process killed " + str(sandbox_pid), Color.DIM_GRAY) - - -func _exit_tree() -> void: - kill_process() diff --git a/project/scripts/sandbox/sandbox_env.gd b/project/scripts/sandbox/sandbox_env.gd new file mode 100644 index 0000000..d1c9893 --- /dev/null +++ b/project/scripts/sandbox/sandbox_env.gd @@ -0,0 +1,80 @@ +extends Resource +class_name SandboxEnv + +@export var zip: String +@export var the_gates_folder: String +@export var the_gates_folder_abs: String +@export var snbx_exe_name: String +@export var start_sh: String +@export var subprocesses_sh: String + +const ENV_FOLDER := "/tmp/sandbox_env" + +var zip_path: String : + get = get_zip_path + +var start: String : + get = get_start_sh + +var subprocesses: String : + get = get_subprocesses_sh + + +var main_pack: String + + +func get_zip_path() -> String: + var executable_dir = OS.get_executable_path().get_base_dir() + "/" + return executable_dir + zip + + +func get_start_sh() -> String: + return ProjectSettings.globalize_path(ENV_FOLDER + "/" + start_sh) + + +func get_subprocesses_sh() -> String: + return ProjectSettings.globalize_path(ENV_FOLDER + "/" + subprocesses_sh) + + +func zip_exists() -> bool: + return FileAccess.file_exists(zip_path) + + +func create_env(snbx_executable: String, gate: Gate) -> void: + Debug.logclr("create_env %s" % [ENV_FOLDER], Color.DIM_GRAY) + UnZip.unzip(zip_path, ENV_FOLDER, true) + + var folder = ENV_FOLDER + "/" + the_gates_folder + var executable = folder + "/" + snbx_exe_name + DirAccess.copy_absolute(snbx_executable, executable) + + main_pack = executable.get_basename() + "." + gate.resource_pack.get_extension() + DirAccess.copy_absolute(gate.resource_pack, main_pack) + main_pack = the_gates_folder_abs + "/" + main_pack.get_file() + + if not gate.shared_libs_dir.is_empty() and DirAccess.dir_exists_absolute(gate.shared_libs_dir): + for file in DirAccess.get_files_at(gate.shared_libs_dir): + var lib = gate.shared_libs_dir + "/" + file + var lib_in_folder = folder + "/" + file + DirAccess.copy_absolute(lib, lib_in_folder) + Debug.logclr(lib_in_folder, Color.DIM_GRAY) + + +func get_subprocesses(ppid: int) -> Array[int]: + var pids: Array[int] = [] + var output = [] + + OS.execute(subprocesses, [str(ppid)], output) + if output.is_empty(): return pids + + var s_pids = output[0].split('\n') + for s_pid in s_pids: + if s_pid.is_empty(): continue + var pid = s_pid.to_int() + pids.append(pid) + + return pids + + +func clean() -> void: + OS.execute("rm", ["-rf", ProjectSettings.globalize_path(ENV_FOLDER)]) diff --git a/project/scripts/loading/sandbox_executable.gd b/project/scripts/sandbox/sandbox_executable.gd similarity index 100% rename from project/scripts/loading/sandbox_executable.gd rename to project/scripts/sandbox/sandbox_executable.gd diff --git a/project/scripts/sandbox/sandbox_manager.gd b/project/scripts/sandbox/sandbox_manager.gd new file mode 100644 index 0000000..9710e4d --- /dev/null +++ b/project/scripts/sandbox/sandbox_manager.gd @@ -0,0 +1,52 @@ +extends Node +class_name SandboxManager + +@export var gate_events: GateEvents +@export var render_result: RenderResult +@export var snbx_executable: SandboxExecutable +@export var snbx_env: SandboxEnv + +var sandbox_pid: int + + +func _ready() -> void: + gate_events.gate_loaded.connect(start_sandbox) + + +func start_sandbox(gate: Gate) -> void: + if not snbx_executable.exists(): + Debug.logerr("Sandbox executable not found at " + snbx_executable.path); return + if not snbx_env.zip_exists(): + Debug.logerr("Sandbox environment not found at " + snbx_env.zip_path); return + + snbx_env.create_env(snbx_executable.path, gate) + +# var pack_file = ProjectSettings.globalize_path(gate.resource_pack) +# var shared_libs = ProjectSettings.globalize_path(gate.shared_libs_dir) + var args = [ + snbx_env.start.get_base_dir(), # cd to dir + "--main-pack", snbx_env.main_pack, +# "--gdext-libs-dir", shared_libs, + "--resolution", "%dx%d" % [render_result.width, render_result.height] + ] + Debug.logclr(snbx_env.start + " " + " ".join(args), Color.DARK_VIOLET) + sandbox_pid = OS.create_process(snbx_env.start, args) + + gate_events.gate_entered_emit() + + +func kill_sandbox() -> void: + if sandbox_pid == 0: return + + var pids = snbx_env.get_subprocesses(sandbox_pid) + pids.append(sandbox_pid) + + for pid in pids: + OS.kill(pid) + Debug.logclr("Process killed " + str(pid), Color.DIM_GRAY) + + snbx_env.clean() + + +func _exit_tree() -> void: + kill_sandbox() diff --git a/project/scripts/sandbox/unzip.gd b/project/scripts/sandbox/unzip.gd new file mode 100644 index 0000000..e133265 --- /dev/null +++ b/project/scripts/sandbox/unzip.gd @@ -0,0 +1,38 @@ +extends Node +class_name UnZip + + +static func unzip(zip_path: String, to_folder: String, contains_symlink: bool = false) -> void: + var reader = ZIPReader.new() + var err = reader.open(zip_path) + if err != OK: Debug.logerr("Cannot open file %s to unzip" % [zip_path]); return + + for path in reader.get_files(): + if path.get_file().is_empty(): # is directory + DirAccess.make_dir_recursive_absolute(to_folder + "/" + path) +# Debug.logclr("makedir %s" % [to_folder + "/" + path], Color.DIM_GRAY) + else: + create_file(reader, path, to_folder, contains_symlink) + + +static func create_file(reader: ZIPReader, path: String, folder: String, contains_symlink: bool) -> void: + var data = reader.read_file(path) + var symlink = "" + + if contains_symlink: + symlink = data.get_string_from_utf8() + if symlink.split("\n").size() != 1: + symlink = "" + + if contains_symlink and symlink.is_absolute_path(): + var link_to = ProjectSettings.globalize_path(folder + "/" + path.get_basename()) + OS.execute("ln", ["-s", symlink, link_to]) +# Debug.logclr("ln -s %s %s" % [symlink, link_to], Color.DIM_GRAY) + else: + var file_path = folder + "/" + path + var file = FileAccess.open(file_path, FileAccess.WRITE) + file.store_buffer(data) + file.close() + if file_path.get_extension() == "sh": + OS.execute("chmod", ["+x", ProjectSettings.globalize_path(file_path)]) +# Debug.logclr("touch %s" % [folder + "/" + path], Color.DIM_GRAY)