summaryrefslogtreecommitdiffstats
path: root/src/tools/analyzer
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/analyzer')
-rw-r--r--src/tools/analyzer/Makefile.am22
-rw-r--r--src/tools/analyzer/Makefile.in853
-rw-r--r--src/tools/analyzer/__init__.py0
-rw-r--r--src/tools/analyzer/modules/__init__.py0
-rw-r--r--src/tools/analyzer/modules/request.py320
-rw-r--r--src/tools/analyzer/parser.py60
-rw-r--r--src/tools/analyzer/source_files.py76
-rw-r--r--src/tools/analyzer/source_journald.py46
-rw-r--r--src/tools/analyzer/source_reader.py27
-rwxr-xr-xsrc/tools/analyzer/sss_analyze5
-rw-r--r--src/tools/analyzer/sss_analyze.py104
11 files changed, 1513 insertions, 0 deletions
diff --git a/src/tools/analyzer/Makefile.am b/src/tools/analyzer/Makefile.am
new file mode 100644
index 0000000..b40043d
--- /dev/null
+++ b/src/tools/analyzer/Makefile.am
@@ -0,0 +1,22 @@
+sss_analyze_pythondir = $(libexecdir)/sssd
+
+dist_sss_analyze_python_SCRIPTS = \
+ sss_analyze \
+ $(NULL)
+
+pkgpythondir = $(python3dir)/sssd
+
+dist_pkgpython_DATA = \
+ __init__.py \
+ source_files.py \
+ source_journald.py \
+ source_reader.py \
+ parser.py \
+ sss_analyze.py \
+ $(NULL)
+
+modulesdir = $(pkgpythondir)/modules
+dist_modules_DATA = \
+ modules/__init__.py \
+ modules/request.py \
+ $(NULL)
diff --git a/src/tools/analyzer/Makefile.in b/src/tools/analyzer/Makefile.in
new file mode 100644
index 0000000..0018403
--- /dev/null
+++ b/src/tools/analyzer/Makefile.in
@@ -0,0 +1,853 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/tools/analyzer
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/version.m4 $(top_srcdir)/src/build_macros.m4 \
+ $(top_srcdir)/src/external/platform.m4 \
+ $(top_srcdir)/src/conf_macros.m4 \
+ $(top_srcdir)/src/external/pkg.m4 \
+ $(top_srcdir)/src/external/libpopt.m4 \
+ $(top_srcdir)/src/external/libtalloc.m4 \
+ $(top_srcdir)/src/external/libtdb.m4 \
+ $(top_srcdir)/src/external/libtevent.m4 \
+ $(top_srcdir)/src/external/libldb.m4 \
+ $(top_srcdir)/src/external/libdhash.m4 \
+ $(top_srcdir)/src/external/libini_config.m4 \
+ $(top_srcdir)/src/external/libgssapi_krb5.m4 \
+ $(top_srcdir)/src/external/pam.m4 \
+ $(top_srcdir)/src/external/ldap.m4 \
+ $(top_srcdir)/src/external/libpcre.m4 \
+ $(top_srcdir)/src/external/krb5.m4 \
+ $(top_srcdir)/src/external/libcares.m4 \
+ $(top_srcdir)/src/external/libcmocka.m4 \
+ $(top_srcdir)/src/external/docbook.m4 \
+ $(top_srcdir)/src/external/sizes.m4 \
+ $(top_srcdir)/src/external/python.m4 \
+ $(top_srcdir)/src/external/selinux.m4 \
+ $(top_srcdir)/src/external/crypto.m4 \
+ $(top_srcdir)/src/external/nsupdate.m4 \
+ $(top_srcdir)/src/external/libkeyutils.m4 \
+ $(top_srcdir)/src/external/libkrad.m4 \
+ $(top_srcdir)/src/external/libnl.m4 \
+ $(top_srcdir)/src/external/systemd.m4 \
+ $(top_srcdir)/src/external/pac_responder.m4 \
+ $(top_srcdir)/src/external/cifsidmap.m4 \
+ $(top_srcdir)/src/external/signal.m4 \
+ $(top_srcdir)/src/external/inotify.m4 \
+ $(top_srcdir)/src/external/samba.m4 \
+ $(top_srcdir)/src/external/sasl.m4 \
+ $(top_srcdir)/src/external/libnfsidmap.m4 \
+ $(top_srcdir)/src/external/cwrap.m4 \
+ $(top_srcdir)/src/external/libresolv.m4 \
+ $(top_srcdir)/src/external/intgcheck.m4 \
+ $(top_srcdir)/src/external/systemtap.m4 \
+ $(top_srcdir)/src/external/service.m4 \
+ $(top_srcdir)/src/external/test_ca.m4 \
+ $(top_srcdir)/src/external/ax_valgrind_check.m4 \
+ $(top_srcdir)/src/external/libjansson.m4 \
+ $(top_srcdir)/src/external/libcurl.m4 \
+ $(top_srcdir)/src/external/libjose.m4 \
+ $(top_srcdir)/src/external/libuuid.m4 \
+ $(top_srcdir)/src/external/libunistring.m4 \
+ $(top_srcdir)/src/external/libpasskey.m4 \
+ $(top_srcdir)/src/external/p11-kit.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_sss_analyze_python_SCRIPTS) \
+ $(dist_modules_DATA) $(dist_pkgpython_DATA) $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(sss_analyze_pythondir)" \
+ "$(DESTDIR)$(modulesdir)" "$(DESTDIR)$(pkgpythondir)"
+SCRIPTS = $(dist_sss_analyze_python_SCRIPTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(dist_modules_DATA) $(dist_pkgpython_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/mkinstalldirs
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CARES_CFLAGS = @CARES_CFLAGS@
+CARES_LIBS = @CARES_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURL_CFLAGS = @CURL_CFLAGS@
+CURL_LIBS = @CURL_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHASH_CFLAGS = @DHASH_CFLAGS@
+DHASH_LIBS = @DHASH_LIBS@
+DLLTOOL = @DLLTOOL@
+DOCBOOK_XSLT = @DOCBOOK_XSLT@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DTRACE = @DTRACE@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@
+ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@
+ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@
+ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FAKETIME = @FAKETIME@
+FGREP = @FGREP@
+FIDO2_CFLAGS = @FIDO2_CFLAGS@
+FIDO2_LIBS = @FIDO2_LIBS@
+FILECMD = @FILECMD@
+GDM_PAM_EXTENSIONS_CFLAGS = @GDM_PAM_EXTENSIONS_CFLAGS@
+GDM_PAM_EXTENSIONS_LIBS = @GDM_PAM_EXTENSIONS_LIBS@
+GMSGFMT = @GMSGFMT@
+GPO_DEFAULT = @GPO_DEFAULT@
+GREP = @GREP@
+GSSAPI_KRB5_CFLAGS = @GSSAPI_KRB5_CFLAGS@
+GSSAPI_KRB5_LIBS = @GSSAPI_KRB5_LIBS@
+HAVE_FAKEROOT = @HAVE_FAKEROOT@
+HAVE_LDAPMODIFY = @HAVE_LDAPMODIFY@
+HAVE_MANPAGES = @HAVE_MANPAGES@
+HAVE_NSS_WRAPPER = @HAVE_NSS_WRAPPER@
+HAVE_PAM_WRAPPER = @HAVE_PAM_WRAPPER@
+HAVE_PYTHON2 = @HAVE_PYTHON2@
+HAVE_PYTHON2_BINDINGS = @HAVE_PYTHON2_BINDINGS@
+HAVE_PYTHON3 = @HAVE_PYTHON3@
+HAVE_PYTHON3_BINDINGS = @HAVE_PYTHON3_BINDINGS@
+HAVE_SELINUX = @HAVE_SELINUX@
+HAVE_SEMANAGE = @HAVE_SEMANAGE@
+HAVE_UID_WRAPPER = @HAVE_UID_WRAPPER@
+INI_CONFIG_CFLAGS = @INI_CONFIG_CFLAGS@
+INI_CONFIG_LIBS = @INI_CONFIG_LIBS@
+INI_CONFIG_V0_CFLAGS = @INI_CONFIG_V0_CFLAGS@
+INI_CONFIG_V0_LIBS = @INI_CONFIG_V0_LIBS@
+INI_CONFIG_V1_1_CFLAGS = @INI_CONFIG_V1_1_CFLAGS@
+INI_CONFIG_V1_1_LIBS = @INI_CONFIG_V1_1_LIBS@
+INI_CONFIG_V1_3_CFLAGS = @INI_CONFIG_V1_3_CFLAGS@
+INI_CONFIG_V1_3_LIBS = @INI_CONFIG_V1_3_LIBS@
+INI_CONFIG_V1_CFLAGS = @INI_CONFIG_V1_CFLAGS@
+INI_CONFIG_V1_LIBS = @INI_CONFIG_V1_LIBS@
+INOTIFY_LIBS = @INOTIFY_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
+JOSE_CFLAGS = @JOSE_CFLAGS@
+JOSE_LIBS = @JOSE_LIBS@
+JOURNALD_CFLAGS = @JOURNALD_CFLAGS@
+JOURNALD_LIBS = @JOURNALD_LIBS@
+KEYUTILS_LIBS = @KEYUTILS_LIBS@
+KRAD_LIBS = @KRAD_LIBS@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LD = @LD@
+LDB_CFLAGS = @LDB_CFLAGS@
+LDB_LIBS = @LDB_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBADD_DL = @LIBADD_DL@
+LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
+LIBADD_TIMER = @LIBADD_TIMER@
+LIBCLOCK_GETTIME = @LIBCLOCK_GETTIME@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_DLLOADERS = @LT_DLLOADERS@
+LT_DLPREOPEN = @LT_DLPREOPEN@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+NDR_KRB5PAC_CFLAGS = @NDR_KRB5PAC_CFLAGS@
+NDR_KRB5PAC_LIBS = @NDR_KRB5PAC_LIBS@
+NDR_NBT_CFLAGS = @NDR_NBT_CFLAGS@
+NDR_NBT_LIBS = @NDR_NBT_LIBS@
+NFSIDMAP_CFLAGS = @NFSIDMAP_CFLAGS@
+NFSIDMAP_LIBS = @NFSIDMAP_LIBS@
+NFSIDMAP_OBJ = @NFSIDMAP_OBJ@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSUPDATE = @NSUPDATE@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENLDAP_CFLAGS = @OPENLDAP_CFLAGS@
+OPENLDAP_LIBS = @OPENLDAP_LIBS@
+OPENSSL = @OPENSSL@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+P11TOOL = @P11TOOL@
+P11_KIT_CFLAGS = @P11_KIT_CFLAGS@
+P11_KIT_LIBS = @P11_KIT_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
+PAM_MISC_LIBS = @PAM_MISC_LIBS@
+PASSKEY_CFLAGS = @PASSKEY_CFLAGS@
+PASSKEY_LIBS = @PASSKEY_LIBS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PCRE_CFLAGS = @PCRE_CFLAGS@
+PCRE_LIBS = @PCRE_LIBS@
+PKG_CONFIG = @PKG_CONFIG@
+PO4A = @PO4A@
+POPT_CFLAGS = @POPT_CFLAGS@
+POPT_LIBS = @POPT_LIBS@
+POSUB = @POSUB@
+PRERELEASE_VERSION = @PRERELEASE_VERSION@
+PYTHON = @PYTHON@
+PYTHON2 = @PYTHON2@
+PYTHON2_CFLAGS = @PYTHON2_CFLAGS@
+PYTHON2_EXEC_PREFIX = @PYTHON2_EXEC_PREFIX@
+PYTHON2_INCLUDES = @PYTHON2_INCLUDES@
+PYTHON2_LIBS = @PYTHON2_LIBS@
+PYTHON2_PREFIX = @PYTHON2_PREFIX@
+PYTHON2_VERSION = @PYTHON2_VERSION@
+PYTHON3 = @PYTHON3@
+PYTHON3_CFLAGS = @PYTHON3_CFLAGS@
+PYTHON3_EXEC_PREFIX = @PYTHON3_EXEC_PREFIX@
+PYTHON3_INCLUDES = @PYTHON3_INCLUDES@
+PYTHON3_LIBS = @PYTHON3_LIBS@
+PYTHON3_PREFIX = @PYTHON3_PREFIX@
+PYTHON3_VERSION = @PYTHON3_VERSION@
+PYTHON_CONFIG = @PYTHON_CONFIG@
+PYTHON_EXEC = @PYTHON_EXEC@
+PYTHON_EXEC_INTG = @PYTHON_EXEC_INTG@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RESOLV_CFLAGS = @RESOLV_CFLAGS@
+RESOLV_LIBS = @RESOLV_LIBS@
+SAMBA_UTIL_CFLAGS = @SAMBA_UTIL_CFLAGS@
+SAMBA_UTIL_LIBS = @SAMBA_UTIL_LIBS@
+SASL_CFLAGS = @SASL_CFLAGS@
+SASL_LIBS = @SASL_LIBS@
+SED = @SED@
+SELINUX_LIBS = @SELINUX_LIBS@
+SEMANAGE_LIBS = @SEMANAGE_LIBS@
+SERVICE = @SERVICE@
+SET_MAKE = @SET_MAKE@
+SGML_CATALOG_FILES = @SGML_CATALOG_FILES@
+SHELL = @SHELL@
+SLAPD = @SLAPD@
+SMBCLIENT_CFLAGS = @SMBCLIENT_CFLAGS@
+SMBCLIENT_LIBS = @SMBCLIENT_LIBS@
+SOFTHSM2_PATH = @SOFTHSM2_PATH@
+SOFTHSM2_UTIL = @SOFTHSM2_UTIL@
+SSH_KEYGEN = @SSH_KEYGEN@
+SSL_CFLAGS = @SSL_CFLAGS@
+SSL_LIBS = @SSL_LIBS@
+SSSD_USER = @SSSD_USER@
+STRIP = @STRIP@
+SYSTEMD_DAEMON_CFLAGS = @SYSTEMD_DAEMON_CFLAGS@
+SYSTEMD_DAEMON_LIBS = @SYSTEMD_DAEMON_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+TALLOC_CFLAGS = @TALLOC_CFLAGS@
+TALLOC_LIBS = @TALLOC_LIBS@
+TDB_CFLAGS = @TDB_CFLAGS@
+TDB_LIBS = @TDB_LIBS@
+TEST_DIR = @TEST_DIR@
+TEVENT_CFLAGS = @TEVENT_CFLAGS@
+TEVENT_LIBS = @TEVENT_LIBS@
+UNICODE_LIBS = @UNICODE_LIBS@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND = @VALGRIND@
+VALGRIND_ENABLED = @VALGRIND_ENABLED@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XMLLINT = @XMLLINT@
+XSLTPROC = @XSLTPROC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+appmodpath = @appmodpath@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cifspluginpath = @cifspluginpath@
+config_def_ccache_dir = @config_def_ccache_dir@
+config_def_ccname_template = @config_def_ccname_template@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dbpath = @dbpath@
+docdir = @docdir@
+dvidir = @dvidir@
+environment_file = @environment_file@
+exec_prefix = @exec_prefix@
+gpocachepath = @gpocachepath@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+initdir = @initdir@
+install_sh = @install_sh@
+krb5authdatapluginpath = @krb5authdatapluginpath@
+krb5pluginpath = @krb5pluginpath@
+krb5rcachedir = @krb5rcachedir@
+ldblibdir = @ldblibdir@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+logpath = @logpath@
+mandir = @mandir@
+mcpath = @mcpath@
+mkdir_p = @mkdir_p@
+nfsidmaplibdir = @nfsidmaplibdir@
+nfslibpath = @nfslibpath@
+nsslibdir = @nsslibdir@
+oldincludedir = @oldincludedir@
+pammoddir = @pammoddir@
+pdfdir = @pdfdir@
+pidpath = @pidpath@
+pipepath = @pipepath@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = $(python3dir)/sssd
+pluginpath = @pluginpath@
+polkitdir = @polkitdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pubconfpath = @pubconfpath@
+py2execdir = @py2execdir@
+py3execdir = @py3execdir@
+pyexecdir = @pyexecdir@
+python2dir = @python2dir@
+python3dir = @python3dir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+secdbpath = @secdbpath@
+session_recording_shell = @session_recording_shell@
+sharedbuilddir = @sharedbuilddir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subidlibpath = @subidlibpath@
+sudolibpath = @sudolibpath@
+sysconfdir = @sysconfdir@
+systemdconfdir = @systemdconfdir@
+systemdunitdir = @systemdunitdir@
+tapset_dir = @tapset_dir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+valgrind_enabled_tools = @valgrind_enabled_tools@
+valgrind_tools = @valgrind_tools@
+winbindpluginpath = @winbindpluginpath@
+sss_analyze_pythondir = $(libexecdir)/sssd
+dist_sss_analyze_python_SCRIPTS = \
+ sss_analyze \
+ $(NULL)
+
+dist_pkgpython_DATA = \
+ __init__.py \
+ source_files.py \
+ source_journald.py \
+ source_reader.py \
+ parser.py \
+ sss_analyze.py \
+ $(NULL)
+
+modulesdir = $(pkgpythondir)/modules
+dist_modules_DATA = \
+ modules/__init__.py \
+ modules/request.py \
+ $(NULL)
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/tools/analyzer/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/tools/analyzer/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-dist_sss_analyze_pythonSCRIPTS: $(dist_sss_analyze_python_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_sss_analyze_python_SCRIPTS)'; test -n "$(sss_analyze_pythondir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sss_analyze_pythondir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sss_analyze_pythondir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sss_analyze_pythondir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sss_analyze_pythondir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-dist_sss_analyze_pythonSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_sss_analyze_python_SCRIPTS)'; test -n "$(sss_analyze_pythondir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ dir='$(DESTDIR)$(sss_analyze_pythondir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-dist_modulesDATA: $(dist_modules_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_modules_DATA)'; test -n "$(modulesdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(modulesdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(modulesdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(modulesdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(modulesdir)" || exit $$?; \
+ done
+
+uninstall-dist_modulesDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_modules_DATA)'; test -n "$(modulesdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(modulesdir)'; $(am__uninstall_files_from_dir)
+install-dist_pkgpythonDATA: $(dist_pkgpython_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_pkgpython_DATA)'; test -n "$(pkgpythondir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgpythondir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgpythondir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgpythondir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgpythondir)" || exit $$?; \
+ done
+
+uninstall-dist_pkgpythonDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_pkgpython_DATA)'; test -n "$(pkgpythondir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgpythondir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(SCRIPTS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(sss_analyze_pythondir)" "$(DESTDIR)$(modulesdir)" "$(DESTDIR)$(pkgpythondir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_modulesDATA install-dist_pkgpythonDATA \
+ install-dist_sss_analyze_pythonSCRIPTS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_modulesDATA uninstall-dist_pkgpythonDATA \
+ uninstall-dist_sss_analyze_pythonSCRIPTS
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-dist_modulesDATA install-dist_pkgpythonDATA \
+ install-dist_sss_analyze_pythonSCRIPTS install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am uninstall-dist_modulesDATA \
+ uninstall-dist_pkgpythonDATA \
+ uninstall-dist_sss_analyze_pythonSCRIPTS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/tools/analyzer/__init__.py b/src/tools/analyzer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tools/analyzer/__init__.py
diff --git a/src/tools/analyzer/modules/__init__.py b/src/tools/analyzer/modules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tools/analyzer/modules/__init__.py
diff --git a/src/tools/analyzer/modules/request.py b/src/tools/analyzer/modules/request.py
new file mode 100644
index 0000000..d661ddd
--- /dev/null
+++ b/src/tools/analyzer/modules/request.py
@@ -0,0 +1,320 @@
+import re
+import logging
+
+from sssd.parser import SubparsersAction
+from sssd.parser import Option
+
+logger = logging.getLogger()
+
+
+class RequestAnalyzer:
+ """
+ A request analyzer module, handles request tracking logic
+ and analysis. Parses input generated from a source Reader.
+ """
+ module_parser = None
+ consumed_logs = []
+ list_opts = [
+ Option('--verbose', 'Verbose output', bool, '-v'),
+ Option('--pam', 'Filter only PAM requests', bool),
+ ]
+
+ show_opts = [
+ Option('cid', 'Track request with this ID', int),
+ Option('--child', 'Include child process logs', bool),
+ Option('--merge', 'Merge logs together sorted by timestamp', bool),
+ Option('--pam', 'Track only PAM requests', bool),
+ ]
+
+ def print_module_help(self, args):
+ """
+ Print the module parser help output
+
+ Args:
+ args (Namespace): argparse parsed arguments
+ """
+ self.module_parser.print_help()
+
+ def setup_args(self, parser_grp, cli):
+ """
+ Setup module parser, subcommands, and options
+
+ Args:
+ parser_grp (argparse.Action): Parser group to nest
+ module and subcommands under
+ """
+ desc = "Analyze request tracking module"
+ self.module_parser = parser_grp.add_parser('request',
+ description=desc,
+ help='Request tracking')
+
+ subparser = self.module_parser.add_subparsers(title=None,
+ dest='subparser',
+ action=SubparsersAction,
+ metavar='COMMANDS')
+
+ subcmd_grp = subparser.add_parser_group('Operation Modes')
+ cli.add_subcommand(subcmd_grp, 'list', 'List recent requests',
+ self.list_requests, self.list_opts)
+ cli.add_subcommand(subcmd_grp, 'show', 'Track individual request ID',
+ self.track_request, self.show_opts)
+
+ self.module_parser.set_defaults(func=self.print_module_help)
+
+ return self.module_parser
+
+ def load(self, args):
+ """
+ Load the appropriate source reader.
+
+ Args:
+ args (Namespace): argparse parsed arguments
+
+ Returns:
+ Instantiated source object
+ """
+ if args.source == "journald":
+ from sssd.source_journald import Journald
+ source = Journald()
+ else:
+ from sssd.source_files import Files
+ source = Files(args.logdir)
+ return source
+
+ def matched_line(self, source, patterns):
+ """
+ Yield lines which match any number of patterns (OR) in
+ provided patterns list.
+
+ Args:
+ source (Reader): source Reader object
+ Yields:
+ lines matching the provided pattern(s)
+ """
+ for line in source:
+ for pattern in patterns:
+ re_obj = re.compile(pattern)
+ if re_obj.search(line):
+ if line.startswith(' * '):
+ continue
+ yield line
+
+ def get_linked_ids(self, source, pattern, regex):
+ """
+ Retrieve list of associated REQ_TRACE ids. Filter
+ only source lines by pattern, then parse out the
+ linked id with the provided regex.
+
+ Args:
+ source (Reader): source Reader object
+ pattern (list of str): regex pattern(s) used for finding
+ linked ids
+ regex (str): regular expression used to extract linked id
+
+ Returns:
+ List of linked ids discovered
+ """
+ linked_ids = []
+ for match in self.matched_line(source, pattern):
+ id_re = re.compile(regex)
+ match = id_re.search(match)
+ if match:
+ found = match.group(0)
+ linked_ids.append(found)
+ return linked_ids
+
+ def consume_line(self, line, source, consume):
+ """
+ Print or consume a line, if merge cli option is provided then consume
+ boolean is set to True
+
+ Args:
+ line (str): line to process
+ source (Reader): source Reader object
+ consume (bool): If True, line is added to consume_logs
+ list, otherwise print line
+
+ Returns:
+ True if line was processed, otherwise False
+ """
+ found_results = True
+ if consume:
+ self.consumed_logs.append(line.rstrip(line[-1]))
+ else:
+ # files source includes newline
+ if type(source).__name__ == 'Files':
+ print(line, end='')
+ else:
+ print(line)
+ return found_results
+
+ def print_formatted_verbose(self, source):
+ """
+ Parse log file and print formatted verbose list_requests output
+
+ Args:
+ source (Reader): source Reader object
+ """
+ data = {}
+ # collect cid log lines from single run through of parsing the log
+ # into dictionary # (cid, ts) -> logline_output
+ for line in source:
+ if "CID#" not in line:
+ continue
+
+ # parse CID and ts from line, key is a tuple of (cid,ts)
+ fields = line.split("[")
+ # timestamp to the minute, cut off seconds, ms
+ ts = fields[0][:17]
+ result = re.search('CID#[0-9]*', fields[3])
+ cid = result.group(0)
+
+ # if mapping exists, append line to output. Otherwise create new mapping
+ if (cid, ts) in data.keys():
+ data[(cid, ts)] += line
+ else:
+ data[(cid, ts)] = line
+
+ # pretty print the data
+ for k, v in data.items():
+ cr_done = []
+ id_done = []
+ for cidline in v.splitlines():
+ plugin = ""
+ name = ""
+ id = ""
+
+ # CR number
+ fields = cidline.split("[")
+ cr_field = fields[3][7:]
+ cr = cr_field.split(":")[0][4:]
+ # Client connected, top-level info line
+ if re.search(r'\[cmd', cidline):
+ self.print_formatted(cidline)
+ # CR Plugin name
+ if re.search("cache_req_send", cidline):
+ plugin = cidline.split('\'')[1]
+ id_done.clear()
+ # Extract CR number
+ fields = cidline.split("[")
+ cr_field = fields[3][7:]
+ cr = cr_field.split(":")[0][4:]
+ # CR Input name
+ elif re.search("cache_req_process_input", cidline):
+ name = cidline.rsplit('[')[-1]
+ # CR Input id
+ elif re.search("cache_req_search_send", cidline):
+ id = cidline.rsplit()[-1]
+
+ if plugin:
+ print(" - " + plugin)
+ if name:
+ # Avoid duplicate output with the same CR #
+ if cr not in cr_done:
+ print(" - " + name[:-1])
+ cr_done.append(cr)
+ if (id and ("UID" in cidline or "GID" in cidline)):
+ if id not in id_done and bool(re.search(r'\d', id)):
+ print(" - " + id)
+ id_done.append(id)
+
+ def print_formatted(self, line):
+ """
+ Parse line and print formatted list_requests output
+
+ Args:
+ line (str): line to parse
+ Returns:
+ Client ID from printed line, 0 otherwise
+ """
+ # exclude backtrace logs
+ if line.startswith(' * '):
+ return 0
+ if "refreshed" in line:
+ return 0
+ ts = line.split(")")[0]
+ ts = ts[1:]
+ fields = line.split("[")
+ cid = fields[3][4:-9]
+ cmd = fields[4][4:-1]
+ uid = fields[5][4:-1]
+ if not uid.isnumeric():
+ uid = fields[6][4:-1]
+ print(f'{ts}: [uid {uid}] CID #{cid}: {cmd}')
+ return cid
+
+ def list_requests(self, args):
+ """
+ List component (default: NSS) responder requests
+
+ Args:
+ args (Namespace): populated argparse namespace
+ """
+ source = self.load(args)
+ component = source.Component.NSS
+ resp = "nss"
+ # Log messages matching the following regex patterns contain
+ # the useful info we need to produce list output
+ patterns = [r'\[cmd']
+ if args.pam:
+ component = source.Component.PAM
+ resp = "pam"
+
+ logger.info(f"******** Listing {resp} client requests ********")
+ source.set_component(component, False)
+
+ if args.verbose:
+ self.print_formatted_verbose(source)
+ else:
+ for line in self.matched_line(source, patterns):
+ if type(source).__name__ == 'Journald':
+ print(line)
+ else:
+ self.print_formatted(line)
+
+ def track_request(self, args):
+ """
+ Print Logs pertaining to individual SSSD client request
+
+ Args:
+ args (Namespace): populated argparse namespace
+ """
+ source = self.load(args)
+ cid = args.cid
+ resp_results = False
+ be_results = False
+ component = source.Component.NSS
+ resp = "nss"
+ pattern = [rf"\[CID#{cid}\]"]
+
+ if args.pam:
+ component = source.Component.PAM
+ resp = "pam"
+
+ logger.info(f"******** Checking {resp} responder for Client ID"
+ f" {cid} *******")
+ source.set_component(component, args.child)
+ for match in self.matched_line(source, pattern):
+ resp_results = self.consume_line(match, source, args.merge)
+
+ logger.info(f"********* Checking Backend for Client ID {cid} ********")
+ pattern = [rf'REQ_TRACE.*\[sssd.{resp} CID #{cid}\]']
+ source.set_component(source.Component.BE, args.child)
+
+ be_id_regex = r'\[RID#[0-9]+\]'
+ be_ids = self.get_linked_ids(source, pattern, be_id_regex)
+
+ pattern.clear()
+ [pattern.append(f'\\{id}') for id in be_ids]
+
+ for match in self.matched_line(source, pattern):
+ be_results = self.consume_line(match, source, args.merge)
+
+ if args.merge:
+ # sort by date/timestamp
+ sorted_list = sorted(self.consumed_logs,
+ key=lambda s: s.split(')')[0])
+ for entry in sorted_list:
+ print(entry)
+ if not resp_results and not be_results:
+ logger.warn(f"ID {cid} not found in logs!")
diff --git a/src/tools/analyzer/parser.py b/src/tools/analyzer/parser.py
new file mode 100644
index 0000000..742694e
--- /dev/null
+++ b/src/tools/analyzer/parser.py
@@ -0,0 +1,60 @@
+import argparse
+
+
+# Based on patch from https://bugs.python.org/issue9341
+class SubparsersAction(argparse._SubParsersAction):
+ """
+ Provide a subparser action that can create subparsers with ability of
+ grouping arguments.
+
+ It is based on the patch from:
+
+ - https://bugs.python.org/issue9341
+ """
+
+ class _PseudoGroup(argparse.Action):
+ def __init__(self, container, title):
+ super().__init__(option_strings=[], dest=title)
+ self.container = container
+ self._choices_actions = []
+
+ def add_parser(self, name, **kwargs):
+ # add the parser to the main Action, but move the pseudo action
+ # in the group's own list
+ parser = self.container.add_parser(name, **kwargs)
+ choice_action = self.container._choices_actions.pop()
+ self._choices_actions.append(choice_action)
+ return parser
+
+ def _get_subactions(self):
+ return self._choices_actions
+
+ def add_parser_group(self, title):
+ # the formatter can handle recursive subgroups
+ grp = SubparsersAction._PseudoGroup(self, title)
+ self._choices_actions.append(grp)
+ return grp
+
+ def add_parser_group(self, title):
+ """
+ Add new parser group.
+
+ :param title: Title.
+ :type title: str
+ :return: Parser group that can have additional parsers attached.
+ :rtype: ``argparse.Action`` extended with ``add_parser`` method
+ """
+ grp = self._PseudoGroup(self, title)
+ self._choices_actions.append(grp)
+ return grp
+
+
+class Option:
+ """
+ Group option attributes for command/subcommand options
+ """
+ def __init__(self, name, help_msg, opt_type, short_opt=None):
+ self.name = name
+ self.short_opt = short_opt
+ self.help_msg = help_msg
+ self.opt_type = opt_type
diff --git a/src/tools/analyzer/source_files.py b/src/tools/analyzer/source_files.py
new file mode 100644
index 0000000..0cadf99
--- /dev/null
+++ b/src/tools/analyzer/source_files.py
@@ -0,0 +1,76 @@
+import glob
+import logging
+
+from sssd.source_reader import Reader
+
+logger = logging.getLogger()
+
+
+class Files(Reader):
+ """
+ A class used to represent a Log Files Reader
+
+ Args:
+ path -- the path where SSSD logs are to
+ be read (default /var/log/sssd/)
+ """
+
+ def __init__(self, path):
+ super().__init__()
+ self.log_files = []
+ self.path = self.resolve_path(path)
+ self.domains = self.get_domain_logfiles()
+
+ def __iter__(self):
+ """
+ Yields:
+ str: The next line in the log file
+ """
+ for files in self.log_files:
+ try:
+ with open(files) as file:
+ for line in file:
+ yield line
+ except FileNotFoundError as err:
+ logger.warning("Could not find domain log file, skipping")
+ logger.warning(err)
+ continue
+
+ def resolve_path(self, path):
+ if path.endswith("/"):
+ return path
+ else:
+ return path + "/"
+
+ def get_domain_logfiles(self, child=False):
+ """ Retrieve list of SSSD log files, exclude rotated (.gz) files """
+ domain_files = []
+ exclude_list = ["ifp", "nss", "pam", "sudo", "autofs",
+ "ssh", "pac", "kcm", ".gz"]
+ if child:
+ file_list = glob.glob(self.path + "*.log")
+ else:
+ file_list = glob.glob(self.path + "sssd_*")
+ for file in file_list:
+ if not any(s in file for s in exclude_list):
+ domain_files.append(file)
+
+ return domain_files
+
+ def set_component(self, component, child):
+ """
+ Switch the reader to interact with a certain SSSD component
+ NSS, PAM, BE
+ """
+ self.log_files = []
+ if component == self.Component.NSS:
+ self.log_files.append(self.path + "sssd_nss.log")
+ elif component == self.Component.PAM:
+ self.log_files.append(self.path + "sssd_pam.log")
+ elif component == self.Component.BE:
+ domains = self.get_domain_logfiles(child)
+ if not domains:
+ raise IOError
+ # error: No domains found?
+ for dom in domains:
+ self.log_files.append(dom)
diff --git a/src/tools/analyzer/source_journald.py b/src/tools/analyzer/source_journald.py
new file mode 100644
index 0000000..22ec2ed
--- /dev/null
+++ b/src/tools/analyzer/source_journald.py
@@ -0,0 +1,46 @@
+from systemd import journal
+
+from sssd.source_reader import Reader
+
+_EXE_PREFIX = "/usr/libexec/sssd/"
+_NSS_MATCH = _EXE_PREFIX + "sssd_nss"
+_PAM_MATCH = _EXE_PREFIX + "sssd_pam"
+_BE_MATCH = _EXE_PREFIX + "sssd_be"
+
+
+class Journald(Reader):
+ """
+ A class used to represent a Journald Reader
+ """
+ def __init__(self):
+ super().__init__()
+ self.reader = journal.Reader()
+ self.reader.this_boot()
+ self.reader.seek_head()
+
+ def __iter__(self):
+ """
+ Yields:
+ str: The next journal entry message, with timestamp if found
+ """
+ self.reader.seek_head()
+ for entry in self.reader:
+ ts = entry['__REALTIME_TIMESTAMP']
+ msg = entry['MESSAGE']
+ if ts:
+ yield f'{ts}: {msg}'
+ else:
+ yield msg
+
+ def set_component(self, component, child):
+ """
+ Switch the reader to interact with a certain SSSD component
+ NSS, PAM, BE
+ """
+ self.reader.flush_matches()
+ if component == self.Component.NSS:
+ self.reader.add_match(_EXE=_NSS_MATCH)
+ elif component == self.Component.PAM:
+ self.reader.add_match(_EXE=_PAM_MATCH)
+ elif component == self.Component.BE:
+ self.reader.add_match(_EXE=_BE_MATCH)
diff --git a/src/tools/analyzer/source_reader.py b/src/tools/analyzer/source_reader.py
new file mode 100644
index 0000000..10d2d37
--- /dev/null
+++ b/src/tools/analyzer/source_reader.py
@@ -0,0 +1,27 @@
+from enum import Enum
+
+from abc import ABC, abstractmethod
+
+
+class Reader(ABC):
+ """
+ An abstract class used to represent a source Reader
+ """
+
+ class Component(Enum):
+ """ SSSD component to enable for reading """
+ NSS = 1 # NSS Responder
+ PAM = 2 # PAM Responder
+ BE = 3 # Backend
+
+ @abstractmethod
+ def __init__(self):
+ pass
+
+ @abstractmethod
+ def __iter__(self):
+ pass
+
+ @abstractmethod
+ def set_component(self):
+ pass
diff --git a/src/tools/analyzer/sss_analyze b/src/tools/analyzer/sss_analyze
new file mode 100755
index 0000000..3f1beaf
--- /dev/null
+++ b/src/tools/analyzer/sss_analyze
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+from sssd import sss_analyze
+
+sss_analyze.run()
diff --git a/src/tools/analyzer/sss_analyze.py b/src/tools/analyzer/sss_analyze.py
new file mode 100644
index 0000000..18b998f
--- /dev/null
+++ b/src/tools/analyzer/sss_analyze.py
@@ -0,0 +1,104 @@
+import argparse
+
+from sssd.modules import request
+from sssd.parser import SubparsersAction
+
+
+class Analyzer:
+ def add_subcommand(self, subcmd_grp, name, help_msg, func, opts):
+ """
+ Add subcommand to existing subcommand group
+
+ Args:
+ name(str): Subcommand name
+ help_msg(str): Help message for subcommand
+ func(function): Function to call on execution
+ opts(list of Object()): List of Option objects to add to subcommand
+ """
+ # Create parser
+ req_parser = subcmd_grp.add_parser(name, help=help_msg)
+
+ # Add subcommand options
+ self._add_subcommand_options(req_parser, opts)
+
+ # Execute func() when argument is called
+ req_parser.set_defaults(func=func)
+
+ def _add_subcommand_options(self, parser, opts):
+ """
+ Add subcommand options to subcommand parser
+
+ Args:
+ parser(str): Subcommand group parser
+ opts(list of Object()): List of Option objects to add to subcommand
+ """
+ for opt in opts:
+ if opt.opt_type is bool:
+ if opt.short_opt is None:
+ parser.add_argument(opt.name, help=opt.help_msg,
+ action='store_true')
+ else:
+ parser.add_argument(opt.name, opt.short_opt,
+ help=opt.help_msg, action='store_true')
+ if opt.opt_type is int:
+ parser.add_argument(opt.name, help=opt.help_msg,
+ type=int)
+
+ def load_modules(self, parser, parser_grp):
+ """
+ Initialize analyzer modules from modules/*
+
+ Args:
+ parser (ArgumentParser): Base parser object
+ parser_grp (argparse.Action): Parser group that can have
+ additional parsers attached.
+ """
+ # Currently only the 'request' module exists
+ req = request.RequestAnalyzer()
+ cli = Analyzer()
+
+ req.setup_args(parser_grp, cli)
+
+ def setup_args(self):
+ """
+ Top-level argument setup function.
+ Setup analyzer argument parsers and subcommand parser/options.
+
+ Returns:
+ parser (ArgumentParser): Base parser object
+ """
+ # top level parser
+ formatter = argparse.RawTextHelpFormatter
+ parser = argparse.ArgumentParser(description='Analyzer tool to assist '
+ 'with SSSD log parsing',
+ formatter_class=formatter)
+ parser.add_argument('--source', default='files', choices=['files',
+ 'journald'])
+ parser.add_argument('--logdir', default='/var/log/sssd/',
+ help='SSSD Log directory to parse log files from')
+
+ # Modules parser group
+ subparser = parser.add_subparsers(title=None,
+ action=SubparsersAction,
+ metavar='COMMANDS')
+ parser_grp = subparser.add_parser_group('Modules')
+
+ # Load modules, subcommands are added in module.setup_args()
+ self.load_modules(parser, parser_grp)
+
+ return parser
+
+ def main(self):
+ parser = self.setup_args()
+ args = parser.parse_args()
+
+ if not hasattr(args, 'func'):
+ parser.print_help()
+ return 0
+
+ args.func(args)
+
+
+def run():
+ analyzer = Analyzer()
+ analyzer.main()