# -*- coding: utf-8 -*- """ List the logged (executed) cron jobs and their commands (uses journald/logfile) NOTE: If using journald, the log level for cron.service should be at least 2 (default is 1). This can be changed with `sudo systemctl edit cron --full`, and ammend `-L 2` to the ExecStart command. TODO: also output a list of scheduled (future) jobs """ from systemd import journal import datetime from logparse import config from logparse.formatting import * from logparse.load_parsers import Parser from logparse.parsers.cron import CronCommand class CronJournald(Parser): def __init__(self): super().__init__() self.name = "cron_journald" self.info = "List the logged (executed) cron jobs and their commands " \ "(uses journald module)" def parse_log(self): logger.debug("Starting cron section") section = Section("cron") # Initiate journald reader j = journal.Reader() j.this_machine() j.log_level(journal.LOG_INFO) j.add_match(_COMM="cron") j.seek_realtime(section.period.startdate) logger.info("Obtaining cron logs") records = [entry for entry in j if "MESSAGE" in entry and " CMD " in entry["MESSAGE"]] if len(records) == 0: logger.warning("Couldn't find any cron commands") return 1 logger.info("Found {0} log records".format(len(records))) logger.debug("Analysing cron commands for each user") command_objects = [] users = {} for record in records: if record["_SOURCE_REALTIME_TIMESTAMP"] < section.period.startdate: logger.warning("Discarding log record from {0} - was " "seek_realtime set properly?".format( record["_SOURCE_REALTIME_TIMESTAMP"])) continue try: cmd_obj = CronCommand(record) if config.prefs.getboolean("cron", "truncate-commands"): cmd_obj.truncate() if not (cmd_obj.match_user(config.prefs.get("cron", "users") .split()) and cmd_obj.match_cmd(config.prefs.get( "cron", "commands").split())): logger.debug("Ignoring cron session by {0} with command " "{1} due to config".format(cmd_obj.user, cmd_obj.cmd)) continue command_objects.append(cmd_obj) if not cmd_obj.user in users: users[cmd_obj.user] = [] users[cmd_obj.user].append(cmd_obj.cmd) except Exception as e: logger.warning("Malformed cron log message: {0}. " "Error message: {1}".format(record["MESSAGE"], str(e))) continue logger.info("Found {0} valid cron sessions".format(len(command_objects))) if config.prefs.getboolean("cron", "summary"): summary_data = Data() summary_data.subtitle = "Total of " + plural("cron session", len(command_objects)) + " for " + plural("user", len(users)) summary_data.items = ["{}: `{}`".format(c.user, c.cmd) for c in command_objects] summary_data.orderbyfreq() summary_data.truncl(config.prefs.getint("logparse", "maxcmd")) section.append_data(summary_data) if config.prefs.getboolean("cron", "list-users"): for user, cmdlist in users.items(): user_data = Data() user_data.subtitle = plural("session", len(cmdlist)) \ + " for " + user + (" (" + plural("unique command", len(set(cmdlist))) + ")" if len(set(cmdlist)) > 1 else "") user_data.items = ["`{0}`".format(cmd) for cmd in cmdlist] user_data.orderbyfreq() user_data.truncl(config.prefs.getint("logparse", "maxcmd")) section.append_data(user_data) logger.debug("Found {0} cron sessions for user {1} " "({2} unique commands): {3}".format( len(cmdlist), user, len(set(cmdlist)), user_data.items)) logger.info("Finished cron section") return section