<commit_before>require File.join(File.dirname(__FILE__), 'base')
require File.join(File.dirname(__FILE__), 'socket')

module Sensu
  class Client
    def self.run(options={})
      client = self.new(options)
      EM::run do
        client.start
        client.trap_signals
      end
    end

    def initialize(options={})
      @logger = Cabin::Channel.get
      base = Sensu::Base.new(options)
      @settings = base.settings
      @timers = Array.new
      @checks_in_progress = Array.new
    end

    def setup_rabbitmq
      @logger.debug('connecting to rabbitmq', {
        :settings => @settings[:rabbitmq]
      })
      connection_failure = Proc.new do
        @logger.fatal('cannot connect to rabbitmq', {
          :settings => @settings[:rabbitmq]
        })
        @logger.fatal('SENSU NOT RUNNING!')
        exit 2
      end
      @rabbitmq = AMQP.connect(@settings[:rabbitmq], :on_tcp_connection_failure => connection_failure)
      @rabbitmq.on_tcp_connection_loss do |connection, settings|
        @logger.warn('reconnecting to rabbitmq')
        connection.reconnect(false, 10)
      end
      @amq = AMQP::Channel.new(@rabbitmq)
      @amq.auto_recovery = true
    end

    def publish_keepalive
      payload = @settings[:client].merge(:timestamp => Time.now.to_i)
      @logger.debug('publishing keepalive', {
        :payload => payload
      })
      @amq.queue('keepalives').publish(payload.to_json)
    end

    def setup_keepalives
      @logger.debug('scheduling keepalives')
      publish_keepalive
      @timers << EM::PeriodicTimer.new(20) do
        if @rabbitmq.connected?
          publish_keepalive
        end
      end
    end

    def publish_result(check)
      payload = {
        :client => @settings[:client][:name],
        :check => check
      }
      @logger.info('publishing check result', {
        :payload => payload
      })
      @amq.queue('results').publish(payload.to_json)
    end

    def execute_check(check)
      @logger.debug('attempting to execute check', {
        :check => check
      })
      unless @checks_in_progress.include?(check[:name])
        @logger.debug('executing check', {
          :check => check
        })
        @checks_in_progress.push(check[:name])
        unmatched_tokens = Array.new
        command = check[:command].gsub(/:::(.*?):::/) do
          token = $1.to_s
          matched = token.split('.').inject(@settings[:client]) do |client, attribute|
            client[attribute]
          end
          if matched.nil?
            unmatched_tokens.push(token)
          end
          matched
        end
        if unmatched_tokens.empty?
          execute = Proc.new do
            started = Time.now.to_f
            begin
              IO.popen(command + ' 2>&1') do |io|
                check[:output] = io.read
              end
              check[:status] = $?.exitstatus
            rescue => error
              @logger.warn('unexpected error', {
                :error => error.to_s
              })
              check[:output] = 'Unexpected error: ' + error.to_s
              check[:status] = 2
            end
            check[:duration] = ('%.3f' % (Time.now.to_f - started)).to_f
            check
          end
          publish = Proc.new do |check|
            unless check[:status].nil?
              publish_result(check)
            end
            @checks_in_progress.delete(check[:name])
          end
          EM::defer(execute, publish)
        else
          @logger.warn('missing client attributes', {
            :check => check,
            :unmatched_tokens => unmatched_tokens
          })
          check[:output] = 'Missing client attributes: ' + unmatched_tokens.join(', ')
          check[:status] = 3
          check[:handle] = false
          publish_result(check)
          @checks_in_progress.delete(check[:name])
        end
      else
        @logger.warn('previous check execution in progress', {
          :check => check
        })
      end
    end

    def setup_subscriptions
      @logger.debug('subscribing to client subscriptions')
      @uniq_queue_name ||= rand(36 ** 32).to_s(36)
      @check_request_queue = @amq.queue(@uniq_queue_name, :auto_delete => true)
      @settings[:client][:subscriptions].uniq.each do |exchange_name|
        @logger.debug('binding queue to exchange', {
          :queue => @uniq_queue_name,
          :exchange => {
            :name => exchange_name
          }
        })
        @check_request_queue.bind(@amq.fanout(exchange_name))
      end
      @check_request_queue.subscribe do |payload|
        begin
          check = JSON.parse(payload, :symbolize_names => true)
          @logger.info('received check request', {
            :check => check
          })
          if @settings.check_exists?(check[:name])
            check.merge!(@settings[:checks][check[:name]])
            execute_check(check)
          elsif @settings[:client][:safe_mode]
            @logger.warn('check is not defined', {
              :check => check
            })
            check[:output] = 'Check is not defined'
            check[:status] = 3
            check[:handle] = false
            publish_result(check)
          else
            execute_check(check)
          end
        rescue JSON::ParserError => error
          @logger.warn('check request payload must be valid json', {
            :payload => payload,
            :error => error.to_s
          })<commit_msg>[safe-mode] provide safe mode hint in check output<commit_after>require File.join(File.dirname(__FILE__), 'base')
require File.join(File.dirname(__FILE__), 'socket')

