logwatcher.py
author Tero Marttila <terom@paivola.fi>
Fri, 21 Mar 2008 02:02:52 +0200
changeset 8 3d648daf9538
parent 6 614161f85d9b
child 10 687b797f709c
permissions -rw-r--r--
WtmpFile && follow_main

committer: Tero Marttila <terom@paivola.fi>
from twisted.internet import protocol, reactor
from twisted.python import log
import sys, re

import api

class TailProcessProtocol (protocol.ProcessProtocol) :
    def __init__ (self, module, name, filters) :
        self.module = module
        self.name = name
        self.filters = filters

        self.buf = ""

    def errReceived (self, data) :
        self.module.error("tail for %s: %s" % (self.name, data))

    def outReceived (self, data) :
        data = self.buf + data
        
        while "\n" in data :
            line, data = data.split("\n", 1)
            
            log.msg("Matching line `%s'..." % line)

            for filter in self.filters :
                out = filter.test(line)

                if out :
                    log.msg("\t%s: %s" % (filter.event_type, out))
                    self.module.sendEvent(filter.event_type, out)

        self.buf = data

    def processEnded (self, reason) :
        msg = "tail process for %s quit: %s" % (self.name, reason.getErrorMessage())

        log.err(msg)
        self.module.error(msg)

class Filter (object) :
    def __init__ (self, regexp, event_type) :
        self.regexp = re.compile(regexp)
        self.event_type = event_type

    def test (self, line) :
        match = self.regexp.search(line)
        
        if match :
            return self._filter(match)
        
    def _filter (self, match) :
        return match.string

class AutoFilter (Filter) :
    # your event type here, as a string
    EVENT = None

    # your regexp here, with named matchgroups
    REGEXP = None

    # your output format, with named interpolation params
    OUTPUT = None
    
    def __init__ (self) :
        super(AutoFilter, self).__init__(self.REGEXP, self.EVENT)
    
    def _filter (self, match) :
        return self.OUTPUT % match.groupdict()

class SudoFilter (AutoFilter) :
    EVENT   = "sudo"
    REGEXP  = "sudo:\s*(?P<username>\S+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\S+) ; COMMAND=(?P<command>.*)"
    OUTPUT  = "%(username)s:%(tty)s - %(pwd)s - `%(command)s` as %(target_user)s"

class SSHFilter (AutoFilter) :
    EVENT   = "ssh"
    REGEXP  = "(?P<success>Accepted|Failed) password for (?P<username>\S+) from (?P<ip>\S+) port (?P<port>\S+) (?P<proto>\S+)"
    OUTPUT  = "%(success)s login for %(username)s from %(ip)s:%(port)s proto %(proto)s"

class ExampleModule (api.Module) :
    name = "logs"
    version = 0x0001
    
    event_types = [
        "error",
        "sudo",
        "ssh",
    ]

    log_files = (
        ("auth.log", "/var/log/auth.log", (
            SudoFilter(),
            SSHFilter(),
        )),
    )

    log_objs = None

    def handleConnect (self) :
        log.msg("Spawning tail processes...")
        
        self.log_objs = dict()

        for name, file, filters in self.log_files :
            log.msg("\t%s - %s..." % (name, file))

            p = self.log_objs[name] = TailProcessProtocol(self, name, filters)

            reactor.spawnProcess(p, "/usr/bin/tail", ["tail", "-n0", "--follow=name", file])
    
    def error (self, msg) :
        self.sendEvent("error", msg)

if __name__ == '__main__' :
    ExampleModule().run()