Class: Style
- Inherits:
-
Object
- Object
- Style
- Defined in:
- lib/style.rb,
lib/style/adapter/rails.rb
Overview
This hooks the HTTP request into Ruby on Rails and Mongrel.
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#mutex ⇒ Object
readonly
Returns the value of attribute mutex.
Instance Method Summary collapse
-
#check_dir(filename) ⇒ Object
Check that the directory of the given filename exists and is a directory, exit otherwise.
-
#check_file(filename) ⇒ Object
Check that the filename given exists and is a file, exit otherwise.
-
#config_file_options(filename) ⇒ Object
Return a hash of options from the config file, or an empty hash.
- #create_socket(bind, port) ⇒ Object
-
#detach ⇒ Object
Detach the process from the controlling terminal, exit otherwise.
-
#exit_with_error(error_message) ⇒ Object
Print the error message and the usage, then exit.
-
#initialize ⇒ Style
constructor
Configure style.
-
#kill_children_gently(pids) ⇒ Object
Kill each of the given pids in order.
-
#kill_gently(pid) ⇒ Object
Try to allow the child to die gracefully, by trying INT, TERM, and then KILL.
-
#load_adapter ⇒ Object
Load the revelent style adapter/framework.
-
#load_handler ⇒ Object
Load the revelent style handler/server.
-
#parse_options ⇒ Object
Parse the command line options, and merge them with the default options and the config file options.
-
#process(command) ⇒ Object
Process the command given.
-
#process_name ⇒ Object
Name of the Style.
-
#process_supervised_command(command) ⇒ Object
Process the command given in supervised mode.
-
#process_unsupervised_command(command) ⇒ Object
Process the command given in unsupervised mode.
-
#redirect_io ⇒ Object
Reset the umask and redirect STDIN to /dev/null and STDOUT and STDERR to the appropriate logfile.
-
#reload_config ⇒ Object
Reload the configuration, used when restarting.
-
#reload_gems ⇒ Object
Clear the gem paths so that restarts can pick up gems that have been added since style was initially started.
-
#restart_stopped_children ⇒ Object
Restart stopping children by waiting on them.
-
#run ⇒ Object
Initialzes Rails and Mongrel with the appropriate environment and settings.
-
#run_child ⇒ Object
Load the relevant handler and adapter and run the server.
-
#run_in_foreground ⇒ Object
Run the program in the foreground instead of daemonizing.
- #run_rails_mongrel ⇒ Object
- #run_rails_scgi ⇒ Object
- #run_rails_thin ⇒ Object
-
#setup_supervisor_signals ⇒ Object
Setup the necessary signals used in supervisory mode:.
-
#signal_supervisor(signal) ⇒ Object
Read the pid file and signal the supervising process, or raise an error.
-
#start ⇒ Object
Start an unsupervised group of processes.
-
#start_child(socket) ⇒ Object
Start a child process.
-
#stop ⇒ Object
Stop an unsupervised group of processes.
-
#supervisor_children_start ⇒ Object
Start all necessary children of the supervisor process (number * fork).
-
#supervisor_exit ⇒ Object
Remove the pid file and exit.
-
#supervisor_loop ⇒ Object
Do the final setup of the supervisor process, and then loop indefinitely.
-
#supervisor_restart_children ⇒ Object
Restart all children of the supervisor process.
-
#supervisor_shutdown ⇒ Object
Set the internal shutdown signal for the supervisor process.
-
#supervisor_start ⇒ Object
Start the supervisor process, detaching it from the controlling terminal.
-
#usage ⇒ Object
The command line usage of the style program.
Constructor Details
#initialize ⇒ Style
Configure style
21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/style.rb', line 21 def initialize @config = {:pidfile=>'style.pid', :number=>1, :port=>9999, :fork=>1, :bind=>'127.0.0.1', :cliconfig=>{}, :killtime=>2, :config=>'style.yaml', :logfile=>'style.log', :children=>{},:sockets=>{}, :adapter_config=>{}, :directory=>'.', :debug=>false, :unsupervised=> false, :adapter=>'rails', :handler=>'mongrel'} @mutex = Mutex.new begin rescue GetoptLong::InvalidOption exit_with_error($!) end end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
18 19 20 |
# File 'lib/style.rb', line 18 def config @config end |
#mutex ⇒ Object (readonly)
Returns the value of attribute mutex.
18 19 20 |
# File 'lib/style.rb', line 18 def mutex @mutex end |
Instance Method Details
#check_dir(filename) ⇒ Object
Check that the directory of the given filename exists and is a directory, exit otherwise
37 38 39 40 41 42 |
# File 'lib/style.rb', line 37 def check_dir(filename) filename = File.(filename) dirname = File.dirname(filename) exit_with_error("Invalid directory: #{dirname}") unless File.directory?(dirname) filename end |
#check_file(filename) ⇒ Object
Check that the filename given exists and is a file, exit otherwise
45 46 47 48 49 |
# File 'lib/style.rb', line 45 def check_file(filename) filename = File.(filename) exit_with_error("Invalid file: #{filename}") unless File.file?(filename) filename end |
#config_file_options(filename) ⇒ Object
Return a hash of options from the config file, or an empty hash
52 53 54 55 56 57 58 |
# File 'lib/style.rb', line 52 def (filename) conf = YAML.load(File.read(filename)) rescue (return Hash.new) return Hash.new unless conf.is_a?(Hash) conf.delete(:directory) conf.delete(:config) conf end |
#create_socket(bind, port) ⇒ Object
60 61 62 63 64 |
# File 'lib/style.rb', line 60 def create_socket(bind, port) socket = TCPServer.new(bind, port) socket.listen(50) socket end |
#detach ⇒ Object
Detach the process from the controlling terminal, exit otherwise
67 68 69 70 71 72 73 |
# File 'lib/style.rb', line 67 def detach unless Process.setsid puts "Cannot detach from controlling terminal" exit(1) end trap(:HUP, 'IGNORE') end |
#exit_with_error(error_message) ⇒ Object
Print the error message and the usage, then exit
76 77 78 79 80 |
# File 'lib/style.rb', line 76 def exit_with_error() puts puts usage exit(1) end |
#kill_children_gently(pids) ⇒ Object
Kill each of the given pids in order
83 84 85 |
# File 'lib/style.rb', line 83 def kill_children_gently(pids) pids.each{|pid| kill_gently(pid)} end |
#kill_gently(pid) ⇒ Object
Try to allow the child to die gracefully, by trying INT, TERM, and then KILL
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/style.rb', line 88 def kill_gently(pid) begin Process.kill('INT', pid) sleep(config[:killtime]) Process.kill('TERM', pid) sleep(config[:killtime]) Process.kill('KILL', pid) rescue return nil end end |
#load_adapter ⇒ Object
Load the revelent style adapter/framework
101 102 103 104 105 106 107 108 109 |
# File 'lib/style.rb', line 101 def load_adapter adapter = config[:adapter].to_s if adapter == 'rails' require "style/adapter/rails" run else require adapter end end |
#load_handler ⇒ Object
Load the revelent style handler/server
112 113 114 |
# File 'lib/style.rb', line 112 def load_handler require "style/handler/#{config[:handler]}" end |
#parse_options ⇒ Object
Parse the command line options, and merge them with the default options and the config file options. Config file options take precendence over the default options, and command line options take precendence over both.
119 120 121 122 123 124 125 126 127 128 129 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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/style.rb', line 119 def cliconfig = config[:cliconfig] GetoptLong.new( [ '--adapter', '-a', GetoptLong::REQUIRED_ARGUMENT ], [ '--bind', '-b', GetoptLong::REQUIRED_ARGUMENT ], [ '--config', '-c', GetoptLong::REQUIRED_ARGUMENT ], [ '--directory', '-d', GetoptLong::REQUIRED_ARGUMENT ], [ '--debug', '-D', GetoptLong::NO_ARGUMENT], [ '--fork', '-f', GetoptLong::REQUIRED_ARGUMENT ], [ '--handler', '-h', GetoptLong::REQUIRED_ARGUMENT ], [ '--killtime', '-k', GetoptLong::REQUIRED_ARGUMENT ], [ '--logfile', '-l', GetoptLong::REQUIRED_ARGUMENT ], [ '--number', '-n', GetoptLong::REQUIRED_ARGUMENT ], [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ], [ '--pidfile', '-P', GetoptLong::REQUIRED_ARGUMENT ], [ '--unsupervised', '-u', GetoptLong::NO_ARGUMENT] ).each do |opt, arg| case opt when '--adapter' cliconfig[:adapter] = arg when '--bind' cliconfig[:bind] = arg when '--config' config[:config] = cliconfig[:config] = arg when '--directory' config[:directory] = arg when '--debug' cliconfig[:debug] = true when '--fork' cliconfig[:fork] = arg.to_i when '--handler' cliconfig[:handler] = arg when '--killtime' cliconfig[:killtime] = arg.to_i when '--logfile' cliconfig[:logfile] = arg when '--number' cliconfig[:number] = arg.to_i when '--port' cliconfig[:port] = arg.to_i when '--pidfile' cliconfig[:pidfile] = arg when '--unsupervised' cliconfig[:unsupervised] = true end end config[:directory] = File.(config[:directory]) Dir.chdir(config[:directory]) rescue (exit_with_error("Invalid directory: #{config[:directory]}")) cliconfig[:config] = File.(check_file(cliconfig[:config])) if cliconfig[:config] config[:config] = File.(config[:config]) reload_config unless config[:debug] [:logfile, :pidfile].each do |opt| cliconfig[opt] = File.(cliconfig[opt]) if cliconfig[opt] config[opt] = File.(config[opt]) config[opt] = check_dir(config[opt]) end end end |
#process(command) ⇒ Object
Process the command given
183 184 185 186 187 188 189 190 191 |
# File 'lib/style.rb', line 183 def process(command) if config[:debug] run_in_foreground elsif config[:unsupervised] process_unsupervised_command(command) else process_supervised_command(command) end end |
#process_name ⇒ Object
Name of the Style
410 411 412 |
# File 'lib/style.rb', line 410 def process_name "style-#{config[:adapter]}-#{config[:handler]} #{Dir.pwd}" end |
#process_supervised_command(command) ⇒ Object
Process the command given in supervised mode. All commands except start just send a signal to the pid in the pid file.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/style.rb', line 195 def process_supervised_command(command) case command when 'decrement' signal_supervisor(:USR2) when 'halt' signal_supervisor(:TERM) when 'increment' signal_supervisor(:USR1) when 'restart' signal_supervisor(:HUP) when 'run' supervisor_loop when 'start' supervisor_start when 'stop' signal_supervisor(:INT) else exit_with_error("Not a valid command: #{command}") end end |
#process_unsupervised_command(command) ⇒ Object
Process the command given in unsupervised mode. Only restart, start, and stop are supported.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/style.rb', line 218 def process_unsupervised_command(command) case command when /\A(decrement|increment|run)\z/ puts "#{$1} not supported in unsupervised mode" exit(1) when 'restart' stop start when 'start' start when /\A(stop|halt)\z/ stop else exit_with_error("Not a valid command: #{command}") end end |
#redirect_io ⇒ Object
Reset the umask and redirect STDIN to /dev/null and STDOUT and STDERR to the appropriate logfile.
237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/style.rb', line 237 def redirect_io File.umask(0000) STDIN.reopen('/dev/null') rescue nil begin STDOUT.reopen(config[:logfile], "a") STDOUT.sync = true rescue STDOUT.reopen('/dev/null') rescue nil end STDERR.reopen(STDOUT) rescue nil STDERR.sync = true end |
#reload_config ⇒ Object
Reload the configuration, used when restarting. Only the following options take effect when reloading: config, killtime, pidfile, adapter, handler, and adapter_config.
253 254 255 256 |
# File 'lib/style.rb', line 253 def reload_config config.merge!((config[:config])) config.merge!(config[:cliconfig]) end |
#reload_gems ⇒ Object
Clear the gem paths so that restarts can pick up gems that have been added since style was initially started
260 261 262 263 264 |
# File 'lib/style.rb', line 260 def reload_gems Gem.clear_paths # This is done by clear_paths starting with rubygems-0.9.4.4 Gem.instance_variable_set(:@searcher, nil) if Gem::RubyGemsVersion < '0.9.4.4' end |
#restart_stopped_children ⇒ Object
Restart stopping children by waiting on them. If the children have died and are in the list of children, restart them.
268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/style.rb', line 268 def restart_stopped_children loop do break unless pid = Process.wait(-1, Process::WNOHANG) #puts "received sigchild, pid #{pid}" mutex.synchronize do if socket = config[:children].delete(pid) start_child(socket) end end end rescue nil end |
#run ⇒ Object
Initialzes Rails and Mongrel with the appropriate environment and settings.
6 7 8 9 10 11 12 13 14 |
# File 'lib/style/adapter/rails.rb', line 6 def run if config[:handler] == 'scgi' run_rails_scgi elsif config[:handler] == 'thin' run_rails_thin else run_rails_mongrel end end |
#run_child ⇒ Object
Load the relevant handler and adapter and run the server
281 282 283 284 |
# File 'lib/style.rb', line 281 def run_child load_handler load_adapter end |
#run_in_foreground ⇒ Object
Run the program in the foreground instead of daemonizing. Only runs on one port, and obviously doesn’t fork.
288 289 290 291 292 |
# File 'lib/style.rb', line 288 def run_in_foreground $STYLE_SOCKET = create_socket(config[:bind], config[:port]) config[:sockets][$STYLE_SOCKET] = config[:port] run_child end |
#run_rails_mongrel ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/style/adapter/rails.rb', line 29 def run_rails_mongrel require 'mongrel/rails' settings = {:cwd => Dir.pwd, :log_file => 'log/rails-mongrel.log', :environment => 'production', :docroot => 'public', :mime_map => nil, :debug => false, :includes => ["mongrel"], :config_script => nil, :num_processors => 1024, :timeout => 0, :user => nil, :group => nil, :prefix => nil}.merge(config[:adapter_config]) ENV['RAILS_ENV'] = settings[:environment] $0 += " environment:#{ENV['RAILS_ENV']}" mongrel = Mongrel::Rails::RailsConfigurator.new(settings) do listener do mime = defaults[:mime_map] ? load_mime_map(defaults[:mime_map], mime) : {} debug "/" if defaults[:debug] uri defaults[:prefix] || "/", :handler => rails(:mime => mime, :prefix => defaults[:prefix]) load_plugins run_config(defaults[:config_script]) if defaults[:config_script] trap("INT") { @log.info("SIGTERM, forced shutdown."); shutdown(force=true) } setup_rails_signals end end mongrel.run mongrel.join end |
#run_rails_scgi ⇒ Object
25 26 27 |
# File 'lib/style/adapter/rails.rb', line 25 def run_rails_scgi RailsSCGIProcessor.new(config[:adapter_config]).listen end |
#run_rails_thin ⇒ Object
16 17 18 19 20 21 22 23 |
# File 'lib/style/adapter/rails.rb', line 16 def run_rails_thin = {:environment=>'production', :address=>config[:bind], :port=>config[:sockets][$STYLE_SOCKET], :pid=>'/dev/null', :log=>'/dev/null', :timeout=>Thin::Server::DEFAULT_TIMEOUT, :max_persistent_conns=>Thin::Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS, :max_conns=>Thin::Server::DEFAULT_MAXIMUM_CONNECTIONS}.merge(config[:adapter_config]) Thin::Controllers::Controller.new().start end |
#setup_supervisor_signals ⇒ Object
Setup the necessary signals used in supervisory mode:
* CLD - Restart any dead children
* HUP - Reload configuration and restart all children
* INT - Gracefully shutdown children and exit
* TERM - Immediately shutdown children and exit
* USR1 - Increase the number of listeners on each port by 1
* USR2 - Decrease the number of listeners on each port by 1
Note that these signals should be sent to the supervising process, the child processes are only required to shutdown on INT and TERM, and will respond to other signals differently depending on the style used.
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'lib/style.rb', line 306 def setup_supervisor_signals trap(:CLD) do # Child Died restart_stopped_children end trap(:HUP) do # Restart Children Dir.chdir(config[:directory]) rescue nil reload_config reload_gems supervisor_restart_children end trap(:INT) do # Graceful Shutdown supervisor_shutdown kill_children_gently(config[:children].keys) supervisor_exit end trap(:TERM) do # Fast Shutdown supervisor_shutdown pids = config[:children].keys Process.kill('TERM', *pids) rescue nil sleep(config[:killtime]) Process.kill('KILL', *pids) rescue nil supervisor_exit end trap(:USR1) do # Increment number of children config[:sockets].keys.each do |socket| mutex.synchronize{start_child(socket)} end end trap(:USR2) do # Decrement number of children config[:children].invert.values.each do |pid| mutex.synchronize do config[:children].delete(pid) kill_gently(pid) end end end end |
#signal_supervisor(signal) ⇒ Object
Read the pid file and signal the supervising process, or raise an error
351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/style.rb', line 351 def signal_supervisor(signal) begin pid = File.read(config[:pidfile]).to_i rescue puts "Can't read pidfile (#{config[:pidfile]}) to send signal #{signal}" exit(1) end if pid > 1 Process.kill(signal, pid) else puts "Illegal value in pidfile" exit(1) end end |
#start ⇒ Object
Start an unsupervised group of processes
383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/style.rb', line 383 def start fork do detach redirect_io config[:number].times do |i| port = config[:port]+i socket = create_socket(config[:bind], port) config[:sockets][socket] = port config[:fork].times{start_child(socket)} end File.open(config[:pidfile], 'wb'){|file| file.print("#{config[:children].keys.join(' ')}")} end end |
#start_child(socket) ⇒ Object
Start a child process. The child process will reset the signals used, as well as close any unused sockets, and then it should listen indefinitely on the provided socket.
369 370 371 372 373 374 375 376 377 378 379 380 |
# File 'lib/style.rb', line 369 def start_child(socket) return if config[:shutdown] pid = fork do $STYLE_SOCKET = socket [:HUP, :INT, :TERM, :USR1, :USR2].each{|signal| trap(signal, 'DEFAULT')} config[:sockets].keys.each{|sock| sock.close unless sock == socket} $0 = "#{process_name} port:#{config[:sockets][socket]}" run_child end #puts "started pid #{pid}" config[:children][pid] = socket end |
#stop ⇒ Object
Stop an unsupervised group of processes
398 399 400 401 402 403 404 405 406 407 |
# File 'lib/style.rb', line 398 def stop if File.file?(config[:pidfile]) pids = nil File.open(config[:pidfile], 'rb'){|f| pids = f.read.split.collect{|x| x.to_i if x.to_i > 1}.compact} if pids.length > 0 kill_children_gently(pids) File.delete(config[:pidfile]) end end end |
#supervisor_children_start ⇒ Object
Start all necessary children of the supervisor process (number * fork)
416 417 418 419 420 421 422 423 |
# File 'lib/style.rb', line 416 def supervisor_children_start config[:number].times do |i| port = config[:port]+i socket = create_socket(config[:bind], port) config[:sockets][socket] = port config[:fork].times{start_child(socket)} end end |
#supervisor_exit ⇒ Object
Remove the pid file and exit
426 427 428 429 |
# File 'lib/style.rb', line 426 def supervisor_exit File.delete(config[:pidfile]) rescue nil exit end |
#supervisor_loop ⇒ Object
Do the final setup of the supervisor process, and then loop indefinitely
432 433 434 435 436 437 438 |
# File 'lib/style.rb', line 432 def supervisor_loop $0 = "#{process_name} supervisor" redirect_io supervisor_children_start setup_supervisor_signals loop{sleep(10) && restart_stopped_children} end |
#supervisor_restart_children ⇒ Object
Restart all children of the supervisor process
441 442 443 444 445 446 447 |
# File 'lib/style.rb', line 441 def supervisor_restart_children config[:children].keys.each do |pid| mutex.synchronize{start_child(config[:children].delete(pid))} sleep(config[:killtime]) kill_gently(pid) end end |
#supervisor_shutdown ⇒ Object
Set the internal shutdown signal for the supervisor process. Once this is set, no children will be restarted
451 452 453 |
# File 'lib/style.rb', line 451 def supervisor_shutdown config[:shutdown] = true end |
#supervisor_start ⇒ Object
Start the supervisor process, detaching it from the controlling terminal
456 457 458 459 460 461 |
# File 'lib/style.rb', line 456 def supervisor_start fork do detach File.open(config[:pidfile], 'wb'){|file| file.print(fork{supervisor_loop})} end end |
#usage ⇒ Object
The command line usage of the style program
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'lib/style.rb', line 464 def usage "style [option value, ...] (decrement|halt|increment|restart|run|start|stop)\n Options:\n -a, --adapter Adapter/Framework to use [rails]\n -b, --bind IP address to bind to [127.0.0.1]\n -c, --config Location of config file [config/style.yaml]\n -d, --directory Working directory [.]\n -D, --debug Run the program in the foreground without forking [No]\n -f, --fork Number of listners on each port [1]\n -h, --handler Handler/Server to use [mongrel]\n -k, --killtime Number of seconds to wait when killing each child [2]\n -l, --logfile Where to redirect STDOUT and STDERR [log/style.log]\n -n, --number Number of ports to which to bind [1]\n -p, --port Starting port to which to bind [9999]\n -P, --pidfile Location of pid file [log/style.pid]\n -u, --unsupervised Whether to run unsupervised [No]\n END\nend\n" |