Class: Muxr::PTYProcess

Inherits:
Object
  • Object
show all
Defined in:
lib/muxr/pty_process.rb

Overview

Owns a single pseudo-terminal pair plus the child shell process attached to the slave side. The parent side is exposed via #io / #read_nonblock / #write.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command: nil, rows: 24, cols: 80, cwd: nil, env_overrides: {}) ⇒ PTYProcess

Returns a new instance of PTYProcess.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/muxr/pty_process.rb', line 9

def initialize(command: nil, rows: 24, cols: 80, cwd: nil, env_overrides: {})
  @rows = rows
  @cols = cols
  @exited = false

  shell = command || ENV["SHELL"] || "/bin/sh"
  env = ENV.to_h.merge("TERM" => "xterm-256color").merge(env_overrides)
  env["LINES"]   = rows.to_s
  env["COLUMNS"] = cols.to_s

  chdir = (cwd && File.directory?(cwd)) ? cwd : Dir.pwd

  @reader, @writer, @pid = PTY.spawn(env, shell, chdir: chdir)
  @io = @reader
  resize(rows, cols)
end

Instance Attribute Details

#colsObject (readonly)

Returns the value of attribute cols.



7
8
9
# File 'lib/muxr/pty_process.rb', line 7

def cols
  @cols
end

#ioObject (readonly)

Returns the value of attribute io.



7
8
9
# File 'lib/muxr/pty_process.rb', line 7

def io
  @io
end

#pidObject (readonly)

Returns the value of attribute pid.



7
8
9
# File 'lib/muxr/pty_process.rb', line 7

def pid
  @pid
end

#rowsObject (readonly)

Returns the value of attribute rows.



7
8
9
# File 'lib/muxr/pty_process.rb', line 7

def rows
  @rows
end

Instance Method Details

#alive?Boolean

Returns:

  • (Boolean)


52
53
54
55
56
57
58
59
# File 'lib/muxr/pty_process.rb', line 52

def alive?
  return false if @exited
  Process.kill(0, @pid)
  true
rescue Errno::ESRCH, Errno::EPERM
  @exited = true
  false
end

#closeObject



67
68
69
70
71
72
73
74
# File 'lib/muxr/pty_process.rb', line 67

def close
  reap
  Process.kill("TERM", @pid) if alive?
  @reader.close unless @reader.closed?
  @writer.close if @writer != @reader && !@writer.closed?
rescue Errno::ESRCH, Errno::EBADF, IOError
  # already gone
end

#cwdObject

Best-effort cwd of the child process. Used to inherit cwd when opening the drawer or for session save/restore. Falls back to nil if the system doesn’t expose the information.



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/muxr/pty_process.rb', line 79

def cwd
  if File.directory?("/proc/#{@pid}")
    File.readlink("/proc/#{@pid}/cwd")
  else
    # macOS / BSD fallback via lsof.
    out = `lsof -a -p #{@pid} -d cwd -Fn 2>/dev/null`
    line = out.lines.find { |l| l.start_with?("n/") }
    line && line[1..].strip
  end
rescue StandardError
  nil
end

#read_nonblock(max = 8192) ⇒ Object



33
34
35
36
37
38
39
40
# File 'lib/muxr/pty_process.rb', line 33

def read_nonblock(max = 8192)
  @reader.read_nonblock(max)
rescue IO::WaitReadable
  nil
rescue EOFError, Errno::EIO
  @exited = true
  nil
end

#reapObject



61
62
63
64
65
# File 'lib/muxr/pty_process.rb', line 61

def reap
  Process.waitpid(@pid, Process::WNOHANG)
rescue Errno::ECHILD
  nil
end

#resize(rows, cols) ⇒ Object



42
43
44
45
46
47
48
49
50
# File 'lib/muxr/pty_process.rb', line 42

def resize(rows, cols)
  @rows = rows
  @cols = cols
  begin
    @reader.winsize = [rows, cols, 0, 0]
  rescue StandardError
    # Some platforms reject zero pixel sizes; ignore.
  end
end

#write(data) ⇒ Object



26
27
28
29
30
31
# File 'lib/muxr/pty_process.rb', line 26

def write(data)
  @writer.write(data)
  @writer.flush
rescue Errno::EIO, IOError, Errno::EPIPE
  @exited = true
end