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.

  • fetch_pid: prepend command with ‘echo $$’ and capture first line of the output as PID. Defaults to true.

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

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

  run_popen(*args,
            stdin: stdin_data,
            stdout: stdout) do |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]



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

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

  run_popen(*args,
            stdin: stdin_data,
            stdout: stdout,
            stderr: stdout) do |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
# 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) do |waiter_thread|
    [stdout.string, stderr.string, waiter_thread.value]
  end
end

#popen2(*args, &block) ⇒ Object

Yields stdin, stdout, waiter_thread into a block.



222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/net-ssh-open3.rb', line 222

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

  run_popen(*args,
            stdin: stdin_inner,
            stdout: stdout_inner,
            block_pipes: [stdin_outer, stdout_outer],
            local_pipes: local_pipes,
            &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
217
218
219
# File 'lib/net-ssh-open3.rb', line 206

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

  run_popen(*args,
            stdin: stdin_inner,
            stdout: stdout_inner,
            stderr: stdout_inner,
            block_pipes: [stdin_outer, stdout_outer],
            local_pipes: local_pipes,
            &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.



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

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

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