net-ssh 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. data/doc/LICENSE-BSD +27 -0
  2. data/doc/LICENSE-GPL +280 -0
  3. data/doc/LICENSE-RUBY +56 -0
  4. data/doc/README +13 -0
  5. data/doc/manual-html/chapter-1.html +333 -0
  6. data/doc/manual-html/chapter-2.html +455 -0
  7. data/doc/manual-html/chapter-3.html +413 -0
  8. data/doc/manual-html/chapter-4.html +353 -0
  9. data/doc/manual-html/chapter-5.html +393 -0
  10. data/doc/manual-html/chapter-6.html +296 -0
  11. data/doc/manual-html/index.html +217 -0
  12. data/doc/manual-html/manual.css +192 -0
  13. data/doc/manual/chapter.erb +18 -0
  14. data/doc/manual/example.erb +18 -0
  15. data/doc/manual/index.erb +29 -0
  16. data/doc/manual/manual.css +192 -0
  17. data/doc/manual/manual.rb +240 -0
  18. data/doc/manual/manual.yml +67 -0
  19. data/doc/manual/page.erb +87 -0
  20. data/doc/manual/parts/channels_callbacks.txt +32 -0
  21. data/doc/manual/parts/channels_loop.txt +14 -0
  22. data/doc/manual/parts/channels_open.txt +20 -0
  23. data/doc/manual/parts/channels_operations.txt +15 -0
  24. data/doc/manual/parts/channels_types.txt +3 -0
  25. data/doc/manual/parts/channels_what_are.txt +7 -0
  26. data/doc/manual/parts/exec_channels.txt +28 -0
  27. data/doc/manual/parts/exec_open.txt +51 -0
  28. data/doc/manual/parts/exec_popen3.txt +35 -0
  29. data/doc/manual/parts/forward_direct.txt +37 -0
  30. data/doc/manual/parts/forward_handlers.txt +16 -0
  31. data/doc/manual/parts/forward_intro.txt +18 -0
  32. data/doc/manual/parts/forward_local.txt +18 -0
  33. data/doc/manual/parts/forward_remote.txt +14 -0
  34. data/doc/manual/parts/intro_author.txt +1 -0
  35. data/doc/manual/parts/intro_getting.txt +39 -0
  36. data/doc/manual/parts/intro_license.txt +6 -0
  37. data/doc/manual/parts/intro_support.txt +7 -0
  38. data/doc/manual/parts/intro_what_is.txt +7 -0
  39. data/doc/manual/parts/intro_what_is_not.txt +3 -0
  40. data/doc/manual/parts/proxy_http.txt +52 -0
  41. data/doc/manual/parts/proxy_intro.txt +1 -0
  42. data/doc/manual/parts/proxy_socks.txt +23 -0
  43. data/doc/manual/parts/session_key.txt +66 -0
  44. data/doc/manual/parts/session_options.txt +42 -0
  45. data/doc/manual/parts/session_session.txt +14 -0
  46. data/doc/manual/parts/session_start.txt +49 -0
  47. data/doc/manual/tutorial.erb +30 -0
  48. data/examples/channel-demo.rb +81 -0
  49. data/examples/port-forward.rb +51 -0
  50. data/examples/process-demo.rb +91 -0
  51. data/examples/remote-net-port-forward.rb +45 -0
  52. data/examples/remote-port-forward.rb +80 -0
  53. data/examples/tail-demo.rb +49 -0
  54. data/lib/net/ssh.rb +52 -0
  55. data/lib/net/ssh/connection/channel.rb +411 -0
  56. data/lib/net/ssh/connection/constants.rb +47 -0
  57. data/lib/net/ssh/connection/driver.rb +343 -0
  58. data/lib/net/ssh/connection/services.rb +72 -0
  59. data/lib/net/ssh/connection/term.rb +90 -0
  60. data/lib/net/ssh/errors.rb +27 -0
  61. data/lib/net/ssh/proxy/errors.rb +34 -0
  62. data/lib/net/ssh/proxy/http.rb +126 -0
  63. data/lib/net/ssh/proxy/socks4.rb +83 -0
  64. data/lib/net/ssh/proxy/socks5.rb +160 -0
  65. data/lib/net/ssh/service/forward/driver.rb +319 -0
  66. data/lib/net/ssh/service/forward/local-network-handler.rb +74 -0
  67. data/lib/net/ssh/service/forward/remote-network-handler.rb +81 -0
  68. data/lib/net/ssh/service/forward/services.rb +76 -0
  69. data/lib/net/ssh/service/process/driver.rb +153 -0
  70. data/lib/net/ssh/service/process/open.rb +193 -0
  71. data/lib/net/ssh/service/process/popen3.rb +160 -0
  72. data/lib/net/ssh/service/process/services.rb +66 -0
  73. data/lib/net/ssh/service/services.rb +44 -0
  74. data/lib/net/ssh/session.rb +242 -0
  75. data/lib/net/ssh/transport/algorithm-negotiator.rb +267 -0
  76. data/lib/net/ssh/transport/compress/compressor.rb +53 -0
  77. data/lib/net/ssh/transport/compress/decompressor.rb +53 -0
  78. data/lib/net/ssh/transport/compress/none-compressor.rb +39 -0
  79. data/lib/net/ssh/transport/compress/none-decompressor.rb +39 -0
  80. data/lib/net/ssh/transport/compress/services.rb +68 -0
  81. data/lib/net/ssh/transport/compress/zlib-compressor.rb +60 -0
  82. data/lib/net/ssh/transport/compress/zlib-decompressor.rb +52 -0
  83. data/lib/net/ssh/transport/constants.rb +66 -0
  84. data/lib/net/ssh/transport/errors.rb +47 -0
  85. data/lib/net/ssh/transport/identity-cipher.rb +61 -0
  86. data/lib/net/ssh/transport/kex/dh-gex.rb +106 -0
  87. data/lib/net/ssh/transport/kex/dh.rb +231 -0
  88. data/lib/net/ssh/transport/kex/services.rb +60 -0
  89. data/lib/net/ssh/transport/ossl/buffer-factory.rb +52 -0
  90. data/lib/net/ssh/transport/ossl/buffer.rb +87 -0
  91. data/lib/net/ssh/transport/ossl/cipher-factory.rb +98 -0
  92. data/lib/net/ssh/transport/ossl/digest-factory.rb +51 -0
  93. data/lib/net/ssh/transport/ossl/hmac-factory.rb +71 -0
  94. data/lib/net/ssh/transport/ossl/hmac/hmac.rb +62 -0
  95. data/lib/net/ssh/transport/ossl/hmac/md5-96.rb +44 -0
  96. data/lib/net/ssh/transport/ossl/hmac/md5.rb +46 -0
  97. data/lib/net/ssh/transport/ossl/hmac/none.rb +46 -0
  98. data/lib/net/ssh/transport/ossl/hmac/services.rb +68 -0
  99. data/lib/net/ssh/transport/ossl/hmac/sha1-96.rb +44 -0
  100. data/lib/net/ssh/transport/ossl/hmac/sha1.rb +45 -0
  101. data/lib/net/ssh/transport/ossl/key-factory.rb +113 -0
  102. data/lib/net/ssh/transport/ossl/services.rb +149 -0
  103. data/lib/net/ssh/transport/packet-stream.rb +210 -0
  104. data/lib/net/ssh/transport/services.rb +146 -0
  105. data/lib/net/ssh/transport/session.rb +296 -0
  106. data/lib/net/ssh/transport/version-negotiator.rb +73 -0
  107. data/lib/net/ssh/userauth/agent.rb +218 -0
  108. data/lib/net/ssh/userauth/constants.rb +35 -0
  109. data/lib/net/ssh/userauth/driver.rb +176 -0
  110. data/lib/net/ssh/userauth/methods/hostbased.rb +119 -0
  111. data/lib/net/ssh/userauth/methods/password.rb +70 -0
  112. data/lib/net/ssh/userauth/methods/publickey.rb +137 -0
  113. data/lib/net/ssh/userauth/methods/services.rb +63 -0
  114. data/lib/net/ssh/userauth/services.rb +126 -0
  115. data/lib/net/ssh/userauth/userkeys.rb +258 -0
  116. data/lib/net/ssh/util/buffer.rb +274 -0
  117. data/lib/net/ssh/util/openssl.rb +146 -0
  118. data/lib/net/ssh/util/prompter.rb +73 -0
  119. data/lib/net/ssh/version.rb +29 -0
  120. data/test/ALL-TESTS.rb +21 -0
  121. data/test/connection/tc_channel.rb +136 -0
  122. data/test/connection/tc_driver.rb +287 -0
  123. data/test/connection/tc_integration.rb +85 -0
  124. data/test/proxy/tc_http.rb +209 -0
  125. data/test/proxy/tc_socks4.rb +148 -0
  126. data/test/proxy/tc_socks5.rb +214 -0
  127. data/test/service/forward/tc_driver.rb +289 -0
  128. data/test/service/forward/tc_local_network_handler.rb +123 -0
  129. data/test/service/forward/tc_remote_network_handler.rb +108 -0
  130. data/test/service/process/tc_driver.rb +79 -0
  131. data/test/service/process/tc_integration.rb +117 -0
  132. data/test/service/process/tc_open.rb +179 -0
  133. data/test/service/process/tc_popen3.rb +164 -0
  134. data/test/tc_integration.rb +79 -0
  135. data/test/transport/compress/tc_none_compress.rb +41 -0
  136. data/test/transport/compress/tc_none_decompress.rb +45 -0
  137. data/test/transport/compress/tc_zlib_compress.rb +61 -0
  138. data/test/transport/compress/tc_zlib_decompress.rb +48 -0
  139. data/test/transport/kex/tc_dh.rb +304 -0
  140. data/test/transport/kex/tc_dh_gex.rb +70 -0
  141. data/test/transport/ossl/fixtures/dsa-encrypted +15 -0
  142. data/test/transport/ossl/fixtures/dsa-encrypted-bad +15 -0
  143. data/test/transport/ossl/fixtures/dsa-unencrypted +12 -0
  144. data/test/transport/ossl/fixtures/dsa-unencrypted-bad +12 -0
  145. data/test/transport/ossl/fixtures/dsa-unencrypted.pub +1 -0
  146. data/test/transport/ossl/fixtures/not-a-private-key +4 -0
  147. data/test/transport/ossl/fixtures/not-supported +2 -0
  148. data/test/transport/ossl/fixtures/rsa-encrypted +18 -0
  149. data/test/transport/ossl/fixtures/rsa-encrypted-bad +18 -0
  150. data/test/transport/ossl/fixtures/rsa-unencrypted +15 -0
  151. data/test/transport/ossl/fixtures/rsa-unencrypted-bad +15 -0
  152. data/test/transport/ossl/fixtures/rsa-unencrypted.pub +1 -0
  153. data/test/transport/ossl/hmac/tc_hmac.rb +58 -0
  154. data/test/transport/ossl/hmac/tc_md5.rb +50 -0
  155. data/test/transport/ossl/hmac/tc_md5_96.rb +50 -0
  156. data/test/transport/ossl/hmac/tc_none.rb +50 -0
  157. data/test/transport/ossl/hmac/tc_sha1.rb +50 -0
  158. data/test/transport/ossl/hmac/tc_sha1_96.rb +50 -0
  159. data/test/transport/ossl/tc_buffer.rb +97 -0
  160. data/test/transport/ossl/tc_buffer_factory.rb +67 -0
  161. data/test/transport/ossl/tc_cipher_factory.rb +84 -0
  162. data/test/transport/ossl/tc_digest_factory.rb +39 -0
  163. data/test/transport/ossl/tc_hmac_factory.rb +72 -0
  164. data/test/transport/ossl/tc_key_factory.rb +199 -0
  165. data/test/transport/tc_algorithm_negotiator.rb +169 -0
  166. data/test/transport/tc_identity_cipher.rb +52 -0
  167. data/test/transport/tc_integration.rb +110 -0
  168. data/test/transport/tc_packet_stream.rb +183 -0
  169. data/test/transport/tc_session.rb +283 -0
  170. data/test/transport/tc_version_negotiator.rb +86 -0
  171. data/test/userauth/methods/tc_hostbased.rb +136 -0
  172. data/test/userauth/methods/tc_password.rb +89 -0
  173. data/test/userauth/methods/tc_publickey.rb +167 -0
  174. data/test/userauth/tc_agent.rb +223 -0
  175. data/test/userauth/tc_driver.rb +190 -0
  176. data/test/userauth/tc_integration.rb +81 -0
  177. data/test/userauth/tc_userkeys.rb +265 -0
  178. data/test/util/tc_buffer.rb +217 -0
  179. metadata +256 -0
