fix UNIT and ID2SYM bugs
[god.git] / bin / god
blob3b1011fde23fed40b0bc0eb92aca47bf68812a37
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[:config] && options[:version]
77 require 'god'
79 # print version
80 puts "Version #{God::VERSION}"
81 exit
82 elsif !options[:config] && 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 !options[:config] && command = ARGV[0]
91 require 'god'
93 # a command was specified
95 # connect to drb unix socket
96 DRb.start_service
97 server = DRbObject.new(nil, God::Socket.socket(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 Exception => e
220 if e.instance_of?(SystemExit)
221 raise
222 else
223 puts e.message
224 puts e.backtrace.join("\n")
225 abort "There was an error in your configuration file (see above)"
229 else
230 # trap and ignore SIGHUP
231 Signal.trap('HUP') {}
233 pid = fork do
234 begin
235 require 'god'
237 log_file = options[:log] || "/dev/null"
239 unless God::EventHandler.loaded?
240 puts
241 puts "***********************************************************************"
242 puts "*"
243 puts "* Event conditions are not available for your installation of god."
244 puts "* You may still use and write custom conditions using the poll system"
245 puts "*"
246 puts "***********************************************************************"
247 puts
250 # set port if requested
251 if options[:port]
252 God.port = options[:port]
255 # set pid if requested
256 if options[:pid]
257 God.pid = options[:pid]
260 # load config
261 if options[:config]
262 unless File.exist?(options[:config])
263 abort "File not found: #{options[:config]}"
266 begin
267 load File.expand_path(options[:config])
268 rescue Exception => e
269 if e.instance_of?(SystemExit)
270 raise
271 else
272 puts e.message
273 puts e.backtrace.join("\n")
274 abort "There was an error in your configuration file (see above)"
279 # reset file descriptors
280 STDIN.reopen "/dev/null"
281 STDOUT.reopen(log_file, "a")
282 STDERR.reopen STDOUT
283 rescue => e
284 puts e.message
285 puts e.backtrace.join("\n")
286 abort "There was a fatal system error while starting god (see above)"
290 if options[:pid]
291 File.open(options[:pid], 'w') { |f| f.write pid }
294 ::Process.detach pid
296 exit
299 rescue Exception => e
300 if e.instance_of?(SystemExit)
301 raise
302 else
303 puts 'Uncaught exception'
304 puts e.message
305 puts e.backtrace.join("\n")