Fix up tests and runner
[ebb.git] / ruby_lib / ebb / runner.rb
blob1d322938d3a7330a2f56f088f9ff8f76ea5482df
1 require 'optparse'
3 module Kernel
4   unless respond_to? :daemonize # Already part of Ruby 1.9, yeah!
5     # Turns the current script into a daemon process that detaches from the console.
6     # It can be shut down with a TERM signal. Taken from ActiveSupport.
7     def daemonize
8       exit if fork                   # Parent exits, child continues.
9       Process.setsid                 # Become session leader.
10       exit if fork                   # Zap session leader. See [1].
11       Dir.chdir "/"                  # Release old working directory.
12       File.umask 0000                # Ensure sensible umask. Adjust as needed.
13       STDIN.reopen "/dev/null"       # Free file descriptors and
14       STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
15       STDERR.reopen STDOUT           # STDOUT/ERR should better go to a logfile.
16       trap("TERM") { exit }
17     end
18   end
19 end
21 module Ebb
22   class Runner
23     # Classes are modules and I hate this 'Base' class pattern. I'm putting
24     # other classes inside this one.
25     autoload :Rails, LIBDIR + '/ebb/runner/rails'
26     
27     # Kill the process which PID is stored in +pid_file+.
28     def self.kill(pid_file, timeout=60)
29       raise ArgumentError, 'You must specify a pid_file to stop deamonized server' unless pid_file 
30       
31       if pid = File.read(pid_file)
32         pid = pid.to_i
33         
34         Process.kill('KILL', pid)
35         Ebb.log.puts "stopped!"
36       else
37         Ebb.log.puts "Can't stop process, no PID found in #{@pid_file}"
38       end
39     rescue Errno::ESRCH # No such process
40       Ebb.log.puts "process not found!"
41     ensure
42       File.delete(pid_file) rescue nil
43     end
45     def self.remove_pid_file(file)
46       File.delete(file) if file && File.exists?(file) && Process.pid == File.read(file)
47     end
49     def self.write_pid_file(file)
50       Ebb.log.puts ">> Writing PID to #{file}"
51       open(file,"w+") { |f| f.write(Process.pid) }
52       File.chmod(0644, file)
53     end
54     
55     attr_reader :options
56     
57     def initialize
58       @parser = OptionParser.new
59       @options = {
60         :port => 4001,
61         :timeout => 60,
62         :threaded_processing => true
63       }
64     end
65     
66     def parse_options(argv)
67       @parser.banner = "Usage: #{self.class} [options] start | stop"
68       @parser.separator ""
69       extra_options if respond_to?(:extra_options)
70       
71       @parser.separator ""
72       #  opts.on("-s", "--socket SOCKET", "listen on socket") { |socket| options[:socket] = socket }
73       @parser.on("-p", "--port PORT", "(default: #{@options[:port]})") { |p| @options[:port]=p }
74       @parser.on("-d", "--daemonize", "Daemonize") { @options[:daemonize] = true }
75       @parser.on("-l", "--log-file FILE", "File to redirect output") { |f| @options[:log_file]=f }
76       @parser.on("-P", "--pid-file FILE", "File to store PID") { |f| @options[:pid_file]=f }
77       # @parser.on("-t", "--timeout SECONDS", "(default: #{@options[:timeout]})") { |s| @options[:timeout]=s }
78       
79       @parser.separator ""
80       @parser.on_tail("-h", "--help", "Show this message") do
81         Ebb.log.puts @parser
82         exit
83       end
84       @parser.on_tail('-v', '--version', "Show version") do
85         Ebb.log.puts "Ebb #{Ebb::VERSION}"
86         exit
87       end
88       
89       @parser.parse!(argv)
90     end
91     
92     def run(argv)
93       parse_options(argv)
94       
95       case argv[0]
96       when 'start'
97         Ebb.log.print("Ebb is loading the application...")
98         Ebb.log.flush()
99         @app = app(@options)
100         Ebb.log.puts("loaded")
101         
102         if @options[:daemonize]
103           pwd = Dir.pwd # Current directory is changed during daemonization, so store it
104           Kernel.daemonize 
105           Dir.chdir pwd
106           trap('HUP', 'IGNORE') # Don't die upon logout
107         end
108         
109         if @options[:log_file]
110           [STDOUT, STDERR].each { |f| f.reopen @options[:log_file], 'a' } 
111         end
112         
113         if @options[:pid_file]
114           Runner.write_pid_file(@options[:pid_file])
115           at_exit do
116             Ebb.log.puts ">> Exiting!"
117             Runner.remove_pid_file(@options[:pid_file])
118           end
119         end
120         
121         Ebb::start_server(@app, @options)
122       when 'stop'
123         Ebb::Runner.kill @options[:pid_file], @options[:timeout]
124       when nil
125         Ebb.log.puts "Command required"
126         Ebb.log.puts @parser
127         exit 1
128       else
129         abort "Invalid command : #{argv[0]}"
130       end
131       
132     end
133   end