From e642c2d398bcf951c21f1a1deb04c45e60fc3a2b Mon Sep 17 00:00:00 2001 From: Jason R Date: Wed, 30 Dec 2015 17:29:43 -0800 Subject: [PATCH] initial attempt at systemd support it works now. Plus comments --- .gitignore | 1 + knockknock-daemon.py | 40 ++++++++++++++++++++++++----- knockknock/LogJournald.py | 54 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 knockknock/LogJournald.py diff --git a/.gitignore b/.gitignore index f932ceb..d0e21c3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea MANIFEST dist +venv/ diff --git a/knockknock-daemon.py b/knockknock-daemon.py index 81e502d..cfda29e 100644 --- a/knockknock-daemon.py +++ b/knockknock-daemon.py @@ -24,9 +24,6 @@ """ import os, sys, pwd, grp - -from knockknock.LogEntry import LogEntry -from knockknock.LogFile import LogFile from knockknock.Profiles import Profiles from knockknock.PortOpener import PortOpener from knockknock.DaemonConfiguration import DaemonConfiguration @@ -48,6 +45,25 @@ def checkConfiguration(): print "/etc/knockknock.d/profiles/ does not exist. You need to setup your profiles first..." sys.exit(3) + # Retreive the system init type from /proc + with open('/proc/1/status', 'r') as f: + global initprocname + initprocname = f.readline().split()[1] + + # Verify whether or not the python-systemd dependency is required as well + # as whether or not it is fulfilled (optimistically written with python3 + # support) + if (sys.version_info > (3, 0)): + import importlib + if initprocname == "systemd" and importlib.util.find_spec("systemd") is None: + print "Your init system was detected as systemd but the python systemd module is not installed. You need to install it first..." + sys.exit(3) + else: + import pkgutil + if initprocname == "systemd" and pkgutil.find_loader("systemd") is None: + print "Your init system was detected as systemd but the python systemd module is not installed. You need to install it first..." + sys.exit(3) + def dropPrivileges(): nobody = pwd.getpwnam('nobody') adm = grp.getgrnam('adm') @@ -62,10 +78,20 @@ def handleFirewall(input, config): def handleKnocks(output, profiles, config): dropPrivileges() - - logFile = LogFile('/var/log/kern.log') + # Attempt to determine logging source here (since it shouldn't require + # elevated privileges to verify this information) based on the system + # init process + if initprocname == "systemd": + from knockknock.LogJournald import JournalReader + logSource = JournalReader() + elif initprocname in ["init", "preinit"]: + from knockknock.LogFile import LogFile + logSource = LogFile('/var/log/kern.log') + else: + print "Failed to find logging source for your init system. Exiting" + sys.exit(3) portOpener = PortOpener(output, config.getDelay()) - knockWatcher = KnockWatcher(config, logFile, profiles, portOpener) + knockWatcher = KnockWatcher(config, logSource, profiles, portOpener) knockWatcher.tailAndProcess() @@ -90,6 +116,6 @@ def main(argv): else: os.close(output) handleFirewall(os.fdopen(input, 'r'), config) - + if __name__ == '__main__': main(sys.argv[1:]) diff --git a/knockknock/LogJournald.py b/knockknock/LogJournald.py new file mode 100644 index 0000000..3ddba3b --- /dev/null +++ b/knockknock/LogJournald.py @@ -0,0 +1,54 @@ +""" +Journald log reader event loop for knockknock-daemon. + +Contains the JournalReader class which takes no parameters and has 1 method: +(tail) +""" +# Copyright (c) 2015 Jason Ritzke +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA +# + +import select +import time +from systemd import journal + + +class JournalReader: + """ + Journald reader for knockknock-daemon. + + Takes no parameters on initialization. Has a single event loop method + called tail that yields new messages in the kernel log. + """ + + def __init__(self): + """Initalization method for JournalReader class.""" + self.j = journal.Reader() + self.j.seek_tail() + self.j.add_match('_TRANSPORT=kernel') + self.p = select.poll() + self.p.register(self.j, self.j.get_events()) + + def tail(self): + """Generator that yields messages from the kernel log.""" + while True: + self.p.poll() + line = self.j.get_next() + if 'MESSAGE' not in line: + time.sleep(.25) + else: + yield line['MESSAGE']