lib/bluepill/controller.rb
require 'fileutils'
require 'bluepill/system'
module Bluepill
class Controller
attr_accessor :base_dir, :log_file, :sockets_dir, :pids_dir
def initialize(options = {})
self.log_file = options[:log_file]
self.base_dir = options[:base_dir]
self.sockets_dir = File.join(base_dir, 'socks')
self.pids_dir = File.join(base_dir, 'pids')
setup_dir_structure
cleanup_bluepill_directory
end
def running_applications
Dir[File.join(sockets_dir, '*.sock')].collect { |x| File.basename(x, '.sock') }
end
def handle_command(application, command, *args)
case command.to_sym
when :status
puts send_to_daemon(application, :status, *args)
when :quit
pid = pid_for(application)
if System.pid_alive?(pid)
::Process.kill('TERM', pid)
puts "Killing bluepilld[#{pid}]"
else
puts "bluepilld[#{pid}] not running"
end
when :log
log_file_location = send_to_daemon(application, :log_file)
log_file_location = log_file if log_file_location.to_s.strip.empty?
requested_pattern = args.first
grep_pattern = self.grep_pattern(application, requested_pattern)
tail = "tail -n 100 -f #{log_file_location} | grep -E '#{grep_pattern}'"
puts "Tailing log for #{requested_pattern}..."
Kernel.exec(tail)
when *Application::PROCESS_COMMANDS
# these need to be sent to the daemon and the results printed out
affected = send_to_daemon(application, command, *args)
if affected.empty?
puts 'No processes effected'
else
puts "Sent #{command} to:"
affected.each do |process|
puts " #{process}"
end
end
else
$stderr.puts(format('Unknown command `%s` (or application `%s` has not been loaded yet)', command, command))
exit(1)
end
end
def send_to_daemon(application, command, *args)
verify_version!(application)
command = [command, *args].join(':')
response = Socket.client_command(base_dir, application, command)
if response.is_a?(Exception)
$stderr.puts 'Received error from server:'
$stderr.puts response.inspect
$stderr.puts response.backtrace.join("\n")
exit(8)
else
response
end
rescue Errno::ECONNREFUSED
abort('Connection Refused: Server is not running')
end
def grep_pattern(application, query = nil)
pattern = [application, query].compact.join(':')
['\[.*', Regexp.escape(pattern), '.*'].compact.join
end
private
def cleanup_bluepill_directory
running_applications.each do |app|
pid = pid_for(app)
next if pid && System.pid_alive?(pid)
pid_file = File.join(pids_dir, "#{app}.pid")
sock_file = File.join(sockets_dir, "#{app}.sock")
System.delete_if_exists(pid_file)
System.delete_if_exists(sock_file)
end
end
def pid_for(app)
pid_file = File.join(pids_dir, "#{app}.pid")
File.exist?(pid_file) && File.read(pid_file).to_i
end
def setup_dir_structure
[@sockets_dir, @pids_dir].each do |dir|
FileUtils.mkdir_p(dir) unless File.exist?(dir)
end
end
def verify_version!(application)
version = Socket.client_command(base_dir, application, 'version')
if version != Bluepill::Version
abort("The running version of your daemon seems to be out of date.\nDaemon Version: #{version}, CLI Version: #{Bluepill::Version}")
end
rescue ArgumentError
abort('The running version of your daemon seems to be out of date.')
end
end
end