logparse / parsers / smbd_journald.pyon commit rename parsers, better journald integration (e1f7605)
   1"""
   2Get login statistics for a samba server daemon (uses journald). Recommended
   3setup in /etc/smbd.conf is to set `logging = syslog@3 ...` (ensure smbd was
   4built with `configure --with-syslog`).
   5"""
   6
   7import re
   8from systemd import journal
   9
  10from logparse import config
  11from logparse.formatting import *
  12from logparse.load_parsers import Parser
  13from logparse.util import LogPeriod, resolve
  14
  15class SmbdJournald(Parser):
  16
  17    def __init__(self):
  18
  19        super().__init__()
  20        self.name = "smbd_journald"
  21        self.info = "Get login statistics for a samba server."
  22
  23    def parse_log(self):
  24
  25        logger.debug("Starting smbd section")
  26        section = Section("smbd")
  27
  28        j = journal.Reader()
  29        j.this_boot()
  30        j.log_level(journal.LOG_DEBUG)
  31        j.add_match(_COMM="smbd")
  32
  33        messages = [entry["MESSAGE"] for entry in j if "MESSAGE" in entry]
  34
  35        total_auths = 0     # total no. of logins for all users and all shares
  36
  37        shares = {}         # file shares (each share is mapped to a list of
  38                            # user-hostname pairs)
  39
  40        logger.debug("Found {0} samba logins".format(str(len(messages))))
  41        logger.debug("Parsing data")
  42
  43        for msg in messages:  # one log file for each client
  44
  45            if "connect to service" in msg:
  46
  47                # Generate list of [('client', 'ip', 'share', 'user')]
  48                entry = re.search("(\w*)\s*\(ipv.:(.+):.+\) connect to service"
  49                    "(\S+) initially as user (\S+)", msg)
  50
  51                try:
  52                    client, ip, share, user = entry.group(1,2,3,4)
  53                except:
  54                    logger.warning("Malformed log message: " + msg)
  55                    continue
  56
  57                if not share in shares:
  58                    share_match = False
  59                    for pattern in config.prefs.get("smbd", "shares").split():
  60                        share_match = re.fullmatch(pattern, share) or share_match
  61                    if not share_match:
  62                        logger.debug("Ignoring share {0} due to config".format(share))
  63                        continue
  64
  65                if (not client.strip()):
  66                    client = ip
  67                userhost = user + '@' + resolve(client, 
  68                        fqdn=config.prefs.get("smbd", "smbd-resolve-domains"))
  69
  70                user_match = False
  71                for pattern in config.prefs.get("smbd", "users").split():
  72                    user_match = re.fullmatch(pattern, userhost) or user_match
  73                if not user_match:
  74                    logger.debug("Ignoring login to {0} by user {1} "
  75                        "due to config".format(share, userhost))
  76                    continue
  77
  78                total_auths += 1
  79                if share in shares:
  80                    shares[share].append(userhost)
  81                else:
  82                    shares[share] = [userhost]
  83
  84        # Format Data() objects
  85
  86        section.append_data(Data(subtitle="Total of {0} authentications"
  87            .format(str(total_auths))))
  88
  89        for share, logins in shares.items():
  90            share_data = Data()
  91            share_data.items = logins
  92            share_data.orderbyfreq()
  93            share_data.truncl(config.prefs.getint("logparse", "maxlist"))
  94            share_data.subtitle = share +  " ({0}, {1})".format(
  95                    plural("user", len(share_data.items)),
  96                    plural("login", len(logins)))
  97            section.append_data(share_data)
  98            logger.debug("Found {0} logins for share {1}".format(
  99                str(len(logins)), share))
 100
 101        logger.info("Finished smbd section")
 102        return section