--- a/api.py Thu Mar 20 18:47:58 2008 +0200
+++ b/api.py Thu Mar 20 19:46:04 2008 +0200
@@ -1,12 +1,15 @@
from twisted.internet import protocol, reactor
from twisted.python import log
from datetime import datetime
+import sys
import buffer
PORT = 34888
SERVER_HOST = "127.0.0.1"
+from api_secret import secret
+
class ModuleInfo (object) :
"""
Some info about a module
@@ -96,6 +99,10 @@
def on_module_init (self, i) :
self._assert(not self.module, "module_init with non-None self.module")
+ peer_secret = i.readVarLen('B')
+
+ self._assert(peer_secret == secret, "Mismatching API secrets!")
+
m = ModuleInfo()
m.name = i.readVarLen('B')
@@ -132,7 +139,7 @@
if self.module :
return str(self.module)
else :
- return super(APIProtocol, self).logPrefix()
+ return super(ServerProtocol, self).logPrefix()
class ClientProtocol (buffer.StreamProtocol, protocol.Protocol) :
RECV_COMMANDS = SERVER_COMMANDS
@@ -142,6 +149,7 @@
log.msg("Connected to API server, sending module init message")
o = self.startCommand('module_init')
+ o.writeVarLen('B', secret)
o.writeVarLen('B', self.factory.name)
o.writeItem("H", self.factory.version)
buffer.writeStringStream(o, 'B', self.factory.event_types)
@@ -171,6 +179,11 @@
reactor.connectTCP(SERVER_HOST, PORT, self)
self.connection = None
+
+ def run (self) :
+ log.startLogging(sys.stderr)
+
+ reactor.run()
def connected (self, connection) :
log.msg("Connected!")
--- a/irc.py Thu Mar 20 18:47:58 2008 +0200
+++ b/irc.py Thu Mar 20 19:46:04 2008 +0200
@@ -1,6 +1,7 @@
from twisted.words.protocols import irc
from twisted.internet import protocol
from twisted.python import log
+import traceback
import buffer
@@ -10,6 +11,10 @@
USERNAME = "fixme"
CHANNEL = "#fixme-test"
+class ReplyException (Exception) :
+ def __init__ (self, reply) :
+ self.reply = reply
+
class BotProtocol (irc.IRCClient, object) :
"""
Fixme IRC bot
@@ -23,6 +28,8 @@
def connectionMade (self) :
log.msg("Connected")
super(BotProtocol, self).connectionMade()
+
+ self.nexus = self.factory.nexus
def connectionLost (self, reason) :
log.msg("Connection lost: %s" % reason)
@@ -57,5 +64,76 @@
def moduleDisconnected (self, module, reason) :
self.send("{modules.%s} disconnected: %s" % (module.name, reason))
+
+ class _noDefault : pass
+ def _lookupCommand (self, command, default=_noDefault) :
+ if '.' in command :
+ raise ReplyException("No support for module commands yet :P")
+ else :
+ method = getattr(self, "cmd_%s" % command, None)
+ if method :
+ return method
+ elif default is self._noDefault :
+ raise ReplyException("No such command '%s'. See `help commands'" % command)
+ else :
+ return default
+
+ def privmsg (self, user, channel, message) :
+ if message.lower().startswith(self.nickname.lower()) :
+ me, command = message.split(":", 1)
+
+ args = command.strip().split()
+ command = args.pop(0)
+
+ try :
+ method = self._lookupCommand(command)
+
+ reply = method(*args)
+
+ if reply :
+ self.send(reply)
+
+ except ReplyException, e :
+ self.send(e.reply)
+
+ except Exception, e :
+ self.send("Error: %s: %s" % (e.__class__.__name__, e))
+ traceback.print_exc()
+
+ def cmd_help (self, cmd="help") :
+ """help <command|module> - Display help about the given command or module"""
+
+ method = self._lookupCommand(cmd, None)
+
+ if method :
+ return method.__doc__
+ else :
+ try :
+ module, addr = self.nexus.getModuleInfo(cmd)
+
+ return "%s is version %d from %s:%d. Events: %s. See `commands %s' for a list of commands" % (module.name, module.version, addr.host, addr.port, ', '.join(module.event_types), module.name)
+
+ except KeyError :
+ raise ReplyException("No command/module called `%s'. See `help commands'" % cmd)
+
+ def cmd_commands (self, module=None) :
+ """commands [<module>] - Show primary commands, or commands in the given module (see `help modules')"""
+
+ if module :
+ raise ReplyException("No support for module commands yet :P")
+ else :
+ return "Commands: %s" % ', '.join(
+ attr_name.split('_', 1)[1]
+ for attr_name in BotProtocol.__dict__.iterkeys()
+ if attr_name.startswith("cmd_")
+ )
+
+ def cmd_modules (self) :
+ """modules - Show a list of connected modules"""
+
+ return "Modules: %s" % ', '.join(
+ module.name
+ for module in self.nexus.getModules()
+ )
--- a/logwatcher.py Thu Mar 20 18:47:58 2008 +0200
+++ b/logwatcher.py Thu Mar 20 19:46:04 2008 +0200
@@ -52,14 +52,31 @@
def _filter (self, match) :
return match.string
-class SudoFilter (Filter) :
- REGEXP = "sudo:\s*(?P<username>\S+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\S+) ; COMMAND=(?P<command>.*)"
+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(SudoFilter, self).__init__(self.REGEXP, "sudo")
+ super(AutoFilter, self).__init__(self.REGEXP, self.EVENT)
+
+ def _filter (self, match) :
+ return self.OUTPUT % match.groupdict()
- def _filter (self, match) :
- return "%(username)s:%(tty)s - %(pwd)s - `%(command)s` as %(target_user)s" % 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"
@@ -67,12 +84,14 @@
event_types = [
"error",
- "sudo"
+ "sudo",
+ "ssh",
]
log_files = (
("auth.log", "/var/log/auth.log", (
SudoFilter(),
+ SSHFilter(),
)),
)
@@ -88,14 +107,11 @@
p = self.log_objs[name] = TailProcessProtocol(self, name, filters)
- reactor.spawnProcess(p, "/usr/bin/tail", ["tail", "--follow=name", file])
+ reactor.spawnProcess(p, "/usr/bin/tail", ["tail", "-n0", "--follow=name", file])
def error (self, msg) :
self.sendEvent("error", msg)
if __name__ == '__main__' :
- log.startLogging(sys.stderr)
-
- module = ExampleModule()
- reactor.run()
+ ExampleModule().run()
--- a/nexus.py Thu Mar 20 18:47:58 2008 +0200
+++ b/nexus.py Thu Mar 20 19:46:04 2008 +0200
@@ -46,6 +46,14 @@
def handleEvent (self, event) :
self.irc.connection.sendEvent(event)
+
+ def getModules (self) :
+ return (module for (module, transport) in self.modules.itervalues())
+
+ def getModuleInfo (self, name) :
+ module, connection = self.modules[name]
+
+ return module, connection.transport.getPeer()
if __name__ == '__main__' :
log.startLogging(sys.stderr)