Class: Snailgun::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/snailgun/server.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sockname = nil) {|_self| ... } ⇒ Server

Returns a new instance of Server.

Yields:

  • (_self)

Yield Parameters:



20
21
22
23
24
25
# File 'lib/snailgun/server.rb', line 20

def initialize(sockname = nil)
  @sockname = sockname || "/tmp/snailgun#{$$}"
  File.delete(@sockname) rescue nil
  @socket = UNIXServer.open(@sockname)
  yield self if block_given?
end

Instance Attribute Details

#socknameObject

Returns the value of attribute sockname.



18
19
20
# File 'lib/snailgun/server.rb', line 18

def sockname
  @sockname
end

Class Method Details

.shellObject



105
106
107
108
109
# File 'lib/snailgun/server.rb', line 105

def self.shell
  shell_opts = ENV['SNAILGUN_SHELL_OPTS']
  args = shell_opts ? Shellwords.shellwords(shell_opts) : []
  system(ENV['SHELL'] || 'bash', *args)
end

Instance Method Details

#interactive!Object

Interactive mode (start a subshell with SNAILGUN_SOCK set up, and terminate the snailgun server when the subshell exits)



113
114
115
116
117
118
119
120
121
122
123
# File 'lib/snailgun/server.rb', line 113

def interactive!
  ENV['SNAILGUN_SOCK'] = @sockname
  pid = Process.fork {
    STDERR.puts "Snailgun starting on #{sockname} - 'exit' to end"
    run
  }
  self.class.shell
  Process.kill('TERM',pid)
  # TODO: wait a few secs for it to die, 'KILL' if required
  STDERR.puts "Snailgun ended"
end

#runObject



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
# File 'lib/snailgun/server.rb', line 27

def run
  while client = @socket.accept
    pid = fork do
      rubylib = nil
      begin
        STDIN.reopen(client.recv_io)
        STDOUT.reopen(client.recv_io)
        STDERR.reopen(client.recv_io)
        nbytes = client.read(4).unpack("N").first
        args, env, cwd, pgid = Marshal.load(client.read(nbytes))
        Dir.chdir(cwd)
        $LOAD_PATH.unshift rubylib if (rubylib = env['RUBYLIB'])
        begin
          Process.setpgid(0, pgid)
        rescue Errno::EPERM
        end
        exit_status = 0
        $SNAILGUN_EXIT = lambda {
          begin
            client.write [exit_status].pack("C")
          rescue Errno::EPIPE
          end
        }
        #This doesn't work in 1.8.6:
        #Thread.new { client.read(1); Thread.main.raise Interrupt }
        Thread.new { client.read(1); exit 1 }
        start_ruby(args)
      rescue SystemExit => e
        exit_status = e.status
        raise  # for the benefit of Test::Unit
      rescue Exception => e
        STDERR.puts "#{e}\n\t#{e.backtrace.join("\n\t")}"
        exit 1
      ensure
        $LOAD_PATH.shift if rubylib
      end
    end
    Process.detach(pid) if pid && pid > 0
    client.close
  end
ensure
  File.delete(@sockname) rescue nil
end

#start_ruby(args) ⇒ Object

Process the received ruby command line. (TODO: implement more options)



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
# File 'lib/snailgun/server.rb', line 72

def start_ruby(args)
  e = []
  OptionParser.new do |opts|
    opts.on("-e EXPR") do |v|
      e << v
    end
    opts.on("-I DIR") do |v|
      $:.unshift v
    end
    opts.on("-r LIB") do |v|
      require v
    end
    # opts.on("-rcatch_exception") do |v|
    # end
    opts.on("-KU") do |v|
      $KCODE = 'u' if RUBY_VERSION < "1.9"
    end
  end.order!(args)

  ARGV.replace(args)
  if !e.empty?
    $0 = '-e'
    e.each { |expr| eval(expr, TOPLEVEL_BINDING) }
  elsif ARGV.empty?
    $0 = '-'
    eval(STDIN.read, TOPLEVEL_BINDING)
  else
    cmd = ARGV.shift
    $0 = cmd
    load(cmd)
  end
end