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 remote fd, String to redirect to a file. If a key is a Symbol, local IO may be specified as a value. In this case, block receives nil for the corresponding IO. Example:

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

    translates to

    '>>/tmp/log 2>&1'
    

    Another example:

    { in: $stdin, out: $stdout, err: $stderr }
    
  • 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]



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

def capture2(*args)
  stdout = StringIO.new
  stdin_data = extract_open3_options(args)[:stdin_data]

  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]



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

def capture2e(*args)
  stdout = StringIO.new
  stdin_data = extract_open3_options(args)[:stdin_data]

  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]



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

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

  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
229
# File 'lib/net-ssh-open3.rb', line 219

def popen2(*args, &block)
  redirects = extract_open3_options(args)[:redirects]
  stdin_inner, stdin_outer = open3_ios_for(:in, redirects)
  stdout_outer, stdout_inner = open3_ios_for(:out, redirects)

  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.



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

def popen2e(*args, &block)
  redirects = extract_open3_options(args)[:redirects]
  stdin_inner, stdin_outer = open3_ios_for(:in, redirects)
  stdout_outer, stdout_inner = open3_ios_for(:out, redirects)

  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.



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

def popen3(*args, &block)
  redirects = extract_open3_options(args)[:redirects]
  stdin_inner, stdin_outer = open3_ios_for(:in, redirects)
  stdout_outer, stdout_inner = open3_ios_for(:out, redirects)
  stderr_outer, stderr_inner = open3_ios_for(:err, redirects)

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