File: open.rb

package info (click to toggle)
libnet-ssh-ruby 1.1.2-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 3,472 kB
  • ctags: 2,465
  • sloc: ruby: 10,848; makefile: 17
file content (193 lines) | stat: -rw-r--r-- 7,720 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#--
# =============================================================================
# Copyright (c) 2004,2005 Jamis Buck (jamis@37signals.com)
# All rights reserved.
#
# This source file is distributed as part of the Net::SSH Secure Shell Client
# library for Ruby. This file (and the library as a whole) may be used only as
# allowed by either the BSD license, or the Ruby license (or, by association
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
# distribution for the texts of these licenses.
# -----------------------------------------------------------------------------
# net-ssh website : http://net-ssh.rubyforge.org
# project website: http://rubyforge.org/projects/net-ssh
# =============================================================================
#++

require 'net/ssh/errors'

module Net
  module SSH
    module Service
      module Process
      
        # A delegate class to manage the processing for a single executed
        # process on the remote host. It opens a channel, executes the process
        # on it, and manages the various callbacks.
        #
        # This service is typically used like this:
        #
        #   Net::SSH.start( 'host', 'user' ) do |session|
        #     session.process.open( "bc" ) do |process|
        #       ...
        #     end
        #   end
        class OpenManager

          # Create a new OpenManager instance on the given connection. It will
          # attempt to execute the given command. If a block is given, the
          # manager will be yielded to the block, and the constructor will not
          # return until all channels are closed.
          def initialize( connection, log, command )
            @log = log
            @command = command
            @channel = connection.open_channel(
              "session", &method( :do_confirm ) )

            if block_given?
              yield self
              connection.loop
            end
          end

          # Register the given block to be invoked when the command has been
          # confirmed to have been successfully started. The block should
          # accept a single parameter, the process instance that was created
          # (+self+).
          def on_success( &block )
            @on_success = block
          end

          # Register the given block to be invoked when the command could not
          # be started. The block should accept two parameters: the process
          # instance (+self+) and a status string. (The status string is
          # currently always +nil+, since SSH itself does not indicate why the
          # program failed to start.)
          def on_failure( &block )
            @on_failure = block
          end

          # Register the given block to be invoked when data is recieved from
          # the invoked command's +stdout+ stream. The block should accept two
          # parameters: the process instance (+self+) and the data string. Note
          # that if the process sends large amounts of data, this method may be
          # invoked multiple times, each time with a portion of the command's
          # output.
          def on_stdout( &block )
            @on_stdout = block
          end

          # Register the given block to be invoked when data is recieved from
          # the invoked command's +stderr+ stream. The block should accept two
          # parameters: the process instance (+self+) and the data string. Note
          # that if the process sends large amounts of data, this method may be
          # invoked multiple times, each time with a portion of the command's
          # error output.
          def on_stderr( &block )
            @on_stderr = block
          end

          # Register the given block to be invoked when the process terminates
          # normally. The block should accept two parameters: the process
          # instance (+self+) and the exit status of the process.
          def on_exit( &block )
            @on_exit = block
          end

          # Send the given data to the process. It will be sent via the
          # process's +stdin+ stream. This method returns immediately.
          def write( data )
            @channel.send_data data
          end

          # Send the given data to the process, appending a newline. As with
          # Kernel::puts, this will not append a newline if the string already
          # has one. See #write.
          def puts( data )
            @channel.send_data data.chomp + "\n"
          end

          # Indicate that no more data will be sent to the process (sends an
          # EOF to the process). The process may continue to send data, but
          # the +stdin+ stream is effectively closed. This will return
          # immediately.
          def close_input
            @channel.send_eof
          end

          # Close the process. All streams (+stdin+, +stdout+, +stderr+) will
          # be closed. Any output that the process had already produced will
          # still be sent, but it will be shut down as soon as possible. This
          # will return immediately.
          def close
            @channel.close
          end

          # Invoked when the channel's opening has been confirmed by the
          # server. This is where the command to execute will be sent to the
          # server.
          def do_confirm( channel )
            channel.on_success(&method(:do_exec_success))
            channel.on_failure(&method(:do_exec_failure))
            channel.exec @command, true
          end

          # Invoked when the invocation of the command has been successful.
          # This registers various callbacks, and then calls the +on_success+
          # callback (if registered).
          def do_exec_success( channel )
            channel.on_data(&method(:do_data))
            channel.on_extended_data(&method(:do_extended_data))
            channel.on_close(&method(:do_close))
            channel.on_request(&method(:do_request))
            @on_success.call( self ) if @on_success
          end

          # Invoked when the invocation of the command failed. This will call
          # the +on_failure+ callback, if registered, or will otherwise raise
          # an exception.
          def do_exec_failure( channel )
            if @on_failure
              @on_failure.call( self, nil )
            else
              raise Net::SSH::Exception,
                "could not execute process (#{@command})"
            end
          end

          # Invoked when data arrives over the channel. This simply delegates to
          # the +on_stdout+ callback, if registered.
          def do_data( channel, data )
            @on_stdout.call( self, data ) if @on_stdout
          end

          # Invoked when extended data arrives over the channel. This simply
          # delegates to the +on_stderr+ callback, if registered, if the type
          # is 1; otherwise it does nothing.
          def do_extended_data( channel, type, data )
            case type
              when 1
                @on_stderr.call( self, data ) if @on_stderr
            end
          end

          # Invoked when the channel is closed. This simply delegates to
          # the +on_exit+ callback, if registered.
          def do_close( channel )
            @on_exit.call( self, @exit_status ) if @on_exit
          end

          # Invoked when a channel request is received.
          def do_request( channel, type, want_reply, data )
            case type
              when "exit-status"
                @exit_status = data.read_long
            end
          end

        end

      end # module Process
    end # module Service
  end # module SSH
end # module Net