use fmt; use io; use os; use os::exec; use strings; use temp; use unix; const static_args = [ "--ro-bind", "/usr", "/usr", "--dir", "/tmp", "--dir", "/var", "--symlink", "../tmp", "/var/tmp", "--proc", "/proc", "--dev", "/dev", "--ro-bind", "/etc/resolv.conf", "/etc/resolv.conf", "--symlink", "usr/lib", "/lib", "--symlink", "usr/lib64", "/lib64", "--symlink", "usr/bin", "/bin", "--symlink", "usr/sbin", "/sbin", "--unshare-all", "--die-with-parent", "--clearenv", "--setenv", "PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "--setenv", "HAREPATH", "/usr/local/src/hare/stdlib", ]; const HARE_COMMAND = "/usr/local/bin/hare"; /// Default command timeout in seconds. const DEFAULT_TIMEOUT = 30; export fn run_code(code: str) (str, str) = { let (uid, gid) = getids(); let args = strings::dupall(static_args); let app_dir = shared_app_dir(&args); // defer runs in reverse order. defer os::rmdirall(app_dir)!; home_dir(&args, uid); let code_path = fmt::asprintf("{}/main.ha", app_dir); defer free(code_path); let code_fp = os::create(code_path, 0o644)!; io::writeall(code_fp, strings::toutf8(code))!; io::close(code_fp)!; command(&args, "/app/main.ha"); let cmd = exec::cmd("bwrap", args...)!; let stdout_pipe = exec::pipe(); exec::addfile(&cmd, os::stdout_file, stdout_pipe.1); let stderr_pipe = exec::pipe(); exec::addfile(&cmd, os::stderr_file, stderr_pipe.1); let proc = exec::start(&cmd)!; io::close(stdout_pipe.1)!; io::close(stderr_pipe.1)!; let stdout_data = io::drain(stdout_pipe.0)!; io::close(stdout_pipe.0)!; let stderr_data = io::drain(stderr_pipe.0)!; io::close(stderr_pipe.0)!; let status = exec::wait(&proc)!; let stdout = strings::fromutf8(stdout_data) as str; let stderr = strings::fromutf8(stderr_data) as str; return (stdout, stderr); }; fn home_dir(args: *[]str, uid: u32) void = { let user_dir = fmt::asprintf("/run/user/{}", uid); let home_dir = fmt::asprintf("{}/home", user_dir); append(args, "--dir"); append(args, home_dir); append(args, "--setenv"); append(args, "HOME"); append(args, home_dir); append(args, "--setenv"); append(args, "XDG_RUNTIME_DIR"); append(args, user_dir); }; fn shared_app_dir(args: *[]str) str = { let tmp_app_dir = temp::dir(); append(args, "--bind"); append(args, tmp_app_dir); append(args, "/app"); append(args, "--chdir"); append(args, "/app"); return tmp_app_dir; }; fn command(args: *[]str, app_file: str) void = { append(args, "/usr/bin/timeout"); let timeout = fmt::asprintf("{}", DEFAULT_TIMEOUT); append(args, timeout); append(args, HARE_COMMAND); append(args, "run"); append(args, app_file); }; fn getids() (uint, uint) = { return (unix::getuid(), unix::getgid()); };