class MindControl::Server

Listens UNIX socket and starts REPL sessions.

Attributes

repl[R]
socket_path[R]

Public Class Methods

new( socket_path, repl ) click to toggle source

@param [String] socket_path Absolute path of UNIX socket. @param [#start] repl Instance of REPL (@see MindControl::REPL).

# File lib/mind_control/server.rb, line 23
def initialize( socket_path, repl )
  @socket_path, @repl = socket_path, repl
end

Public Instance Methods

get_client_id( socket ) click to toggle source

Return id string for connected client.

@param [UNIXSocket] socket Client socket. @return [String]

# File lib/mind_control/server.rb, line 111
def get_client_id( socket )
  # UNIX socket return effective UID/GID for connected client
  euid, _ = socket.getpeereid

  # Find record in /etc/passwd
  user_info = Etc.getpwuid euid

  return "#{user_info.name} (#{user_info.gecos})"
end
handle_client_connection( socket, client_id ) click to toggle source

Starts REPL session in separate thread for connected client.

@param [Socket] socket Client socket. @param [String] client_id ID string for connected client.

# File lib/mind_control/server.rb, line 127
def handle_client_connection( socket, client_id )
  info "Starting new MindControl session for user #{client_id} ..."

  # Client will send us his STDIN and STDOUT
  client_stdin, client_stdout = socket.recv_io, socket.recv_io

  # Start REPL
  repl.start client_stdin, client_stdout

  info "MindControl session for user #{client_id} has ended!"
rescue Exception => e
  error_message = "REPL exception: #{e.message} (#{e.class.name})\n#{e.backtrace.join( "\n" )}"
  error error_message

  # Send error to client
  client_stdout.puts error_message if client_stdout
end
running?() click to toggle source

Server is running?

# File lib/mind_control/server.rb, line 79
def running?
  @server_thread && @server_thread.alive?
end
start() click to toggle source

Start server.

# File lib/mind_control/server.rb, line 30
def start
  return if running?

  info "Starting MindControl server on #{socket_path} ..."

  # Storage for client threads
  client_threads = ThreadGroup.new

  # Start acceptor thread
  @server_thread = Thread.new do
    begin
      start_server_loop( socket_path ) do |client_socket|
        # Process client in new thread
        client_threads.add Thread.new {
          begin
            handle_client_connection( client_socket, get_client_id( client_socket ))
          ensure
            # We MUST close the socket
            client_socket.close
          end
        }
      end
    ensure
      # Kill all client threads on server stop
      client_threads.list.each( &:kill )
    end
  end

  # We should known if our server has failed
  @server_thread.abort_on_exception = true
end
start_server_loop( socket_path, &block ) click to toggle source

Starts UNIX server, accepts clients and yields its sockets in loop.

@param [String] socket_path Path to UNIX socket to bind to. @yeilds [UNIXSocket]

# File lib/mind_control/server.rb, line 89
def start_server_loop( socket_path, &block )
  # Remove old file
  FileUtils.rm_f socket_path
  FileUtils.mkdir_p File.dirname( socket_path )

  UNIXServer.open( socket_path ) do |server|
    loop do
      # Wait for client
      block.call server.accept
    end
  end
ensure
  # Cleanup
  FileUtils.rm socket_path
end
stop() click to toggle source

Stop server.

# File lib/mind_control/server.rb, line 65
def stop
  return unless running?

  info "Stopping MindControl server ..."

  # Kill acceptor thread
  @server_thread.kill
  @server_thread.join
  @server_thread = nil
end