Parent

Included Modules

Net::SCP

Net::SCP implements the SCP (Secure CoPy) client protocol, allowing Ruby programs to securely and programmatically transfer individual files or entire directory trees to and from remote servers. It provides support for multiple simultaneous SCP copies working in parallel over the same connection, as well as for synchronous, serial copies.

Basic usage:

  require 'net/scp'

  Net::SCP.start("remote.host", "username", :password => "passwd") do |scp|
    # synchronous (blocking) upload; call blocks until upload completes
    scp.upload! "/local/path", "/remote/path"

    # asynchronous upload; call returns immediately and requires SSH
    # event loop to run
    channel = scp.upload("/local/path", "/remote/path")
    channel.wait
  end

Net::SCP also provides an open-uri tie-in, so you can use the Kernel#open method to open and read a remote file:

  # if you just want to parse SCP URL's:
  require 'uri/scp'
  url = URI.parse("scp://user@remote.host/path/to/file")

  # if you want to read from a URL voa SCP:
  require 'uri/open-scp'
  puts open("scp://user@remote.host/path/to/file").read

Lastly, Net::SCP adds a method to the Net::SSH::Connection::Session class, allowing you to easily grab a Net::SCP reference from an existing Net::SSH session:

  require 'net/ssh'
  require 'net/scp'

  Net::SSH.start("remote.host", "username", :password => "passwd") do |ssh|
    ssh.scp.download! "/remote/path", "/local/path"
  end

Progress Reporting

By default, uploading and downloading proceed silently, without any outword indication of their progress. For long running uploads or downloads (and especially in interactive environments) it is desirable to report to the user the progress of the current operation.

To receive progress reports for the current operation, just pass a block to # or # (or one of their variants):

  scp.upload!("/path/to/local", "/path/to/remote") do |ch, name, sent, total|
    puts "#{name}: #{sent}/#{total}"
  end

Whenever a new chunk of data is recieved for or sent to a file, the callback will be invoked, indicating the name of the file (local for downloads, remote for uploads), the number of bytes that have been sent or received so far for the file, and the size of the file.

Attributes

session[R]

The underlying Net::SSH session that acts as transport for the SCP packets.

Public Class Methods

download!(host, username, remote, local=nil, options={}, &progress) click to toggle source

Starts up a new SSH connection using the host and username parameters, instantiates a new SCP session on top of it, and then begins a download from remote to local. If the options hash includes an :ssh key, the value for that will be passed to the SSH connection as options (e.g., to set the password, etc.). All other options are passed to the # method. If a block is given, it will be used to report progress (see “Progress Reporting”, under Net::SCP).

     # File lib/net/scp.rb, line 233
233:     def self.download!(host, username, remote, local=nil, options={}, &progress)
234:       options = options.dup
235:       start(host, username, options.delete(:ssh) || {}) do |scp|
236:         return scp.download!(remote, local, options, &progress)
237:       end
238:     end
new(session) click to toggle source

Creates a new Net::SCP session on top of the given Net::SSH session object.

     # File lib/net/scp.rb, line 246
246:     def initialize(session)
247:       @session = session
248:       self.logger = session.logger
249:     end
start(host, username, options={}) click to toggle source

Starts up a new SSH connection and instantiates a new SCP session on top of it. If a block is given, the SCP session is yielded, and the SSH session is closed automatically when the block terminates. If no block is given, the SCP session is returned.

     # File lib/net/scp.rb, line 196
196:     def self.start(host, username, options={})
197:       session = Net::SSH.start(host, username, options)
198:       scp = new(session)
199: 
200:       if block_given?
201:         begin
202:           yield scp
203:           session.loop
204:         ensure
205:           session.close
206:         end
207:       else
208:         return scp
209:       end
210:     end
upload!(host, username, local, remote, options={}, &progress) click to toggle source

