Title: [498] trunk/javasand: Fix so javasand works with JRuby 0.9.9, and prepare for 0.0.2
Revision
498
Author
olabini
Date
2007-04-25 03:49:31 -0400 (Wed, 25 Apr 2007)

Log Message

Fix so javasand works with JRuby 0.9.9, and prepare for 0.0.2

Modified Paths


Added Paths

Diff

Modified: trunk/javasand/javasand.gemspec (497 => 498)


--- trunk/javasand/javasand.gemspec	2007-04-25 05:27:22 UTC (rev 497)
+++ trunk/javasand/javasand.gemspec	2007-04-25 07:49:31 UTC (rev 498)
@@ -2,9 +2,9 @@
 
 spec = Gem::Specification.new do |s|
   s.name = "javasand"
-  s.version = "0.0.1"
+  s.version = "0.0.2"
   s.author = "JRuby-extras"
-  s.email = "[EMAIL PROTECTED]"
+  s.email = "[EMAIL PROTECTED]"
   s.homepage = "http://jruby-extras.rubyforge.org/"
   s.platform = Gem::Platform::RUBY #should be JAVA
   s.summary = "Sandbox support for JRuby. Only usable with JRuby"
@@ -12,7 +12,7 @@
   s.files = candidates.delete_if do |item| item.include?(".svn") || item.include?("rdoc") end
   s.require_path = "lib"
   s.has_rdoc = false
-  s.requirements << "JRuby with the org.jruby.Profile-class"
+  s.requirements << "JRuby 0.9.9 or larger"
 end
 
 if $0 == __FILE__

Added: trunk/javasand/lib/sandbox/irb.rb (0 => 498)


--- trunk/javasand/lib/sandbox/irb.rb	                        (rev 0)
+++ trunk/javasand/lib/sandbox/irb.rb	2007-04-25 07:49:31 UTC (rev 498)
@@ -0,0 +1,95 @@
+require 'irb'
+require 'sandbox'
+
+class Sandbox::IRB
+
+  def initialize(box)
+    @box, @sig, @p = box, :IN_IRB
+    @box_errors = %w[StandardError ScriptError].map { |x| @box.eval(x) }
+    @prompt = {:start => ">> ", :continue => ".. ", :nested => ".. ",
+      :string => "   ", :return => "=> %s\n"}
+  end
+
+  def box_eval(str)
+    @box.eval(str)
+  end
+
+  def start(io)
+    scanner = RubyLex.new
+    scanner.exception_on_syntax_error = false
+    scanner.set_prompt do |ltype, indent, continue, line_no|
+      if ltype
+        f = @prompt[:string]
+      elsif continue
+        f = @prompt[:continue]
+      elsif indent > 0
+        f = @prompt[:nested]
+      else
+        f = @prompt[:start]
+      end
+      f = "" unless f
+      @p = prompt(f, ltype, indent, line_no)
+    end
+
+    scanner.set_input(io) do
+      signal_status(:IN_INPUT) do
+        io.print @p
+        io.gets
+      end
+    end
+
+    scanner.each_top_level_statement do |line, line_no|
+      signal_status(:IN_EVAL) do
+        line.untaint
+        begin
+          val = box_eval(line)
+          io.puts @prompt[:return] % [val.inspect]
+        rescue Sandbox::Exception, Sandbox::TimeoutError => e
+          io.print e, "\n"
+        end
+      end
+    end
+  end
+
+  def prompt(prompt, ltype, indent, line_no)
+    p = prompt.dup
+    p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
+      case $2
+      # when "N"
+      #   @context.irb_name
+      # when "m"
+      #   @context.main.to_s
+      # when "M"
+      #   @context.main.inspect
+      when "l"
+        ltype
+      when "i"
+        if $1 
+          format("%" + $1 + "d", indent)
+        else
+          indent.to_s
+        end
+      when "n"
+        if $1 
+          format("%" + $1 + "d", line_no)
+        else
+          line_no.to_s
+        end
+      when "%"
+        "%"
+      end
+    end
+    p
+  end
+
+  def signal_status(status)
+    return yield if @sig == :IN_LOAD
+    sig_back = @sig
+    @sig = status
+    begin
+      yield
+    ensure
+      @sig = sig_back
+    end
+  end
+end

Added: trunk/javasand/lib/sandbox/prelude.rb (0 => 498)


--- trunk/javasand/lib/sandbox/prelude.rb	                        (rev 0)
+++ trunk/javasand/lib/sandbox/prelude.rb	2007-04-25 07:49:31 UTC (rev 498)
@@ -0,0 +1,21 @@
+# Alternate "safer" versions of Ruby methods.  Mostly non-blocking.
+[Fixnum, Bignum, Float].each do |klass|
+  klass.class_eval do
+
+    # A very weak version of pow, it doesn't work on Floats, but it's
+    # gonna fill the most common uses for now.
+    def ** x
+      case x
+      when 0; 1
+      when 1; self
+      else
+        y = 1
+        while 0 <= (x -= 1) do
+          y *= self
+        end
+        y
+      end
+    end
+
+  end
+end

Added: trunk/javasand/lib/sandbox/server.rb (0 => 498)


