Class: Style
- Inherits:
-
Object
- Object
- Style
- Defined in:
- lib/style.rb,
lib/style/adapter/rails.rb,
lib/style/adapter/ramaze.rb,
lib/style/adapter/generic.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
This requires config[:script] || ‘style_adapter’, which means you can use it to host other supported web frameworks.
-
#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=>'log/style.pid', :number=>1, :port=>9999, :style=>'RailsMongrel', :fork=>1, :bind=>'127.0.0.1', :cliconfig=>{}, :killtime=>2, :config=>'config/style.yaml', :logfile=>'log/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 |
# File 'lib/style.rb', line 101 def load_adapter require "style/adapter/#{config[:adapter]}" end |
#load_handler ⇒ Object
Load the revelent style handler/server
106 107 108 |
# File 'lib/style.rb', line 106 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.
113 114 115 116 117 118 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 |
# File 'lib/style.rb', line 113 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
177 178 179 180 181 182 183 184 185 |
# File 'lib/style.rb', line 177 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
405 406 407 |
# File 'lib/style.rb', line 405 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.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/style.rb', line 189 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.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/style.rb', line 212 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.
231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/style.rb', line 231 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.
247 248 249 250 |
# File 'lib/style.rb', line 247 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
254 255 256 257 258 |
# File 'lib/style.rb', line 254 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.
262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/style.rb', line 262 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
This requires config[:script] || ‘style_adapter’, which means you can use it to host other supported web frameworks. Those frameworks should start the server that style is currently using, in order to pick up the correct socket.
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
275 276 277 278 279 |
# File 'lib/style.rb', line 275 def run_child load_handler load_adapter run end |
#run_in_foreground ⇒ Object
Run the program in the foreground instead of daemonizing. Only runs on one port, and obviously doesn’t fork.
283 284 285 286 287 |
# File 'lib/style.rb', line 283 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.
301 302 303 304 305 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 |
# File 'lib/style.rb', line 301 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
346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/style.rb', line 346 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
378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/style.rb', line 378 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.
364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/style.rb', line 364 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
393 394 395 396 397 398 399 400 401 402 |
# File 'lib/style.rb', line 393 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)
411 412 413 414 415 416 417 418 |
# File 'lib/style.rb', line 411 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
421 422 423 424 |
# File 'lib/style.rb', line 421 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
427 428 429 430 431 432 433 |
# File 'lib/style.rb', line 427 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
436 437 438 439 440 441 442 |
# File 'lib/style.rb', line 436 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
446 447 448 |
# File 'lib/style.rb', line 446 def supervisor_shutdown config[:shutdown] = true end |
#supervisor_start ⇒ Object
Start the supervisor process, detaching it from the controlling terminal
451 452 453 454 455 456 |
# File 'lib/style.rb', line 451 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
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 |
# File 'lib/style.rb', line 459 def usage <<-END style [option value, ...] (decrement|halt|increment|restart|run|start|stop) Options: -a, --adapter Adapter/Framework to use [rails] -b, --bind IP address to bind to [127.0.0.1] -c, --config Location of config file [config/style.yaml] -d, --directory Working directory [.] -D, --debug Run the program in the foreground without forking [No] -f, --fork Number of listners on each port [1] -h, --handler Handler/Server to use [mongrel] -k, --killtime Number of seconds to wait when killing each child [2] -l, --logfile Where to redirect STDOUT and STDERR [log/style.log] -n, --number Number of ports to which to bind [1] -p, --port Starting port to which to bind [9999] -P, --pidfile Location of pid file [log/style.pid] -u, --unsupervised Whether to run unsupervised [No] END end |