Class: MindControl::Server

Inherits:
Object
  • Object
show all
Includes:
Loggable
Defined in:
lib/mind_control/server.rb

Overview

Listens UNIX socket and starts REPL sessions.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(socket_path, repl) ⇒ Server

Returns a new instance of Server.

Parameters:

  • socket_path (String)

    Absolute path of UNIX socket.

  • repl (#start)

    Instance of REPL (@see MindControl::REPL).



23
24
25
# File 'lib/mind_control/server.rb', line 23

def initialize( socket_path, repl )
  @socket_path, @repl = socket_path, repl
end

Instance Attribute Details

#replObject (readonly)

Returns the value of attribute repl.



17
18
19
# File 'lib/mind_control/server.rb', line 17

def repl
  @repl
end

#socket_pathObject (readonly)

Returns the value of attribute socket_path.



16
17
18
# File 'lib/mind_control/server.rb', line 16

def socket_path
  @socket_path
end

Instance Method Details

#get_client_id(socket) ⇒ String

Return id string for connected client.

Parameters:

  • socket (UNIXSocket)

    Client socket.

Returns:

  • (String)


111
112
113
114
115
116
117
118
119
# 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
   = Etc.getpwuid euid

  return "#{.name} (#{.gecos})"
end

#handle_client_connection(socket, client_id) ⇒ Object

Starts REPL session in separate thread for connected client.

Parameters:

  • socket (Socket)

    Client socket.

  • client_id (String)

    ID string for connected client.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# 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?Boolean

Server is running?

Returns:

  • (Boolean)


79
80
81
# File 'lib/mind_control/server.rb', line 79

def running?
  @server_thread && @server_thread.alive?
end

#startObject

Start server.



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
# 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) ⇒ Object

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

Parameters:

  • socket_path (String)

    Path to UNIX socket to bind to.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 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

#stopObject

Stop server.



65
66
67
68
69
70
71
72
73
74
# 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