godot-xterm/addons/godot_xterm/nodes/pty/unix/pty_unix.gd

138 lines
3.2 KiB
GDScript
Raw Permalink Normal View History

# Derived from https://github.com/microsoft/node-pty/blob/main/src/unixTerminal.ts
# Copyright (c) 2012-2015, Christopher Jeffrey (MIT License).
# Copyright (c) 2016, Daniel Imms (MIT License).
# Copyright (c) 2018, Microsoft Corporation (MIT License).
# Copyright (c) 2021, Leroy Hopson (MIT License).
tool
extends "../pty.gd"
const PTYUnix = preload("./pty_unix.gdns")
const FALLBACK_FILE = "sh"
# Security warning: use this option with great caution, as opened file descriptors
# with higher privileges might leak to the child program.
var uid: int
var gid: int
var thread: Thread
var _fd: int = -1
var _exit_cb: FuncRef
static func get_uid() -> int:
return -1 # Not implemented.
static func get_gid() -> int:
return -1 # Not implemented.
func _ready():
if autostart:
fork()
func _resize(cols: int, rows: int) -> void:
if _fd < 0:
return
PTYUnix.new().resize(_fd, cols, rows)
func _fork_thread(args):
var result = preload("./pty_unix.gdns").new().callv("fork", args)
return result
func fork(
file: String = OS.get_environment("SHELL"),
args: PoolStringArray = PoolStringArray(),
cwd = LibuvUtils.get_cwd(),
p_cols: int = DEFAULT_COLS,
p_rows: int = DEFAULT_ROWS,
uid: int = -1,
gid: int = -1,
utf8 = true
):
# File.
if file.empty():
file = FALLBACK_FILE
# Environment variables.
# If we are using OS env vars, sanitize them to remove variables that might confuse our terminal.
var final_env := _sanitize_env(LibuvUtils.get_os_environ()) if use_os_env else {}
for key in env.keys():
final_env[key] = env[key]
var parsed_env: PoolStringArray = _parse_env(final_env)
# Exit callback.
_exit_cb = FuncRef.new()
_exit_cb.set_instance(self)
_exit_cb.set_function("_on_exit")
# Actual fork.
var result = PTYUnix.new().fork( # VERY IMPORTANT: The must be set null or 0, otherwise will get an ENOTSOCK error after connecting our pipe to the fd.
file, null, args, parsed_env, cwd, cols, rows, uid, gid, utf8, _exit_cb
)
if result[0] != OK:
push_error("Fork failed.")
status = STATUS_ERROR
return FAILED
_fd = result[1].fd
if _fd < 0:
push_error("File descriptor must be a non-negative integer value.")
status = STATUS_ERROR
return FAILED
pid = result[1].pid
_pipe = Pipe.new()
_pipe.open(_fd)
# Must connect to signal AFTER opening, otherwise we will get error ENOTSOCK.
_pipe.connect("data_received", self, "_on_pipe_data_received")
return OK
func open(cols: int = DEFAULT_COLS, rows: int = DEFAULT_ROWS) -> Array:
return PTYUnix.new().open(cols, rows)
func _exit_tree():
_exit_cb = null
if pid > 1:
LibuvUtils.kill(pid, Signal.SIGHUP)
func _on_pipe_data_received(data):
emit_signal("data_received", data)
func _on_exit(exit_code: int, signum: int) -> void:
if is_instance_valid(self):
pid = -1
emit_signal("exited", exit_code, signum)
func _sanitize_env(env: Dictionary) -> Dictionary:
# Make sure we didn't start our server from inside tmux.
env.erase("TMUX")
env.erase("TMUX_PANE")
# Make sure we didn't start our server from inside screen.
# http://web.mit.edu/gnu/doc/html/screen_20.html
env.erase("STY")
env.erase("WINDOW")
# Delete some variables that might confuse our terminal.
env.erase("WINDOWID")
env.erase("TERMCAP")
env.erase("COLUMNS")
env.erase("LINES")
return env