fix terminate's premature death and add threading
[god.git] / bin / god
blob13e0eb544ec2c50d5d75c5fc363037ca67346f74
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 OptionParser.new do |opts|
12 opts.banner = <<-EOF
13 Usage: god [command] [options]
15 Commands:
16 start <watch or group name>
17 restart <watch or group name>
18 stop <watch or group name>
19 monitor <watch or group name>
20 unmonitor <watch or group name>
21 load <file>
22 log <watch name>
23 status
24 terminate
26 Options:
27 EOF
29 opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
30 options[:config] = x
31 end
33 opts.on("-pPORT", "--port PORT", "Communications port") do |x|
34 options[:port] = x
35 end
37 opts.on("-PFILE", "--pid FILE", "Where to write the PID file") do |x|
38 options[:pid] = x
39 end
41 opts.on("-lFILE", "--log FILE", "Where to write the log file") do |x|
42 options[:log] = x
43 end
45 opts.on("-D", "--no-daemonize", "Don't daemonize") do
46 options[:daemonize] = false
47 end
49 opts.on("-v", "--version", "Print the version number and exit") do
50 options[:version] = true
51 end
53 opts.on("-V", "Print extended version and build information") do
54 options[:info] = true
55 end
56 end.parse!
58 if options[:version]
59 require 'god'
61 # print version
62 puts "Version #{God::VERSION}"
63 exit!(0)
64 elsif options[:info]
65 require 'god'
67 puts "Version: #{God::VERSION}"
68 puts "Polls: enabled"
69 puts "Events: " + God::EventHandler.event_system
71 exit!(0)
72 elsif command = ARGV[0]
73 require 'god'
75 # a command was specified
77 # connect to remote drb
78 DRb.start_service
79 server = DRbObject.new nil, "druby://127.0.0.1:#{options[:port]}"
81 begin
82 server.ping
83 rescue DRb::DRbConnError
84 puts "The server is not available (or you do not have permissions to access it)"
85 exit!
86 rescue => e
87 puts e.message
88 puts e.backtrace.join("\n")
89 exit!
90 end
92 if command == 'load'
93 file = ARGV[1]
95 puts "Sending '#{command}' command"
97 code = File.read(file)
99 watches = server.running_load(code)
101 # output response
102 puts 'The following watches were affected:'
103 watches.each do |w|
104 puts ' ' + w.name
107 puts "Done"
108 elsif command == 'status'
109 watches = server.status
110 watches.keys.sort.each do |name|
111 state = watches[name][:state]
112 puts "#{name}: #{state}"
114 elsif command == 'log'
115 begin
116 Signal.trap('INT') { exit!(0) }
117 name = ARGV[1]
118 t = Time.at(0)
119 loop do
120 print server.running_log(name, t)
121 t = Time.now
122 sleep 1
124 rescue God::NoSuchWatchError
125 puts "No such watch"
126 rescue DRb::DRbConnError
127 puts "The server went away"
128 rescue => e
129 puts e.message
130 puts e.backtrace.join("\n")
131 ensure
132 exit!(0)
134 elsif command == 'terminate'
135 t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
136 if server.stop_all
137 t.kill; STDOUT.puts
138 puts 'Stopped all watches'
139 else
140 t.kill; STDOUT.puts
141 puts 'Could not stop all watches within 10 seconds'
144 begin
145 server.terminate
146 abort 'Could not stop god'
147 rescue DRb::DRbConnError
148 puts 'Stopped god'
149 exit!(0)
151 else
152 # get the name of the watch/group
153 name = ARGV[1]
155 begin
156 puts "Sending '#{command}' command"
157 puts "This may take a few seconds depending on the grace periods assigned to your watches"
159 # send command
160 watches = server.control(name, command)
162 # output response
163 puts 'The following watches were affected:'
164 watches.each do |w|
165 puts ' ' + w.name
167 rescue God::InvalidCommandError
168 abort "Command '#{command}' is not valid. Run 'god --help' for usage"
172 exit!(0)
173 else
174 # start god
175 if !options[:daemonize]
176 require 'god'
178 if options[:port]
179 God.port = options[:port]
182 load File.expand_path(options[:config])
183 else
184 pid = fork do
185 begin
186 require 'god'
188 log_file = options[:log] || "/dev/null"
190 STDIN.reopen "/dev/null"
191 STDOUT.reopen(log_file, "a")
192 STDERR.reopen STDOUT
194 puts "Starting god"
196 unless God::EventHandler.loaded?
197 puts
198 puts "***********************************************************************"
199 puts "*"
200 puts "* Event conditions are not available for your installation of god."
201 puts "* You may still use and write custom conditions using the poll system"
202 puts "*"
203 puts "***********************************************************************"
204 puts
207 puts "Resetting file descriptors"
209 puts "Loading config"
211 if options[:port]
212 God.port = options[:port]
215 load File.expand_path(options[:config])
217 Signal.trap('HUP') {}
218 rescue => e
219 File.open('god.log', 'a') { |f| f.puts e.message + "\n" + e.backtrace }
220 abort "!!! ERROR - See god.log !!!"
224 if options[:pid]
225 File.open(options[:pid], 'w') { |f| f.write pid }
228 ::Process.detach pid
230 exit!(0)