require 'eventmachine' require 'action_cable_client' module Adminix class Watcher def self.run!(options) watcher = Watcher.new(options) if options[:stop_daemon] watcher.stop else options[:daemonize] ? watcher.start : watcher.run! end end SERVICE_CHANNEL = 'ServiceChannel'.freeze def initialize(opts) @sync_period = config.watcher_sync_period @pid_full = '/tmp/adminix.pid' @service = Service.instance @ws_client = nil @ws_uri = "#{config.websocket_path}?secret_key=#{config.secret_key}" @ws_channel = { channel: 'ServiceChannel', service_id: @service.id } @ws_logs_channel = { channel: 'LogsChannel', service_id: @service.id } @watching_files = [] if EM.epoll? EM.epoll elsif EM.kqueue? EM.kqueue else Kernel.warn('Neither epoll nor kqueue are supported.') end end def run! Adminix::Service.instance.count_logs_lines EventMachine.run do Signal.trap("INT") { EventMachine.stop } Signal.trap("TERM") { EventMachine.stop } initialize_ws initialize_ws_logs # Sync watcher EventMachine.add_periodic_timer(config.watcher_sync_period) do @ws_client.perform(:sync, @service.to_cable) if @ws_client.subscribed? end # Logs watcher if config.logs_enabled log_files_exists = false config.watch_log_files.each do |file_path| dirname = File.dirname(file_path) Helpers::File.mkdir_p(dirname) unless File.directory?(dirname) Helpers::File.touch(file_path) unless File.exists?(file_path) if File.exists?(file_path) log_files_exists = true EventMachine.watch_file(file_path, Adminix::LogWatchHandler) end end if log_files_exists EventMachine.add_periodic_timer(config.logs_sync_period) do Adminix::Service.instance.sync_logs(@ws_logs_client) end end end end end def initialize_ws @ws_client = ActionCableClient.new(@ws_uri, @ws_channel) @ws_client.connected do |msg| puts 'Service connection established' end @ws_client.disconnected do system.log 'Service disconnected. Reconnecting...' sleep(2) # @ws_client.connect! initialize_ws end @ws_client.received do |msg| message = msg['message'] || {} case message['type'] when 'restart' then @service.restart! when 'sync' then @service.sync(@ws_client, message['data']) end end # @ws_client.pinged do |_data| # puts 'ping' # end end def initialize_ws_logs @ws_logs_client = ActionCableClient.new(@ws_uri, @ws_logs_channel) @ws_logs_client.connected do |msg| puts 'Logs connection established' end @ws_logs_client.disconnected do system.log 'Logs disconnected. Reconnecting...' sleep(2) # @ws_logs_client.connect! initialize_ws_logs end # @ws_logs_client.pinged do |_data| # puts 'logs ping' # end @ws_logs_client.received { |msg| } end def get_pid if File.exists?(@pid_full) file = File.new(@pid_full, "r") pid = file.read file.close pid else 0 end end def start pid = get_pid if pid != 0 warn "Daemon is already running" exit -1 end pid = fork { run! } begin file = File.new(@pid_full, "w") file.write(pid) file.close Process.detach(pid) rescue => exc Process.kill('TERM', pid) warn "Cannot start daemon: #{exc.message}" end end def stop pid = get_pid begin EM.stop rescue end if pid != 0 Process.kill('HUP', pid.to_i) File.delete(@pid_full) system.log "Stopped" else warn "Daemon is not running" exit -1 end end private def config() Config.instance end def system() System.instance end end end