Starts up a new SSH connection using the host and username parameters, instantiates a new SCP session on top of it, and then begins an upload from local to remote. If the options hash includes an :ssh key, the value for that will be passed to the SSH connection as options (e.g., to set the password, etc.). All other options are passed to the # method. If a block is given, it will be used to report progress (see “Progress Reporting”, under Net::SCP).

     # File lib/net/scp.rb, line 219
219:     def self.upload!(host, username, local, remote, options={}, &progress)
220:       options = options.dup
221:       start(host, username, options.delete(:ssh) || {}) do |scp|
222:         scp.upload!(local, remote, options, &progress)
223:       end
224:     end

Public Instance Methods

download(remote, local, options={}, &progress) click to toggle source

Inititiate a synchronous (non-blocking) download from remote to local. The following options are recognized:

  • :recursive - the remote parameter refers to a remote directory, which should be downloaded to a new directory named local on the local machine.

  • :preserve - the atime and mtime of the file should be preserved.

  • :verbose - the process should result in verbose output on the server end (useful for debugging).

This method will return immediately, returning the Net::SSH::Connection::Channel object that will support the download. To wait for the download to finish, you can either call the # method on the channel, or otherwise run the Net::SSH event loop until the channel’s # method returns false.

  channel = scp.download("/remote/path", "/local/path")
  channel.wait
     # File lib/net/scp.rb, line 299
299:     def download(remote, local, options={}, &progress)
300:       start_command(:download, local, remote, options, &progress)
301:     end
download!(remote, local=nil, options={}, &progress) click to toggle source

Same as #, but blocks until the download finishes. Identical to calling # and then calling the # method on the channel object that is returned.

  scp.download!("/remote/path", "/local/path")

If local is nil, and the download is not recursive (e.g., it is downloading only a single file), the file will be downloaded to an in-memory buffer and the resulting string returned.

  data = download!("/remote/path")
     # File lib/net/scp.rb, line 314
314:     def download!(remote, local=nil, options={}, &progress)
315:       destination = local ? local : StringIO.new
316:       download(remote, destination, options, &progress).wait
317:       local ? true : destination.string
318:     end
upload(local, remote, options={}, &progress) click to toggle source

Inititiate a synchronous (non-blocking) upload from local to remote. The following options are recognized:

  • :recursive - the local parameter refers to a local directory, which should be uploaded to a new directory named remote on the remote server.

  • :preserve - the atime and mtime of the file should be preserved.

  • :verbose - the process should result in verbose output on the server end (useful for debugging).

  • :chunk_size - the size of each “chunk” that should be sent. Defaults to 2048. Changing this value may improve throughput at the expense of decreasing interactivity.

This method will return immediately, returning the Net::SSH::Connection::Channel object that will support the upload. To wait for the upload to finish, you can either call the # method on the channel, or otherwise run the Net::SSH event loop until the channel’s # method returns false.

  channel = scp.upload("/local/path", "/remote/path")
  channel.wait
     # File lib/net/scp.rb, line 271
271:     def upload(local, remote, options={}, &progress)
272:       start_command(:upload, local, remote, options, &progress)
273:     end
upload!(local, remote, options={}, &progress) click to toggle source

Same as #, but blocks until the upload finishes. Identical to calling # and then calling the # method on the channel object that is returned. The return value is not defined.

     # File lib/net/scp.rb, line 278
278:     def upload!(local, remote, options={}, &progress)
279:       upload(local, remote, options, &progress).wait
280:     end

Private Instance Methods

await_response(channel, next_state) click to toggle source

