enhanced docs and usage
[god.git] / bin / god
blobbcc171d64317f4e19c983aca2d54858463d96c87
1 #!/usr/bin/env ruby
3 $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
5 require 'rubygems'
6 require 'optparse'
7 require 'drb'
9 options = {:daemonize => true, :port => 17165}
11 opts = OptionParser.new do |opts|
12 opts.banner = <<-EOF
13 Usage:
14 Starting:
15 god -c <config file> [-p <port>] [-P <file>] [-l <file>] [-D]
17 Querying:
18 god <command> <argument> [-p <port>]
19 god <command> [-p <port>]
20 god -v
21 god -V (must be run as root to be accurate on Linux)
23 Commands:
24 start <task or group name> start task or group
25 restart <task or group name> restart task or group
26 stop <task or group name> stop task or group
27 monitor <task or group name> monitor task or group
28 unmonitor <task or group name> unmonitor task or group
29 load <file> load a config into a running god
30 log <task name> show realtime log for given task
31 status show status of each task
32 terminate kill god and all tasks
34 Options:
35 EOF
37 opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
38 options[:config] = x
39 end
41 opts.on("-pPORT", "--port PORT", "Communications port") do |x|
42 options[:port] = x
43 end
45 opts.on("-PFILE", "--pid FILE", "Where to write the PID file") do |x|
46 options[:pid] = x
47 end
49 opts.on("-lFILE", "--log FILE", "Where to write the log file") do |x|
50 options[:log] = x
51 end
53 opts.on("-D", "--no-daemonize", "Don't daemonize") do
54 options[:daemonize] = false
55 end
57 opts.on("-v", "--version", "Print the version number and exit") do
58 options[:version] = true
59 end
61 opts.on("-V", "Print extended version and build information") do
62 options[:info] = true
63 end
64 end
66 opts.parse!
68 if options[:version]
69 require 'god'
71 # print version
72 puts "Version #{God::VERSION}"
73 exit!(0)
74 elsif options[:info]
75 require 'god'
77 puts "Version: #{God::VERSION}"
78 puts "Polls: enabled"
79 puts "Events: " + God::EventHandler.event_system
81 exit!(0)
82 elsif command = ARGV[0]
83 require 'god'
85 # a command was specified
87 # connect to remote drb
88 DRb.start_service
89 server = DRbObject.new nil, "druby://127.0.0.1:#{options[:port]}"
91 begin
92 server.ping
93 rescue DRb::DRbConnError
94 puts "The server is not available (or you do not have permissions to access it)"
95 exit!
96 rescue => e
97 puts e.message
98 puts e.backtrace.join("\n")
99 exit!
102 if command == 'load'
103 file = ARGV[1]
105 puts "Sending '#{command}' command"
107 code = File.read(file)
109 watches = server.running_load(code)
111 # output response
112 puts 'The following watches were affected:'
113 watches.each do |w|
114 puts ' ' + w.name
116 elsif command == 'status'
117 begin
118 watches = server.status
119 watches.keys.sort.each do |name|
120 state = watches[name][:state]
121 puts "#{name}: #{state}"
123 rescue => e
124 puts e.message
125 puts e.backtrace.join("\n")
127 elsif command == 'log'
128 begin
129 Signal.trap('INT') { exit!(0) }
130 name = ARGV[1]
131 t = Time.at(0)
132 loop do
133 print server.running_log(name, t)
134 t = Time.now
135 sleep 1
137 rescue God::NoSuchWatchError
138 puts "No such watch"
139 rescue DRb::DRbConnError
140 puts "The server went away"
141 rescue => e
142 puts e.message
143 puts e.backtrace.join("\n")
145 elsif command == 'terminate'
146 begin
147 t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
148 if server.stop_all
149 t.kill; STDOUT.puts
150 puts 'Stopped all watches'
151 else
152 t.kill; STDOUT.puts
153 puts 'Could not stop all watches within 10 seconds'
155 rescue => e
156 puts e.message
157 puts e.backtrace.join("\n")
160 begin
161 server.terminate
162 abort 'Could not stop god'
163 rescue DRb::DRbConnError
164 puts 'Stopped god'
166 else
167 # get the name of the watch/group
168 name = ARGV[1]
170 begin
171 puts "Sending '#{command}' command"
173 t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
175 # send command
176 watches = server.control(name, command)
178 # output response
179 t.kill; STDOUT.puts
180 puts 'The following watches were affected:'
181 watches.each do |w|
182 puts ' ' + w.name
184 rescue God::InvalidCommandError
185 t.kill rescue nil; STDOUT.puts
186 abort "Command '#{command}' is not valid. Run 'god --help' for usage"
187 rescue => e
188 puts e.message
189 puts e.backtrace.join("\n")
193 exit!(0)
194 elsif options[:config]
195 # start god
196 if !options[:daemonize]
197 require 'god'
199 if options[:port]
200 God.port = options[:port]
203 begin
204 load File.expand_path(options[:config])
205 rescue => e
206 puts e.message
207 puts e.backtrace.join("\n")
208 abort "There was an error in your configuration file (see above)"
210 else
211 # trap and ignore SIGHUP
212 Signal.trap('HUP') {}
214 pid = fork do
215 begin
216 require 'god'
218 log_file = options[:log] || "/dev/null"
220 unless God::EventHandler.loaded?
221 puts
222 puts "***********************************************************************"
223 puts "*"
224 puts "* Event conditions are not available for your installation of god."
225 puts "* You may still use and write custom conditions using the poll system"
226 puts "*"
227 puts "***********************************************************************"
228 puts
231 # set port if requested
232 if options[:port]
233 God.port = options[:port]
236 # load config
237 begin
238 load File.expand_path(options[:config])
239 rescue => e
240 puts e.message
241 puts e.backtrace.join("\n")
242 abort "There was an error in your configuration file (see above)"
245 # reset file descriptors
246 STDIN.reopen "/dev/null"
247 STDOUT.reopen(log_file, "a")
248 STDERR.reopen STDOUT
249 rescue => e
250 puts e.message
251 puts e.backtrace.join("\n")
252 abort "There was a fatal system error while starting god (see above)"
256 if options[:pid]
257 File.open(options[:pid], 'w') { |f| f.write pid }
260 ::Process.detach pid
262 exit!(0)
264 else
265 puts opts.help