--- trunk/javasand/lib/sandbox/server.rb	                        (rev 0)
+++ trunk/javasand/lib/sandbox/server.rb	2007-04-25 07:49:31 UTC (rev 498)
@@ -0,0 +1,140 @@
+require 'socket'
+require 'sandbox/irb'
+
+class Sandbox::IRBServer
+  attr_reader :acceptor
+  attr_reader :workers
+  attr_reader :host
+  attr_reader :port
+  attr_reader :timeout
+  attr_reader :num_processors
+
+  def initialize(host, port, num_processors=(2**30-1), timeout=0)
+    @socket = TCPServer.new(host, port) 
+    @host = host
+    @port = port
+    @workers = ThreadGroup.new
+    @timeout = timeout
+    @num_processors = num_processors
+    @death_time = 60
+    @sessions = {}
+  end
+
+  def randid
+    abc = %{ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz} 
+    (1..20).map { abc[rand(abc.size),1] }.join
+  end
+
+  def new_sandbox
+    Sandbox.safe(:timeout => 10)
+  end
+
+  def process_client(client)
+    begin
+      case client.gets
+      when /^LOGIN (\w+)/
+        sess = $1
+      else
+        sess = randid
+      end
+
+      @sessions[sess] ||= new_sandbox
+      client.puts sess
+      Sandbox::IRB.new(@sessions[sess]).start(client)
+    rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
+      # ignored
+    rescue Errno::EMFILE
+      reap_dead_workers('too many files')
+    rescue Object
+      STDERR.puts "#{Time.now}: ERROR: #$!"
+      STDERR.puts $!.backtrace.join("\n")
+    ensure
+      client.close unless client.closed?
+    end
+  end
+
+  # Used internally to kill off any worker threads that have taken too long
+  # to complete processing.  Only called if there are too many processors
+  # currently servicing.  It returns the count of workers still active
+  # after the reap is done.  It only runs if there are workers to reap.
+  def reap_dead_workers(reason='unknown')
+    if @workers.list.length > 0
+      STDERR.puts "#{Time.now}: Reaping [EMAIL PROTECTED] threads for slow workers because of '#{reason}'"
+      mark = Time.now
+      @workers.list.each do |w|
+        w[:started_on] = Time.now if not w[:started_on]
+
+        if mark - w[:started_on] > @death_time + @timeout
+          STDERR.puts "Thread #{w.inspect} is too old, killing."
+          w.raise(TimeoutError.new("Timed out thread."))
+        end
+      end
+    end
+
+    return @workers.list.length
+  end
+
+  # Performs a wait on all the currently running threads and kills any that take
+  # too long.  Right now it just waits 60 seconds, but will expand this to
+  # allow setting.  The @timeout setting does extend this waiting period by
+  # that much longer.
+  def graceful_shutdown
+    while reap_dead_workers("shutdown") > 0
+      STDERR.print "Waiting for [EMAIL PROTECTED] requests to finish, could take [EMAIL PROTECTED] + @timeout} seconds."
+      sleep @death_time / 10
+    end
+  end
+
+
+  # Runs the thing.  It returns the thread used so you can "join" it.  You can also
+  # access the HttpServer::acceptor attribute to get the thread later.
+  def run
+    BasicSocket.do_not_reverse_lookup=true
+
+    @acceptor = Thread.new do
+      while true
+        begin
+          client = @socket.accept
+          worker_list = @workers.list
+
+          if worker_list.length >= @num_processors
+            STDERR.puts "Server overloaded with #{worker_list.length} processors ([EMAIL PROTECTED] max). Dropping connection."
+            client.close
+            reap_dead_workers("max processors")
+          else
+            thread = Thread.new { process_client(client) }
+            thread.abort_on_exception = true
+            thread[:started_on] = Time.now
+            @workers.add(thread)
+
+            sleep @timeout/100 if @timeout > 0
+          end
+        rescue StopServer
+          @socket.close if not @socket.closed?
+          break
+        rescue Errno::EMFILE
+          reap_dead_workers("too many open files")
+          sleep 0.5
+        rescue Errno::ECONNABORTED
+          # client closed the socket even before accept
+          client.close if not client.closed?
+        end
+      end
+
+      graceful_shutdown
+    end
+
+    return @acceptor
+  end
+
+  # Stops the acceptor thread and then causes the worker threads to finish
+  # off the request queue before finally exiting.
+  def stop
+    stopper = Thread.new do 
+      exc = StopServer.new
+      @acceptor.raise(exc)
+    end
+    stopper.priority = 10
+  end
+
+end

Added: trunk/javasand/lib/sandbox.rb (0 => 498)


