rescue *all* exceptions from god commands, check for config file
[god.git] / bin / god
blob83382ab488e6084c3c0bb671a8922cdc8322d30b
1 #!/usr/bin/env ruby
3 $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
5 require 'rubygems'
6 require 'optparse'
7 require 'drb'
9 begin
10 options = {:daemonize => true, :port => 17165}
12 opts = OptionParser.new do |opts|
13 opts.banner = <<-EOF
14 Usage:
15 Starting:
16 god -c <config file> [-p <port> | -b] [-P <file>] [-l <file>] [-D]
18 Querying:
19 god <command> <argument> [-p <port>]
20 god <command> [-p <port>]
21 god -v
22 god -V (must be run as root to be accurate on Linux)
24 Commands:
25 start <task or group name> start task or group
26 restart <task or group name> restart task or group
27 stop <task or group name> stop task or group
28 monitor <task or group name> monitor task or group
29 unmonitor <task or group name> unmonitor task or group
30 load <file> load a config into a running god
31 log <task name> show realtime log for given task
32 status show status of each task
33 quit stop god
34 terminate stop god and all tasks
36 Options:
37 EOF
39 opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
40 options[:config] = x
41 end
43 opts.on("-pPORT", "--port PORT", "Communications port (default 17165)") do |x|
44 options[:port] = x
45 end
47 opts.on("-b", "--auto-bind", "Auto-bind to an unused port number") do
48 options[:port] = "0"
49 end
51 opts.on("-PFILE", "--pid FILE", "Where to write the PID file") do |x|
52 options[:pid] = x
53 end
55 opts.on("-lFILE", "--log FILE", "Where to write the log file") do |x|
56 options[:log] = x
57 end
59 opts.on("-D", "--no-daemonize", "Don't daemonize") do
60 options[:daemonize] = false
61 end
63 opts.on("-v", "--version", "Print the version number and exit") do
64 options[:version] = true
65 end
67 opts.on("-V", "Print extended version and build information") do
68 options[:info] = true
69 end
70 end
72 opts.parse!
74 if options[:version]
75 require 'god'
77 # print version
78 puts "Version #{God::VERSION}"
79 exit!(0)
80 elsif options[:info]
81 require 'god'
83 puts "Version: #{God::VERSION}"
84 puts "Polls: enabled"
85 puts "Events: " + God::EventHandler.event_system
87 exit!(0)
88 elsif command = ARGV[0]
89 require 'god'
91 # a command was specified
93 # connect to remote drb
94 DRb.start_service
95 server = DRbObject.new nil, "druby://127.0.0.1:#{options[:port]}"
97 begin
98 server.ping
99 rescue DRb::DRbConnError
100 puts "The server is not available (or you do not have permissions to access it)"
101 exit!
102 rescue => e
103 puts e.message
104 puts e.backtrace.join("\n")
105 exit!
108 if command == 'load'
109 file = ARGV[1]
111 puts "Sending '#{command}' command"
113 unless File.exist?(file)
114 abort "File not found: #{file}"
117 watches = server.running_load(File.read(file), File.expand_path(file))
119 # output response
120 puts 'The following watches were affected:'
121 watches.each do |w|
122 puts ' ' + w.name
124 elsif command == 'status'
125 begin
126 watches = server.status
127 watches.keys.sort.each do |name|
128 state = watches[name][:state]
129 puts "#{name}: #{state}"
131 rescue => e
132 puts e.message
133 puts e.backtrace.join("\n")
135 elsif command == 'log'
136 begin
137 Signal.trap('INT') { exit!(0) }
138 name = ARGV[1]
139 t = Time.at(0)
140 loop do
141 print server.running_log(name, t)
142 t = Time.now
143 sleep 1
145 rescue God::NoSuchWatchError
146 puts "No such watch"
147 rescue DRb::DRbConnError
148 puts "The server went away"
149 rescue => e
150 puts e.message
151 puts e.backtrace.join("\n")
153 elsif command == 'quit'
154 begin
155 server.terminate
156 abort 'Could not stop god'
157 rescue DRb::DRbConnError
158 puts 'Stopped god'
160 elsif command == 'terminate'
161 begin
162 t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
163 if server.stop_all
164 t.kill; STDOUT.puts
165 puts 'Stopped all watches'
166 else
167 t.kill; STDOUT.puts
168 puts 'Could not stop all watches within 10 seconds'
170 rescue => e
171 puts e.message
172 puts e.backtrace.join("\n")
175 begin
176 server.terminate
177 abort 'Could not stop god'
178 rescue DRb::DRbConnError
179 puts 'Stopped god'
181 else
182 # get the name of the watch/group
183 name = ARGV[1]
185 begin
186 puts "Sending '#{command}' command"
188 t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
190 # send command
191 watches = server.control(name, command)
193 # output response
194 t.kill; STDOUT.puts
195 puts 'The following watches were affected:'
196 watches.each do |w|
197 puts ' ' + w.name
199 rescue God::InvalidCommandError
200 t.kill rescue nil; STDOUT.puts
201 abort "Command '#{command}' is not valid. Run 'god --help' for usage"
202 rescue => e
203 puts e.message
204 puts e.backtrace.join("\n")
208 exit!(0)
209 else
210 # start god
211 if !options[:daemonize]
212 require 'god'
214 if options[:port]
215 God.port = options[:port]
218 if options[:config]
219 unless File.exist?(options[:config])
220 abort "File not found: #{options[:config]}"
223 begin
224 load File.expand_path(options[:config])
225 rescue => e
226 puts e.message
227 puts e.backtrace.join("\n")
228 abort "There was an error in your configuration file (see above)"
231 else
232 # trap and ignore SIGHUP
233 Signal.trap('HUP') {}
235 pid = fork do
236 begin
237 require 'god'
239 log_file = options[:log] || "/dev/null"
241 unless God::EventHandler.loaded?
242 puts
243 puts "***********************************************************************"
244 puts "*"
245 puts "* Event conditions are not available for your installation of god."
246 puts "* You may still use and write custom conditions using the poll system"
247 puts "*"
248 puts "***********************************************************************"
249 puts
252 # set port if requested
253 if options[:port]
254 God.port = options[:port]
257 # load config
258 if options[:config]
259 unless File.exist?(options[:config])
260 abort "File not found: #{options[:config]}"
263 begin
264 load File.expand_path(options[:config])
265 rescue => e
266 puts e.message
267 puts e.backtrace.join("\n")
268 abort "There was an error in your configuration file (see above)"
272 # reset file descriptors
273 STDIN.reopen "/dev/null"
274 STDOUT.reopen(log_file, "a")
275 STDERR.reopen STDOUT
276 rescue => e
277 puts e.message
278 puts e.backtrace.join("\n")
279 abort "There was a fatal system error while starting god (see above)"
283 if options[:pid]
284 File.open(options[:pid], 'w') { |f| f.write pid }
287 ::Process.detach pid
289 exit!(0)
291 # else
292 # puts opts.help
294 rescue Exception => e
295 puts e.message
296 puts e.backtrace.join("\n")
297 exit!(1)