change task mutex to monitor and synchronize move method
[god.git] / bin / god
blob293146d0a42ea59237aed93b0d0d015212fe1d14
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> | -b] [-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 quit stop god
33 terminate stop god and all tasks
35 Options:
36 EOF
38 opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
39 options[:config] = x
40 end
42 opts.on("-pPORT", "--port PORT", "Communications port (default 17165)") do |x|
43 options[:port] = x
44 end
46 opts.on("-b", "--auto-bind", "Auto-bind to an unused port number") do
47 options[:port] = "0"
48 end
50 opts.on("-PFILE", "--pid FILE", "Where to write the PID file") do |x|
51 options[:pid] = x
52 end
54 opts.on("-lFILE", "--log FILE", "Where to write the log file") do |x|
55 options[:log] = x
56 end
58 opts.on("-D", "--no-daemonize", "Don't daemonize") do
59 options[:daemonize] = false
60 end
62 opts.on("-v", "--version", "Print the version number and exit") do
63 options[:version] = true
64 end
66 opts.on("-V", "Print extended version and build information") do
67 options[:info] = true
68 end
69 end
71 opts.parse!
73 if options[:version]
74 require 'god'
76 # print version
77 puts "Version #{God::VERSION}"
78 exit!(0)
79 elsif options[:info]
80 require 'god'
82 puts "Version: #{God::VERSION}"
83 puts "Polls: enabled"
84 puts "Events: " + God::EventHandler.event_system
86 exit!(0)
87 elsif command = ARGV[0]
88 require 'god'
90 # a command was specified
92 # connect to remote drb
93 DRb.start_service
94 server = DRbObject.new nil, "druby://127.0.0.1:#{options[:port]}"
96 begin
97 server.ping
98 rescue DRb::DRbConnError
99 puts "The server is not available (or you do not have permissions to access it)"
100 exit!
101 rescue => e
102 puts e.message
103 puts e.backtrace.join("\n")
104 exit!
107 if command == 'load'
108 file = ARGV[1]
110 puts "Sending '#{command}' command"
112 code = File.read(file)
114 watches = server.running_load(code)
116 # output response
117 puts 'The following watches were affected:'
118 watches.each do |w|
119 puts ' ' + w.name
121 elsif command == 'status'
122 begin
123 watches = server.status
124 watches.keys.sort.each do |name|
125 state = watches[name][:state]
126 puts "#{name}: #{state}"
128 rescue => e
129 puts e.message
130 puts e.backtrace.join("\n")
132 elsif command == 'log'
133 begin
134 Signal.trap('INT') { exit!(0) }
135 name = ARGV[1]
136 t = Time.at(0)
137 loop do
138 print server.running_log(name, t)
139 t = Time.now
140 sleep 1
142 rescue God::NoSuchWatchError
143 puts "No such watch"
144 rescue DRb::DRbConnError
145 puts "The server went away"
146 rescue => e
147 puts e.message
148 puts e.backtrace.join("\n")
150 elsif command == 'quit'
151 begin
152 server.terminate
153 abort 'Could not stop god'
154 rescue DRb::DRbConnError
155 puts 'Stopped god'
157 elsif command == 'terminate'
158 begin
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'
167 rescue => e
168 puts e.message
169 puts e.backtrace.join("\n")
172 begin
173 server.terminate
174 abort 'Could not stop god'
175 rescue DRb::DRbConnError
176 puts 'Stopped god'
178 else
179 # get the name of the watch/group
180 name = ARGV[1]
182 begin
183 puts "Sending '#{command}' command"
185 t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
187 # send command
188 watches = server.control(name, command)
190 # output response
191 t.kill; STDOUT.puts
192 puts 'The following watches were affected:'
193 watches.each do |w|
194 puts ' ' + w.name
196 rescue God::InvalidCommandError
197 t.kill rescue nil; STDOUT.puts
198 abort "Command '#{command}' is not valid. Run 'god --help' for usage"
199 rescue => e
200 puts e.message
201 puts e.backtrace.join("\n")
205 exit!(0)
206 elsif options[:config]
207 # start god
208 if !options[:daemonize]
209 require 'god'
211 if options[:port]
212 God.port = options[:port]
215 begin
216 load File.expand_path(options[:config])
217 rescue => e
218 puts e.message
219 puts e.backtrace.join("\n")
220 abort "There was an error in your configuration file (see above)"
222 else
223 # trap and ignore SIGHUP
224 Signal.trap('HUP') {}
226 pid = fork do
227 begin
228 require 'god'
230 log_file = options[:log] || "/dev/null"
232 unless God::EventHandler.loaded?
233 puts
234 puts "***********************************************************************"
235 puts "*"
236 puts "* Event conditions are not available for your installation of god."
237 puts "* You may still use and write custom conditions using the poll system"
238 puts "*"
239 puts "***********************************************************************"
240 puts
243 # set port if requested
244 if options[:port]
245 God.port = options[:port]
248 # load config
249 begin
250 load File.expand_path(options[:config])
251 rescue => e
252 puts e.message
253 puts e.backtrace.join("\n")
254 abort "There was an error in your configuration file (see above)"
257 # reset file descriptors
258 STDIN.reopen "/dev/null"
259 STDOUT.reopen(log_file, "a")
260 STDERR.reopen STDOUT
261 rescue => e
262 puts e.message
263 puts e.backtrace.join("\n")
264 abort "There was a fatal system error while starting god (see above)"
268 if options[:pid]
269 File.open(options[:pid], 'w') { |f| f.write pid }
272 ::Process.detach pid
274 exit!(0)
276 else
277 puts opts.help