lib/rexec/server.rb in rexec-1.2.1 vs lib/rexec/server.rb in rexec-1.2.3

- old
+ new

@@ -1,62 +1,95 @@ -# Copyright (c) 2007 Samuel Williams. Released under the GNU GPLv3. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz> # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. require 'pathname' require 'rexec/task' require 'rexec/connection' module RExec - - class InvalidConnectionError < Exception - end - - @@connection_code = (Pathname.new(__FILE__).dirname + "connection.rb").read - @@client_code = (Pathname.new(__FILE__).dirname + "client.rb").read - - # Start a remote ruby server. This function is a structural cornerstone. This code runs the command you - # supply (this command should start an instance of ruby somewhere), sends it the code in - # <tt>connection.rb</tt> and <tt>client.rb</tt> as well as the code you supply. - # - # Once the remote ruby instance is set up and ready to go, this code will return (or yield) the connection - # and pid of the executed command. - # - # From this point, you can send and receive objects, and interact with the code you provided within a - # remote ruby instance. - # - # If <tt>command</tt> is a shell such as "/bin/sh", and we need to start ruby separately, you can supply - # <tt>options[:ruby] = "/usr/bin/ruby"</tt> to explicitly start the ruby command. - def self.start_server(code, command, options = {}, &block) - options[:passthrough] = :err unless options[:passthrough] - - send_code = Proc.new do |cin| - cin.puts(@@connection_code) - cin.puts(@@client_code) - cin.puts(code) - end - - if block_given? - Task.open(command, options) do |process| - conn = Connection.build(process, options, &send_code) - - yield conn, process.pid - end - else - process = Task.open(command, options) - conn = Connection.build(process, options, &send_code) - - return conn, process.pid - end - end + + # Indicates that a connection could not be established because the pipes were not available or not connected. + class InvalidConnectionError < Exception + end + + # The connection code which is sent to the client to be used for bi-directional communication. + CONNECTION_CODE = (Pathname.new(__FILE__).dirname + "connection.rb").read + + # The client code which sets up the connection object and initialises communciation. + CLIENT_CODE = (Pathname.new(__FILE__).dirname + "client.rb").read + + # Start a remote ruby server. This function is a structural cornerstone. This code runs the command you + # supply (this command should start an instance of ruby somewhere), sends it the code in + # +connection.rb+ and +client.rb+ as well as the code you supply. + # + # Once the remote ruby instance is set up and ready to go, this code will return (or yield) the connection + # and pid of the executed command. + # + # From this point, you can send and receive objects, and interact with the code you provided within a + # remote ruby instance. + # + # For a local shell, you could specify +"ruby"+ as the command. For a remote shell via SSH, you could specify + # +"ssh example.com ruby"+. + # + # ==== Example + # Create a file called +client.rb+ on the server. This file contains code to be executed on the client. This + # file can assume the existance of an object called +$connection+: + # + # $connection.run do |object| + # case(object[0]) + # when :bounce + # $connection.send_object(object[1]) + # end + # end + # + # Then, on the server, create a new program +server.rb+ which will be used to coordinate the execution of code: + # + # shell = "ssh example.com ruby" + # client_code = (Pathname.new(__FILE__).dirname + "./client.rb").read + # + # RExec::start_server(client_code, shell) do |connection, pid| + # connection.send_object([:bounce, "Hello World!"]) + # result = connection.receive_object + # end + # + def self.start_server(code, command, options = {}, &block) + options[:passthrough] = :err unless options[:passthrough] + + send_code = Proc.new do |cin| + cin.puts(CONNECTION_CODE) + cin.puts(CLIENT_CODE) + cin.puts(code) + end + + if block_given? + Task.open(command, options) do |process| + conn = Connection.build(process, options, &send_code) + + yield conn, process.pid + + conn.stop + end + else + process = Task.open(command, options) + conn = Connection.build(process, options, &send_code) + + return conn, process.pid + end + end end