module Sensu
  class Client
    def self.run(options={})
      client = self.new(options)
      EM::run do
        client.start
        client.trap_signals
      end
    end

    def initialize(options={})
      @logger = Cabin::Channel.get
      base = Sensu::Base.new(options)
      @settings = base.settings
      @timers = Array.new
      @checks_in_progress = Array.new
    end

    def setup_rabbitmq
      @logger.debug('connecting to rabbitmq', {
        :settings => @settings[:rabbitmq]
      })
      connection_failure = Proc.new do
        @logger.fatal('cannot connect to rabbitmq', {
          :settings => @settings[:rabbitmq]
        })
        @logger.fatal('SENSU NOT RUNNING!')
        exit 2
      end
      @rabbitmq = AMQP.connect(@settings[:rabbitmq], :on_tcp_connection_failure => connection_failure)
      @rabbitmq.on_tcp_connection_loss do |connection, settings|
        @logger.warn('reconnecting to rabbitmq')
        connection.reconnect(false, 10)
      end
      @amq = AMQP::Channel.new(@rabbitmq)
      @amq.auto_recovery = true
    end

    def publish_keepalive
      payload = @settings[:client].merge(:timestamp => Time.now.to_i)
      @logger.debug('publishing keepalive', {
        :payload => payload
      })
      @amq.queue('keepalives').publish(payload.to_json)
    end

    def setup_keepalives
      @logger.debug('scheduling keepalives')
      publish_keepalive
      @timers << EM::PeriodicTimer.new(20) do
        if @rabbitmq.connected?
          publish_keepalive
        end
      end
    end

    def publish_result(check)
      payload = {
        :client => @settings[:client][:name],
        :check => check
      }
      @logger.info('publishing check result', {
        :payload => payload
      })
      @amq.queue('results').publish(payload.to_json)
    end

    def execute_check(check)
      @logger.debug('attempting to execute check', {
        :check => check
      })
      unless @checks_in_progress.include?(check[:name])
        @logger.debug('executing check', {
          :check => check
        })
        @checks_in_progress.push(check[:name])
        unmatched_tokens = Array.new
        command = check[:command].gsub(/:::(.*?):::/) do
          token = $1.to_s
          matched = token.split('.').inject(@settings[:client]) do |client, attribute|
            client[attribute]
          end
          if matched.nil?
            unmatched_tokens.push(token)
          end
          matched
        end
        if unmatched_tokens.empty?
          execute = Proc.new do
            started = Time.now.to_f
            begin
              IO.popen(command + ' 2>&1') do |io|
                check[:output] = io.read
              end
              check[:status] = $?.exitstatus
            rescue => error
              @logger.warn('unexpected error', {
                :error => error.to_s
              })
              check[:output] = 'Unexpected error: ' + error.to_s
              check[:status] = 2
            end
            check[:duration] = ('%.3f' % (Time.now.to_f - started)).to_f
            check
          end
          publish = Proc.new do |check|
            unless check[:status].nil?
              publish_result(check)
            end
            @checks_in_progress.delete(check[:name])
          end
          EM::defer(execute, publish)
        else
          @logger.warn('missing client attributes', {
            :check => check,
            :unmatched_tokens => unmatched_tokens
          })
          check[:output] = 'Missing client attributes: ' + unmatched_tokens.join(', ')
          check[:status] = 3
          check[:handle] = false
          publish_result(check)
          @checks_in_progress.delete(check[:name])
        end
      else
        @logger.warn('previous check execution in progress', {
          :check => check
        })
      end
    end

    def setup_subscriptions
      @logger.debug('subscribing to client subscriptions')
      @uniq_queue_name ||= rand(36 ** 32).to_s(36)
      @check_request_queue = @amq.queue(@uniq_queue_name, :auto_delete => true)
      @settings[:client][:subscriptions].uniq.each do |exchange_name|
        @logger.debug('binding queue to exchange', {
          :queue => @uniq_queue_name,
          :exchange => {
            :name => exchange_name
          }
        })
        @check_request_queue.bind(@amq.fanout(exchange_name))
      end
      @check_request_queue.subscribe do |payload|
        begin
          check = JSON.parse(payload, :symbolize_names => true)
          @logger.info('received check request', {
            :check => check
          })
          if @settings.check_exists?(check[:name])
            check.merge!(@settings[:checks][check[:name]])
            execute_check(check)
          elsif @settings[:client][:safe_mode]
            @logger.warn('check is not defined', {
              :check => check
            })
            check[:output] = 'Check is not defined (safe mode)'
            check[:status] = 3
            check[:handle] = false
            publish_result(check)
          else
            execute_check(check)
          end
        rescue JSON::ParserError => error
          @logger.warn('check request payload must be valid json', {
            :payload => payload,
            :error => error.to_s
          })
{'fraction_non_alphanumeric': 0.11978072179077204, 'fraction_numerical': 0.0032891731384193696, 'mean_word_length': 1.9103961712310555, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7277564', 'n_tokens_mistral': 3155, 'n_tokens_neox': 3057, 'n_words': 619}