terom@11: from twisted.internet import protocol, reactor terom@11: from twisted.python import log terom@8: import socket, os, time terom@7: terom@11: import _utmp, api, sys terom@11: terom@11: POLL_INTERVAL = 5 terom@11: terom@11: def getPidCmd (pid) : terom@11: try : terom@11: fh = open("/proc/%d/cmdline" % pid, "r") terom@11: cmdline = fh.read() terom@11: fh.close() terom@11: terom@11: return repr("%d:%s" % (pid, cmdline)) terom@11: except IOError, OSError : terom@11: return "[%d]" % pid terom@7: terom@7: class WtmpEntry (object) : terom@7: _ATTRS = ( terom@11: "type", "pid", "line", "id", "user", "host", "exit", "session", "tv", "addr" terom@7: ) terom@7: terom@8: @staticmethod terom@8: def fromFile (file) : terom@8: bytes = file.read(_utmp.size()) terom@8: terom@8: if not bytes : terom@8: return terom@8: terom@8: result = _utmp.parse(bytes) terom@8: terom@8: wtmp = WtmpEntry() terom@8: terom@8: for name, field in zip(wtmp._ATTRS, result) : terom@11: if isinstance(field, str) and name not in ("addr", ) : terom@8: field = field.rstrip("\0") terom@8: terom@8: setattr(wtmp, name, field) terom@8: terom@11: # convert the address terom@11: family = socket.AF_INET terom@11: addr = wtmp.addr[0:4] terom@11: for byte in wtmp.addr[4:] : terom@11: if byte : terom@11: family = socket.AF_INET6 terom@11: addr = wtmp.addr terom@11: break terom@11: terom@11: wtmp.addr = socket.inet_ntop(family, addr) terom@11: wtmp.pid = getPidCmd(wtmp.pid) terom@8: terom@8: return wtmp terom@8: terom@7: def __str__ (self) : terom@7: return " ".join("%s=%s" % (key, getattr(self, key)) for key in self._ATTRS) terom@7: terom@8: class WtmpFile (object) : terom@8: def __init__ (self, path="/var/log/wtmp") : terom@8: self.path = path terom@8: self.file = open(path) terom@8: self.file.seek(0, os.SEEK_END) terom@7: terom@8: def tryRead (self) : terom@8: return WtmpEntry.fromFile(self.file) terom@7: terom@7: def read_entries (file) : terom@7: while True : terom@7: wtmp = read_entry(file) terom@7: terom@7: if wtmp : terom@7: yield wtmp terom@7: else : terom@7: return terom@7: terom@8: def follow_main () : terom@8: wtmp = WtmpFile() terom@8: terom@8: while True : terom@8: item = wtmp.tryRead() terom@8: terom@8: if item : terom@8: print item terom@8: else : terom@8: time.sleep(2) terom@8: terom@7: def test_main () : terom@7: fh = open("/var/log/wtmp", "r") terom@7: terom@7: for item in read_entries(fh) : terom@7: print item terom@7: terom@11: class WtmpModule (api.Module) : terom@11: name = "wtmp" terom@11: version = 0x0001 terom@11: terom@11: event_types = [ terom@11: "wtmp", terom@11: ] terom@11: terom@11: def handleConnect (self) : terom@11: log.msg("Following default wtmp file...") terom@11: terom@11: self.wtmp = WtmpFile() terom@11: terom@11: log.msg("Starting poll timer") terom@11: terom@11: self.poll() terom@11: terom@11: log.msg("Running") terom@11: terom@11: def poll (self) : terom@11: terom@11: while True : terom@11: item = self.wtmp.tryRead() terom@11: terom@11: if item : terom@11: log.msg(" -- %s" % item) terom@11: self.sendEvent("wtmp", str(item)) terom@11: else : terom@11: return reactor.callLater(POLL_INTERVAL, self.poll) terom@11: terom@7: if __name__ == '__main__' : terom@11: WtmpModule().run() terom@7: