put watch back into previous state after a god load
[god.git] / bin / god
blob0641d25cdc3c844f459a333ba55489505ded7703
1 #!/usr/bin/env ruby
3 STDOUT.sync = true
5 $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
7 require 'rubygems'
8 require 'optparse'
9 require 'drb'
11 begin
12 options = {:daemonize => true, :port => 17165}
14 opts = OptionParser.new do |opts|
15 opts.banner = <<-EOF
16 Usage:
17 Starting:
18 god -c <config file> [-p <port> | -b] [-P <file>] [-l <file>] [-D]
20 Querying:
21 god <command> <argument> [-p <port>]
22 god <command> [-p <port>]
23 god -v
24 god -V (must be run as root to be accurate on Linux)
26 Commands:
27 start <task or group name> start task or group
28 restart <task or group name> restart task or group
29 stop <task or group name> stop task or group
30 monitor <task or group name> monitor task or group
31 unmonitor <task or group name> unmonitor task or group
32 load <file> load a config into a running god
33 log <task name> show realtime log for given task
34 status show status of each task
35 quit stop god
36 terminate stop god and all tasks
38 Options:
39 EOF
41 opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
42 options[:config] = x
43 end
45 opts.on("-pPORT", "--port PORT", "Communications port (default 17165)") do |x|
46 options[:port] = x
47 end
49 opts.on("-b", "--auto-bind", "Auto-bind to an unused port number") do
50 options[:port] = "0"
51 end
53 opts.on("-PFILE", "--pid FILE", "Where to write the PID file") do |x|
54 options[:pid] = x
55 end
57 opts.on("-lFILE", "--log FILE", "Where to write the log file") do |x|
58 options[:log] = x
59 end
61 opts.on("-D", "--no-daemonize", "Don't daemonize") do
62 options[:daemonize] = false
63 end
65 opts.on("-v", "--version", "Print the version number and exit") do
66 options[:version] = true
67 end
69 opts.on("-V", "Print extended version and build information") do
70 options[:info] = true
71 end
72 end
74 opts.parse!
76 if options[:version]
77 require 'god'
79 # print version
80 puts "Version #{God::VERSION}"
81 exit
82 elsif options[:info]
83 require 'god'
85 puts "Version: #{God::VERSION}"
86 puts "Polls: enabled"
87 puts "Events: " + God::EventHandler.event_system
89 exit
90 elsif command = ARGV[0]
91 require 'god'
93 # a command was specified
95 # connect to remote drb
96 DRb.start_service
97 server = DRbObject.new nil, "druby://127.0.0.1:#{options[:port]}"
99 begin
100 server.ping
101 rescue DRb::DRbConnError
102 puts "The server is not available (or you do not have permissions to access it)"
103 abort
106 if command == 'load'
107 file = ARGV[1]
109 puts "Sending '#{command}' command"
110 puts
112 unless File.exist?(file)
113 abort "File not found: #{file}"
116 names, errors = *server.running_load(File.read(file), File.expand_path(file))
118 # output response
119 unless names.empty?
120 puts 'The following tasks were affected:'
121 names.each do |w|
122 puts ' ' + w
126 unless errors.empty?
127 puts errors
128 exit(1)
130 elsif command == 'status'
131 watches = server.status
132 watches.keys.sort.each do |name|
133 state = watches[name][:state]
134 puts "#{name}: #{state}"
136 elsif command == 'log'
137 begin
138 Signal.trap('INT') { exit }
139 name = ARGV[1]
140 t = Time.at(0)
141 loop do
142 print server.running_log(name, t)
143 t = Time.now
144 sleep 1
146 rescue God::NoSuchWatchError
147 puts "No such watch"
148 rescue DRb::DRbConnError
149 puts "The server went away"
151 elsif command == 'quit'
152 begin
153 server.terminate
154 abort 'Could not stop god'
155 rescue DRb::DRbConnError
156 puts 'Stopped god'
158 elsif command == 'terminate'
159 t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
160 if server.stop_all
161 t.kill; STDOUT.puts
162 puts 'Stopped all watches'
163 else
164 t.kill; STDOUT.puts
165 puts 'Could not stop all watches within 10 seconds'
168 begin
169 server.terminate
170 abort 'Could not stop god'
171 rescue DRb::DRbConnError
172 puts 'Stopped god'
174 else
175 # get the name of the watch/group
176 name = ARGV[1]
178 begin
179 puts "Sending '#{command}' command"
181 t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
183 # send command
184 watches = server.control(name, command)
186 # output response
187 t.kill; STDOUT.puts
188 unless watches.empty?
189 puts 'The following watches were affected:'
190 watches.each do |w|
191 puts ' ' + w
193 else
194 puts 'No matching task or group'
196 rescue God::InvalidCommandError
197 t.kill rescue nil; STDOUT.puts
198 abort "Command '#{command}' is not valid. Run 'god --help' for usage"
201 else
202 # start god
203 $run = true
205 if !options[:daemonize]
206 require 'god'
208 if options[:port]
209 God.port = options[:port]
212 if options[:config]
213 unless File.exist?(options[:config])
214 abort "File not found: #{options[:config]}"
217 begin
218 load File.expand_path(options[:config])
219 rescue => e
220 puts e.message
221 puts e.backtrace.join("\n")
222 abort "There was an error in your configuration file (see above)"
225 else
226 # trap and ignore SIGHUP
227 Signal.trap('HUP') {}
229 pid = fork do
230 begin
231 require 'god'
233 log_file = options[:log] || "/dev/null"
235 unless God::EventHandler.loaded?
236 puts
237 puts "***********************************************************************"
238 puts "*"
239 puts "* Event conditions are not available for your installation of god."
240 puts "* You may still use and write custom conditions using the poll system"
241 puts "*"
242 puts "***********************************************************************"
243 puts
246 # set port if requested
247 if options[:port]
248 God.port = options[:port]
251 # load config
252 if options[:config]
253 unless File.exist?(options[:config])
254 abort "File not found: #{options[:config]}"
257 begin
258 load File.expand_path(options[:config])
259 rescue => e
260 puts e.message
261 puts e.backtrace.join("\n")
262 abort "There was an error in your configuration file (see above)"
266 # reset file descriptors
267 STDIN.reopen "/dev/null"
268 STDOUT.reopen(log_file, "a")
269 STDERR.reopen STDOUT
270 rescue => e
271 puts e.message
272 puts e.backtrace.join("\n")
273 abort "There was a fatal system error while starting god (see above)"
277 if options[:pid]
278 File.open(options[:pid], 'w') { |f| f.write pid }
281 ::Process.detach pid
283 exit
286 rescue Exception => e
287 if e.instance_of?(SystemExit)
288 raise
289 else
290 puts e.message
291 puts e.backtrace.join("\n")