Module: Net::SSH::Open3

Included in:
Connection::Session
Defined in:
lib/net-ssh-open3.rb

Overview

Net::SSH Open3 extensions. All methods have the same argument list.

optional env: custom environment variables Hash. Note that SSH server typically restricts changeable variables to a very small set, e.g. for OpenSSH see AcceptEnv in /etc/ssh/sshd_config (AcceptEnv LANG LC_*)

command: a single shell command (like in sh -c), or an executable program.

optional arg1, arg2, ...: arguments to an executable mentioned above.

optional options: options hash, keys:

  • redirects: Hash of redirections which will be appended to a command line (you can’t transfer a pipe to a remote system). Key: one of :in, :out, :err or a String, value: Integer to redirect to fd, String to redirect to a file. Example:

    { '>>' => '/tmp/log', err: 1 }
    

    translates to

    '>>/tmp/log 2>&1'
    
  • channel_retries: Integer number of retries in case of channel open failure (ssh server usually limits a session to 10 channels), or an array of [retries, delay]

  • stdin_data: for capture* only, specifies data to be immediately sent to stdin of a remote process. stdin is immediately closed then.

  • logger: an object which responds to debug/info/warn/error and optionally init/stdin/stdout/stderr to log debug information and data exchange stream

  • pty: true or a Hash of PTY settings to request a pseudo-TTY, see Net::SSH documentation for more information. A note about sending TERM/QUIT: use modes, e.g.:

    Net::SSH.start('localhost', ENV['USER']).capture2e('cat', pty: {
        modes: {
          Net::SSH::Connection::Term::VINTR => 0x01020304, # INT on this 4-byte-sequence
          Net::SSH::Connection::Term::VQUIT => 0xdeadbeef, # QUIT on this 4-byte sequence
          Net::SSH::Connection::Term::VEOF => 0xfacefeed, # EOF sequence
          Net::SSH::Connection::Term::ECHO => 0, # disable echoing
          Net::SSH::Connection::Term::ISIG => 1 # enable sending signals
        }
      },
      stdin_data: [0xDEADBEEF].pack('L'),
      logger: Class.new { alias method_missing puts; def respond_to?(_); true end }.new)
    # log skipped ...
    # => ["", #<Net::SSH::Process::Status: pid 1744 QUIT (signal 3) core true>]
    

    Note that just closing stdin is not enough for PTY. You should explicitly send VEOF as a first char of a line, see termios(3).

Instance Method Summary collapse

Instance Method Details

#capture2(*args) ⇒ Object

Captures stdout only. Returns [String, Net::SSH::Process::Status]



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/net-ssh-open3.rb', line 146

def capture2(*args)
  stdout = StringIO.new
  stdin_data = args.last[:stdin_data] if Hash === args.last

  run_popen(*args,
            stdin: stdin_data,
            stdout: stdout,
            block_pipes: [stdout]) do |stdout, waiter_thread|
    [stdout.string, waiter_thread.value]
  end
end

#capture2e(*args) ⇒ Object

Captures stdout and stderr into one string. Returns [String, Net::SSH::Process::Status]



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/net-ssh-open3.rb', line 159

def capture2e(*args)
  stdout = StringIO.new
  stdin_data = args.last[:stdin_data] if Hash === args.last

  run_popen(*args,
            stdin: stdin_data,
            stdout: stdout,
            stderr: stdout,
            block_pipes: [stdout]) do |stdout, waiter_thread|
    [stdout.string, waiter_thread.value]
  end
end

#capture3(*args) ⇒ Object

Captures stdout and stderr into separate strings. Returns [String, String, Net::SSH::Process::Status]



173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/net-ssh-open3.rb', line 173

def capture3(*args)
  stdout, stderr = StringIO.new, StringIO.new
  stdin_data = args.last[:stdin_data] if Hash === args.last

  run_popen(*args,
            stdin: stdin_data,
            stdout: stdout,
            stderr: stderr,
            block_pipes: [stdout, stderr]) do |stdout, stderr, waiter_thread|
    [stdout.string, stderr.string, waiter_thread.value]
  end
end

#popen2(*args, &block) ⇒ Object

Yields stdin, stdout, waiter_thread into a block.



219
220
221
222
223
224
225
226
227
228
# File 'lib/net-ssh-open3.rb', line 219

def popen2(*args, &block)
  stdin_inner, stdin_outer = IO.pipe
  stdout_outer, stdout_inner = IO.pipe

  run_popen(*args,
            stdin: stdin_inner,
            stdout: stdout_inner,
            block_pipes: [stdin_outer, stdout_outer],
            &block)
end

#popen2e(*args, &block) ⇒ Object

Yields stdin, stdout-stderr, waiter_thread into a block.



206
207
208
209
210
211
212
213
214
215
216
# File 'lib/net-ssh-open3.rb', line 206

def popen2e(*args, &block)
  stdin_inner, stdin_outer = IO.pipe
  stdout_outer, stdout_inner = IO.pipe

  run_popen(*args,
            stdin: stdin_inner,
            stdout: stdout_inner,
            stderr: stdout_inner,
            block_pipes: [stdin_outer, stdout_outer],
            &block)
end

#popen3(*args, &block) ⇒ Object

Opens pipes to a remote process. Yields stdin, stdout, stderr, waiter_thread into a block. Will wait for a process to finish. Joining (or getting a value of) waither_thread inside a block will wait for a process right there. ‘status’ Thread-Attribute of waiter_thread holds an instance of Net::SSH::Process::Status for a remote process. Careful: don’t forget to read stderr, otherwise if your process generates too much stderr output the pipe may overload and ssh loop will get stuck writing to it.



192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/net-ssh-open3.rb', line 192

def popen3(*args, &block)
  stdin_inner, stdin_outer = IO.pipe
  stdout_outer, stdout_inner = IO.pipe
  stderr_outer, stderr_inner = IO.pipe

  run_popen(*args,
            stdin: stdin_inner,
            stdout: stdout_inner,
            stderr: stderr_inner,
            block_pipes: [stdin_outer, stdout_outer, stderr_outer],
            &block)
end