Class: Pssh::Pty

Inherits:
Object
  • Object
show all
Defined in:
lib/pssh/pty.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePty

Returns a new instance of Pty.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/pssh/pty.rb', line 12

def initialize
  @stream = ''
  set_command
  clear_environment
  Thread.new do
    begin
      @read, @write, @pid = PTY.spawn(@command)
      @write.winsize = $stdout.winsize
      if new?
        system("clear")
        pssh = <<-BANNER
# [ pssh terminal ]
# Type `exit` to terminate this terminal.
BANNER
        $stdout.puts pssh
        Signal.trap(:WINCH) do
          resize!
        end
        system("stty raw -echo")
      end

      begin
        io = [@read]
        io << $stdin if new?
        rs, ws = IO.select(io)
        r = rs[0]
        while (data = r.read_nonblock(2048)) do
          if new? && r == $stdin
            @write.write_nonblock data
          else
            $stdout.write_nonblock data if new?
            data.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '')
            data.encode!('UTF-8', 'UTF-16')
            if data.valid_encoding?
              store data
              Pssh.socket.write data
            end
          end
        end
      rescue Exception => e
        if e.is_a?(Errno::EAGAIN)
          retry
        else
          system("stty -raw echo") if new?
          puts 'Terminating Pssh.'
          Kernel.exit!
        end
      end
    end
  end
end

Instance Attribute Details

#attach_cmdObject (readonly)

Returns the value of attribute attach_cmd.



10
11
12
# File 'lib/pssh/pty.rb', line 10

def attach_cmd
  @attach_cmd
end

#pathObject (readonly)

Returns the value of attribute path.



9
10
11
# File 'lib/pssh/pty.rb', line 9

def path
  @path
end

#pidObject (readonly)

Returns the value of attribute pid.



7
8
9
# File 'lib/pssh/pty.rb', line 7

def pid
  @pid
end

#readObject (readonly)

Returns the value of attribute read.



4
5
6
# File 'lib/pssh/pty.rb', line 4

def read
  @read
end

#streamObject (readonly)

attr_reader :write



6
7
8
# File 'lib/pssh/pty.rb', line 6

def stream
  @stream
end

Instance Method Details

#clear_environmentObject



64
65
66
67
# File 'lib/pssh/pty.rb', line 64

def clear_environment
  ENV['TMUX'] = nil
  ENV['STY'] = nil
end

#existing?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/pssh/pty.rb', line 73

def existing?
  @existing_socket
end

#new?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/pssh/pty.rb', line 69

def new?
  !existing?
end

#resize!Object

Public: Resizes the PTY session based on all the open windows.

Returns nothing.



117
118
119
120
121
122
123
# File 'lib/pssh/pty.rb', line 117

def resize!
  winsizes = Pssh.socket.sessions.values.map { |sess| sess[:winsize] }
  winsizes << $stdout.winsize if new?
  y = winsizes.map { |w| w[0] }.min
  x = winsizes.map { |w| w[1] }.min
  @write.winsize = [ y, x ]
end

#send_display_message(user) ⇒ Object

Public: Sends a message to the tmux or screen display notifying of a new user that has connected.

Returns nothing.



129
130
131
132
133
134
135
136
137
138
# File 'lib/pssh/pty.rb', line 129

def send_display_message(user)
  if @existing_socket
    case Pssh.command.to_sym
    when :tmux
      `tmux -S #{@path} display-message "#{user} has connected"`
    when :screen
      `screen -S #{@path} -X wall "#{user} has connected"`
    end
  end
end

#set_commandObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/pssh/pty.rb', line 77

def set_command
  case Pssh.command.to_sym
  when :tmux
    if ENV['TMUX']
      @path = ENV['TMUX'].split(',').first
      @existing_socket = true
      @command = "tmux -S #{@path} attach"
    else
      @path = "/tmp/#{Pssh.default_socket_path}"
      @command = "tmux -S #{@path} new"
    end
    @attach_cmd = "tmux -S #{@path} attach"
  when :screen
    if ENV['STY']
      @path = ENV['STY']
      @existing_socket = true
      @command = "screen -S #{@path} -X multiuser on && screen -x #{@path}"
    else
      @path = Pssh.default_socket_path
      @command = "screen -S #{@path}"
      puts @command
    end
    @attach_cmd = "screen -x #{@path}"
  else
    @path = nil
    @command = ENV['SHELL'] || (`which zsh` && 'zsh') || (`which sh` && 'sh') || 'bash'
  end
end

#store(data) ⇒ Object

Internal: Store data to the stream so that when a new connection is started we can send all that data and give them the visual.

Returns nothing.



144
145
146
147
# File 'lib/pssh/pty.rb', line 144

def store(data)
  @stream << data
  @stream = @stream[-Pssh.cache_length..-1] if @stream.length > Pssh.cache_length
end

#write(data) ⇒ Object

Public: Writes to the open stream if they have access.

Returns nothing.



109
110
111
# File 'lib/pssh/pty.rb', line 109

def write(data)
  @write.write_nonblock data if Pssh.io_mode['w']
end