Class: CertificateDepot::Worker
- Inherits:
-
Object
- Object
- CertificateDepot::Worker
- Defined in:
- lib/certificate_depot/worker.rb
Overview
A worker runs in a separate process created by the server. It hangs around and polls the server socket for incoming connections. Once it finds one it tried to process it.
The worker has a lifeline to the server. The lifeline is a pipe used to signal the server. When the worker goes down the server can detect the severed lifeline. If the worker needs the server to stop sleeping it can write to the lifeline.
Instance Attribute Summary collapse
-
#lifeline ⇒ Object
Returns the value of attribute lifeline.
-
#server ⇒ Object
Returns the value of attribute server.
Class Method Summary collapse
-
.help(command) ⇒ Object
Returns help text for a certain command.
-
.parse_command(command) ⇒ Object
Parses a command issues by a client.
Instance Method Summary collapse
-
#cleanup ⇒ Object
Cleanup all worker resources.
-
#generate(socket, distinguished_name) ⇒ Object
Generates a new client certificate and writes it to the socket.
-
#help(socket, command) ⇒ Object
Writes help to the socket about a topic or a list of commands.
-
#initialize(server) ⇒ Worker
constructor
Creates a new worker instance.
-
#process_incoming_socket(socket, address) ⇒ Object
Processes an incoming request.
-
#run ⇒ Object
Starts the mainloop for the worker.
-
#run_command(socket, *args) ⇒ Object
Runs a command and writes the result to the request socket.
-
#signals_want_shutdown ⇒ Object
Returns true when the signals received by the process demand a shutdown.
-
#trap_signals ⇒ Object
Installs signal traps to listen for incoming signals to the process.
Constructor Details
#initialize(server) ⇒ Worker
Creates a new worker instance. The first argument is a server instance.
14 15 16 17 |
# File 'lib/certificate_depot/worker.rb', line 14 def initialize(server) @server = server @signals = [] end |
Instance Attribute Details
#lifeline ⇒ Object
Returns the value of attribute lifeline.
11 12 13 |
# File 'lib/certificate_depot/worker.rb', line 11 def lifeline @lifeline end |
#server ⇒ Object
Returns the value of attribute server.
11 12 13 |
# File 'lib/certificate_depot/worker.rb', line 11 def server @server end |
Class Method Details
.help(command) ⇒ Object
Returns help text for a certain command
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/certificate_depot/worker.rb', line 130 def self.help(command) case command when 'generate' "GENERATE generate <distinguished name> RETURNS A private key and certificate in PEM format. EXAMPLE generate /UID=12/CN=Bob Owner,[email protected] " when 'help' "HELP help <command> RETURNS A description of the command. EXAMPLE help generate " when 'shutdown' "SHUTDOWN shutdown RETURNS Kills the current worker handling the request. EXAMPLE shutdown " else "Unknown command #{command}" end end |
.parse_command(command) ⇒ Object
Parses a command issues by a client.
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/certificate_depot/worker.rb', line 115 def self.parse_command(command) parts = command.split(' ') parts[0] = parts[0].intern if parts[0] case parts[0] when :generate parts[1] = OpenSSL::X509::Name.parse(parts[1].to_s) if parts[1] when :revoke parts[1] = parts[1].to_i if parts[1] end parts end |
Instance Method Details
#cleanup ⇒ Object
Cleanup all worker resources
95 96 97 98 99 100 101 |
# File 'lib/certificate_depot/worker.rb', line 95 def cleanup server.log.info("Shutting down") begin lifeline.close rescue Errno::EPIPE end end |
#generate(socket, distinguished_name) ⇒ Object
Generates a new client certificate and writes it to the socket
20 21 22 23 24 25 26 27 28 |
# File 'lib/certificate_depot/worker.rb', line 20 def generate(socket, distinguished_name) attributes = { :type => :client, :subject => distinguished_name } keypair, certificate = server.depot.generate_keypair_and_certificate(attributes) socket.write keypair.private_key.to_s socket.write certificate.certificate.to_s end |
#help(socket, command) ⇒ Object
Writes help to the socket about a topic or a list of commands
31 32 33 34 35 36 37 |
# File 'lib/certificate_depot/worker.rb', line 31 def help(socket, command) if command socket.write(self.class.help(command.downcase)) else socket.write("generate help shutdown\n") end end |
#process_incoming_socket(socket, address) ⇒ Object
Processes an incoming request. Parse the command, run the command, and close the socket.
56 57 58 59 60 61 |
# File 'lib/certificate_depot/worker.rb', line 56 def process_incoming_socket(socket, address) input = socket.gets server.log.debug("Got input: #{input.strip}") run_command(socket, *self.class.parse_command(input)) socket.close end |
#run ⇒ Object
Starts the mainloop for the worker. The mainloop sleeps until one of the following three things happens: server gets a new request, activity on the lifeline to the server, or 2 seconds go by.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/certificate_depot/worker.rb', line 66 def run trap_signals loop do break if signals_want_shutdown begin # IO.select returns either a triplet of lists with IO objects that # need attention or nil on timeout of 2 seconds. if needy = IO.select([server.socket, lifeline], nil, [server.socket, lifeline], 2) server.log.debug("Detected activity on: #{needy.inspect}") # If the lifeline is active the server went down and we need to go # down as well. break if needy.flatten.any? { |io| !io.respond_to?(:accept_nonblock) } # Otherwise we handle incoming requests needy.flatten.each do |io| if io.respond_to?(:accept_nonblock) begin process_incoming_socket(*io.accept_nonblock) rescue Errno::EAGAIN, Errno::ECONNABORTED end end end end rescue EOFError, Errno::EAGAIN, Errno::EINTR, Errno::EBADF, IOError end end cleanup end |
#run_command(socket, *args) ⇒ Object
Runs a command and writes the result to the request socket.
40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/certificate_depot/worker.rb', line 40 def run_command(socket, *args) args = args.dup command = args.shift case command when :generate generate(socket, args[0]) when :help help(socket, args[0]) when :shutdown exit 1 end end |
#signals_want_shutdown ⇒ Object
Returns true when the signals received by the process demand a shutdown
110 111 112 |
# File 'lib/certificate_depot/worker.rb', line 110 def signals_want_shutdown !@signals.empty? end |
#trap_signals ⇒ Object
Installs signal traps to listen for incoming signals to the process.
104 105 106 107 |
# File 'lib/certificate_depot/worker.rb', line 104 def trap_signals trap(:QUIT) { @signals << :QUIT } trap(:EXIT) { @signals << :EXIT } end |