--- trunk/javasand/lib/sandbox.rb	                        (rev 0)
+++ trunk/javasand/lib/sandbox.rb	2007-04-25 07:49:31 UTC (rev 498)
@@ -0,0 +1,90 @@
+require 'sand_table'
+require 'thread'
+
+module Sandbox
+
+  BUILD = "#{VERSION}.#{REV_ID[6..-3]}" #:nodoc:
+  PRELUDE = File.expand_path("../sandbox/prelude.rb", __FILE__) #:nodoc:
+
+  #
+  # Stands in for an exception raised within the sandbox during evaluation.
+  # (See Sandbox#eval.)
+  #
+  class Exception
+  end
+
+  #
+  # Raised when the duration of a sandbox evaluation exceeds a specified
+  # timeout.  (See Sandbox#eval.)
+  #
+  class TimeoutError < Exception
+  end
+
+  class Full
+    private :_eval
+    #
+    # call-seq:
+    #    sandbox.eval(str, opts={})   => obj
+    #
+    # Evaluates +str+ as Ruby code inside the sandbox and returns
+    # the result.  If an option hash +opts+ is provided, any options
+    # specified in it take precedence over options specified when +sandbox+
+    # was created.  (See Sandbox.new.)
+    #
+    # Available options include:
+    #
+    # [:timeout]  The maximum time in seconds which Sandbox#eval is allowed to
+    #             run before it is forcibly terminated.
+    # [:safelevel]  The $SAFE level to use during evaluation in the sandbox.
+    #
+    # If evaluation times out, Sandbox#eval will raise a
+    # Sandbox::TimeoutError.  If no timeout is specified, Sandbox#eval will
+    # be allowed to run indefinitely.
+    #
+    def eval(str, opts = {})
+      opts = @options.merge(opts)
+      if opts[:timeout] or opts[:safelevel]
+        th, exc, timed_out = nil, nil, false
+        safelevel = opts[:safelevel]
+        val = nil
+        th = Thread.start(str) do
+          $SAFE = safelevel if safelevel and safelevel > $SAFE
+          begin
+            val = _eval(str)
+          rescue Exception => exc
+          end
+        end
+        th.join(opts[:timeout])
+        if th.alive?
+          if th.respond_to? :kill!
+            th.kill!
+          else
+            th.kill
+          end
+          timed_out = true
+        end
+        if timed_out
+          raise TimeoutError, "#{self.class}#eval timed out"
+        elsif exc
+          raise exc
+        else
+          val
+        end
+      else
+        _eval(str)
+      end
+    end
+
+    #
+    # call-seq:
+    #    sandbox.load(portname, opts={})   => obj
+    #
+    # Reads all available data from the given I/O port +portname+ and
+    # then evaluates it as a string in +sandbox+.  (See Sandbox#eval.)
+    #
+    def load(io, opts = {})
+      eval(IO.read(io), opts)
+    end
+  end
+
+end

Modified: trunk/javasand/src/java/SandTableService.java (497 => 498)


--- trunk/javasand/src/java/SandTableService.java	2007-04-25 05:27:22 UTC (rev 497)
+++ trunk/javasand/src/java/SandTableService.java	2007-04-25 07:49:31 UTC (rev 498)
@@ -19,9 +19,9 @@
 
         cSandboxFull.attr_accessor(new IRubyObject[]{runtime.newString("options")});
         CallbackFactory fullcb = runtime.callbackFactory(SandboxFull.class);
-        mSandbox.getMetaClass().defineFastMethod("new",fullcb.getOptSingletonMethod("s_new"));
-        mSandbox.getMetaClass().defineFastMethod("safe",fullcb.getOptSingletonMethod("s_safe"));
-        mSandbox.getMetaClass().defineFastMethod("current",fullcb.getSingletonMethod("s_current"));
+        mSandbox.getMetaClass().defineFastMethod("new",fullcb.getFastOptSingletonMethod("s_new"));
+        mSandbox.getMetaClass().defineFastMethod("safe",fullcb.getFastOptSingletonMethod("s_safe"));
+        mSandbox.getMetaClass().defineFastMethod("current",fullcb.getFastSingletonMethod("s_current"));
 
         cSandboxFull.getMetaClass().defineFastMethod("new",fullcb.getFastOptSingletonMethod("newInstance"));
         cSandboxFull.defineFastMethod("initialize",fullcb.getFastOptSingletonMethod("initialize"));

Modified: trunk/javasand/src/java/org/jruby/ext/sandbox/SandboxFull.java (497 => 498)


--- trunk/javasand/src/java/org/jruby/ext/sandbox/SandboxFull.java	2007-04-25 05:27:22 UTC (rev 497)
+++ trunk/javasand/src/java/org/jruby/ext/sandbox/SandboxFull.java	2007-04-25 07:49:31 UTC (rev 498)
@@ -38,6 +38,7 @@
 import org.jruby.RubyObject;
 import org.jruby.Profile;
 
+import org.jruby.runtime.Arity;
 import org.jruby.runtime.Block;
 import org.jruby.runtime.CallbackFactory;
 import org.jruby.runtime.load.BasicLibraryService;
@@ -73,7 +74,7 @@
     }
 
     public static IRubyObject initialize(IRubyObject recv, IRubyObject[] args) {
-        args = recv.scanArgs(args,0,1);
+        args = Arity.scanArgs(recv.getRuntime(),args,0,1);
         if(args[0].isNil()) {
             args[0] = RubyHash.newHash(recv.getRuntime());
         }
_______________________________________________
Jruby-extras-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/jruby-extras-devel

Reply via email to