net-ssh 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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