@@ -0,0 +1,76 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Net::SSH Secure Shell Client
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # net-ssh website : http://net-ssh.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/net-ssh
14
+ # =============================================================================
15
+ #++
16
+
17
+ module Net
18
+ module SSH
19
+ module Service
20
+ module Forward
21
+
22
+ # Register the services pertaining to port forwarding.
23
+ def register_services( container )
24
+
25
+ # All port forwarding services go in the :forward namespace.
26
+ container.namespace_define :forward do |ns|
27
+
28
+ # The :driver service manages all port forwarding. It is declared
29
+ # as deferred so that it is not actually instantiated until it is
30
+ # used--otherwise, it would be instantiated as soon as it was added
31
+ # to the list of available services, whether it was ever used or
32
+ # not.
33
+ ns.driver :model => :singleton_deferred do |c,p|
34
+ require 'net/ssh/service/forward/driver'
35
+ Driver.new( c[:connection][:driver],
36
+ c[:transport][:buffers],
37
+ c[:log_for, p],
38
+ :local => c[:local_network_handler],
39
+ :remote => c[:remote_network_handler] )
40
+ end
41
+
42
+ # A constant service, used to indicate the maximum block size to be
43
+ # passed over a forwarded connection.
44
+ ns.read_block_size { 64 * 1024 }
45
+
46
+ # The :local_network_handler service returns a proc object that
47
+ # creates new LocalNetworkHandler instances for a given client
48
+ # connection. This is used for forwarding ports on the local host.
49
+ ns.local_network_handler :model => :singleton_deferred do |c,p|
50
+ require 'net/ssh/service/forward/local-network-handler'
51
+ log = c[:log_for, p]
52
+ block_size = c[:read_block_size]
53
+ lambda do |client|
54
+ LocalNetworkHandler.new( log, block_size, client )
55
+ end
56
+ end
57
+
58
+ # The :remote_network_handler service returns a proc object that
59
+ # creates new RemoteNetworkHandler instances for a given port and
60
+ # host. This is used for forwarding ports on a remote host.
61
+ ns.remote_network_handler :model => :singleton_deferred do |c,p|
62
+ require 'net/ssh/service/forward/remote-network-handler'
63
+ log = c[:log_for, p]
64
+ block_size = c[:read_block_size]
65
+ lambda do |port, host|
66
+ RemoteNetworkHandler.new( log, block_size, port, host )
67
+ end
68
+ end
69
+ end
70
+ end
71
+ module_function :register_services
72
+
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,153 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Net::SSH Secure Shell Client
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # net-ssh website : http://net-ssh.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/net-ssh
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'net/ssh/errors'
18
+
19
+ module Net
20
+ module SSH
21
+ module Service
22
+ module Process
23
+
24
+ # Represents a process executing on the remote machine. It also provides
25
+ # a simple interface for interacting with such a remote process.
26
+ #
27
+ # It may be used in either of two ways. The first allows multiple
28
+ # processes to be invoked on the remote machine and run in parallel
29
+ # over the same session. Because of this, it is a bit complicated to
30
+ # set up:
31
+ #
32
+ # require 'net/ssh'
33
+ #
34
+ # Net::SSH.start( 'host', 'user', 'passwd' ) do |session|
35
+ # process = session.process.open( "bc" )
36
+ # dialog = [ "5+5", "7*12", "1+2*5/(7+3)" ]
37
+ #
38
+ # process.on_success do |p|
39
+ # puts "requesting computation of '#{dialog.first}'"
40
+ # p.puts dialog.shift
41
+ # end
42
+ #
43
+ # process.on_failure do |p, status|
44
+ # puts "process failed to start (#{status})"
45
+ # end
46
+ #
47
+ # process.on_stdout do |p, data|
48
+ # puts "--> #{data}"
49
+ # if dialog.empty?
50
+ # p.close_input
51
+ # else
52
+ # puts "requesting computation of '#{dialog.first}'"
53
+ # p.puts dialog.shift
54
+ # end
55
+ # end
56
+ #
57
+ # process.on_stderr do |p, data|
58
+ # puts "[stderr]--> #{data}"
59
+ # end
60
+ #
61
+ # process.on_exit do |p, status|
62
+ # puts "process finished with exit status: #{status}"
63
+ # end
64
+ #
65
+ # session.loop
66
+ # end
67
+ #
68
+ # Naturally, not all of the callbacks used above are required. If you
69
+ # omit any of them, they will simply not be called. However, you
70
+ # *should* do something when the process is successfully started
71
+ # (+on_success+), and you *should* do something when data is recieved
72
+ # over stdout (+on_stdout+). Lastly, you *must* execute
73
+ # <tt>session.loop</tt> in order to process the connection.
74
+ #
75
+ # The simpler way to use this service is only available when you
76
+ # are not handling multiple parallel processes--you can only use it
77
+ # when the process you are executing is the only task you are using the
78
+ # SSH connection for. It is reminiscent of the popen interface: you
79
+ # invoke a command and get three pseudo-IO objects back--one for the
80
+ # command's "stdin" stream, one for it's "stdout" stream, and one for
81
+ # it's "stderr" stream. You may then write to the "stdin" stream, and
82
+ # read from the "stdout" and "stderr" streams.
83
+ #
84
+ # For example:
85
+ #
86
+ # require 'net/ssh'
87
+ #
88
+ # Net::SSH.start( 'host', 'user', 'passwd' ) do |session|
89
+ # input, output, error = session.process.popen3( "bc" )
90
+ # input.puts "5+5"
91
+ # puts "5+5=#{output.read}"
92
+ # input.puts "7*12"
93
+ # puts "7*12=#{output.read}"
94
+ # input.puts "1+2*5/(7+3)"
95
+ # puts "1+2*5/(7+3)=#{output.read}"
96
+ # input.puts "quit"
97
+ # end
98
+ #
99
+ # One caveat with this format: the process cannot be explicitly
100
+ # terminated from the client side--the process must terminate on its
101
+ # own (for example, by recieving a "quit" command, as used above). If
102
+ # the command does not support any means of gracefully aborting it,
103
+ # then the only way to kill the command is to terminate the connection.
104
+ #
105
+ # A slightly cleaner approach uses blocks to denote the lifespan of the
106
+ # process. When the block terminates, the process is killed (if it is
107
+ # still running):
108
+ #
109
+ # require 'net/ssh'
110
+ #
111
+ # Net::SSH.start( 'host', 'user', 'passwd' ) do |session|
112
+ # session.process.popen3( "cat" ) do |input, output, error|
113
+ # input.puts "hello"
114
+ # puts "echo: #{output.read}"
115
+ # input.puts "world"
116
+ # puts "echo: #{output.read}"
117
+ # end
118
+ # end
119
+ class Driver
120
+
121
+ # Create a new Driver instance, using the given log and handlers
122
+ # hash.
123
+ def initialize( connection, log, handlers )
124
+ @connection = connection
125
+ @log = log
126
+ @handlers = handlers
127
+ end
128
+
129
+ def open( command )
130
+ @log.debug "opening '#{command}'" if @log.debug?
131
+ process = @handlers[ :open ].call( command )
132
+
133
+ if block_given?
134
+ yield process
135
+ @connection.loop
136
+ return nil
137
+ end
138
+
139
+ process
140
+ end
141
+
142
+ def popen3( command, &block )
143
+ @log.debug "popen3 '#{command}'" if @log.debug?
144
+ mgr = @handlers[ :popen3 ]
145
+ mgr.popen3( command, &block )
146
+ end
147
+
148
+ end
149
+
150
+ end # module Process
151
+ end # module Service
152
+ end # module SSH
153
+ end # module Net
@@ -0,0 +1,193 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Net::SSH Secure Shell Client
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # net-ssh website : http://net-ssh.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/net-ssh
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'net/ssh/errors'
18
+
19
+ module Net
20
+ module SSH
21
+ module Service
22
+ module Process
23
+
24
+ # A delegate class to manage the processing for a single executed
25
+ # process on the remote host. It opens a channel, executes the process
26
+ # on it, and manages the various callbacks.
27
+ #
28
+ # This service is typically used like this:
29
+ #
30
+ # Net::SSH.start( 'host', 'user' ) do |session|
31
+ # session.process.open( "bc" ) do |process|
32
+ # ...
33
+ # end
34
+ # end
35
+ class OpenManager
36
+
37
+ # Create a new OpenManager instance on the given connection. It will
38
+ # attempt to execute the given command. If a block is given, the
39
+ # manager will be yielded to the block, and the constructor will not
40
+ # return until all channels are closed.
41
+ def initialize( connection, log, command )
42
+ @log = log
43
+ @command = command
44
+ @channel = connection.open_channel(
45
+ "session", &method( :do_confirm ) )
46
+
47
+ if block_given?
48
+ yield self
49
+ connection.loop
50
+ end
51
+ end
52
+
53
+ # Register the given block to be invoked when the command has been
54
+ # confirmed to have been successfully started. The block should
55
+ # accept a single parameter, the process instance that was created
56
+ # (+self+).
57
+ def on_success( &block )
58
+ @on_success = block
59
+ end
60
+
61
+ # Register the given block to be invoked when the command could not
62
+ # be started. The block should accept two parameters: the process
63
+ # instance (+self+) and a status string. (The status string is
64
+ # currently always +nil+, since SSH itself does not indicate why the
65
+ # program failed to start.)
66
+ def on_failure( &block )
67
+ @on_failure = block
68
+ end
69
+
70
+ # Register the given block to be invoked when data is recieved from
71
+ # the invoked command's +stdout+ stream. The block should accept two
72
+ # parameters: the process instance (+self+) and the data string. Note
73
+ # that if the process sends large amounts of data, this method may be
74
+ # invoked multiple times, each time with a portion of the command's
75
+ # output.
76
+ def on_stdout( &block )
77
+ @on_stdout = block
78
+ end
79
+
80
+ # Register the given block to be invoked when data is recieved from
81
+ # the invoked command's +stderr+ stream. The block should accept two
82
+ # parameters: the process instance (+self+) and the data string. Note
83
+ # that if the process sends large amounts of data, this method may be
84
+ # invoked multiple times, each time with a portion of the command's
85
+ # error output.
86
+ def on_stderr( &block )
87
+ @on_stderr = block
88
+ end
89
+
90
+ # Register the given block to be invoked when the process terminates
91
+ # normally. The block should accept two parameters: the process
92
+ # instance (+self+) and the exit status of the process.
93
+ def on_exit( &block )
94
+ @on_exit = block
95
+ end
96
+
97
+ # Send the given data to the process. It will be sent via the
98
+ # process's +stdin+ stream. This method returns immediately.
99
+ def write( data )
100
+ @channel.send_data data
101
+ end
102
+
103
+ # Send the given data to the process, appending a newline. As with
104
+ # Kernel::puts, this will not append a newline if the string already
105
+ # has one. See #write.
106
+ def puts( data )
107
+ @channel.send_data data.chomp + "\n"
108
+ end
109
+
110
+ # Indicate that no more data will be sent to the process (sends an
111
+ # EOF to the process). The process may continue to send data, but
112
+ # the +stdin+ stream is effectively closed. This will return
113
+ # immediately.
114
+ def close_input
115
+ @channel.send_eof
116
+ end
117
+
118
+ # Close the process. All streams (+stdin+, +stdout+, +stderr+) will
119
+ # be closed. Any output that the process had already produced will
120
+ # still be sent, but it will be shut down as soon as possible. This
121
+ # will return immediately.
122
+ def close
123
+ @channel.close
124
+ end
125
+
126
+ # Invoked when the channel's opening has been confirmed by the
127
+ # server. This is where the command to execute will be sent to the
128
+ # server.
129
+ def do_confirm( channel )
130
+ channel.on_success &method(:do_exec_success)
131
+ channel.on_failure &method(:do_exec_failure)
132
+ channel.exec @command, true
133
+ end
134
+
135
+ # Invoked when the invocation of the command has been successful.
136
+ # This registers various callbacks, and then calls the +on_success+
137
+ # callback (if registered).
138
+ def do_exec_success( channel )
139
+ channel.on_data &method(:do_data)
140
+ channel.on_extended_data &method(:do_extended_data)
141
+ channel.on_close &method(:do_close)
142
+ channel.on_request &method(:do_request)
143
+ @on_success.call( self ) if @on_success
144
+ end
145
+
146
+ # Invoked when the invocation of the command failed. This will call
147
+ # the +on_failure+ callback, if registered, or will otherwise raise
148
+ # an exception.
149
+ def do_exec_failure( channel )
150
+ if @on_failure
151
+ @on_failure.call( self, nil )
152
+ else
153
+ raise Net::SSH::Exception,
154
+ "could not execute process (#{@command})"
155
+ end
156
+ end
157
+
158
+ # Invoked when data arrives over the channel. This simply delegates to
159
+ # the +on_stdout+ callback, if registered.
160
+ def do_data( channel, data )
161
+ @on_stdout.call( self, data ) if @on_stdout
162
+ end
163
+
164
+ # Invoked when extended data arrives over the channel. This simply
165
+ # delegates to the +on_stderr+ callback, if registered, if the type
166
+ # is 1; otherwise it does nothing.
167
+ def do_extended_data( channel, type, data )
168
+ case type
169
+ when 1
170
+ @on_stderr.call( self, data ) if @on_stderr
171
+ end
172
+ end
173
+
174
+ # Invoked when the channel is closed. This simply delegates to
175
+ # the +on_exit+ callback, if registered.
176
+ def do_close( channel )
177
+ @on_exit.call( self, @exit_status ) if @on_exit
178
+ end
179
+
180
+ # Invoked when a channel request is received.
181
+ def do_request( channel, type, want_reply, data )
182
+ case type
183
+ when "exit-status"
184
+ @exit_status = data.read_long
185
+ end
186
+ end
187
+
188
+ end
189
+
190
+ end # module Process
191
+ end # module Service
192
+ end # module SSH
193
+ end # module Net
@@ -0,0 +1,160 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Net::SSH Secure Shell Client
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # net-ssh website : http://net-ssh.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/net-ssh
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'net/ssh/errors'
18
+
19
+ module Net
20
+ module SSH
21
+ module Service
22
+ module Process
23
+
24
+ # A delegate class for managing popen3 requests for remote processes.
25
+ class POpen3Manager
26
+
27
+ # Create a new POpen3Manager instance on the given connection.
28
+ def initialize( connection, log )
29
+ @connection = connection
30
+ @log = log
31
+ end
32
+
33
+ # Invokes the given command synchronously on the current connection.
34
+ # (This means that parallel commands and operations cannot be
35
+ # executed when this method is used.) This will return +nil+ if the
36
+ # method could not be executed. If the command is successfully
37
+ # invoked, and a block is given, the block is then invoked with the
38
+ # input, output, and error streams of the command as parameters, and
39
+ # the channel is closed as soon as the block terminates. If a block
40
+ # is not given, the input, output, and error channels are returned
41
+ # and the process *might* not terminate until the session itself
42
+ # terminates.
43
+ def popen3( command )
44
+ @connection.open_channel( "session" ) do |chan|
45
+
46
+ chan.on_success do |ch|
47
+ input = SSHStdinPipe.new( ch )
48
+ output = SSHStdoutPipe.new( ch )
49
+ error = SSHStderrPipe.new( ch )
50
+
51
+ if block_given?
52
+ yield input, output, error
53
+ chan.close
54
+ else
55
+ return [ input, output, error ]
56
+ end
57
+ end
58
+
59
+ chan.on_failure do |ch|
60
+ chan.close
61
+ end
62
+
63
+ chan.exec command, true
64
+ end
65
+
66
+ @connection.loop
67
+ return nil
68
+ end
69
+
70
+ # A specialized class for use by the Net::SSH "popen3" service.
71
+ # An instance of this class represents a means of writing data to an
72
+ # SSH channel. This class should never be instantiated directly; use
73
+ # the popen3 method instead.
74
+ class SSHStdinPipe
75
+
76
+ # Create a new +stdin+ pipe on the given channel.
77
+ def initialize( channel )
78
+ @channel = channel
79
+ end
80
+
81
+ # Write the given data as channel data to the underlying channel.
82
+ def write( data )
83
+ @channel.send_data data
84
+ end
85
+
86
+ # Write the given data as channel data to the underlying channel,
87
+ # appending a newline character (if one isn't already appended).
88
+ def puts( data )
89
+ write data.chomp + "\n"
90
+ end
91
+
92
+ end
93
+
94
+ # An abstract class representing a writable stream on a channel. This
95
+ # is subclassed by SSHStdoutPipe and SSHStderrPipe.
96
+ class SSHOutputPipe
97
+
98
+ # Create a new output pipe on the given channel.
99
+ def initialize( channel )
100
+ @channel = channel
101
+ @data = ""
102
+ end
103
+
104
+ # Read all available bytes from the pipe. If there are no available
105
+ # bytes, then this will block until data becomes available.
106
+ def read
107
+ if @data.length < 1
108
+ @channel.connection.loop { @data.length < 1 }
109
+ end
110
+
111
+ data, @data = @data, ""
112
+ return data
113
+ end
114
+
115
+ end
116
+
117
+ # A specialization of SSHOutputPipe that represents specifically the
118
+ # +stdout+ stream of a process. It should only be used by popen3.
119
+ class SSHStdoutPipe < SSHOutputPipe
120
+
121
+ # Create a new +stdout+ stream on the given channel. Only one such
122
+ # pipe should ever be associated with a channel.
123
+ def initialize( channel )
124
+ super( channel )
125
+ channel.on_data &method(:do_data)
126
+ end
127
+
128
+ # Invoked when data is recieved from the channel. It simply
129
+ # accumulates all data until a +read+ is invoked.
130
+ def do_data( channel, data )
131
+ @data << data
132
+ end
133
+
134
+ end
135
+
136
+ # A specialization of SSHOutputPipe that represents specifically the
137
+ # +stderr+ stream of a process. It should only be used by popen3.
138
+ class SSHStderrPipe < SSHOutputPipe
139
+
140
+ # Create a new +stderr+ stream on the given channel. Only one such
141
+ # pipe should ever be associated with a channel.
142
+ def initialize( channel )
143
+ super( channel )
144
+ channel.on_extended_data &method(:do_data)
145
+ end
146
+
147
+ # Invoked when data is recieved from the channel. It simply
148
+ # accumulates all data until a +read+ is invoked.
149
+ def do_data( channel, type, data )
150
+ @data << data if type == 1
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+
157
+ end # module Process
158
+ end # module Service
159
+ end # module SSH
160
+ end # module Net