class Autodrop

Constants

CONF_DEFAULTS
CONF_KEYS
VERSION

Public Class Methods

loadconf(file) click to toggle source
# File autodrop, line 60
def self.loadconf file
  conf = YAML.load File.read file
  raise "wrong format" unless conf.is_a? Hash
  conf = CONF_DEFAULTS.merge conf

  badkw = conf.keys - CONF_KEYS
  raise "unknown keywords - #{badkw.join ","}" unless badkw.empty?

  [ KW_COUNT, KW_DURATION ].each { |k|
    unless conf[k].is_a? Integer
      raise "integer required - #{k}: #{conf[k].inspect}"
    end
  }
  [ KW_DROP_COMMAND, KW_INPUT, KW_LOG, KW_PIDFILE ].each { |k|
    unless conf[k].is_a? String
      raise "string required - #{k}: #{conf[k].inspect}"
    end
  }
  conf[KW_DROP_COMMAND] = conf[KW_DROP_COMMAND].split

  conf[KW_PATTERNS].map! { |item|
    unless [ String, Regexp ].include? item.class
      raise "string or regexp required - #{item.inspect}"
    end
    Regexp.new item
  }

  conf
end

Public Instance Methods

run(config) click to toggle source
# File autodrop, line 111
def run config
  @logger = nil
  case config[KW_LOG]
  when "syslog"
    @logger = SyslogLogger.new
  when "stdout"
    @logger = StdoutLogger.new
  else
    begin
      @logger = FileLogger.new config[KW_LOG]
    rescue
      warn "autodrop: can not open - #{config[KW_LOG]}"
      exit 1
    end
  end

  if config[KW_DAEMON]
    if Process.respond_to? :daemon
      Process.daemon
    else
      exit if fork
      Process.setsid
      exit if fork
      Dir.chdir "/"
      STDIN.reopen "/dev/null"
      STDOUT.reopen "/dev/null"
      STDERR.reopen "/dev/null"
    end
    File.write config[KW_PIDFILE], Process.pid
  end

  trap("SIGINT") { terminate "SIGINT" }
  trap("SIGTERM") { terminate "SIGTERM" }
  trap("SIGHUP") { terminate "SIGHUP" }

  @logger.log "start watching - #{config[KW_INPUT]}"
  unless File.ftype(config[KW_INPUT]) == "fifo"
    raise "not a named pipe - #{config[KW_INPUT]}"
  end
  fifofp = File.open config[KW_INPUT], File::RDWR

  @dogs = {}
  loop {
    ready = select [fifofp]
    next unless ready

    addr = nil
    line = ready[0][0].gets
    config[KW_PATTERNS].each { |pat|
      if line =~ pat
        addr = $1
        break
      end
    }
    next unless addr

    dog = @dogs[addr]
    if dog
      dog.count += 1
      dog.expire = Time.now + dog.duration
      @logger.log "#{dog.addr} bark! (#{dog.count})" if config[KW_VERBOSE]
      next
    end

    dog = Dog.new addr, config
    Thread.new(dog) { |dog|
      begin
        @dogs[dog.addr] = dog

        @logger.log "#{dog.addr} bark! (#{dog.count})" if config[KW_VERBOSE]

        loop {
          break if Time.now >= dog.expire

          if dog.count >= dog.count_max
            @logger.log "#{dog.addr} DROP"
            out = IO.popen(dog.drop_command, "r+", :err => [ :child, :out ]) { |io|
              buf = []
              while l = io.gets
                buf.push l.chomp
              end
              buf
            }
            st = $?.exitstatus
            if st != 0
              @logger.log "DROP fail. command exit status #{st}"
              out.each { |l| @logger.log "|#{l}" }
            end
            break
          end

          # @logger.log "#{dog.addr} grrr" if config[KW_VERBOSE]
          sleep 1
        }
      rescue => ex
        @logger.log "error in worker"
        @logger.log "|#{ex}"
        ex.backtrace.each { |l| @logger.log "|#{l}" }
      ensure
        @dogs.delete dog.addr
        @logger.log "#{dog.addr} leave" if config[KW_VERBOSE]
      end
    }
  }
rescue => ex
  @logger.log ex.message
  ex.backtrace.each { |l| @logger.log "|#{l}" }
  terminate "error", 1
ensure
  File.unlink config[KW_PIDFILE] rescue nil
end

Private Instance Methods

terminate(whymsg, status = 0) click to toggle source
# File autodrop, line 90
def terminate whymsg, status = 0
  @logger.log "terminate (#{whymsg})"
  exit status
end