Causes the state machine to enter the “await response” state, where things just pause until the server replies with a 0 (see #), at which point the state machine will pick up at next_state and continue processing.

     # File lib/net/scp.rb, line 369
369:       def await_response(channel, next_state)
370:         channel[:state] = :await_response
371:         channel[:next ] = next_state.to_sym
372:         # check right away, to see if the response is immediately available
373:         await_response_state(channel)
374:       end
await_response_state(channel) click to toggle source

The action invoked while the state machine remains in the “await response” state. As long as there is no data ready to process, the machine will remain in this state. As soon as the server replies with an integer 0 as the only byte, the state machine is kicked into the next state (see await_response). If the response is not a 0, an exception is raised.

     # File lib/net/scp.rb, line 382
382:       def await_response_state(channel)
383:         return if channel[:buffer].available == 0
384:         c = channel[:buffer].read_byte
385:         raise "#{c.chr}#{channel[:buffer].read}" if c != 0
386:         channel[:next], channel[:state] = nil, channel[:next]
387:         send("#{channel[:state]}_state", channel)
388:       end
finish_state(channel) click to toggle source

The action invoked when the state machine is in the “finish” state. It just tells the server not to expect any more data from this end of the pipe, and allows the pipe to drain until the server closes it.

     # File lib/net/scp.rb, line 393
393:       def finish_state(channel)
394:         channel.eof!
395:       end
progress_callback(channel, name, sent, total) click to toggle source

Invoked to report progress back to the client. If a callback was not set, this does nothing.

     # File lib/net/scp.rb, line 399
399:       def progress_callback(channel, name, sent, total)
400:         channel[:callback].call(channel, name, sent, total) if channel[:callback]
401:       end
scp_command(mode, options) click to toggle source

Constructs the scp command line needed to initiate and SCP session for the given mode (:upload or :download) and with the given options (:verbose, :recursive, :preserve). Returns the command-line as a string, ready to execute.

     # File lib/net/scp.rb, line 326
326:       def scp_command(mode, options)
327:         command = "scp "
328:         command << (mode == :upload ? "-t" : "-f")
329:         command << " -v" if options[:verbose]
330:         command << " -r" if options[:recursive]
331:         command << " -p" if options[:preserve]
332:         command
333:       end
shellescape(str) click to toggle source

Imported from ruby 1.9.2 shellwords.rb

     # File lib/net/scp.rb, line 404
404:       def shellescape(str)
405:         # ruby 1.8.7+ implements String#shellescape
406:         return str.shellescape if str.respond_to? :shellescape
407: 
408:         # An empty argument will be skipped, so return empty quotes.
409:         return "''" if str.empty?
410: 
411:         str = str.dup
412: 
413:         # Process as a single byte sequence because not all shell
414:         # implementations are multibyte aware.
415:         str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
416: 
417:         # A LF cannot be escaped with a backslash because a backslash + LF
418:         # combo is regarded as line continuation and simply ignored.
419:         str.gsub!(/\n/, "'\n'")
420: 
421:         return str
422:       end
start_command(mode, local, remote, options={}, &callback) click to toggle source

Opens a new SSH channel and executes the necessary SCP command over it (see #). It then sets up the necessary callbacks, and sets up a state machine to use to process the upload or download. (See Net::SCP::Upload and Net::SCP::Download).

     # File lib/net/scp.rb, line 339
339:       def start_command(mode, local, remote, options={}, &callback)
340:         session.open_channel do |channel|
341:           command = "#{scp_command(mode, options)} #{shellescape remote}"
342:           channel.exec(command) do |ch, success|
343:             if success
344:               channel[:local   ] = local
345:               channel[:remote  ] = remote
346:               channel[:options ] = options.dup
347:               channel[:callback] = callback
348:               channel[:buffer  ] = Net::SSH::Buffer.new
349:               channel[:state   ] = "#{mode}_start"
350:               channel[:stack   ] = []
351: 
352:               channel.on_close                  { |ch| raise Net::SCP::Error, "SCP did not finish successfully (#{ch[:exit]})" if ch[:exit] != 0 }
353:               channel.on_data                   { |ch, data| channel[:buffer].append(data) }
354:               channel.on_extended_data          { |ch, type, data| debug { data.chomp } }
355:               channel.on_request("exit-status") { |ch, data| channel[:exit] = data.read_long }
356:               channel.on_process                { send("#{channel[:state]}_state", channel) }
357:             else
358:               channel.close
359:               raise Net::SCP::Error, "could not exec scp on the remote host"
360:             end
361:           end
362:         end
363:       end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.