diff --git a/CHANGELOG.md b/CHANGELOG.md
index b70143c..0c03567 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
 # Change Log
 
+## 1.1.1 - 2022-03-15
+
+### Fixed
+- Restore support for access control filenames without a group
+
+## 1.1.0 - 2022-02-24
+
+### Added
+- Started building with C++17
+- Tree-like list-devices output
+- Added CAP_AUDIT_WRITE capability to service file
+- Added support for lower OpenSSL versions prior to 1.1.0
+- Added a new signal: DevicePolicyApplied
+
+### Fixed/Changed
+- Moved PIDFile from /var/run to /run
+- Fixed linker isssues with disable-static
+- Enhanced bash-completion script
+- Make username/group checking consistent with useradd manual page definition 
+  (with addition of capital letters)
+- Fixed multiple IPC related bugs
+- Fixed race condition when accessing port/connect_type for USB devices
+- Using bundled catch v2.13.8 
+- Using bundled PEGTL v3.2.5
+- Fixed usbguard-rule-parser file opening
+- Fix unauthorized access via D-Bus [CVE-2019-25058]
+
+
 ## 1.0.0 - 2021-01-13
 
 ### Added
@@ -13,8 +41,8 @@
 - Daemon does not apply the policy when
   "change" action event appears anymore
 - IPCClientPrivate@disconnect is thread safe
-- Enforced loading of files from .d/ direcory
-  in alfabetical order
+- Enforced loading of files from .d/ directory
+  in alphabetical order
 - Improved CLI behaviour to be consistent
 - Clarified rule's label documentation
 
@@ -160,7 +188,7 @@
 
 ### Changed
 - Qt Applet: disabled session management
-- usbguard-daemon console logging output is enabled by default now.   Previously,
+- usbguard-daemon console logging output is enabled by default now. Previously,
   the -k option had to be passed to enable the output.
 - Replaced --enable-maintainer-mode configure option with --enable-full-test-suite
   option. When the new option is not used during the configure phase, only a basic
@@ -171,7 +199,7 @@
 - Reformatted source code to conform to the code style.
 - Made the configuration parser strict. Unknown directives and wrong syntax will
   cause an error.
-- Reformated documentation from markdown to asciidoc format.
+- Reformatted documentation from markdown to asciidoc format.
 
 ## 0.7.0 - 2017-04-12
 ### Added
@@ -200,7 +228,7 @@
 
 ### Removed
 - Removed UDev based device manager backend and UDev related dependencies.
-- Removed UDev development files/API dependecy
+- Removed UDev development files/API dependency
 
 ### Changed
 - Reset Linux root hub bcdDevice value before updating device hash. This is
@@ -461,4 +489,3 @@
 
 ### Fixed
 - Resolved issues: #1 #2 #5 #6 #10 #11
-
diff --git a/LICENSE b/LICENSE
index d6a9326..d159169 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
-GNU GENERAL PUBLIC LICENSE
+                    GNU GENERAL PUBLIC LICENSE
                        Version 2, June 1991
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
@@ -290,8 +290,8 @@ to attach them to the start of each source file to most effectively
 convey the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
-    {description}
-    Copyright (C) {year}  {fullname}
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
 
     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
@@ -329,7 +329,7 @@ necessary.  Here is a sample; alter the names:
   Yoyodyne, Inc., hereby disclaims all copyright interest in the program
   `Gnomovision' (which makes passes at compilers) written by James Hacker.
 
-  {signature of Ty Coon}, 1 April 1989
+  <signature of Ty Coon>, 1 April 1989
   Ty Coon, President of Vice
 
 This General Public License does not permit incorporating your program into
@@ -337,4 +337,3 @@ proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
 library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.
-
diff --git a/Makefile.am b/Makefile.am
index 402854f..f4ce03d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,6 +28,7 @@ EXTRA_DIST =\
 	VERSION \
 	CHANGELOG.md \
 	src/astylerc \
+	src/test_filesystem.cpp \
 	scripts/usbguard-zsh-completion \
 	scripts/modeline.vim \
 	scripts/astyle.sh \
@@ -51,6 +52,11 @@ $(top_builddir)/src/build-config.h: $(top_builddir)/src/build-config.h.in
 DISTCLEANFILES=\
 	$(BUILT_SOURCES)
 
+AM_DISTCHECK_CONFIGURE_FLAGS=\
+	--enable-full-test-suite \
+	--with-bundled-catch \
+	--with-bundled-pegtl
+
 CLEANFILES=
 
 man_ADOC_FILES=\
@@ -167,6 +173,7 @@ libusbguard_la_CPPFLAGS=\
 	-I$(top_srcdir)/src/Library/public \
 	-I$(top_builddir)/src/Library/IPC \
 	${BOOST_CPPFLAGS} \
+	${PTHREAD_CPPFLAGS} \
 	@qb_CFLAGS@ \
 	@protobuf_CFLAGS@ \
 	@crypto_CFLAGS@ \
@@ -185,7 +192,9 @@ libusbguard_la_LIBADD=\
 	@pegtl_LIBS@ \
 	@atomic_LIBS@ \
 	@umockdev_LIBS@ \
-	${BOOST_IOSTREAMS_LIB}
+	${BOOST_IOSTREAMS_LIB} \
+	${PTHREAD_CFLAGS} \
+	${PTHREAD_LIBS}
 
 EXTRA_DIST+=\
 	src/Library/IPC/Devices.proto \
@@ -435,7 +444,7 @@ usbguard_LDADD=\
 	$(top_builddir)/libusbguard.la \
 	${PTHREAD_LIBS}
 
-if BASH_COMPLETION_ENABLED
+if ENABLE_BASH_COMPLETION
 bashcompletiondir = $(BASH_COMPLETION_DIR)
 dist_bashcompletion_DATA = $(top_srcdir)/scripts/bash_completion/usbguard
 endif
diff --git a/Makefile.in b/Makefile.in
index 836005b..abfc039 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -177,7 +177,9 @@ am__uninstall_files_from_dir = { \
          $(am__cd) "$$dir" && rm -f $$files; }; \
   }
 LTLIBRARIES = $(lib_LTLIBRARIES)
-libusbguard_la_DEPENDENCIES =
+am__DEPENDENCIES_1 =
+libusbguard_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
 am__dirstamp = $(am__leading_dot)dirstamp
 am_libusbguard_la_OBJECTS = src/Common/libusbguard_la-Utility.lo \
 	src/Common/libusbguard_la-LDAPUtil.lo \
@@ -261,7 +263,6 @@ am_usbguard_OBJECTS = src/CLI/usbguard-usbguard.$(OBJEXT) \
 	src/CLI/usbguard-usbguard-add-user.$(OBJEXT) \
 	src/CLI/usbguard-usbguard-remove-user.$(OBJEXT)
 usbguard_OBJECTS = $(am_usbguard_OBJECTS)
-am__DEPENDENCIES_1 =
 usbguard_DEPENDENCIES = $(top_builddir)/libusbguard.la \
 	$(am__DEPENDENCIES_1)
 usbguard_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
@@ -703,6 +704,7 @@ protobuf_LIBS = @protobuf_LIBS@
 psdir = @psdir@
 qb_CFLAGS = @qb_CFLAGS@
 qb_LIBS = @qb_LIBS@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 seccomp_CFLAGS = @seccomp_CFLAGS@
 seccomp_LIBS = @seccomp_LIBS@
@@ -720,7 +722,7 @@ umockdev_LIBS = @umockdev_LIBS@
 SUBDIRS = src/Tests/
 ACLOCAL_AMFLAGS = -I m4
 EXTRA_DIST = LICENSE usbguard-daemon.conf.in usbguard.service.in \
-	VERSION CHANGELOG.md src/astylerc \
+	VERSION CHANGELOG.md src/astylerc src/test_filesystem.cpp \
 	scripts/usbguard-zsh-completion scripts/modeline.vim \
 	scripts/astyle.sh scripts/reformat-sources.sh \
 	scripts/usb-descriptor-collect.sh $(man_ADOC_FILES) \
@@ -734,6 +736,11 @@ BUILT_SOURCES = src/build-config.h $(am__append_1) \
 DISTCLEANFILES = \
 	$(BUILT_SOURCES)
 
+AM_DISTCHECK_CONFIGURE_FLAGS = \
+	--enable-full-test-suite \
+	--with-bundled-catch \
+	--with-bundled-pegtl
+
 CLEANFILES = $(man_ROFF_FILES) $(man_ROFF_FILES:.roff=) \
 	$(top_builddir)/usbguard-daemon.conf $(am__append_2) \
 	$(nodist_libusbguard_la_SOURCES) $(am__append_7)
@@ -779,6 +786,7 @@ libusbguard_la_CPPFLAGS = \
 	-I$(top_srcdir)/src/Library/public \
 	-I$(top_builddir)/src/Library/IPC \
 	${BOOST_CPPFLAGS} \
+	${PTHREAD_CPPFLAGS} \
 	@qb_CFLAGS@ \
 	@protobuf_CFLAGS@ \
 	@crypto_CFLAGS@ \
@@ -797,7 +805,9 @@ libusbguard_la_LIBADD = \
 	@pegtl_LIBS@ \
 	@atomic_LIBS@ \
 	@umockdev_LIBS@ \
-	${BOOST_IOSTREAMS_LIB}
+	${BOOST_IOSTREAMS_LIB} \
+	${PTHREAD_CFLAGS} \
+	${PTHREAD_LIBS}
 
 nodist_libusbguard_la_SOURCES = \
 	src/Library/IPC/Message.pb.cc \
@@ -1008,8 +1018,8 @@ usbguard_LDADD = \
 	$(top_builddir)/libusbguard.la \
 	${PTHREAD_LIBS}
 
-@BASH_COMPLETION_ENABLED_TRUE@bashcompletiondir = $(BASH_COMPLETION_DIR)
-@BASH_COMPLETION_ENABLED_TRUE@dist_bashcompletion_DATA = $(top_srcdir)/scripts/bash_completion/usbguard
+@ENABLE_BASH_COMPLETION_TRUE@bashcompletiondir = $(BASH_COMPLETION_DIR)
+@ENABLE_BASH_COMPLETION_TRUE@dist_bashcompletion_DATA = $(top_srcdir)/scripts/bash_completion/usbguard
 usbguard_rule_parser_SOURCES = \
 	src/CLI/usbguard-rule-parser.cpp
 
@@ -2887,6 +2897,10 @@ dist-xz: distdir
 	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
 	$(am__post_remove_distdir)
 
+dist-zstd: distdir
+	tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
+	$(am__post_remove_distdir)
+
 dist-tarZ: distdir
 	@echo WARNING: "Support for distribution archives compressed with" \
 		       "legacy program 'compress' is deprecated." >&2
@@ -2929,6 +2943,8 @@ distcheck: dist
 	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
 	*.zip*) \
 	  unzip $(distdir).zip ;;\
+	*.tar.zst*) \
+	  zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
 	esac
 	chmod -R a-w $(distdir)
 	chmod u+w $(distdir)
@@ -3311,26 +3327,26 @@ uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8
 	clean-libLTLIBRARIES clean-libtool clean-sbinPROGRAMS cscope \
 	cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \
 	dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \
-	distcheck distclean distclean-compile distclean-generic \
-	distclean-hdr distclean-libtool distclean-tags distcleancheck \
-	distdir distuninstallcheck dvi dvi-am html html-am info \
-	info-am install install-am install-binPROGRAMS install-data \
-	install-data-am install-data-hook \
-	install-dist_bashcompletionDATA install-dvi install-dvi-am \
-	install-exec install-exec-am install-html install-html-am \
-	install-info install-info-am install-libLTLIBRARIES \
-	install-man install-man1 install-man5 install-man8 install-pdf \
-	install-pdf-am install-pkgconfigDATA install-pkgincludeHEADERS \
-	install-ps install-ps-am install-sbinPROGRAMS install-strip \
-	installcheck installcheck-am installdirs installdirs-am \
-	maintainer-clean maintainer-clean-generic mostlyclean \
-	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
-	pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
-	uninstall-binPROGRAMS uninstall-dist_bashcompletionDATA \
-	uninstall-hook uninstall-libLTLIBRARIES uninstall-man \
-	uninstall-man1 uninstall-man5 uninstall-man8 \
-	uninstall-pkgconfigDATA uninstall-pkgincludeHEADERS \
-	uninstall-sbinPROGRAMS
+	dist-zstd distcheck distclean distclean-compile \
+	distclean-generic distclean-hdr distclean-libtool \
+	distclean-tags distcleancheck distdir distuninstallcheck dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-binPROGRAMS install-data install-data-am \
+	install-data-hook install-dist_bashcompletionDATA install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am \
+	install-libLTLIBRARIES install-man install-man1 install-man5 \
+	install-man8 install-pdf install-pdf-am install-pkgconfigDATA \
+	install-pkgincludeHEADERS install-ps install-ps-am \
+	install-sbinPROGRAMS install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \
+	uninstall-dist_bashcompletionDATA uninstall-hook \
+	uninstall-libLTLIBRARIES uninstall-man uninstall-man1 \
+	uninstall-man5 uninstall-man8 uninstall-pkgconfigDATA \
+	uninstall-pkgincludeHEADERS uninstall-sbinPROGRAMS
 
 .PRECIOUS: Makefile
 
diff --git a/README.adoc b/README.adoc
index ae4a6c2..624b37d 100644
--- a/README.adoc
+++ b/README.adoc
@@ -15,13 +15,7 @@ image::https://img.shields.io/github/license/USBGuard/usbguard.svg[License, link
 == About
 
 USBGuard is a software framework for implementing USB device authorization policies (what kind of USB devices are authorized) as well as method of use policies (how a USB device may interact with the system).
-Simply put, it is a USB device whitelisting tool.
-
-WARNING: The 0.x releases are not production ready packages.
-They serve for tech-preview and user feedback purposes only.
-Please share your feedback or request a feature in the Github issue trackers for each project:
-
- * https://github.com/USBGuard/usbguard/issues/new[Report a bug or request a feature in *usbguard*]
+Simply put, it is a USB device allowlisting tool.
 
 == Documentation
 
@@ -35,7 +29,16 @@ Please share your feedback or request a feature in the Github issue trackers for
 
 == Compilation & Installation
 
-To compile the sources from a release tarball, you'll need the development files for:
+WARNING: *Prior to starting the USBGuard daemon (or service) for the first time*
+         (but after installation)
+         we need to
+         generate a rules file for USBGuard so that the currently attached
+         USB devices (in particular mouse and keyboard) keep working
+         so that you will not **get locked out of your system**.
+         More on that below at <<before-the-first-start, Before the First Start>>.
+
+To compile the source code, you will require at least C{plus}{plus}17. +
+If you are compiling sources from a release tarball, you'll need the development files for:
 
  * https://github.com/ClusterLabs/libqb[libqb] - used for local UNIX socket based IPC
  * https://github.com/google/protobuf[protobuf] - used for IPC message (de)serialization
@@ -47,19 +50,61 @@ Optionally, you may want to install:
  * https://github.com/seccomp/libseccomp[libseccomp] - used to implement a syscall whitelist
  * https://people.redhat.com/sgrubb/libcap-ng/[libcap-ng] - used to drop process capabilities
 
+If you are on a Debian based GNU/Linux distribution like Ubuntu 21.10,
+installation of all build dependencies would be something like this:
+
+    $ sudo apt update && \
+      sudo apt install --no-install-recommends -V \
+        asciidoc autoconf automake bash-completion build-essential catch2 \
+        docbook-xml docbook-xsl git ldap-utils libaudit-dev libcap-ng-dev \
+        libdbus-glib-1-dev libldap-dev libpolkit-gobject-1-dev libprotobuf-dev \
+        libqb-dev libseccomp-dev libsodium-dev libtool libxml2-utils \
+        libumockdev-dev pkg-config protobuf-compiler sudo tao-pegtl-dev xsltproc
+
 And then do:
 
-    $ ./configure --with-crypto-library=sodium # or "gcrypt", based on your preference
+    $ ./configure        # for arguments of interest see below
     $ make
+    $ make check         # if you would like to run the test suite
     $ sudo make install
 
-After the sources are successfully built, you can run the test suite by executing:
+Configure arguments that deserve explicit mentioning (quoting `./configure --help` output):
+
+      --enable-systemd        install the systemd service unit file (default=no)
+
+      --with-crypto-library   Select crypto backend library. Supported values:
+                              sodium, gcrypt, openssl.
+
+      --with-bundled-catch    Build using the bundled Catch library
+
+      --with-bundled-pegtl    Build using the bundled PEGTL library
 
-    $ make check
+      --with-ldap             Build USBGuard with ldap support
 
 If you want to compile the sources in a cloned repository, you'll have to run the `./autogen.sh` script.
 It will fetch the sources (via git submodules) of https://github.com/taocpp/PEGTL/[PEGTL] and https://github.com/philsquared/Catch[Catch].
-The script will then initialize the autotools based build system.
+The script will then initialize the autotools based build system, e.g. generate the `./configure` script.
+
+== Before the First Start
+
+*Prior to starting the USBGuard daemon (or service) for the first time*
+(but after installation)
+we need to
+generate a rules file for USBGuard so that the currently attached
+USB devices (in particular mouse and keyboard) keep working
+so that you will not **get locked out of your system**.
+
+A rules file can be generated like this:
+
+    $ sudo sh -c 'usbguard generate-policy > /etc/usbguard/rules.conf'
+
+After that, you can safely start service `usbguard`:
+
+    $ sudo systemctl start usbguard.service
+
+And you can make systemd start the service every time your boot your machine:
+
+    $ sudo systemctl enable usbguard.service
 
 == License
 
diff --git a/VERSION b/VERSION
index 3eefcb9..524cb55 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.0
+1.1.1
diff --git a/aclocal.m4 b/aclocal.m4
index fa9dabb..ed66b07 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.2 -*- Autoconf -*-
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -364,7 +364,7 @@ AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
         [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
 ])dnl PKG_HAVE_DEFINE_WITH_MODULES
 
-# Copyright (C) 2002-2018 Free Software Foundation, Inc.
+# Copyright (C) 2002-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -379,7 +379,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
 [am__api_version='1.16'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.16.1], [],
+m4_if([$1], [1.16.2], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -395,14 +395,14 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.16.1])dnl
+[AM_AUTOMAKE_VERSION([1.16.2])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -454,7 +454,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -485,7 +485,7 @@ AC_CONFIG_COMMANDS_PRE(
 Usually this means the macro was only invoked conditionally.]])
 fi])])
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -676,7 +676,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
 
 # Generate code to set up dependency tracking.              -*- Autoconf -*-
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -715,7 +715,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
   done
   if test $am_rc -ne 0; then
     AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
-    for automatic dependency tracking.  Try re-running configure with the
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE="gmake" (or whatever is
+    necessary).  You can also try re-running configure with the
     '--disable-dependency-tracking' option to at least be able to build
     the package (albeit without support for automatic dependency tracking).])
   fi
@@ -742,7 +744,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -939,7 +941,7 @@ for _am_header in $config_headers :; do
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -960,7 +962,7 @@ if test x"${install_sh+set}" != xset; then
 fi
 AC_SUBST([install_sh])])
 
-# Copyright (C) 2003-2018 Free Software Foundation, Inc.
+# Copyright (C) 2003-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -981,7 +983,7 @@ AC_SUBST([am__leading_dot])])
 
 # Check to see how 'make' treats includes.	            -*- Autoconf -*-
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1024,7 +1026,7 @@ AC_SUBST([am__quote])])
 
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1063,7 +1065,7 @@ fi
 
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1092,7 +1094,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1139,7 +1141,7 @@ AC_LANG_POP([C])])
 # For backward compatibility.
 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1158,7 +1160,7 @@ AC_DEFUN([AM_RUN_LOG],
 
 # Check to make sure that the build environment is sane.    -*- Autoconf -*-
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1239,7 +1241,7 @@ AC_CONFIG_COMMANDS_PRE(
 rm -f conftest.file
 ])
 
-# Copyright (C) 2009-2018 Free Software Foundation, Inc.
+# Copyright (C) 2009-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1299,7 +1301,7 @@ AC_SUBST([AM_BACKSLASH])dnl
 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
 ])
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1327,7 +1329,7 @@ fi
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006-2018 Free Software Foundation, Inc.
+# Copyright (C) 2006-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1346,7 +1348,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004-2018 Free Software Foundation, Inc.
+# Copyright (C) 2004-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
diff --git a/config/compile b/config/compile
index 99e5052..23fcba0 100755
--- a/config/compile
+++ b/config/compile
@@ -3,7 +3,7 @@
 
 scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 # Written by Tom Tromey <tromey@cygnus.com>.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -53,7 +53,7 @@ func_file_conv ()
 	  MINGW*)
 	    file_conv=mingw
 	    ;;
-	  CYGWIN*)
+	  CYGWIN* | MSYS*)
 	    file_conv=cygwin
 	    ;;
 	  *)
@@ -67,7 +67,7 @@ func_file_conv ()
 	mingw/*)
 	  file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
 	  ;;
-	cygwin/*)
+	cygwin/* | msys/*)
 	  file=`cygpath -m "$file" || echo "$file"`
 	  ;;
 	wine/*)
diff --git a/config/depcomp b/config/depcomp
index 65cbf70..6b39162 100755
--- a/config/depcomp
+++ b/config/depcomp
@@ -3,7 +3,7 @@
 
 scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 
 # 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
diff --git a/config/install-sh b/config/install-sh
index 8175c64..20d8b2e 100755
--- a/config/install-sh
+++ b/config/install-sh
@@ -451,7 +451,18 @@ do
     trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
 
     # Copy the file name to the temp name.
-    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+    (umask $cp_umask &&
+     { test -z "$stripcmd" || {
+	 # Create $dsttmp read-write so that cp doesn't create it read-only,
+	 # which would cause strip to fail.
+	 if test -z "$doit"; then
+	   : >"$dsttmp" # No need to fork-exec 'touch'.
+	 else
+	   $doit touch "$dsttmp"
+	 fi
+       }
+     } &&
+     $doit_exec $cpprog "$src" "$dsttmp") &&
 
     # and set any options; do chmod last to preserve setuid bits.
     #
diff --git a/config/ltmain.sh b/config/ltmain.sh
index 7f3523d..8dab662 100644
--- a/config/ltmain.sh
+++ b/config/ltmain.sh
@@ -2124,7 +2124,7 @@ fi
 # a configuration failure hint, and exit.
 func_fatal_configuration ()
 {
-    func_fatal_error ${1+"$@"} \
+    func__fatal_error ${1+"$@"} \
       "See the $PACKAGE documentation for more information." \
       "Fatal configuration error."
 }
@@ -2415,17 +2415,10 @@ libtool_validate_options ()
     # preserve --debug
     test : = "$debug_cmd" || func_append preserve_args " --debug"
 
-    case $host in
-      # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
-      # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
-      *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
-        # don't eliminate duplications in $postdeps and $predeps
-        opt_duplicate_compiler_generated_deps=:
-        ;;
-      *)
-        opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
-        ;;
-    esac
+    # Keeping compiler generated duplicates in $postdeps and $predeps is not
+    # harmful, and is necessary in a majority of systems that use it to satisfy
+    # symbol dependencies.
+    opt_duplicate_compiler_generated_deps=:
 
     $opt_help || {
       # Sanity checks first:
@@ -7272,12 +7265,10 @@ func_mode_link ()
       # -tp=*                Portland pgcc target processor selection
       # --sysroot=*          for sysroot support
       # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
-      # -specs=*             GCC specs files
       # -stdlib=*            select c++ std lib with clang
       -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
       -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
-      -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
-      -specs=*)
+      -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*)
         func_quote_for_eval "$arg"
 	arg=$func_quote_for_eval_result
         func_append compile_command " $arg"
diff --git a/config/missing b/config/missing
index 625aeb1..8d0eaad 100755
--- a/config/missing
+++ b/config/missing
@@ -3,7 +3,7 @@
 
 scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
diff --git a/config/test-driver b/config/test-driver
index b8521a4..89dba1e 100755
--- a/config/test-driver
+++ b/config/test-driver
@@ -3,7 +3,7 @@
 
 scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 2011-2018 Free Software Foundation, Inc.
+# Copyright (C) 2011-2020 Free Software Foundation, Inc.
 #
 # 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
diff --git a/configure b/configure
index a5a56d5..2093b67 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for usbguard 1.0.0.
+# Generated by GNU Autoconf 2.69 for usbguard 1.1.1.
 #
 # Report bugs to <usbguard@usbguard.org>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='usbguard'
 PACKAGE_TARNAME='usbguard'
-PACKAGE_VERSION='1.0.0'
-PACKAGE_STRING='usbguard 1.0.0'
+PACKAGE_VERSION='1.1.1'
+PACKAGE_STRING='usbguard 1.1.1'
 PACKAGE_BUGREPORT='usbguard@usbguard.org'
 PACKAGE_URL=''
 
@@ -649,6 +649,8 @@ DBUS_ENABLED_FALSE
 DBUS_ENABLED_TRUE
 SYSTEMD_SUPPORT_ENABLED_FALSE
 SYSTEMD_SUPPORT_ENABLED_TRUE
+ENABLE_BASH_COMPLETION_FALSE
+ENABLE_BASH_COMPLETION_TRUE
 BASH_COMPLETION_DIR
 ANALYZE_CONFIGURE_ARGS
 SYSTEMD_UNIT_DIR
@@ -808,6 +810,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -859,6 +862,7 @@ enable_tsan
 enable_full_test_suite
 enable_debug_build
 enable_systemd
+with_bash_completion_dir
 '
       ac_precious_vars='build_alias
 host_alias
@@ -937,6 +941,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1189,6 +1194,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1326,7 +1340,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1439,7 +1453,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures usbguard 1.0.0 to adapt to many kinds of systems.
+\`configure' configures usbguard 1.1.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1479,6 +1493,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1509,7 +1524,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of usbguard 1.0.0:";;
+     short | recursive ) echo "Configuration of usbguard 1.1.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1565,6 +1580,9 @@ Optional Packages:
   --with-polkit           Install the PolicyKit configuration if D-Bus support
                           is also enabled
   --with-ldap             Build USBGuard with ldap support
+  --with-bash-completion-dir=PATH
+                          Enable bash auto-completion. Uses pkgconfig if no
+                          path given. [default=yes]
 
 Some influential environment variables:
   CC          C compiler command
@@ -1689,7 +1707,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-usbguard configure 1.0.0
+usbguard configure 1.1.1
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2609,7 +2627,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by usbguard $as_me 1.0.0, which was
+It was created by usbguard $as_me 1.1.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -3477,7 +3495,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='usbguard'
- VERSION='1.0.0'
+ VERSION='1.1.1'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -3705,27 +3723,27 @@ EXTERNAL_CFLAGS="$CFLAGS"
 EXTERNAL_CPPFLAGS="$CPPFLAGS"
 
 COMMON_WARNING_FLAGS=" -pedantic"
-COMMON_WARNING_FLAGS+=" -Wno-unknown-pragmas"
-COMMON_WARNING_FLAGS+=" -Wall"
-COMMON_WARNING_FLAGS+=" -Wextra"
-COMMON_WARNING_FLAGS+=" -Wformat=2"
-COMMON_WARNING_FLAGS+=" -Wredundant-decls"
-COMMON_WARNING_FLAGS+=" -Wcast-align"
-COMMON_WARNING_FLAGS+=" -Wmissing-declarations"
-COMMON_WARNING_FLAGS+=" -Wmissing-include-dirs"
-COMMON_WARNING_FLAGS+=" -Wmissing-format-attribute"
-COMMON_WARNING_FLAGS+=" -Wswitch-enum"
-COMMON_WARNING_FLAGS+=" -Wswitch-default"
-COMMON_WARNING_FLAGS+=" -Winvalid-pch"
-COMMON_WARNING_FLAGS+=" -Wformat-nonliteral"
-COMMON_WARNING_FLAGS+=" -Wno-deprecated-register"
-#COMMON_WARNING_FLAGS+=" -flto -Wodr"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-unknown-pragmas"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wall"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wextra"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wformat=2"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wredundant-decls"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wcast-align"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wmissing-declarations"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wmissing-include-dirs"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wmissing-format-attribute"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wswitch-enum"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wswitch-default"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Winvalid-pch"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wformat-nonliteral"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-deprecated-register"
+#COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -flto -Wodr"
 
 #
 # Workaround for older compilers warning about { }
 # not initializing all struct fields.
 #
-COMMON_WARNING_FLAGS+=" -Wno-missing-field-initializers"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-missing-field-initializers"
 
 #
 # Don't warn about implicit fallthrough
@@ -4803,7 +4821,7 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wno_implicit_fallthrough" >&5
 $as_echo "$ax_cv_check_cflags___Wno_implicit_fallthrough" >&6; }
 if test "x$ax_cv_check_cflags___Wno_implicit_fallthrough" = xyes; then :
-  COMMON_WARNING_FLAGS+=" -Wno-implicit-fallthrough"
+  COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-implicit-fallthrough"
 else
   :
 fi
@@ -4812,7 +4830,7 @@ fi
 #
 # Final project CXXFLAGS are set after configure checks.
 #
-CXXFLAGS="-std=c++11 $EXTERNAL_CXXFLAGS"
+CXXFLAGS="-std=c++17 $EXTERNAL_CXXFLAGS"
 CFLAGS="-std=c99 $EXTERNAL_CFLAGS"
 CPPFLAGS="-DHAVE_BUILD_CONFIG_H $EXTERNAL_CPPFLAGS"
 
@@ -4846,7 +4864,7 @@ LT_CURRENT=1
 # Increment if library source code changed at all.
 # Set to 0 if you increment CURRENT.
 #
-LT_REVISION=0
+LT_REVISION=1
 
 #
 # The difference between the newest and oldest interfaces
@@ -16882,7 +16900,6 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
-
 # Check if libatomic is available, might be required for emulating
 # atomic intrinsics on some platforms.
 #
@@ -16953,6 +16970,103 @@ fi
 
 
 
+# GNU (or just POSIX) basename(3) function?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for basename function" >&5
+$as_echo_n "checking for basename function... " >&6; }
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+    #define _GNU_SOURCE
+    #include <cstring>
+    int main(int argc, char ** argv) {
+        ::basename(*argv);
+        return 0;
+    }
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+    have_gnu_basename=yes
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: GNU" >&5
+$as_echo "GNU" >&6; }
+
+else
+
+    have_gnu_basename=no
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: POSIX" >&5
+$as_echo "POSIX" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test "x${have_gnu_basename}" = xyes; then :
+
+
+$as_echo "#define HAVE_GNU_BASENAME 1" >>confdefs.h
+
+
+fi
+
+# GNU or XSI strerror_r(3) function?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strerror_r function" >&5
+$as_echo_n "checking for strerror_r function... " >&6; }
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+    #undef _POSIX_C_SOURCE
+    #define _GNU_SOURCE
+    #include <cstring>
+    int main(int argc, char ** argv) {
+        char * res = ::strerror_r(0, 0, 0);
+        return 0;
+    }
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+    have_gnu_strerror_r=yes
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: GNU" >&5
+$as_echo "GNU" >&6; }
+
+else
+
+    have_gnu_strerror_r=no
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: XSI" >&5
+$as_echo "XSI" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test "x${have_gnu_strerror_r}" = xyes; then :
+
+
+$as_echo "#define HAVE_GNU_STRERROR_R 1" >>confdefs.h
+
+
+fi
+
 #
 # Checks for required libraries.
 #
@@ -18803,14 +18917,14 @@ else
 fi
 
 if test "x$with_bundled_catch" = xyes; then
-	catch_CFLAGS="-I\$(top_srcdir)/src/ThirdParty/Catch/include"
+	catch_CFLAGS="-I\$(top_srcdir)/src/ThirdParty/Catch/single_include/catch2"
 	catch_LIBS=""
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: Using bundled Catch library" >&5
 $as_echo "$as_me: Using bundled Catch library" >&6;}
 	catch_summary="bundled; $catch_CFLAGS $catch_LIBS"
 else
 	SAVE_CPPFLAGS=$CPPFLAGS
-	CPPFLAGS="-std=c++11 $CPPFLAGS -I/usr/include/catch"
+	CPPFLAGS="-std=c++17 $CPPFLAGS -I/usr/include/catch2"
 	ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
 ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -18835,7 +18949,7 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
-	catch_CFLAGS="-I/usr/include/catch"
+	catch_CFLAGS="-I/usr/include/catch2"
 	catch_LIBS=""
 	CPPFLAGS=$SAVE_CPPFLAGS
 	catch_summary="system-wide; $catch_CFLAGS $catch_LIBS"
@@ -18843,6 +18957,64 @@ fi
 
 
 
+#
+# stdc++fs (for PEGTL >=3 below, e.g. with GCC 8)
+#
+STDCXX_FS_LINKTEST_FILENAME="${srcdir}"/src/test_filesystem.cpp
+STDCXX_FS_LINKTEST_SOURCE="$(cat "${STDCXX_FS_LINKTEST_FILENAME}")"
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need to link to -lstdc++fs for PEGTL explicitly" >&5
+$as_echo_n "checking whether we need to link to -lstdc++fs for PEGTL explicitly... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+${STDCXX_FS_LINKTEST_SOURCE}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    stdcxxfs_LIBS=''
+
+else
+
+    stdcxxfs_LIBS='-lstdc++fs'
+    SAVE_LIBS=${LIBS}
+    LIBS="${LIBS} ${stdcxxfs_LIBS}"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+${STDCXX_FS_LINKTEST_SOURCE}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: ERROR" >&5
+$as_echo "ERROR" >&6; }
+        as_fn_error $? "Link test failed both with and without ${stdcxxfs_LIBS}; something is broken, please check file config.log for details." "$LINENO" 5
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LIBS=${SAVE_LIBS}
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
 #
 # PEGTL C++ library
 #
@@ -18857,14 +19029,14 @@ fi
 if test "x$with_bundled_pegtl" = xyes; then
 	pegtl_CFLAGS="-I\$(top_srcdir)/src/ThirdParty/PEGTL/include"
 	pegtl_AC_CFLAGS="-I$srcdir/src/ThirdParty/PEGTL/include"
-	pegtl_LIBS=""
+	pegtl_LIBS="${stdcxxfs_LIBS}"
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: Using bundled PEGTL library" >&5
 $as_echo "$as_me: Using bundled PEGTL library" >&6;}
 	pegtl_summary="bundled; $pegtl_CFLAGS $pegtl_LIBS"
 else
 	pegtl_CFLAGS=""
 	pegtl_AC_CFLAGS=""
-	pegtl_LIBS=""
+	pegtl_LIBS="${stdcxxfs_LIBS}"
 	pegtl_summary="system-wide; $pegtl_CFLAGS $pegtl_LIBS"
 fi
 
@@ -18872,7 +19044,7 @@ fi
 
 
 SAVE_CPPFLAGS=$CPPFLAGS
-CPPFLAGS="-std=c++11 $CPPFLAGS $pegtl_AC_CFLAGS"
+CPPFLAGS="-std=c++17 $CPPFLAGS $pegtl_AC_CFLAGS"
 ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
 ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -18924,12 +19096,12 @@ if test -n "$dbus_CFLAGS"; then
     pkg_cv_dbus_CFLAGS="$dbus_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 gio-2.0\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "dbus-1 gio-2.0") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 gio-2.0 polkit-gobject-1\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 gio-2.0 polkit-gobject-1") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_dbus_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 gio-2.0" 2>/dev/null`
+  pkg_cv_dbus_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 gio-2.0 polkit-gobject-1" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -18941,12 +19113,12 @@ if test -n "$dbus_LIBS"; then
     pkg_cv_dbus_LIBS="$dbus_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 gio-2.0\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "dbus-1 gio-2.0") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 gio-2.0 polkit-gobject-1\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 gio-2.0 polkit-gobject-1") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_dbus_LIBS=`$PKG_CONFIG --libs "dbus-1 gio-2.0" 2>/dev/null`
+  pkg_cv_dbus_LIBS=`$PKG_CONFIG --libs "dbus-1 gio-2.0 polkit-gobject-1" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -18967,9 +19139,9 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        dbus_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 gio-2.0" 2>&1`
+	        dbus_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 gio-2.0 polkit-gobject-1" 2>&1`
         else
-	        dbus_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 gio-2.0" 2>&1`
+	        dbus_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 gio-2.0 polkit-gobject-1" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$dbus_PKG_ERRORS" >&5
@@ -19375,7 +19547,6 @@ ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
 
-  ASAN_FLAGS="$ASAN_FLAGS -static-libasan"
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -faddress-sanitizer" >&5
 $as_echo_n "checking whether C++ compiler accepts -faddress-sanitizer... " >&6; }
 if ${ax_cv_check_cxxflags___faddress_sanitizer+:} false; then :
@@ -19493,6 +19664,41 @@ else
   :
 fi
 
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -static-libasan" >&5
+$as_echo_n "checking whether C++ compiler accepts -static-libasan... " >&6; }
+if ${ax_cv_check_cxxflags___static_libasan+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  ax_check_save_flags=$CXXFLAGS
+  CXXFLAGS="$CXXFLAGS  -static-libasan"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ax_cv_check_cxxflags___static_libasan=yes
+else
+  ax_cv_check_cxxflags___static_libasan=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  CXXFLAGS=$ax_check_save_flags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___static_libasan" >&5
+$as_echo "$ax_cv_check_cxxflags___static_libasan" >&6; }
+if test "x$ax_cv_check_cxxflags___static_libasan" = xyes; then :
+  ASAN_FLAGS="$ASAN_FLAGS -static-libasan"
+else
+  :
+fi
+
   ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -20703,6 +20909,15 @@ fi
 
 
 
+# Check whether --with-bash-completion-dir was given.
+if test "${with_bash_completion_dir+set}" = set; then :
+  withval=$with_bash_completion_dir;
+else
+  with_bash_completion_dir=yes
+fi
+
+
+if test "x$with_bash_completion_dir" = "xyes"; then
 
 pkg_failed=no
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BASH_COMPLETION" >&5
@@ -20762,18 +20977,20 @@ fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$BASH_COMPLETION_PKG_ERRORS" >&5
 
-	bash_completion=no
+	BASH_COMPLETION_DIR="$datadir/bash-completion/completions"
 elif test $pkg_failed = untried; then
      	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-	bash_completion=no
+	BASH_COMPLETION_DIR="$datadir/bash-completion/completions"
 else
 	BASH_COMPLETION_CFLAGS=$pkg_cv_BASH_COMPLETION_CFLAGS
 	BASH_COMPLETION_LIBS=$pkg_cv_BASH_COMPLETION_LIBS
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	bash_completion_dir="`$PKG_CONFIG --variable=completionsdir bash-completion`"
-   bash_completion=yes
+	BASH_COMPLETION_DIR=$($PKG_CONFIG --variable=completionsdir bash-completion)
+fi
+else
+	BASH_COMPLETION_DIR="$with_bash_completion_dir"
 fi
 
 if test "x$debug" = xyes; then
@@ -20828,19 +21045,14 @@ fi
 ANALYZE_CONFIGURE_ARGS=$ac_configure_args
 
 
-case "$bash_completion_dir" in
-  /usr/share/*|/usr/local/share/*)
-    bash_completion_dir=$(echo "$bash_completion_dir" | sed -r 's,^(/usr/share|/usr/local/share),${datadir},')
-    ;;
-  /usr/*|/usr/local/*)
-    bash_completion_dir=$(echo "$bash_completion_dir" | sed -r 's,^(/usr|/usr/local),${prefix},')
-    ;;
-  /*)
-    bash_completion_dir='${prefix}'"$bash_completion_dir"
-    ;;
-esac
 
-BASH_COMPLETION_DIR=$bash_completion_dir
+ if test "x$with_bash_completion_dir" != "xno"; then
+  ENABLE_BASH_COMPLETION_TRUE=
+  ENABLE_BASH_COMPLETION_FALSE='#'
+else
+  ENABLE_BASH_COMPLETION_TRUE='#'
+  ENABLE_BASH_COMPLETION_FALSE=
+fi
 
 
  if test "x$systemd" = xyes ; then
@@ -21109,6 +21321,10 @@ if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
   as_fn_error $? "conditional \"am__fastdepCXX\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${ENABLE_BASH_COMPLETION_TRUE}" && test -z "${ENABLE_BASH_COMPLETION_FALSE}"; then
+  as_fn_error $? "conditional \"ENABLE_BASH_COMPLETION\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${SYSTEMD_SUPPORT_ENABLED_TRUE}" && test -z "${SYSTEMD_SUPPORT_ENABLED_FALSE}"; then
   as_fn_error $? "conditional \"SYSTEMD_SUPPORT_ENABLED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -21534,7 +21750,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by usbguard $as_me 1.0.0, which was
+This file was extended by usbguard $as_me 1.1.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -21600,7 +21816,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-usbguard config.status 1.0.0
+usbguard config.status 1.1.1
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -22794,7 +23010,9 @@ $as_echo X/"$am_mf" |
     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
 as_fn_error $? "Something went wrong bootstrapping makefile fragments
-    for automatic dependency tracking.  Try re-running configure with the
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE=\"gmake\" (or whatever is
+    necessary).  You can also try re-running configure with the
     '--disable-dependency-tracking' option to at least be able to build
     the package (albeit without support for automatic dependency tracking).
 See \`config.log' for more details" "$LINENO" 5; }
@@ -23571,7 +23789,7 @@ echo " libseccomp: $libseccomp_summary"
 echo "  libcap-ng: $libcap_ng_summary"
 echo "   protobuf: $protobuf_summary"
 echo "      Catch: $catch_summary"
-echo "      PEGTL: $pegtl_summary; version <= 2.6.0: $have_pegtl_lte_260"
+echo "      PEGTL: $pegtl_summary"
 echo "      GDBus: $dbus_summary"
 echo "   umockdev: $umockdev_summary"
 echo
diff --git a/configure.ac b/configure.ac
index 9a337a1..0f3e79a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,27 +20,27 @@ EXTERNAL_CFLAGS="$CFLAGS"
 EXTERNAL_CPPFLAGS="$CPPFLAGS"
 
 COMMON_WARNING_FLAGS=" -pedantic"
-COMMON_WARNING_FLAGS+=" -Wno-unknown-pragmas"
-COMMON_WARNING_FLAGS+=" -Wall"
-COMMON_WARNING_FLAGS+=" -Wextra"
-COMMON_WARNING_FLAGS+=" -Wformat=2"
-COMMON_WARNING_FLAGS+=" -Wredundant-decls"
-COMMON_WARNING_FLAGS+=" -Wcast-align"
-COMMON_WARNING_FLAGS+=" -Wmissing-declarations"
-COMMON_WARNING_FLAGS+=" -Wmissing-include-dirs"
-COMMON_WARNING_FLAGS+=" -Wmissing-format-attribute"
-COMMON_WARNING_FLAGS+=" -Wswitch-enum"
-COMMON_WARNING_FLAGS+=" -Wswitch-default"
-COMMON_WARNING_FLAGS+=" -Winvalid-pch"
-COMMON_WARNING_FLAGS+=" -Wformat-nonliteral"
-COMMON_WARNING_FLAGS+=" -Wno-deprecated-register"
-#COMMON_WARNING_FLAGS+=" -flto -Wodr"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-unknown-pragmas"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wall"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wextra"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wformat=2"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wredundant-decls"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wcast-align"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wmissing-declarations"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wmissing-include-dirs"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wmissing-format-attribute"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wswitch-enum"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wswitch-default"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Winvalid-pch"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wformat-nonliteral"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-deprecated-register"
+#COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -flto -Wodr"
 
 #
 # Workaround for older compilers warning about { }
 # not initializing all struct fields.
 #
-COMMON_WARNING_FLAGS+=" -Wno-missing-field-initializers"
+COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-missing-field-initializers"
 
 #
 # Don't warn about implicit fallthrough
@@ -49,12 +49,12 @@ COMMON_WARNING_FLAGS+=" -Wno-missing-field-initializers"
 #       newer compiler versions.
 #
 AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough],
-                      [COMMON_WARNING_FLAGS+=" -Wno-implicit-fallthrough"], [], [])
+                      [COMMON_WARNING_FLAGS="${COMMON_WARNING_FLAGS} -Wno-implicit-fallthrough"], [], [])
 
 #
 # Final project CXXFLAGS are set after configure checks.
 #
-CXXFLAGS="-std=c++11 $EXTERNAL_CXXFLAGS"
+CXXFLAGS="-std=c++17 $EXTERNAL_CXXFLAGS"
 CFLAGS="-std=c99 $EXTERNAL_CFLAGS"
 CPPFLAGS="-DHAVE_BUILD_CONFIG_H $EXTERNAL_CPPFLAGS"
 
@@ -88,7 +88,7 @@ LT_CURRENT=1
 # Increment if library source code changed at all.
 # Set to 0 if you increment CURRENT.
 #
-LT_REVISION=0
+LT_REVISION=1
 
 #
 # The difference between the newest and oldest interfaces
@@ -105,7 +105,7 @@ AC_SUBST(LT_REVISION)
 AC_SUBST(LT_AGE)
 
 AC_ARG_ENABLE([coverage],
-     [AC_HELP_STRING([--enable-coverage], [enable instrumented compilation for code coverage testing (default=no)])],
+     [AS_HELP_STRING([--enable-coverage], [enable instrumented compilation for code coverage testing (default=no)])],
      [case "${enableval}" in
        yes) coverage=yes ;;
        no)  coverage=no ;;
@@ -118,7 +118,7 @@ if test "x$coverage" = xyes; then
 fi
 
 AC_ARG_ENABLE([werror],
-     [AC_HELP_STRING([--enable-werror], [treat compiler warnings as errors (default=no)])],
+     [AS_HELP_STRING([--enable-werror], [treat compiler warnings as errors (default=no)])],
      [case "${enableval}" in
        yes) werror=yes ;;
        no)  werror=no ;;
@@ -136,8 +136,7 @@ AC_PROG_CXX
 AC_PROG_CC_C99
 AC_PROG_INSTALL
 AC_PROG_MAKE_SET
-AM_PROG_LIBTOOL
-AC_PROG_LIBTOOL
+LT_INIT
 
 # Check if libatomic is available, might be required for emulating
 # atomic intrinsics on some platforms.
@@ -153,6 +152,51 @@ AC_CHECK_LIB([atomic], [__atomic_add_fetch_8], [
 ], [atomic_LIBS=""])
 AC_SUBST([atomic_LIBS])
 
+# GNU (or just POSIX) basename(3) function?
+AC_MSG_CHECKING([for basename function])
+AC_LANG_PUSH([C++])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+    #define _GNU_SOURCE
+    #include <cstring>
+    int main(int argc, char ** argv) {
+        ::basename(*argv);
+        return 0;
+    }
+])], [
+    have_gnu_basename=yes
+    AC_MSG_RESULT([GNU])
+], [
+    have_gnu_basename=no
+    AC_MSG_RESULT([POSIX])
+])
+AC_LANG_POP()
+AS_IF([test "x${have_gnu_basename}" = xyes], [
+  AC_DEFINE([HAVE_GNU_BASENAME], [1], [Wether the GNU version of function basename(3) is available (or just the POSIX one).])
+], [])
+
+# GNU or XSI strerror_r(3) function?
+AC_MSG_CHECKING([for strerror_r function])
+AC_LANG_PUSH([C++])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+    #undef _POSIX_C_SOURCE
+    #define _GNU_SOURCE
+    #include <cstring>
+    int main(int argc, char ** argv) {
+        char * res = ::strerror_r(0, 0, 0);
+        return 0;
+    }
+])], [
+    have_gnu_strerror_r=yes
+    AC_MSG_RESULT([GNU])
+], [
+    have_gnu_strerror_r=no
+    AC_MSG_RESULT([XSI])
+])
+AC_LANG_POP()
+AS_IF([test "x${have_gnu_strerror_r}" = xyes], [
+  AC_DEFINE([HAVE_GNU_STRERROR_R], [1], [Wether the GNU version of function strerror_r(3) is available (or just the XSI one).])
+], [])
+
 #
 # Checks for required libraries.
 #
@@ -344,17 +388,17 @@ fi
 #
 AC_ARG_WITH([bundled-catch], AS_HELP_STRING([--with-bundled-catch], [Build using the bundled Catch library]), [with_bundled_catch=$withval], [with_bundled_catch=no])
 if test "x$with_bundled_catch" = xyes; then
-	catch_CFLAGS="-I\$(top_srcdir)/src/ThirdParty/Catch/include"
+	catch_CFLAGS="-I\$(top_srcdir)/src/ThirdParty/Catch/single_include/catch2"
 	catch_LIBS=""
 	AC_MSG_NOTICE([Using bundled Catch library])
 	catch_summary="bundled; $catch_CFLAGS $catch_LIBS"
 else
 	SAVE_CPPFLAGS=$CPPFLAGS
-	CPPFLAGS="-std=c++11 $CPPFLAGS -I/usr/include/catch"
+	CPPFLAGS="-std=c++17 $CPPFLAGS -I/usr/include/catch2"
 	AC_LANG_PUSH([C++])
 	AC_CHECK_HEADER([catch.hpp], [], [AC_MSG_FAILURE(catch.hpp not found or not usable. Re-run with --with-bundled-catch to use the bundled library.)])
 	AC_LANG_POP
-	catch_CFLAGS="-I/usr/include/catch"
+	catch_CFLAGS="-I/usr/include/catch2"
 	catch_LIBS=""
 	CPPFLAGS=$SAVE_CPPFLAGS
 	catch_summary="system-wide; $catch_CFLAGS $catch_LIBS"
@@ -362,6 +406,30 @@ fi
 AC_SUBST([catch_CFLAGS])
 AC_SUBST([catch_LIBS])
 
+#
+# stdc++fs (for PEGTL >=3 below, e.g. with GCC 8)
+#
+STDCXX_FS_LINKTEST_FILENAME="${srcdir}"/src/test_filesystem.cpp
+STDCXX_FS_LINKTEST_SOURCE="$(cat "${STDCXX_FS_LINKTEST_FILENAME}")"
+AC_LANG_PUSH([C++])
+AC_MSG_CHECKING([whether we need to link to -lstdc++fs for PEGTL explicitly])
+AC_LINK_IFELSE([AC_LANG_SOURCE([${STDCXX_FS_LINKTEST_SOURCE}])], [
+    AC_MSG_RESULT([no])
+    stdcxxfs_LIBS=''
+], [
+    stdcxxfs_LIBS='-lstdc++fs'
+    SAVE_LIBS=${LIBS}
+    LIBS="${LIBS} ${stdcxxfs_LIBS}"
+    AC_LINK_IFELSE([AC_LANG_SOURCE([${STDCXX_FS_LINKTEST_SOURCE}])], [
+        AC_MSG_RESULT([yes])
+    ], [
+        AC_MSG_RESULT([ERROR])
+        AC_MSG_ERROR([Link test failed both with and without ${stdcxxfs_LIBS}; something is broken, please check file config.log for details.])
+    ])
+    LIBS=${SAVE_LIBS}
+])
+AC_LANG_POP()
+
 #
 # PEGTL C++ library
 #
@@ -369,13 +437,13 @@ AC_ARG_WITH([bundled-pegtl], AS_HELP_STRING([--with-bundled-pegtl], [Build using
 if test "x$with_bundled_pegtl" = xyes; then
 	pegtl_CFLAGS="-I\$(top_srcdir)/src/ThirdParty/PEGTL/include"
 	pegtl_AC_CFLAGS="-I$srcdir/src/ThirdParty/PEGTL/include"
-	pegtl_LIBS=""
+	pegtl_LIBS="${stdcxxfs_LIBS}"
 	AC_MSG_NOTICE([Using bundled PEGTL library])
 	pegtl_summary="bundled; $pegtl_CFLAGS $pegtl_LIBS"
 else
 	pegtl_CFLAGS=""
 	pegtl_AC_CFLAGS=""
-	pegtl_LIBS=""
+	pegtl_LIBS="${stdcxxfs_LIBS}"
 	pegtl_summary="system-wide; $pegtl_CFLAGS $pegtl_LIBS"
 fi
 AC_SUBST([pegtl_CFLAGS])
@@ -383,7 +451,7 @@ AC_SUBST([pegtl_AC_CFLAGS])
 AC_SUBST([pegtl_LIBS])
 
 SAVE_CPPFLAGS=$CPPFLAGS
-CPPFLAGS="-std=c++11 $CPPFLAGS $pegtl_AC_CFLAGS"
+CPPFLAGS="-std=c++17 $CPPFLAGS $pegtl_AC_CFLAGS"
 AC_LANG_PUSH([C++])
 AC_CHECK_HEADER([tao/pegtl.hpp],
 		[AC_DEFINE([HAVE_TAO_PEGTL_HPP], [1], [PEGTL header file with .hpp extension is present])],
@@ -394,12 +462,12 @@ CPPFLAGS=$SAVE_CPPFLAGS
 #
 # GDBus
 #
-AC_ARG_WITH([dbus], AC_HELP_STRING([--with-dbus], [Build the DBus Bridge service]), [], [with_dbus=yes])
+AC_ARG_WITH([dbus], AS_HELP_STRING([--with-dbus], [Build the DBus Bridge service]), [], [with_dbus=yes])
 if test "x$with_dbus" = xyes; then
   #
   # Check for required D-Bus modules
   #
-  PKG_CHECK_MODULES([dbus], [dbus-1 gio-2.0],
+  PKG_CHECK_MODULES([dbus], [dbus-1 gio-2.0 polkit-gobject-1],
   [AC_DEFINE([HAVE_DBUS], [1], [Required GDBus API available])
   dbus_summary="system-wide; $dbus_CFLAGS $dbus_LIBS"],
   [AC_MSG_FAILURE([Required D-Bus modules (dbus-1, gio-2.0) not found!])]
@@ -466,7 +534,7 @@ fi
 #
 # PolicyKit
 #
-AC_ARG_WITH([polkit], AC_HELP_STRING([--with-polkit], [Install the PolicyKit configuration if D-Bus support is also enabled]), [], [with_polkit=yes])
+AC_ARG_WITH([polkit], AS_HELP_STRING([--with-polkit], [Install the PolicyKit configuration if D-Bus support is also enabled]), [], [with_polkit=yes])
 if test "x$with_polkit" = xyes; then
   #
   # Check for required PolicyKit modules
@@ -491,7 +559,7 @@ fi
 #
 # LDAP
 #
-AC_ARG_WITH([ldap], AC_HELP_STRING([--with-ldap], [Build USBGuard with ldap support]), [], [with_ldap=no])
+AC_ARG_WITH([ldap], AS_HELP_STRING([--with-ldap], [Build USBGuard with ldap support]), [], [with_ldap=no])
 if test "x$with_ldap" = xyes; then
   #
   # Check for LDAP ldap.h
@@ -532,7 +600,6 @@ AC_ARG_ENABLE([asan],
 
 if test "x$enable_asan" = xyes; then
   AC_LANG_PUSH([C++])
-  ASAN_FLAGS="$ASAN_FLAGS -static-libasan"
   AX_CHECK_COMPILE_FLAG([-faddress-sanitizer],
                         [ASAN_FLAGS="$ASAN_FLAGS -faddress-sanitizer"],
                         [enable_asan=no], [])
@@ -551,6 +618,9 @@ if test "x$enable_asan" = xyes; then
   AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer],
                         [ASAN_FLAGS="$ASAN_FLAGS -fno-omit-frame-pointer"],
                         [], [])
+  AX_CHECK_COMPILE_FLAG([-static-libasan],
+                        [ASAN_FLAGS="$ASAN_FLAGS -static-libasan"],
+                        [], [])
   AC_LANG_POP
 fi
 #
@@ -648,7 +718,7 @@ AC_LANG_POP
 
 # ./configure script options
 AC_ARG_ENABLE([debug-build],
-     [AC_HELP_STRING([--enable-debug-build], [enable debugging flags (default=no)])],
+     [AS_HELP_STRING([--enable-debug-build], [enable debugging flags (default=no)])],
      [case "${enableval}" in
        yes) debug=yes ;;
        no)  debug=no ;;
@@ -656,18 +726,25 @@ AC_ARG_ENABLE([debug-build],
      esac], [debug=no])
 
 AC_ARG_ENABLE([systemd],
-     [AC_HELP_STRING([--enable-systemd], [install the systemd service unit file (default=no)])],
+     [AS_HELP_STRING([--enable-systemd], [install the systemd service unit file (default=no)])],
      [case "${enableval}" in
        yes) systemd=yes ;;
        no)  systemd=no ;;
        *) AC_MSG_ERROR([bad value ${enableval} for --enable-systemd]) ;;
      esac], [systemd=no])
 
+AC_ARG_WITH([bash-completion-dir],
+	AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
+		[Enable bash auto-completion. Uses pkgconfig if no path given. @<:@default=yes@:>@]),
+	[], [with_bash_completion_dir=yes])
 
-PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
-  [bash_completion_dir="`$PKG_CONFIG --variable=completionsdir bash-completion`"
-   bash_completion=yes],
-  [bash_completion=no])
+if test "x$with_bash_completion_dir" = "xyes"; then
+	PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
+		[BASH_COMPLETION_DIR=$($PKG_CONFIG --variable=completionsdir bash-completion)],
+		[BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
+else
+	BASH_COMPLETION_DIR="$with_bash_completion_dir"
+fi
 
 if test "x$debug" = xyes; then
    CXXFLAGS="$CXXFLAGS $CXXFLAGS_DEBUG_ENABLED"
@@ -706,19 +783,8 @@ fi
 
 AC_SUBST([ANALYZE_CONFIGURE_ARGS], $ac_configure_args)
 
-case "$bash_completion_dir" in
-  /usr/share/*|/usr/local/share/*)
-    bash_completion_dir=$(echo "$bash_completion_dir" | sed -r 's,^(/usr/share|/usr/local/share),${datadir},')
-    ;;
-  /usr/*|/usr/local/*)
-    bash_completion_dir=$(echo "$bash_completion_dir" | sed -r 's,^(/usr|/usr/local),${prefix},')
-    ;;
-  /*)
-    bash_completion_dir='${prefix}'"$bash_completion_dir"
-    ;;
-esac
-
-AC_SUBST([BASH_COMPLETION_DIR], $bash_completion_dir)
+AC_SUBST([BASH_COMPLETION_DIR])
+AM_CONDITIONAL([ENABLE_BASH_COMPLETION], [test "x$with_bash_completion_dir" != "xno"])
 
 AM_CONDITIONAL([SYSTEMD_SUPPORT_ENABLED], [test "x$systemd" = xyes ])
 AM_CONDITIONAL([DBUS_ENABLED], [test "x$with_dbus" = xyes ])
@@ -768,7 +834,7 @@ echo " libseccomp: $libseccomp_summary"
 echo "  libcap-ng: $libcap_ng_summary"
 echo "   protobuf: $protobuf_summary"
 echo "      Catch: $catch_summary"
-echo "      PEGTL: $pegtl_summary; version <= 2.6.0: $have_pegtl_lte_260"
+echo "      PEGTL: $pegtl_summary"
 echo "      GDBus: $dbus_summary"
 echo "   umockdev: $umockdev_summary"
 echo
diff --git a/debian/changelog b/debian/changelog
index b4a64c9..4cb9c02 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+usbguard (1.1.1-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * Drop patch 0004-Don-t-use-the-symlink-var-run-but-use-run-directly.patch,
+    present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 16 Mar 2022 03:06:47 -0000
+
 usbguard (1.0.0+ds-2) unstable; urgency=medium
 
   * Fix policy generation in postinst in chroot (Closes: #981759)
diff --git a/debian/patches/0001-Set-IPCAllowedGroups-to-root-plugdev.patch b/debian/patches/0001-Set-IPCAllowedGroups-to-root-plugdev.patch
index 34328f4..944593e 100644
--- a/debian/patches/0001-Set-IPCAllowedGroups-to-root-plugdev.patch
+++ b/debian/patches/0001-Set-IPCAllowedGroups-to-root-plugdev.patch
@@ -8,10 +8,10 @@ Forwarded: not-needed
  usbguard-daemon.conf.in | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
-diff --git a/usbguard-daemon.conf.in b/usbguard-daemon.conf.in
-index 00c5c07..6d3ee13 100644
---- a/usbguard-daemon.conf.in
-+++ b/usbguard-daemon.conf.in
+Index: usbguard/usbguard-daemon.conf.in
+===================================================================
+--- usbguard.orig/usbguard-daemon.conf.in
++++ usbguard/usbguard-daemon.conf.in
 @@ -152,7 +152,7 @@ IPCAllowedUsers=root
  #
  # IPCAllowedGroups=groupname1 groupname2 ...
diff --git a/debian/patches/0003-disable-test-that-depends-on-binary-data.patch b/debian/patches/0003-disable-test-that-depends-on-binary-data.patch
index 360b65a..c5c80f4 100644
--- a/debian/patches/0003-disable-test-that-depends-on-binary-data.patch
+++ b/debian/patches/0003-disable-test-that-depends-on-binary-data.patch
@@ -7,10 +7,10 @@ Forwarded: not-needed
  src/Tests/USB/test-descriptor-parser.sh | 1 +
  1 file changed, 1 insertion(+)
 
-diff --git a/src/Tests/USB/test-descriptor-parser.sh b/src/Tests/USB/test-descriptor-parser.sh
-index 97e7e91..fe9ae8a 100755
---- a/src/Tests/USB/test-descriptor-parser.sh
-+++ b/src/Tests/USB/test-descriptor-parser.sh
+Index: usbguard/src/Tests/USB/test-descriptor-parser.sh
+===================================================================
+--- usbguard.orig/src/Tests/USB/test-descriptor-parser.sh
++++ usbguard/src/Tests/USB/test-descriptor-parser.sh
 @@ -24,6 +24,7 @@
  # Usage: test-descriptor-parser.sh <usbguard-binary-path> <test-data-dir>
  #        test-descriptor-parser.sh
diff --git a/debian/patches/0004-Don-t-use-the-symlink-var-run-but-use-run-directly.patch b/debian/patches/0004-Don-t-use-the-symlink-var-run-but-use-run-directly.patch
deleted file mode 100644
index 2f6589c..0000000
--- a/debian/patches/0004-Don-t-use-the-symlink-var-run-but-use-run-directly.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From: Birger Schacht <birger@rantanplan.org>
-Date: Thu, 14 Jan 2021 16:30:00 +0100
-Subject: Don't use the symlink /var/run but use /run directly
-
-Forwarded: https://github.com/USBGuard/usbguard/pull/446
----
- usbguard.service.in | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/usbguard.service.in b/usbguard.service.in
-index 0d7e193..2ec8c63 100644
---- a/usbguard.service.in
-+++ b/usbguard.service.in
-@@ -12,7 +12,7 @@ IPAddressDeny=any
- LockPersonality=yes
- MemoryDenyWriteExecute=yes
- NoNewPrivileges=yes
--PIDFile=/var/run/usbguard.pid
-+PIDFile=/run/usbguard.pid
- PrivateDevices=yes
- PrivateTmp=yes
- ProtectControlGroups=yes
diff --git a/debian/patches/disable-002_cli_devices.patch b/debian/patches/disable-002_cli_devices.patch
index 59c9b1c..0d76929 100644
--- a/debian/patches/disable-002_cli_devices.patch
+++ b/debian/patches/disable-002_cli_devices.patch
@@ -8,10 +8,10 @@ don't provide.
  src/Tests/UseCase/002_cli_devices.sh | 2 ++
  1 file changed, 2 insertions(+)
 
-diff --git a/src/Tests/UseCase/002_cli_devices.sh b/src/Tests/UseCase/002_cli_devices.sh
-index d9cb275..ccb40f0 100755
---- a/src/Tests/UseCase/002_cli_devices.sh
-+++ b/src/Tests/UseCase/002_cli_devices.sh
+Index: usbguard/src/Tests/UseCase/002_cli_devices.sh
+===================================================================
+--- usbguard.orig/src/Tests/UseCase/002_cli_devices.sh
++++ usbguard/src/Tests/UseCase/002_cli_devices.sh
 @@ -22,6 +22,8 @@
  # dummy_hcd modules. This test must be run by a user that can elevate
  # their privileges using sudo.
diff --git a/debian/patches/series b/debian/patches/series
index 0485cc5..30babec 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,4 +1,3 @@
 disable-002_cli_devices.patch
 0001-Set-IPCAllowedGroups-to-root-plugdev.patch
 0003-disable-test-that-depends-on-binary-data.patch
-0004-Don-t-use-the-symlink-var-run-but-use-run-directly.patch
diff --git a/doc/man/usbguard-daemon.conf.5.adoc b/doc/man/usbguard-daemon.conf.5.adoc
index c09edb3..17e7b5c 100644
--- a/doc/man/usbguard-daemon.conf.5.adoc
+++ b/doc/man/usbguard-daemon.conf.5.adoc
@@ -162,6 +162,8 @@ Available sections and privileges:
 
  ** list: Get values of run-time parameters.
 
+ ** listen: Listen to property parameter changes.
+
 The following is a generally usable and reasonably safe example of an access control file.
 It allows one to modify USB device authorization state (`Devices=modify`), list USB devices (`Devices=list`), listen to USB device related events (`Devices=listen`), list USB authorization policy rules (`Policy=list`) and listen to exception events (`Exceptions=listen`):
 
diff --git a/doc/man/usbguard-ldap.conf.5.adoc b/doc/man/usbguard-ldap.conf.5.adoc
index 1a1daed..3ac35b3 100644
--- a/doc/man/usbguard-ldap.conf.5.adoc
+++ b/doc/man/usbguard-ldap.conf.5.adoc
@@ -19,7 +19,7 @@ Keys are case insensitive but values are not.
 == OPTIONS
 *URI* 'address'::
     The USBGuard Daemon will use this address to connect to this LDAP server.
-    
+
     `URI ldap://127.0.0.1/`
 
 *ROOTDN* 'root domain name'::
@@ -41,7 +41,7 @@ Keys are case insensitive but values are not.
     This is supposed to be domain name of the USBGuard organizational unit.
     This option helps to the daemon found the right sub-tree with rules.
     If not set the daemon will use the default (ou=USBGuard,dc=example,dc=com).
-    
+
     `USBGUARDBASE ou=USBGuard,dc=example,dc=com`
 
 *RULEQUERY* 'query string'::
@@ -51,7 +51,7 @@ Keys are case insensitive but values are not.
     If the USBGuard host is asterisk it will match but this same rule must not contain negation of matched host.
 
     `RULEQUERY (&(cn=Rule*)(|(&(USBGuardHost=host)(!(USBGuardHost=!host)))(&(USBGuardHost=\*)(!(USBGuardHost=!host)))))`
-    
+
 *UPDATEINTERVAL* 'interval'::
     This is an interval that defines periodicity of update.
     The default is one hour.
diff --git a/doc/man/usbguard-rules.conf.5.adoc b/doc/man/usbguard-rules.conf.5.adoc
index 5e8b57e..4366f69 100644
--- a/doc/man/usbguard-rules.conf.5.adoc
+++ b/doc/man/usbguard-rules.conf.5.adoc
@@ -54,7 +54,7 @@ Three types of target are recognized:
 === Device Specification
 Except the target, all the other fields of a rule are optional.
 A rule where only the `target` is specified will match any device.
-That allows the policy administator to write an explicit default target.
+That allows the policy administrator to write an explicit default target.
 If no rule from the policy is applicable to the device, an implicit target configured in usbguard-daemon.conf(5) will be used.
 However, if one wants to narrow the applicability of a rule to a set of devices or one device only, it's possible to do so with device attributes and rule conditions.
 
diff --git a/doc/man/usbguard.1.adoc b/doc/man/usbguard.1.adoc
index 39f7b3a..8984a1c 100644
--- a/doc/man/usbguard.1.adoc
+++ b/doc/man/usbguard.1.adoc
@@ -81,6 +81,9 @@ Available options:
 *-b, --blocked*::
     List blocked devices.
 
+*-t, --tree*::
+    List devices in a tree format.
+
 *-h, --help*::
     Show help.
 
@@ -282,6 +285,7 @@ The 'privileges' are expected to be in the form of a list separated by a colon:
 ....
 
 Consult the usbguard-daemon.conf(5) man-page for a detailed list of available privileges in each section.
+You can also use 'ALL' instead of 'privileges' to automatically assign all relevant privileges to a given section.
 
 
 === *remove-user* 'name' ['OPTIONS']
diff --git a/m4/libgcrypt.m4 b/m4/libgcrypt.m4
index 6cf482f..9a29eb5 100644
--- a/m4/libgcrypt.m4
+++ b/m4/libgcrypt.m4
@@ -23,7 +23,7 @@ dnl
 AC_DEFUN([AM_PATH_LIBGCRYPT],
 [ AC_REQUIRE([AC_CANONICAL_HOST])
   AC_ARG_WITH(libgcrypt-prefix,
-            AC_HELP_STRING([--with-libgcrypt-prefix=PFX],
+            AS_HELP_STRING([--with-libgcrypt-prefix=PFX],
                            [prefix where LIBGCRYPT is installed (optional)]),
      libgcrypt_config_prefix="$withval", libgcrypt_config_prefix="")
   if test x$libgcrypt_config_prefix != x ; then
diff --git a/scripts/astyle.sh b/scripts/astyle.sh
index ee2c1b9..83254ee 100755
--- a/scripts/astyle.sh
+++ b/scripts/astyle.sh
@@ -20,4 +20,4 @@
 PROJECT_ROOT="$(dirname "$0")/../"
 ASTYLERC_PATH="${PROJECT_ROOT}/src/astylerc"
 
-exec astyle $(< "${ASTYLERC_PATH}") $@
+exec astyle $(< "${ASTYLERC_PATH}") "$@"
diff --git a/scripts/bash_completion/usbguard b/scripts/bash_completion/usbguard
old mode 100755
new mode 100644
index 3b5ca67..312e912
--- a/scripts/bash_completion/usbguard
+++ b/scripts/bash_completion/usbguard
@@ -1,62 +1,387 @@
-# Completion for usbguard
-get_ids()
-{
-    local id_column=4
-    usbguard list-devices 2>/dev/null | cut -d " " -f "${id_column}"
-}
-get_rules()
-{
-    local id_column=1
-    usbguard list-rules 2>/dev/null | cut -d ":" -f "${id_column}"
-}
-_usbguard()
-{
-    local cur prev opts
-    COMPREPLY=()
-    cur="${COMP_WORDS[COMP_CWORD]}"
-    prev="${COMP_WORDS[COMP_CWORD-1]}"
-    opts="list-devices allow-device block-device reject-device list-rules append-rule remove-rule generate-policy watch read-descriptor"
-    case "${prev}" in
-        allow-device|block-device|reject-device)
-        local ids
-        ids=$( get_ids)
-	    # Maybe user running this is not allowed to list devices
-	    if [ $? -ne 0 ]; then
-            COMPREPLY=""
-            return 0
-	    fi
-            COMPREPLY=( $(compgen -W "${ids}"  "${cur}") )
-            return 0
-            ;;
+_usbguard_get_ids() {
+    usbguard list-devices 2>/dev/null | cut -d ":" -f1
+}
 
-        remove-rule)
-        local rules
-        rules=$( get_rules )
-	    # Maybe user running this is not allowed to list rules
-	    if [ $? -ne 0 ]; then
-            COMPREPLY=""
-            return 0
-	    fi
-            COMPREPLY=( $(compgen -W "${rules}"  "${cur}") )
-            return 0
-            ;;
+_usbguard_get_rules() {
+    usbguard list-rules 2>/dev/null | cut -d ":" -f1
+}
 
-        generate-policy)
-            opts=" --with-ports --no-ports-sn --target --no-hashes --hash-only --help"
-            COMPREPLY=( $(compgen -W "${opts}" "${cur}") )
-            return 0
-            ;;
+_usbguard_in_option() {
+    # Determines whether the cursor is currently on an option value (word that
+    # starts with a dash). If it is the case, return true, false otherwise.
+    #
+    # Parameters
+    #   None
+    #
+    # Returns
+    #   retval          (int)           True / False
+    #
+    if [[ "$cur" == -* ]]; then
+        return 0
+    else
+        return 1
+    fi
+}
+
+_usbguard_contains() {
+    # Copied from https://github.com/qtc-de/completion-helpers
+    #
+    # Takes two strings containing space separated words and checks if one of the
+    # words in the second list matches one in the first.
+    # E.g.: `_usbguard_contains "test test2" "no nope test"` returns true.
+    #
+    # Parameters
+    #   list_lookup     (string)        Space separated words to search in
+    #   list_search     (string)        Space separated words to search
+    #
+    # Returns
+    #   retval          (int)           Error / Success
+    #
+    # Side effects
+    #   None
+    #
+    for word in $2; do
 
-        list-device)
-            opts=" --allowed --blocked --help"
-            COMPREPLY=( $(compgen -W "${opts}" "${cur}") )
+        if [[ $1 =~ (^|[[:space:]])${word}($|[[:space:]]) ]]; then
             return 0
+        fi
+
+    done
+
+    return 1
+}
+
+_usbguard_short_to_long() {
+    # Copied from https://github.com/qtc-de/completion-helpers
+    #
+    # Takes a short option name and a a list that represents the current option list.
+    # The functions assumes that the long option for a certain short option directly
+    # follows the short option inside the argument list. The corresponding long
+    # option is then returned inside the $long variable.
+    #
+    # Parameters
+    #   short            (string)        Name of the short option (with - prefix)
+    #   opts             (list)          Space separated words (option list)
+    #
+    # Returns
+    #   retval           (int)           Error / Success
+    #
+    # Side effects
+    #   stores the corresponding long option inside the $long variable
+    #
+    local short ret
+
+    short=$1
+    shift
+
+    ret=0
+    long=
+
+    while (( "$#" )); do
+
+        if [[ "$1" == "$short" ]]; then
+            shift
+            if [[ $1 =~ ^--[^[:space:]]+$ ]]; then
+                long=$1
+                return 0
+            fi
+            return 1
+        fi
+
+        shift
+    done
+
+    return 1
+}
+
+_usbguard_filter() {
+    # Copied from https://github.com/qtc-de/completion-helpers
+    #
+    # Takes the name of a variable that contains the current option list as a string.
+    # Iterates over the current command line and removes all options that are already
+    # present.
+    #
+    # A second string of space separated words can be passed
+    # that are excluded from filtering. This is useful, when certain options are allowed
+    # to appear multiple times.
+    #
+    # By default, the function will try to remove long options if their corresponding short
+    # options are used on the command line. To work correctly, this requires that each short
+    # option has a long option that directly follows the short option in the options list. If
+    # this assumption is incorrect, you should set a third parameter for _usbguard_filter to 'false'.
+    #
+    # Parameters
+    #   opts            (string)        Space separated words to filter (call by ref)
+    #   exclude         (string)        Space separated words to exclude
+    #   remove_longs    (string)        Decides whether short-to-long translation is attempted (true|false)
+    #
+    # Returns
+    #   retval           (int)           Error / Success
+    #
+    # Side effects
+    #   filtered option list is stored in first variable (passed by ref)
+    #
+    local Opts=$1 filter exclude cur long remove_longs
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+    filter=" ${!Opts} "
+    exclude=$2
+    remove_longs=$3
+
+    # iterate over each word inside the current command line
+    for var in ${COMP_LINE}; do
+
+        # exclude the current word to allow full specified options to be space completed
+        if [[ "$var" == "$cur" ]]; then
+            continue
+
+        # exclude words from the exclusion list
+        elif _usbguard_contains "$exclude" $var; then
+            continue
+
+        # otherwise remove the word from the filter list
+        else
+            # if short option, remove long option
+            if [[ "$remove_longs" != "false" && $var =~ ^-[a-zA-Z0-9]+$ ]]; then
+                _usbguard_short_to_long $var $filter && \
+                filter=( "${filter//[[:space:]]${long}[[:space:]]/ }" )
+            fi
+
+            # remove actual option
+            filter=( "${filter//[[:space:]]${var}[[:space:]]/ }" )
+        fi
+
+    done
+
+    _upvars -v $Opts "$filter"
+}
+
+_usbguard_filter_shorts() {
+    # Copied from https://github.com/qtc-de/completion-helpers
+    #
+    # Takes a string of space separated words and removes all short options from it.
+    # Completion of short options is a matter of taste. The maintainers of bash-completion
+    # do not recommend it. Instead, completions should only handle completion for arguments
+    # that are required by short options, but to not complete short options themselves.
+    #
+    # Parameters
+    #   opts            (string)        Space separated option list (call by ref)
+    #
+    # Returns
+    #   retval           (int)           Error / Success
+    #
+    # Side effects
+    #   filtered option list is stored in first variable (passed by ref)
+    #
+    local Opts=$1 filter
+
+    filter=" ${!Opts} "
+
+    for word in $filter; do
+
+        if [[ $word =~ ^-[a-zA-Z0-9]+$ ]]; then
+            filter=( "${filter//[[:space:]]${word}[[:space:]]/ }" )
+        fi
+
+    done
+
+    _upvars -v $Opts "$filter"
+}
+
+
+_usbguard_comp_list() {
+    # This function is used for list completion (comma separated words). If an option
+    # expects a comma separated list of arguments, you can call this function with the
+    # desired set of available list arguments. The function does already populate the
+    # COMPREPLY variable and you normally want to return from the completion script
+    # after calling it.
+    #
+    # Additionally to the array of available options, it is possible to specify an
+    # integer as second argument that specifies the maximum amount of selectable
+    # options. After this amount of options was specified, space completion is enabled.
+    #
+    # Parameters
+    #   options         (string)        Space separated list of available list options
+    #   max             (int)           Maximum number of list items
+    #
+    # Returns
+    #   retval          (int)           Error / Success
+    #
+    local cur prev prev_arr options=" $1 " count=1
+
+    compopt -o nospace
+    cur="${COMP_WORDS[COMP_CWORD]}"
+
+    if [[ "$cur" != ?*,* ]]; then
+        mapfile -t COMPREPLY < <(compgen -W "${options}" -- "${cur}")
+        return 0
+
+    else
+        prev="${cur%,*}"
+        cur="${cur##*,}"
+        prev_arr=${prev//,/ }
+
+        for word in $prev_arr; do
+
+            count=$((count+1))
+            if _usbguard_contains "$options" $word; then
+                options=( "${options//[[:space:]]${word}[[:space:]]/ }" )
+                continue
+            fi
+
+        done
+
+        if [[ $count -ge $2 ]]; then
+            compopt +o nospace
+        fi
+
+        mapfile -t COMPREPLY < <(compgen -P "$prev," -W "${options}" -- "${cur}")
+        return 0
+    fi
+}
+
+_usbguard() {
+    local args cur prev words cword value_options
+
+    _init_completion || return
+    _count_args
+
+    COMPREPLY=()
+
+    # If there was no positional argument provided yet, complete commands
+    if [[ $args -eq 1 ]]; then
+        opts="get-parameter set-parameter list-devices allow-device block-device reject-device list-rules append-rule"
+        opts="${opts} remove-rule generate-policy watch read-descriptor add-user remove-user"
+
+    else
+        opts='-h --help'
+        case "${words[1]}" in
+
+            get-parameter)
+                if ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    opts="InsertedDevicePolicy ImplicitPolicyTarget"
+                fi
+                ;;
+
+            set-parameter)
+                if ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    opts="InsertedDevicePolicy ImplicitPolicyTarget"
+
+                elif ! _usbguard_in_option && [[ $args -eq 3 ]]; then
+                    return 0
+
+                else
+                    opts="$opts -v --verbose"
+                fi
+                ;;
+
+            list-devices)
+                opts="$opts -a --allowed -b --blocked -t --tree"
+                ;;
+
+            list-rules)
+                if _usbguard_contains "-l --label" $prev; then
+                    return 0
+                fi
+
+                opts="$opts -d --show-devices -l --label"
+                ;;
+
+            allow-device|block-device|reject-device)
+                if ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    opts=$(_usbguard_get_ids || :)
+                else
+                    opts="$opts -p --permanent"
+                fi
+                ;;
+
+            append-rule)
+                if _usbguard_contains "-a --after" $prev; then
+                    opts=$(_usbguard_get_rules || :)
+
+                elif ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    return 0
+
+                else
+                    opts="$opts -a --after -t --temporary"
+                fi
+                ;;
+
+            remove-rule)
+                if ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    opts=$(_usbguard_get_rules || :)
+                fi
+                ;;
+
+            generate-policy)
+                if _usbguard_contains "-d --devpath" $prev; then
+                    _filedir
+                    return 0
+
+                elif _usbguard_contains "-t --target" $prev; then
+                    opts="allow block reject"
+
+                elif _usbguard_contains "-b --usbguardbase -o --objectclass -n --name-prefix" $prev; then
+                    return 0
+
+                else
+                    opts="$opts -p --with-ports -P --no-ports-sn -d --devpath -t --target -X --no-hashes"
+                    opts="$opts -H --hash-only -L --ldif -b --usbguardbase -o --objectclass -n --name-prefix"
+                fi
+                ;;
+
+            watch)
+                if _usbguard_contains "-e --exec" $prev; then
+                    _filedir
+                    return 0
+                fi
+
+                opts="$opts -w --wait -o --once -e --exec"
+                ;;
+
+            read-descriptor)
+                if ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    _filedir
+                    return 0
+                fi
+                ;;
+
+            add-user)
+                value_options="-p --policy -d --devices -e --exceptions -P --parameters"
+                _count_args "" "@(${value_options// /|})"
+
+                if _usbguard_contains "$value_options" $prev; then
+                    _usbguard_comp_list "modify list listen" 3
+                    return 0
+
+                elif ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    _usergroup
+                    return 0
+
+                else
+                    opts="$opts -u --user -g --group $value_options"
+                fi
+                ;;
+
+            remove-user)
+                if ! _usbguard_in_option && [[ $args -eq 2 ]]; then
+                    _usergroup
+                    return 0
+                fi
+
+                opts="$opts -u --user -g --group"
+                ;;
+
+            *)
             ;;
-        *)
-        ;;
-    esac
+        esac
+    fi
 
-    COMPREPLY=( $(compgen -W "${opts}" "${cur}") )
+    _usbguard_filter "opts"
+    _usbguard_filter_shorts "opts"
+
+    mapfile -t COMPREPLY < <(compgen -W "${opts}" -- "${cur}")
     return 0
 }
+
 complete -F _usbguard usbguard
diff --git a/scripts/reformat-sources.sh b/scripts/reformat-sources.sh
index a476d51..e4e41d3 100755
--- a/scripts/reformat-sources.sh
+++ b/scripts/reformat-sources.sh
@@ -32,5 +32,6 @@ set -ex
 
 find "${PROJECT_ROOT}/src" \
 	-type f -not -path '*ThirdParty/*' \
+	-not \( -name \*.pb.h -o -name build-config.h \) \
 	\( -name '*.cpp' -or -name '*.hpp' -or -name '*.c' -or -name '*.h' \) \
 	-exec "${ASTYLE}" --preserve-date --suffix=none --lineend=linux --formatted "{}" \;
diff --git a/src/CLI/IPCSignalWatcher.cpp b/src/CLI/IPCSignalWatcher.cpp
index b658f91..88013bd 100644
--- a/src/CLI/IPCSignalWatcher.cpp
+++ b/src/CLI/IPCSignalWatcher.cpp
@@ -127,6 +127,28 @@ namespace usbguard
     }
   }
 
+  void IPCSignalWatcher::DevicePolicyApplied(uint32_t id,
+    Rule::Target target_new,
+    const std::string& device_rule,
+    uint32_t rule_id)
+  {
+    std::cout << "[device] PolicyApplied: id=" << id << std::endl;
+    std::cout << " target_new=" << Rule::targetToString(target_new) << std::endl;
+    std::cout << " device_rule=" << device_rule << std::endl;
+    std::cout << " rule_id=" << rule_id << std::endl;
+
+    if (hasOpenExecutable()) {
+      const std::map<std::string, std::string> env = {
+        { "USBGUARD_IPC_SIGNAL", "Device.PolicyApplied" },
+        { "USBGUARD_DEVICE_ID", std::to_string(id) },
+        { "USBGUARD_DEVICE_TARGET_NEW", Rule::targetToString(target_new) },
+        { "USBGUARD_DEVICE_RULE", device_rule },
+        { "USBGUARD_DEVICE_RULE_ID", std::to_string(rule_id) }
+      };
+      runExecutable(env);
+    }
+  }
+
   void IPCSignalWatcher::PropertyParameterChanged(const std::string& name,
     const std::string& value_old,
     const std::string& value_new)
diff --git a/src/CLI/IPCSignalWatcher.hpp b/src/CLI/IPCSignalWatcher.hpp
index 00dcedd..94e4a01 100644
--- a/src/CLI/IPCSignalWatcher.hpp
+++ b/src/CLI/IPCSignalWatcher.hpp
@@ -46,6 +46,11 @@ namespace usbguard
       const std::string& device_rule,
       uint32_t rule_id) override;
 
+    void DevicePolicyApplied(uint32_t id,
+      Rule::Target target_new,
+      const std::string& device_rule,
+      uint32_t rule_id) override;
+
     void PropertyParameterChanged(const std::string& name,
       const std::string& value_old,
       const std::string& value_new) override;
diff --git a/src/CLI/usbguard-apply-device-policy.cpp b/src/CLI/usbguard-apply-device-policy.cpp
index 9603b76..64cbd7d 100644
--- a/src/CLI/usbguard-apply-device-policy.cpp
+++ b/src/CLI/usbguard-apply-device-policy.cpp
@@ -43,7 +43,7 @@ namespace usbguard
   {
     std::string target_string = Rule::targetToString(target);
     stream << " Usage: " << usbguard_arg0 << " " << target_string
-        << "-device [OPTIONS] (<id> | <rule> | <partial-rule>)" << std::endl;
+      << "-device [OPTIONS] (<id> | <rule> | <partial-rule>)" << std::endl;
     stream << std::endl;
     stream << " Options:" << std::endl;
     stream << "  -p, --permanent  Make the decision permanent. A device specific " << target_string << std::endl;
@@ -54,7 +54,9 @@ namespace usbguard
 
   static bool isNumeric(const std::string& s)
   {
-    return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
+    return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) {
+      return !std::isdigit(c);
+    }) == s.end();
   }
 
   int usbguard_apply_device_policy(int argc, char** argv, Rule::Target target)
@@ -100,13 +102,14 @@ namespace usbguard
     /*
      * Interpret arguments as a rule/partial-rule
      */
-
     std::string rule_string;
-    if (argc == 1)
-        rule_string = argv[0];
+
+    if (argc == 1) {
+      rule_string = argv[0];
+    }
     else {
-        std::vector<std::string> arguments(argv, argv + argc);
-        rule_string = joinElements(arguments.begin(), arguments.end());
+      std::vector<std::string> arguments(argv, argv + argc);
+      rule_string = joinElements(arguments.begin(), arguments.end());
     }
 
     try { /* Check whether rule target has been supplied */
@@ -124,6 +127,7 @@ namespace usbguard
     for (auto device_rule : ipc.listDevices(rule_string)) {
       if (target != device_rule.getTarget()) {
         uint32_t id = device_rule.getRuleID();
+
         try {
           ipc.applyDevicePolicy(id, target, permanent);
         }
@@ -139,6 +143,7 @@ namespace usbguard
         }
       }
     }
+
     return EXIT_SUCCESS;
   }
 } /* namespace usbguard */
diff --git a/src/CLI/usbguard-list-devices.cpp b/src/CLI/usbguard-list-devices.cpp
index 54eb0d5..0125dd6 100644
--- a/src/CLI/usbguard-list-devices.cpp
+++ b/src/CLI/usbguard-list-devices.cpp
@@ -26,15 +26,18 @@
 #include "usbguard/IPCClient.hpp"
 
 #include <iostream>
+#include <map>
+#include <vector>
 
 namespace usbguard
 {
-  static const char* options_short = "hab";
+  static const char* options_short = "habt";
 
   static const struct ::option options_long[] = {
     { "help", no_argument, nullptr, 'h' },
-    { "blocked", no_argument, nullptr, 'b' },
     { "allowed", no_argument, nullptr, 'a' },
+    { "blocked", no_argument, nullptr, 'b' },
+    { "tree", no_argument, nullptr, 't' },
     { nullptr, 0, nullptr, 0 }
   };
 
@@ -45,14 +48,133 @@ namespace usbguard
     stream << " Options:" << std::endl;
     stream << "  -a, --allowed  List allowed devices." << std::endl;
     stream << "  -b, --blocked  List blocked devices." << std::endl;
+    stream << "  -t, --tree     List devices in a tree format." << std::endl;
     stream << "  -h, --help     Show this help." << std::endl;
     stream << std::endl;
   }
 
+  /**
+   * @brief Prints list of devices in a classic format
+   *
+   * @param rules Device rules to print
+   */
+  static void classicFormat(const std::vector<Rule>& rules)
+  {
+    for (const auto& rule : rules) {
+      std::cout << rule.getRuleID() << ": " << rule.toString() << std::endl;
+    }
+  }
+
+  /**
+   * @brief Recursively prints the tree nodes
+   *
+   * @param tree Rules organized into a tree structure
+   * @param node Node of a tree
+   * @param prefix Helper string used for prefixing the output
+   */
+  static void printNode(
+    const std::map<std::string, std::pair<Rule, std::vector<std::string>>>& tree,
+    const std::pair<Rule, std::vector<std::string>>& node,
+    const std::string& prefix)
+  {
+    const auto& rule = node.first;
+    const auto& children = node.second;
+
+    if (rule) {
+      std::cout << rule.getRuleID() << ": "
+        << Rule::targetToString(rule.getTarget()) << " "
+        << rule.getName() << std::endl;
+    }
+
+    if (children.empty()) {
+      return;
+    }
+
+    for (unsigned i = 0; i < children.size() - 1; ++i) {
+      std::cout << prefix << "├── ";
+      printNode(tree, tree.at(children[i]), prefix + "│   ");
+    }
+
+    std::cout << prefix << "└── ";
+    printNode(tree, tree.at(children.back()), prefix + "    ");
+  }
+
+  /**
+   * @brief Recursively prints the rule tree
+   *
+   * @param tree Rules organized into a tree structure
+   */
+  static void printTree(const std::map<std::string, std::pair<Rule, std::vector<std::string>>>& tree)
+  {
+    std::cout << "." << std::endl;
+    std::vector<std::pair<Rule, std::vector<std::string>>> roots;
+
+    for (const auto& it : tree) {
+      if (!it.second.first) {
+        roots.push_back(it.second);
+      }
+    }
+
+    for (const auto& root : roots) {
+      auto children = root.second;
+
+      for (const auto& child : children) {
+        if (root == roots.back() && child == children.back()) {
+          std::cout << "└── ";
+          printNode(tree, tree.at(child), "    ");
+        }
+        else {
+          std::cout << "├── ";
+          printNode(tree, tree.at(child), "│   ");
+        }
+      }
+    }
+  }
+
+  /**
+   * @brief Prints list of devices in a tree format
+   *
+   * Complexity O(n*log(n)), where n = rules.size
+   *
+   * @param rules Device rules to print
+   */
+  static void treeFormat(const std::vector<Rule>& rules)
+  {
+    /*
+     * key: hash
+     * value: (rule, children_hashes)
+     */
+    std::map<std::string, std::pair<Rule, std::vector<std::string>>> tree;
+
+    for (const auto& rule : rules) {
+      auto hash = rule.getHash();
+      auto p_hash = rule.getParentHash();
+      auto hash_it = tree.find(hash);
+      auto p_hash_it = tree.find(p_hash);
+
+      if (p_hash_it == tree.end()) {
+        tree.insert({p_hash, {{}, {hash}}});
+      }
+      else {
+        p_hash_it->second.second.push_back(hash);
+      }
+
+      if (hash_it == tree.end()) {
+        tree.insert({hash, {rule, {}}});
+      }
+      else {
+        hash_it->second.first = rule;
+      }
+    }
+
+    printTree(tree);
+  }
+
   int usbguard_list_devices(int argc, char* argv[])
   {
     bool list_blocked = false;
     bool list_allowed = false;
+    bool tree_format = false;
     int opt = 0;
 
     while ((opt = getopt_long(argc, argv, options_short, options_long, nullptr)) != -1) {
@@ -69,6 +191,10 @@ namespace usbguard
         list_blocked = true;
         break;
 
+      case 't':
+        tree_format = true;
+        break;
+
       case '?':
         showHelp(std::cerr);
 
@@ -77,22 +203,20 @@ namespace usbguard
       }
     }
 
-    const bool list_everything = (list_blocked == list_allowed);
     std::string query = "match";
 
-    if (!list_everything) {
-      if (list_allowed) {
-        query = "allow";
-      }
-      else {
-        query = "block";
-      }
+    if (list_blocked != list_allowed) {
+      query = list_allowed ? "allow" : "block";
     }
 
     usbguard::IPCClient ipc(/*connected=*/true);
+    auto device_rules = ipc.listDevices(query);
 
-    for (auto device_rule : ipc.listDevices(query)) {
-      std::cout << device_rule.getRuleID() << ": " << device_rule.toString() << std::endl;
+    if (tree_format) {
+      treeFormat(device_rules);
+    }
+    else {
+      classicFormat(device_rules);
     }
 
     return EXIT_SUCCESS;
diff --git a/src/CLI/usbguard-rule-parser.cpp b/src/CLI/usbguard-rule-parser.cpp
index dcaacca..7b1f00a 100644
--- a/src/CLI/usbguard-rule-parser.cpp
+++ b/src/CLI/usbguard-rule-parser.cpp
@@ -20,14 +20,24 @@
   #include <build-config.h>
 #endif
 
+#include "usbguard/Exception.hpp"
 #include "usbguard/Rule.hpp"
 #include "usbguard/RuleParser.hpp"
 
-#include <iostream>
-#ifndef _GNU_SOURCE
-  #define _GNU_SOURCE
+#ifdef HAVE_GNU_BASENAME
+  /* GNU version of basename(3) */
+  #ifndef _GNU_SOURCE
+    #define _GNU_SOURCE
+  #endif
+  #include <cstring>
+#else
+  /* POSIX version of basename(3) */
+  #include <libgen.h>
 #endif
-#include <cstring>
+
+#include <cstdlib>  // for free(3)
+#include <cstring>  // for strdup(3)
+#include <iostream>
 #include <fstream>
 
 #include <getopt.h>
@@ -43,14 +53,16 @@ static const struct ::option options_long[] = {
 
 static void showHelp(std::ostream& stream, const char* usbguard_arg0)
 {
-  stream << " Usage: " << ::basename(usbguard_arg0) << " [OPTIONS] <rule_spec>" << std::endl;
-  stream << " Usage: " << ::basename(usbguard_arg0) << " [OPTIONS] -f <file>" << std::endl;
+  char* const writeable_arg0 = ::strdup(usbguard_arg0);
+  stream << " Usage: " << ::basename(writeable_arg0) << " [OPTIONS] <rule_spec>" << std::endl;
+  stream << " Usage: " << ::basename(writeable_arg0) << " [OPTIONS] -f <file>" << std::endl;
   stream << std::endl;
   stream << " Options:" << std::endl;
   stream << "  -f, --file       Interpret the argument as a path to a file that should be parsed." << std::endl;
   stream << "  -t, --trace      Enable parser tracing." << std::endl;
   stream << "  -h, --help       Show this help." << std::endl;
   stream << std::endl;
+  ::free(writeable_arg0);
 }
 
 int main(int argc, char** argv)
@@ -93,8 +105,14 @@ int main(int argc, char** argv)
 
   try {
     if (from_file) {
-      const std::string rule_file(argv[0]);
+      const char* const rule_file_filename = argv[0];
+      const std::string rule_file(rule_file_filename);
       std::ifstream stream(rule_file);
+
+      if (! stream.is_open()) {
+        throw usbguard::ErrnoException("Rules file could not be opened", rule_file_filename, errno);
+      }
+
       size_t line = 0;
 
       while (stream.good()) {
@@ -129,6 +147,9 @@ int main(int argc, char** argv)
     std::cerr << "^-- " << ex.hint() << std::endl;
     std::cerr.width(1);
   }
+  catch (const usbguard::Exception& ex) {
+    std::cerr << "! ERROR: " << ex.message() << std::endl;
+  }
   catch (const std::exception& ex) {
     std::cerr << "! EXCEPTION: " << ex.what() << std::endl;
   }
diff --git a/src/CLI/usbguard.cpp b/src/CLI/usbguard.cpp
index a8080a8..08da644 100644
--- a/src/CLI/usbguard.cpp
+++ b/src/CLI/usbguard.cpp
@@ -26,10 +26,16 @@
 #include <map>
 #include <iostream>
 
-#ifndef _GNU_SOURCE
-  #define _GNU_SOURCE
+#ifdef HAVE_GNU_BASENAME
+  /* GNU version of basename(3) */
+  #ifndef _GNU_SOURCE
+    #define _GNU_SOURCE
+  #endif
+  #include <cstring>
+#else
+  /* POSIX version of basename(3) */
+  #include <libgen.h>
 #endif
-#include <cstring> /* GNU version of basename(3) */
 
 #include "usbguard.hpp"
 #include "usbguard-get-parameter.hpp"
diff --git a/src/Common/Utility.cpp b/src/Common/Utility.cpp
index c435535..aee50ce 100644
--- a/src/Common/Utility.cpp
+++ b/src/Common/Utility.cpp
@@ -542,6 +542,32 @@ namespace usbguard
     std::sort(rulefile_list.begin(), rulefile_list.end());
     return rulefile_list;
   }
+
+  bool isValidName(const std::string& name)
+  {
+    const char* s = name.data();
+
+    if (('\0' == *s) ||
+      !((('a' <= *s) && ('z' >= *s)) ||
+        (('A' <= *s) && ('Z' >= *s)) ||
+        ('_' == *s))) {
+      return false;
+    }
+
+    while ('\0' != *++s) {
+      if (!((('a' <= *s) && ('z' >= *s)) ||
+          (('A' <= *s) && ('Z' >= *s)) ||
+          (('0' <= *s) && ('9' >= *s)) ||
+          ('_' == *s) ||
+          ('-' == *s) ||
+          (('$' == *s) && ('\0' == *(s + 1))))) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
 } /* namespace usbguard */
 
 /* vim: set ts=2 sw=2 et */
diff --git a/src/Common/Utility.hpp b/src/Common/Utility.hpp
index 4e90364..d49e24d 100644
--- a/src/Common/Utility.hpp
+++ b/src/Common/Utility.hpp
@@ -161,7 +161,7 @@ namespace usbguard
   std::string parentPath(const std::string& path);
 
   /**
-   * Retrun the number of path components.
+   * Return the number of path components.
    */
   std::size_t countPathComponents(const std::string& path);
 
@@ -316,6 +316,15 @@ namespace usbguard
     return ss.str();
   }
 
+  /**
+   * @brief Checks whether a given name is a valid group/user name
+   *
+   * User/group names must match [A-Za-z_][A-Za-z0-9_-]*[$]
+   *
+   * @param name Name to check
+   * @return True if given name is valid, false otherwise
+   */
+  bool isValidName(const std::string& name);
 
 } /* namespace usbguard */
 
diff --git a/src/DBus/DBusBridge.cpp b/src/DBus/DBusBridge.cpp
index 76471e7..891ed09 100644
--- a/src/DBus/DBusBridge.cpp
+++ b/src/DBus/DBusBridge.cpp
@@ -15,12 +15,15 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 // Authors: Daniel Kopecek <dkopecek@redhat.com>
+// Authors: Sebastian Pipping <sebastian@pipping.org>
 //
 #ifdef HAVE_BUILD_CONFIG_H
   #include <build-config.h>
 #endif
 
 #include "DBusBridge.hpp"
+#include <polkit/polkit.h>
+
 namespace usbguard
 {
   DBusBridge::DBusBridge(GDBusConnection* const gdbus_connection,
@@ -77,6 +80,10 @@ namespace usbguard
   void DBusBridge::handleRootMethodCall(const std::string& method_name, GVariant* parameters, GDBusMethodInvocation* invocation)
   {
     if (method_name == "getParameter") {
+      if (! isAuthorizedByPolkit(invocation)) {
+        return;
+      }
+
       const char* name_cstr = nullptr;
       g_variant_get(parameters, "(&s)", &name_cstr);
       std::string name(name_cstr);
@@ -86,6 +93,10 @@ namespace usbguard
     }
 
     if (method_name == "setParameter") {
+      if (! isAuthorizedByPolkit(invocation)) {
+        return;
+      }
+
       const char* name_cstr = nullptr;
       const char* value_cstr = nullptr;
       g_variant_get(parameters, "(&s&s)", &name_cstr, &value_cstr);
@@ -104,6 +115,10 @@ namespace usbguard
   void DBusBridge::handlePolicyMethodCall(const std::string& method_name, GVariant* parameters, GDBusMethodInvocation* invocation)
   {
     if (method_name == "listRules") {
+      if (! isAuthorizedByPolkit(invocation)) {
+        return;
+      }
+
       const char* label_cstr = nullptr;
       g_variant_get(parameters, "(&s)", &label_cstr);
       std::string label(label_cstr);
@@ -136,6 +151,10 @@ namespace usbguard
     }
 
     if (method_name == "appendRule") {
+      if (! isAuthorizedByPolkit(invocation)) {
+        return;
+      }
+
       const char* rule_spec_cstr = nullptr;
       uint32_t parent_id = 0;
       gboolean temporary = false;
@@ -147,6 +166,10 @@ namespace usbguard
     }
 
     if (method_name == "removeRule") {
+      if (! isAuthorizedByPolkit(invocation)) {
+        return;
+      }
+
       uint32_t rule_id = 0;
       g_variant_get(parameters, "(u)", &rule_id);
       removeRule(rule_id);
@@ -162,7 +185,13 @@ namespace usbguard
   void DBusBridge::handleDevicesMethodCall(const std::string& method_name, GVariant* parameters,
     GDBusMethodInvocation* invocation)
   {
+    USBGUARD_LOG(Debug) << "dbus devices method call: " << method_name;
+
     if (method_name == "listDevices") {
+      if (! isAuthorizedByPolkit(invocation)) {
+        return;
+      }
+
       const char* query_cstr = nullptr;
       g_variant_get(parameters, "(&s)", &query_cstr);
       std::string query(query_cstr);
@@ -195,10 +224,16 @@ namespace usbguard
     }
 
     if (method_name == "applyDevicePolicy") {
+      if (! isAuthorizedByPolkit(invocation)) {
+        return;
+      }
+
       uint32_t device_id = 0;
       uint32_t target_integer = 0;
       gboolean permanent = false;
       g_variant_get(parameters, "(uub)", &device_id, &target_integer, &permanent);
+      USBGUARD_LOG(Debug) << "DBus: applyDevicePolicy: Parsed device_id: " << device_id << " target_integer: " << target_integer <<
+        " and permanent: " << permanent;
       const Rule::Target target = Rule::targetFromInteger(target_integer);
       const uint32_t rule_id = applyDevicePolicy(device_id, target, permanent);
       g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", rule_id));
@@ -270,6 +305,27 @@ namespace usbguard
     }
   }
 
+  void DBusBridge::DevicePolicyApplied(uint32_t id,
+    Rule::Target target_new,
+    const std::string& device_rule,
+    uint32_t rule_id)
+  {
+    GVariantBuilder* gv_builder_attributes = deviceRuleToAttributes(device_rule);
+    g_dbus_connection_emit_signal(p_gdbus_connection, nullptr,
+      DBUS_DEVICES_PATH, DBUS_DEVICES_INTERFACE, "DevicePolicyApplied",
+      g_variant_new("(uusua{ss})",
+        id,
+        Rule::targetToInteger(target_new),
+        device_rule.c_str(),
+        rule_id,
+        gv_builder_attributes),
+      nullptr);
+
+    if (gv_builder_attributes != nullptr) {
+      g_variant_builder_unref(gv_builder_attributes);
+    }
+  }
+
   void DBusBridge::PropertyParameterChanged(const std::string& name,
     const std::string& value_old,
     const std::string& value_new)
@@ -337,13 +393,118 @@ namespace usbguard
     g_variant_builder_add(builder, "{ss}",
       "with-interface",
       with_interface_string.c_str());
-
     g_variant_builder_add(builder, "{ss}",
       "with-connect-type",
       device_rule.getWithConnectType().c_str());
-
     return builder;
   }
+
+  std::string DBusBridge::formatGError(GError* error)
+  {
+    if (error) {
+      std::stringstream formatGError;
+      formatGError << error->message << " (code " << error->code << ")";
+      return formatGError.str();
+    }
+    else {
+      return "unknown error";
+    }
+  }
+
+  bool DBusBridge::isAuthorizedByPolkit(GDBusMethodInvocation* invocation)
+  {
+    GError* error = NULL;
+    USBGUARD_LOG(Trace) << "Extracting bus name...";
+    const gchar* const /*no-free!*/ bus_name = g_dbus_method_invocation_get_sender (invocation);
+
+    if (! bus_name) {
+      USBGUARD_LOG(Trace) << "Failed to extract bus name.";
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Extracted bus name \"" << bus_name << "\".";
+    USBGUARD_LOG(Trace) << "Extracting interface name...";
+    const gchar* const /*no-free!*/ interfaceName = g_dbus_method_invocation_get_interface_name(invocation);
+
+    if (! interfaceName) {
+      USBGUARD_LOG(Trace) << "Failed to extract interface name.";
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Extracted interface name \"" << interfaceName << "\".";
+    USBGUARD_LOG(Trace) << "Extracting method name...";
+    const gchar* const /*no-free!*/ methodName = g_dbus_method_invocation_get_method_name(invocation);
+
+    if (! methodName) {
+      USBGUARD_LOG(Trace) << "Failed to extract method name.";
+      return false;
+    }
+
+    std::stringstream action_id;
+    action_id << interfaceName << "." << methodName;
+    USBGUARD_LOG(Trace) << "Extracted method name \"" << methodName << "\".";
+    USBGUARD_LOG(Trace) << "Creating a system bus Polkit subject...";
+    PolkitSubject* const subject = polkit_system_bus_name_new(bus_name);
+
+    if (! subject) {
+      USBGUARD_LOG(Trace) << "Failed to create Polkit subject.";
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Created.";
+    USBGUARD_LOG(Trace) << "Connecting with Polkit authority...";
+    PolkitAuthority* const authority = polkit_authority_get_sync(/*cancellable=*/ NULL, &error);
+
+    if (! authority || error) {
+      USBGUARD_LOG(Trace) << "Failed to connect to Polkit authority: " << formatGError(error) << ".";
+      g_error_free(error);
+      g_object_unref(authority);
+      g_object_unref(subject);
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Connected.";
+    USBGUARD_LOG(Trace) << "Customizing Polkit authentication dialog...";
+    PolkitDetails* const details = polkit_details_new();
+
+    if (! details) {
+      USBGUARD_LOG(Trace) << "Failed to customize the Polkit authentication dialog.";
+      g_object_unref(authority);
+      g_object_unref(subject);
+      return false;
+    }
+
+    polkit_details_insert (details, "polkit.message", "This USBGuard action needs authorization");
+    USBGUARD_LOG(Trace) << "Customized.";
+    USBGUARD_LOG(Trace) << "Checking authorization of action \"" << action_id.str() << "\" with Polkit ...";
+    const PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
+    PolkitAuthorizationResult* const result = polkit_authority_check_authorization_sync
+      (authority,
+        subject,
+        action_id.str().c_str(),
+        details,
+        flags,
+        /*cancellable=*/ NULL,
+        &error);
+
+    if (! result || error) {
+      USBGUARD_LOG(Trace) << "Failed to check back with Polkit for authoriation: " << formatGError(error) << ".";
+      g_error_free(error);
+      g_object_unref(result);
+      g_object_unref(details);
+      g_object_unref(authority);
+      g_object_unref(subject);
+      return false;
+    }
+
+    gboolean isAuthorized = polkit_authorization_result_get_is_authorized(result);
+    USBGUARD_LOG(Trace) << (isAuthorized ? "Authorized" : "Not authorized") << ".";
+    g_object_unref(result);
+    g_object_unref(details);
+    g_object_unref(authority);
+    g_object_unref(subject);
+    return isAuthorized;
+  }
 } /* namespace usbguard */
 
 /* vim: set ts=2 sw=2 et */
diff --git a/src/DBus/DBusBridge.hpp b/src/DBus/DBusBridge.hpp
index cd370ba..e1eb472 100644
--- a/src/DBus/DBusBridge.hpp
+++ b/src/DBus/DBusBridge.hpp
@@ -63,6 +63,11 @@ namespace usbguard
       const std::string& device_rule,
       uint32_t rule_id) override;
 
+    void DevicePolicyApplied(uint32_t id,
+      Rule::Target target_new,
+      const std::string& device_rule,
+      uint32_t rule_id) override;
+
     void PropertyParameterChanged(const std::string& name,
       const std::string& value_old,
       const std::string& value_new) override;
@@ -83,6 +88,8 @@ namespace usbguard
       bool rule_match,
       uint32_t rule_id);
 
+    static std::string formatGError(GError* error);
+    static bool isAuthorizedByPolkit(GDBusMethodInvocation* invocation);
 
     GDBusConnection* const p_gdbus_connection;
     void(*p_ipc_callback)(bool);
diff --git a/src/DBus/DBusInterface.xml b/src/DBus/DBusInterface.xml
index 3b78fe9..96999bd 100644
--- a/src/DBus/DBusInterface.xml
+++ b/src/DBus/DBusInterface.xml
@@ -152,8 +152,8 @@
       If the permanent flag is set to True, a rule will be appended to the policy or an exiting device
       rule will be modified in order to permanently store the authorization decision.
 
-      Sucessfull exection of this method will cause the DevicePolicyChanged signal to be broadcasted if
-      the device authorization target was different than the applied target.
+      Successful execution of this method will cause the DevicePolicyChanged signal to be broadcasted if
+      the device authorization target was different from the applied target.
       -->
     <method name="applyDevicePolicy">
       <arg name="id" direction="in" type="u"/>
@@ -205,6 +205,7 @@
       DevicePolicyChanged:
        @id: Device id of the device
        @target_old: Previous authorization target in numerical form.
+                    0 = Allow. 1 = Block. 2 = Reject.
        @target_new: Current authorization target in numerical form.
        @device_rule: Device specific rule.
        @rule_id: A rule id of the matched rule. Otherwise a reserved rule id value is used.
@@ -234,6 +235,41 @@
       <arg name="rule_id" direction="out" type="u"/>
       <arg name="attributes" direction="out" type="a{ss}"/>
     </signal>
+
+    <!--
+      DevicePolicyApplied:
+       @id: Device id of the device
+       @target_new: Current authorization target in numerical form.
+                    0 = Allow. 1 = Block. 2 = Reject.
+       @device_rule: Device specific rule.
+       @rule_id: A rule id of the matched rule. Otherwise a reserved rule id value is used.
+                 Reserved values are:
+                     4294967294 (UINT32_MAX - 1) for an implicit rule, e.g. 
+                     ImplicitPolicyTarget or InsertedDevicePolicy.
+       @attributes: A dictionary of device attributes and their values.
+
+      Notify about a change of a USB device.
+      This is a superset of DevicePolicyChanged and will always be thrown
+      when a device is inserted, authorised, or rejected.
+
+      The device attribute dictionary contains the following attributes:
+        - id (the USB device ID in the form VID:PID)
+        - name
+        - serial
+        - via-port
+        - hash
+        - parent-hash
+        - with-interface
+        - with-connect-type (either "hardwired", "hotplug", or the empty string for unknown)
+
+     -->
+    <signal name="DevicePolicyApplied">
+      <arg name="id" direction="out" type="u"/>
+      <arg name="target_new" direction="out" type="u"/>
+      <arg name="device_rule" direction="out" type="s"/>
+      <arg name="rule_id" direction="out" type="u"/>
+      <arg name="attributes" direction="out" type="a{ss}"/>
+    </signal>
   </interface>
 </node>
 
diff --git a/src/DBus/gdbus-server.cpp b/src/DBus/gdbus-server.cpp
index ccdf62a..33620ba 100644
--- a/src/DBus/gdbus-server.cpp
+++ b/src/DBus/gdbus-server.cpp
@@ -20,7 +20,19 @@
   #include <build-config.h>
 #endif
 
-#include <stdlib.h>
+#ifdef HAVE_GNU_BASENAME
+  /* GNU version of basename(3) */
+  #ifndef _GNU_SOURCE
+    #define _GNU_SOURCE
+  #endif
+  #include <cstring>
+#else
+  /* POSIX version of basename(3) */
+  #include <libgen.h>
+#endif
+
+#include <cstdlib>  // e.g. for free(3)
+#include <cstring>  // e.g. for strdup(3)
 #include <iostream>
 #include <getopt.h>
 #include "DBusBridge.hpp"
@@ -208,13 +220,15 @@ static const struct ::option options_long[] = {
 
 static void showHelp(std::ostream& stream)
 {
-  stream << " Usage: " << ::basename(usbguard_arg0) << " [OPTIONS]" << std::endl;
+  char* const writeable_arg0 = ::strdup(usbguard_arg0);
+  stream << " Usage: " << ::basename(writeable_arg0) << " [OPTIONS]" << std::endl;
   stream << std::endl;
   stream << " Options:" << std::endl;
   stream << "  -s, --system   Listen on the system bus." << std::endl;
   stream << "  -S, --session  Listen on the session bus." << std::endl;
   stream << "  -h, --help     Show this help." << std::endl;
   stream << std::endl;
+  ::free(writeable_arg0);
 }
 
 int
diff --git a/src/DBus/org.usbguard1.policy b/src/DBus/org.usbguard1.policy
index ce84239..2b0cebf 100644
--- a/src/DBus/org.usbguard1.policy
+++ b/src/DBus/org.usbguard1.policy
@@ -1,23 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
         "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
-        
+
 <policyconfig>
   <vendor>The USBGuard Project</vendor>
   <vendor_url>https://github.org/USBGuard/usbguard</vendor_url>
 
   <action id="org.usbguard.Policy1.listRules">
     <description>List the rule set (policy) used by the USBGuard daemon</description>
-    <message>Prevents from listing the USBGuard policy</message>
+    <message>Prevents listing the USBGuard policy</message>
     <defaults>
       <allow_inactive>no</allow_inactive>
-      <allow_active>auth_self_keep_session</allow_active>
+      <allow_active>auth_self_keep</allow_active>
     </defaults>
   </action>
 
   <action id="org.usbguard.Policy1.appendRule">
     <description>Append a new rule to the policy</description>
-    <message>Prevents from appending rules to the USBGuard policy</message>
+    <message>Prevents appending rules to the USBGuard policy</message>
     <defaults>
       <allow_inactive>no</allow_inactive>
       <allow_active>auth_admin</allow_active>
@@ -33,36 +33,36 @@
     </defaults>
   </action>
 
-  <action id="org.usbguard.Devices1.listDevices">
-    <description>List all USB devices recognized by the USBGuard daemon</description>
-    <message>Prevents from listing USB devices recognized by the USBGuard daemon</message>
+  <action id="org.usbguard.Devices1.applyDevicePolicy">
+    <description>Apply a policy to a device in USBGuard</description>
+    <message>Prevents applying a policy to a device in USBGuard</message>
     <defaults>
       <allow_inactive>no</allow_inactive>
-      <allow_active>auth_self_keep_session</allow_active>
+      <allow_active>auth_admin</allow_active>
     </defaults>
   </action>
 
-  <action id="org.usbguard.Devices1.allowDevice">
-    <description>Authorize a USB device via the USBGuard daemon to interact with the system</description>
-    <message>Prevents from authorizing USB devices via the USBGuard daemon</message>
+  <action id="org.usbguard.Devices1.listDevices">
+    <description>List all USB devices recognized by the USBGuard daemon</description>
+    <message>Prevents listing USB devices recognized by the USBGuard daemon</message>
     <defaults>
       <allow_inactive>no</allow_inactive>
-      <allow_active>auth_admin</allow_active>
+      <allow_active>auth_self_keep</allow_active>
     </defaults>
   </action>
 
-  <action id="org.usbguard.Devices1.blockDevice">
-    <description>Deauthorize a USB device via the USBGuard daemon</description>
-    <message>Prevents from deauthorizing USB devices via the USBGuard daemon</message>
+  <action id="org.usbguard1.getParameter">
+    <description>Get the value of a runtime parameter</description>
+    <message>Prevents getting values of runtime USBGuard parameters</message>
     <defaults>
       <allow_inactive>no</allow_inactive>
-      <allow_active>auth_admin</allow_active>
+      <allow_active>auth_self_keep</allow_active>
     </defaults>
   </action>
 
-  <action id="org.usbguard.Devices1.rejectDevice">
-    <description>Remove a USB device via the USBGuard daemon</description>
-    <message>Prevents from removing USB devices via the USBGuard daemon</message>
+  <action id="org.usbguard1.setParameter">
+    <description>Set the value of a runtime parameter</description>
+    <message>Prevents setting values of runtime USBGuard parameters</message>
     <defaults>
       <allow_inactive>no</allow_inactive>
       <allow_active>auth_admin</allow_active>
diff --git a/src/Daemon/Daemon.cpp b/src/Daemon/Daemon.cpp
index 9dd5fbc..4ec2d93 100644
--- a/src/Daemon/Daemon.cpp
+++ b/src/Daemon/Daemon.cpp
@@ -411,6 +411,9 @@ namespace usbguard
     catch (const RuleParserError& ex) {
       throw Exception("Rules", _nss.getSourceInfo(), ex.hint());
     }
+    catch (const Exception& ex) {
+      throw ex;
+    }
     catch (const std::exception& ex) {
       throw Exception("Rules", _nss.getSourceInfo(), ex.what());
     }
@@ -443,12 +446,25 @@ namespace usbguard
   void Daemon::parseIPCAccessControlFilename(const std::string& basename, std::string* const ptr_user,
     std::string* const ptr_group)
   {
+    // There are five supported forms:
+    // - "<user>:<group>"
+    // - "<user>:"
+    // - "<user>"
+    // - ":<group>"
+    // - ":"
     const auto ug_separator = basename.find_first_of(":");
     const bool has_group = ug_separator != std::string::npos;
     const std::string user = basename.substr(0, ug_separator);
     const std::string group = has_group ? basename.substr(ug_separator + 1) : std::string();
-    checkIPCAccessControlName(user);
-    checkIPCAccessControlName(group);
+
+    if (! user.empty()) {
+      checkIPCAccessControlName(user);
+    }
+
+    if (! group.empty()) {
+      checkIPCAccessControlName(group);
+    }
+
     *ptr_user = user;
     *ptr_group = group;
   }
@@ -849,6 +865,9 @@ namespace usbguard
       _dm->applyDevicePolicy(device->getID(),
         matched_rule->getTarget());
     const bool target_changed = target_old != device_post->getTarget();
+    std::shared_ptr<const Rule> device_rule = \
+      device_post->getDeviceRule(/*with_port=*/true,
+        /*with_parent_hash=*/true);
 
     if (target_changed || matched_rule->getRuleID() == Rule::ImplicitID) {
       if (target_changed) {
@@ -860,9 +879,6 @@ namespace usbguard
         USBGUARD_LOG(Debug) << "Implicit rule matched";
       }
 
-      std::shared_ptr<const Rule> device_rule = \
-        device_post->getDeviceRule(/*with_port=*/true,
-          /*with_parent_hash=*/true);
       DevicePolicyChanged(device->getID(),
         target_old,
         device_post->getTarget(),
@@ -870,6 +886,10 @@ namespace usbguard
         matched_rule->getRuleID());
     }
 
+    DevicePolicyApplied(device->getID(),
+      device_post->getTarget(),
+      device_rule->toString(),
+      matched_rule->getRuleID());
     matched_rule->updateMetaDataCounters(/*applied=*/true);
     audit_event.success();
   }
diff --git a/src/Daemon/NSHandler.cpp b/src/Daemon/NSHandler.cpp
index 9cd0845..92783e9 100644
--- a/src/Daemon/NSHandler.cpp
+++ b/src/Daemon/NSHandler.cpp
@@ -79,7 +79,7 @@ namespace usbguard
         ret = "SourceLOCAL::" + _rulesPath;
       }
       else {
-        ret = "SourceLOCAL::uknown";
+        ret = "SourceLOCAL::unknown";
       }
 
       break;
@@ -154,7 +154,10 @@ namespace usbguard
     std::ifstream nss(_nsswitch_path);
 
     if (!nss.is_open()) {
-      throw ErrnoException("NSSwitch parsing", _nsswitch_path, errno);
+      USBGUARD_LOG(Info) << "Error when opening nsswitch file: " << _nsswitch_path << ": " << ErrnoException::reasonFromErrno(errno);
+      USBGUARD_LOG(Info) << "Using default value FILES";
+      _source = SourceType::LOCAL;
+      return;
     }
 
     _parser.parseStream(nss);
diff --git a/src/Daemon/RuleSetFactory.cpp b/src/Daemon/RuleSetFactory.cpp
index 32cc215..8d96417 100644
--- a/src/Daemon/RuleSetFactory.cpp
+++ b/src/Daemon/RuleSetFactory.cpp
@@ -75,7 +75,7 @@ namespace usbguard
         }
       }
 
-      if (ruleSet.empty()){
+      if (ruleSet.empty()) {
         USBGUARD_LOG(Warning) << "RuleFile not set; Modification of the permanent policy won't be possible.";
         ruleSet = generateDefaultRuleSet();
       }
diff --git a/src/Daemon/main.cpp b/src/Daemon/main.cpp
index 8ba9fd9..616af0d 100644
--- a/src/Daemon/main.cpp
+++ b/src/Daemon/main.cpp
@@ -161,15 +161,24 @@ int main(int argc, char* argv[])
 
   /* Setup seccomp allowlist & drop capabilities */
   if (use_seccomp_allowlist) {
+#if defined(HAVE_SECCOMP)
+
     if (!setupSeccompWhitelist()) {
+      USBGUARD_LOG(Error) << "Unable to setup seccomp";
       return EXIT_FAILURE;
     }
+
+#else
+    USBGUARD_LOG(Error) << "USBGuard was not compiled with libseccomp support";
+    return EXIT_FAILURE;
+#endif
   }
 
   if (drop_capabilities) {
 #if defined(HAVE_LIBCAPNG)
     setupCapabilities();
 #else
+    USBGUARD_LOG(Error) << "USBGuard was not compiled with libcap-ng support";
     return EXIT_FAILURE;
 #endif
   }
@@ -203,7 +212,7 @@ int main(int argc, char* argv[])
     USBGUARD_LOG(Error) << ex.what();
   }
   catch (...) {
-    USBGUARD_LOG(Error) << "Unknown excepton caught while starting the process";
+    USBGUARD_LOG(Error) << "Unknown exception caught while starting the process";
   }
 
   return ret;
diff --git a/src/Library/DeviceBase.cpp b/src/Library/DeviceBase.cpp
index ca6f882..ddf7917 100644
--- a/src/Library/DeviceBase.cpp
+++ b/src/Library/DeviceBase.cpp
@@ -24,6 +24,7 @@
 #include "DeviceManagerBase.hpp"
 #include "SysFSDevice.hpp"
 #include "Common/FDInputStream.hpp"
+#include "Common/Utility.hpp"
 
 #include "usbguard/Exception.hpp"
 #include "usbguard/Logger.hpp"
@@ -89,7 +90,40 @@ namespace usbguard
     /*
      * Set connect type
      */
-    setConnectType(sysfs_device.readAttribute("port/connect_type", /*strip_last_null=*/true, /*optional=*/true));
+    int retry = 0;
+    std::string connect_type;
+    /*
+     * Host controllers are directly connected to SoC/PCI bus
+     * hence do not have port/connect_type in sysfs.
+     * Filter out host controllers to avoid unnecessary wait in the loop.
+     */
+    const auto before = std::chrono::steady_clock::now();
+
+    if (!hasPrefix(getPort(), "usb")) {
+      while (retry < 30) {
+        connect_type = sysfs_device.readAttribute("port/connect_type", /*strip_last_null=*/true, /*optional=*/true);
+
+        if (connect_type != "") {
+          break;
+        }
+
+        USBGUARD_LOG(Trace) << "connect_type is empty, will attempt again";
+        usleep(1000);
+        retry++;
+      }
+
+      const auto after = std::chrono::steady_clock::now();
+      const auto duration_nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(after - before).count();
+      USBGUARD_LOG(Info) << "***Setting connect type=" << connect_type <<" after "<< retry << " retries (took " <<
+        duration_nanoseconds << "ns)";
+
+      if (connect_type == "") {
+        USBGUARD_LOG(Warning) << "port/connect_type is empty for non host controller device " << getPort() << " after waiting for " <<
+          duration_nanoseconds << "ns";
+      }
+    }
+
+    setConnectType(connect_type);
     /*
      * Process USB descriptor data.
      *
@@ -98,10 +132,6 @@ namespace usbguard
      *
      */
     FDInputStream descriptor_stream(sysfs_device.openAttribute("descriptors"));
-    /*
-     * Find out the descriptor data stream size
-     */
-    size_t descriptor_expected_size = 0;
 
     if (!descriptor_stream.good()) {
       throw ErrnoException("DeviceBase", sysfs_device.getPath(), errno);
@@ -110,7 +140,7 @@ namespace usbguard
     initializeHash();
     USBDescriptorParser parser(*this);
 
-    if ((descriptor_expected_size = parser.parse(descriptor_stream)) < sizeof(USBDeviceDescriptor)) {
+    if (parser.parse(descriptor_stream) < sizeof(USBDeviceDescriptor)) {
       throw Exception("DeviceBase", sysfs_device.getPath(),
         "USB descriptor parser processed less data than the size of a USB device descriptor");
     }
diff --git a/src/Library/DeviceManagerBase.cpp b/src/Library/DeviceManagerBase.cpp
index 297cdcb..b2bcc94 100644
--- a/src/Library/DeviceManagerBase.cpp
+++ b/src/Library/DeviceManagerBase.cpp
@@ -149,8 +149,6 @@ namespace usbguard
     }
 
 #endif
-    /* UNREACHABLE */
-    return std::string();
   }
 
   int DeviceManagerBase::ueventOpen()
diff --git a/src/Library/DeviceManagerPrivate.cpp b/src/Library/DeviceManagerPrivate.cpp
index 308df7a..73f84fd 100644
--- a/src/Library/DeviceManagerPrivate.cpp
+++ b/src/Library/DeviceManagerPrivate.cpp
@@ -117,6 +117,7 @@ namespace usbguard
       return _device_map.at(id);
     }
     catch (...) {
+      USBGUARD_LOG(Debug) << "Lookup error: " << id;
       throw Exception("Device lookup", "device id", "id doesn't exist");
     }
   }
diff --git a/src/Library/Hash.cpp b/src/Library/Hash.cpp
index c409515..7d26bac 100644
--- a/src/Library/Hash.cpp
+++ b/src/Library/Hash.cpp
@@ -39,10 +39,24 @@ namespace usbguard
     crypto_hash_sha256_init(&_state);
 #endif
 #if defined(USBGUARD_USE_OPENSSL)
-    if ((_state = EVP_MD_CTX_new()) == nullptr)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+
+    if ((_state = EVP_MD_CTX_new()) == nullptr) {
+      throw std::runtime_error("Dynamic memory allocation of message digest context failed.");
+    }
+
+#else
+
+    if ((_state = EVP_MD_CTX_create()) == nullptr) {
       throw std::runtime_error("Dynamic memory allocation of message digest context failed.");
-    if (!EVP_DigestInit_ex(_state, EVP_sha256(), nullptr))
+    }
+
+#endif
+
+    if (!EVP_DigestInit_ex(_state, EVP_sha256(), nullptr)) {
       throw std::runtime_error("Context initialization of message digest context failed.");
+    }
+
 #endif
 #if defined(USBGUARD_USE_LIBGCRYPT)
     gcry_md_open(&_state, GCRY_MD_SHA256, 0);
@@ -101,7 +115,11 @@ namespace usbguard
   Hash::~Hash()
   {
 #if defined(USBGUARD_USE_OPENSSL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
     EVP_MD_CTX_free(_state);
+#else
+    EVP_MD_CTX_destroy(_state);
+#endif
 #endif
     release();
   }
@@ -134,8 +152,11 @@ namespace usbguard
     crypto_hash_sha256_update(&_state, reinterpret_cast<const uint8_t*>(ptr), size);
 #endif
 #if defined(USBGUARD_USE_OPENSSL)
-    if (!EVP_DigestUpdate(_state, reinterpret_cast<const uint8_t*>(ptr), size))
+
+    if (!EVP_DigestUpdate(_state, reinterpret_cast<const uint8_t*>(ptr), size)) {
       throw std::runtime_error("Hashing data into message digest context failed.");
+    }
+
 #endif
 #if defined(USBGUARD_USE_LIBGCRYPT)
     gcry_md_write(_state, ptr, size);
@@ -158,8 +179,11 @@ namespace usbguard
         crypto_hash_sha256_update(&_state, buffer, buflen);
 #endif
 #if defined(USBGUARD_USE_OPENSSL)
-        if (!EVP_DigestUpdate(_state, buffer, buflen))
+
+        if (!EVP_DigestUpdate(_state, buffer, buflen)) {
           throw std::runtime_error("Hashing data into message digest context failed.");
+        }
+
 #endif
 #if defined(USBGUARD_USE_LIBGCRYPT)
         gcry_md_write(_state, buffer, buflen);
@@ -182,8 +206,11 @@ namespace usbguard
 #if defined(USBGUARD_USE_OPENSSL)
     uint8_t hash_binary[EVP_MAX_MD_SIZE];
     unsigned int hash_len;
-    if (!EVP_DigestFinal_ex(_state, hash_binary, &hash_len))
+
+    if (!EVP_DigestFinal_ex(_state, hash_binary, &hash_len)) {
       throw std::runtime_error("Digest value retrieval failed.");
+    }
+
     const uint8_t* const hash_buffer = hash_binary;
     const size_t hash_buflen = hash_len;
 #endif
diff --git a/src/Library/Hash.hpp b/src/Library/Hash.hpp
index 546ed69..06dfd94 100644
--- a/src/Library/Hash.hpp
+++ b/src/Library/Hash.hpp
@@ -59,7 +59,7 @@ namespace usbguard
     crypto_hash_sha256_state _state;
 #endif
 #if defined(USBGUARD_USE_OPENSSL)
-    EVP_MD_CTX *_state;
+    EVP_MD_CTX* _state;
 #endif
 #if defined(USBGUARD_USE_LIBGCRYPT)
     gcry_md_hd_t _state {nullptr};
diff --git a/src/Library/IPC/Devices.proto b/src/Library/IPC/Devices.proto
index ba7ea1f..8cfb2a0 100644
--- a/src/Library/IPC/Devices.proto
+++ b/src/Library/IPC/Devices.proto
@@ -51,6 +51,13 @@ message DevicePolicyChangedSignal {
   required uint32 rule_id = 5;
 }
 
+message DevicePolicyAppliedSignal {
+  required uint32 id = 1;
+  required uint32 target_new = 2;
+  required string device_rule = 3;
+  required uint32 rule_id = 4;
+}
+
 message PropertyParameterChangedSignal {
   required string name = 1;
   required string value_old = 2;
diff --git a/src/Library/IPCClientPrivate.cpp b/src/Library/IPCClientPrivate.cpp
index 5ba6c02..6aa5201 100644
--- a/src/Library/IPCClientPrivate.cpp
+++ b/src/Library/IPCClientPrivate.cpp
@@ -85,6 +85,7 @@ namespace usbguard
     registerHandler<IPC::Exception>(&IPCClientPrivate::handleException);
     registerHandler<IPC::DevicePresenceChangedSignal>(&IPCClientPrivate::handleDevicePresenceChangedSignal);
     registerHandler<IPC::DevicePolicyChangedSignal>(&IPCClientPrivate::handleDevicePolicyChangedSignal);
+    registerHandler<IPC::DevicePolicyAppliedSignal>(&IPCClientPrivate::handleDevicePolicyAppliedSignal);
     registerHandler<IPC::PropertyParameterChangedSignal>(&IPCClientPrivate::handlePropertyParameterChangedSignal);
 
     if (connected) {
@@ -143,7 +144,6 @@ namespace usbguard
       << " do_wait=" << do_wait;
     USBGUARD_LOG(Trace) << "_qb_conn=" << _qb_conn
       << " _qb_fd=" << _qb_fd;
-
     std::unique_lock<std::mutex> disconnect_lock(_disconnect_mutex);
 
     if (_qb_conn != nullptr && _qb_fd >= 0) {
@@ -445,16 +445,17 @@ namespace usbguard
     return devices;
   }
 
-  bool IPCClientPrivate::checkIPCPermissions(const IPCServer::AccessControl::Section& section, const IPCServer::AccessControl::Privilege& privilege)
+  bool IPCClientPrivate::checkIPCPermissions(const IPCServer::AccessControl::Section& section,
+    const IPCServer::AccessControl::Privilege& privilege)
   {
     IPC::checkIPCPermissions message_out;
     message_out.mutable_request()->set_uid(getuid());
     message_out.mutable_request()->set_gid(getgid());
     message_out.mutable_request()->set_section(
-        IPCServer::AccessControl::sectionToString(section)
+      IPCServer::AccessControl::sectionToString(section)
     );
     message_out.mutable_request()->set_privilege(
-        IPCServer::AccessControl::privilegeToString(privilege)
+      IPCServer::AccessControl::privilegeToString(privilege)
     );
     auto message_in = qbIPCSendRecvMessage(message_out);
     return message_in->response().permit();
@@ -522,6 +523,17 @@ namespace usbguard
       signal->rule_id());
   }
 
+  void IPCClientPrivate::handleDevicePolicyAppliedSignal(IPC::MessagePointer& message_in, IPC::MessagePointer& message_out)
+  {
+    (void)message_out;
+    const IPC::DevicePolicyAppliedSignal* const signal = \
+      reinterpret_cast<const IPC::DevicePolicyAppliedSignal*>(message_in.get());
+    _p_instance.DevicePolicyApplied(signal->id(),
+      Rule::targetFromInteger(signal->target_new()),
+      signal->device_rule(),
+      signal->rule_id());
+  }
+
   void IPCClientPrivate::handlePropertyParameterChangedSignal(IPC::MessagePointer& message_in, IPC::MessagePointer& message_out)
   {
     (void)message_out;
diff --git a/src/Library/IPCClientPrivate.hpp b/src/Library/IPCClientPrivate.hpp
index 35ff50c..d92a1d4 100644
--- a/src/Library/IPCClientPrivate.hpp
+++ b/src/Library/IPCClientPrivate.hpp
@@ -65,7 +65,8 @@ namespace usbguard
     uint32_t applyDevicePolicy(uint32_t id, Rule::Target target, bool permanent);
     const std::vector<Rule> listDevices(const std::string& query);
 
-    bool checkIPCPermissions(const IPCServer::AccessControl::Section& section, const IPCServer::AccessControl::Privilege& privilege);
+    bool checkIPCPermissions(const IPCServer::AccessControl::Section& section,
+      const IPCServer::AccessControl::Privilege& privilege);
 
     void processReceiveEvent();
 
@@ -109,6 +110,7 @@ namespace usbguard
     void handleException(IPC::MessagePointer& message_in, IPC::MessagePointer& message_out);
     void handleDevicePresenceChangedSignal(IPC::MessagePointer& message_in, IPC::MessagePointer& message_out);
     void handleDevicePolicyChangedSignal(IPC::MessagePointer& message_in, IPC::MessagePointer& message_out);
+    void handleDevicePolicyAppliedSignal(IPC::MessagePointer& message_in, IPC::MessagePointer& message_out);
     void handlePropertyParameterChangedSignal(IPC::MessagePointer& message_in, IPC::MessagePointer& message_out);
 
     IPCClient& _p_instance;
diff --git a/src/Library/IPCPrivate.cpp b/src/Library/IPCPrivate.cpp
index 2a661e9..1b8f23f 100644
--- a/src/Library/IPCPrivate.cpp
+++ b/src/Library/IPCPrivate.cpp
@@ -38,14 +38,15 @@ namespace usbguard
     { 0x02, "usbguard.IPC.applyDevicePolicy" },
     { 0x03, "usbguard.IPC.DevicePresenceChangedSignal" },
     { 0x04, "usbguard.IPC.DevicePolicyChangedSignal" },
-    { 0x05, "usbguard.IPC.PropertyParameterChangedSignal" },
-    { 0x06, "usbguard.IPC.listRules" },
-    { 0x07, "usbguard.IPC.appendRule" },
-    { 0x08, "usbguard.IPC.removeRule" },
-    { 0x09, "usbguard.IPC.Exception" },
-    { 0x0a, "usbguard.IPC.getParameter" },
-    { 0x0b, "usbguard.IPC.setParameter" },
-    { 0x0c, "usbguard.IPC.checkIPCPermissions" }
+    { 0x05, "usbguard.IPC.DevicePolicyAppliedSignal" },
+    { 0x06, "usbguard.IPC.PropertyParameterChangedSignal" },
+    { 0x07, "usbguard.IPC.listRules" },
+    { 0x08, "usbguard.IPC.appendRule" },
+    { 0x09, "usbguard.IPC.removeRule" },
+    { 0x0a, "usbguard.IPC.Exception" },
+    { 0x0b, "usbguard.IPC.getParameter" },
+    { 0x0c, "usbguard.IPC.setParameter" },
+    { 0x0d, "usbguard.IPC.checkIPCPermissions" }
   };
 
   uint32_t IPC::messageTypeNameToNumber(const std::string& name)
diff --git a/src/Library/IPCServerPrivate.cpp b/src/Library/IPCServerPrivate.cpp
index b1343f9..548a726 100644
--- a/src/Library/IPCServerPrivate.cpp
+++ b/src/Library/IPCServerPrivate.cpp
@@ -535,6 +535,10 @@ namespace usbguard
       return IPCServer::AccessControl::Section::DEVICES;
     }
 
+    if (name == "usbguard.IPC.DevicePolicyAppliedSignal") {
+      return IPCServer::AccessControl::Section::DEVICES;
+    }
+
     if (name == "usbguard.IPC.PropertyParameterChangedSignal") {
       return IPCServer::AccessControl::Section::PARAMETERS;
     }
@@ -567,10 +571,10 @@ namespace usbguard
   bool IPCServerPrivate::authenticateIPCConnectionDAC(uid_t uid, gid_t gid, IPCServer::AccessControl* const ac_ptr) const
   {
     USBGUARD_LOG(Trace) << "uid=" << uid << " gid=" << gid << " ac_ptr=" << ac_ptr;
-    return \
-      matchACLByUID(uid, ac_ptr) || \
-      matchACLByGID(gid, ac_ptr) || \
-      matchACLByName(uid, gid, ac_ptr);
+    bool matched_uid = matchACLByUID(uid, ac_ptr);
+    bool matched_gid = matchACLByGID(gid, ac_ptr);
+    bool matched_name = matchACLByName(uid, gid, ac_ptr);
+    return matched_uid || matched_gid || matched_name;
   }
 
   bool IPCServerPrivate::matchACLByUID(uid_t uid, IPCServer::AccessControl* const ac_ptr) const
@@ -1000,7 +1004,8 @@ namespace usbguard
     IPCServer::AccessControl access_control = IPCServer::AccessControl();
     const bool auth = qbIPCConnectionAllowed(uid, gid, &access_control);
     IPCServer::AccessControl::Section section = IPCServer::AccessControl::sectionFromString(message_in->request().section());
-    IPCServer::AccessControl::Privilege privilege = IPCServer::AccessControl::privilegeFromString(message_in->request().privilege());
+    IPCServer::AccessControl::Privilege privilege = IPCServer::AccessControl::privilegeFromString(
+        message_in->request().privilege());
     const bool permit = auth && access_control.hasPrivilege(section, privilege);
     IPC::checkIPCPermissions* const message_out = message_in->New();
     message_out->MergeFrom(*message_in);
@@ -1036,6 +1041,19 @@ namespace usbguard
     qbIPCBroadcastMessage(&signal);
   }
 
+  void IPCServerPrivate::DevicePolicyApplied(uint32_t id,
+    Rule::Target target_new,
+    const std::string& device_rule,
+    uint32_t rule_id)
+  {
+    IPC::DevicePolicyAppliedSignal signal;
+    signal.set_id(id);
+    signal.set_target_new(Rule::targetToInteger(target_new));
+    signal.set_device_rule(device_rule);
+    signal.set_rule_id(rule_id);
+    qbIPCBroadcastMessage(&signal);
+  }
+
   void IPCServerPrivate::PropertyParameterChanged(const std::string& name,
     const std::string& value_old,
     const std::string& value_new)
diff --git a/src/Library/IPCServerPrivate.hpp b/src/Library/IPCServerPrivate.hpp
index 98ebf9c..25f9ac3 100644
--- a/src/Library/IPCServerPrivate.hpp
+++ b/src/Library/IPCServerPrivate.hpp
@@ -63,6 +63,11 @@ namespace usbguard
       const std::string& device_rule,
       uint32_t rule_id);
 
+    void DevicePolicyApplied(uint32_t id,
+      Rule::Target target_new,
+      const std::string& device_rule,
+      uint32_t rule_id);
+
     void PropertyParameterChanged(const std::string& name,
       const std::string& value_old,
       const std::string& value_new);
diff --git a/src/Library/RuleParser/Grammar.hpp b/src/Library/RuleParser/Grammar.hpp
index c80eb2d..aebb727 100644
--- a/src/Library/RuleParser/Grammar.hpp
+++ b/src/Library/RuleParser/Grammar.hpp
@@ -34,29 +34,29 @@ namespace usbguard
     /*
      * Rule language keywords
      */
-    struct str_allow : TAOCPP_PEGTL_STRING("allow") {};
-    struct str_block : TAOCPP_PEGTL_STRING("block") {};
-    struct str_reject : TAOCPP_PEGTL_STRING("reject") {};
-    struct str_match : TAOCPP_PEGTL_STRING("match") {};
-    struct str_device : TAOCPP_PEGTL_STRING("device") {};
-
-    struct str_name : TAOCPP_PEGTL_STRING("name") {};
-    struct str_hash : TAOCPP_PEGTL_STRING("hash") {};
-    struct str_parent_hash : TAOCPP_PEGTL_STRING("parent-hash") {};
-    struct str_via_port : TAOCPP_PEGTL_STRING("via-port") {};
-    struct str_with_interface : TAOCPP_PEGTL_STRING("with-interface") {};
-    struct str_with_connect_type : TAOCPP_PEGTL_STRING("with-connect-type") {};
-    struct str_serial : TAOCPP_PEGTL_STRING("serial") {};
-    struct str_if : TAOCPP_PEGTL_STRING("if") {};
-    struct str_id : TAOCPP_PEGTL_STRING("id") {};
-    struct str_label : TAOCPP_PEGTL_STRING("label") {};
-
-    struct str_all_of : TAOCPP_PEGTL_STRING("all-of") {};
-    struct str_one_of : TAOCPP_PEGTL_STRING("one-of") {};
-    struct str_none_of : TAOCPP_PEGTL_STRING("none-of") {};
-    struct str_equals : TAOCPP_PEGTL_STRING("equals") {};
-    struct str_equals_ordered : TAOCPP_PEGTL_STRING("equals-ordered") {};
-    struct str_match_all: TAOCPP_PEGTL_STRING("match-all") {};
+    struct str_allow : TAO_PEGTL_STRING("allow") {};
+    struct str_block : TAO_PEGTL_STRING("block") {};
+    struct str_reject : TAO_PEGTL_STRING("reject") {};
+    struct str_match : TAO_PEGTL_STRING("match") {};
+    struct str_device : TAO_PEGTL_STRING("device") {};
+
+    struct str_name : TAO_PEGTL_STRING("name") {};
+    struct str_hash : TAO_PEGTL_STRING("hash") {};
+    struct str_parent_hash : TAO_PEGTL_STRING("parent-hash") {};
+    struct str_via_port : TAO_PEGTL_STRING("via-port") {};
+    struct str_with_interface : TAO_PEGTL_STRING("with-interface") {};
+    struct str_with_connect_type : TAO_PEGTL_STRING("with-connect-type") {};
+    struct str_serial : TAO_PEGTL_STRING("serial") {};
+    struct str_if : TAO_PEGTL_STRING("if") {};
+    struct str_id : TAO_PEGTL_STRING("id") {};
+    struct str_label : TAO_PEGTL_STRING("label") {};
+
+    struct str_all_of : TAO_PEGTL_STRING("all-of") {};
+    struct str_one_of : TAO_PEGTL_STRING("one-of") {};
+    struct str_none_of : TAO_PEGTL_STRING("none-of") {};
+    struct str_equals : TAO_PEGTL_STRING("equals") {};
+    struct str_equals_ordered : TAO_PEGTL_STRING("equals-ordered") {};
+    struct str_match_all: TAO_PEGTL_STRING("match-all") {};
 
     /*
      * Generic rule attribute
diff --git a/src/Library/UEventParser.cpp b/src/Library/UEventParser.cpp
index 1fb23ec..7d7e10f 100644
--- a/src/Library/UEventParser.cpp
+++ b/src/Library/UEventParser.cpp
@@ -28,7 +28,11 @@
 
 #include <fstream>
 
-#include <tao/pegtl/contrib/tracer.hpp>
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+  #include <tao/pegtl/contrib/trace.hpp>
+#else
+  #include <tao/pegtl/contrib/tracer.hpp>
+#endif
 using namespace tao;
 
 namespace usbguard
@@ -130,7 +134,11 @@ namespace usbguard
         tao::pegtl::parse<G, UEventParser::actions>(in, uevent);
       }
       else {
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+        tao::pegtl::complete_trace<G, UEventParser::actions>(in, uevent);
+#else
         tao::pegtl::parse<G, UEventParser::actions, tao::pegtl::tracer>(in, uevent);
+#endif
       }
     }
     catch (...) {
diff --git a/src/Library/UMockdevDeviceDefinition.cpp b/src/Library/UMockdevDeviceDefinition.cpp
index a8abb09..6bbf3a3 100644
--- a/src/Library/UMockdevDeviceDefinition.cpp
+++ b/src/Library/UMockdevDeviceDefinition.cpp
@@ -26,7 +26,11 @@
 #include <Common/Utility.hpp>
 
 #include <tao/pegtl.hpp>
-#include <tao/pegtl/contrib/tracer.hpp>
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+  #include <tao/pegtl/contrib/trace.hpp>
+#else
+  #include <tao/pegtl/contrib/tracer.hpp>
+#endif
 
 namespace usbguard
 {
@@ -49,12 +53,12 @@ namespace usbguard
      *  S:linkname: device node symlink (without the /dev/ prefix); ignored right now.
      */
 
-    struct str_path_prefix : TAOCPP_PEGTL_STRING("P:") {};
-    struct str_property_prefix : TAOCPP_PEGTL_STRING("E:") {};
-    struct str_ascii_attr_prefix : TAOCPP_PEGTL_STRING("A:") {};
-    struct str_binary_attr_prefix : TAOCPP_PEGTL_STRING("H:") {};
-    struct str_link_prefix : TAOCPP_PEGTL_STRING("L:") {};
-    struct str_name_prefix : TAOCPP_PEGTL_STRING("N:") {};
+    struct str_path_prefix : TAO_PEGTL_STRING("P:") {};
+    struct str_property_prefix : TAO_PEGTL_STRING("E:") {};
+    struct str_ascii_attr_prefix : TAO_PEGTL_STRING("A:") {};
+    struct str_binary_attr_prefix : TAO_PEGTL_STRING("H:") {};
+    struct str_link_prefix : TAO_PEGTL_STRING("L:") {};
+    struct str_name_prefix : TAO_PEGTL_STRING("N:") {};
 
     struct line_rest
       : star<not_at<ascii::eol>, not_at<eof>, ascii::any> {};
@@ -330,7 +334,11 @@ namespace usbguard
 
     try {
       tao::pegtl::string_input<> input(definitions_string, "<string>");
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+      tao::pegtl::complete_trace<UMockdevParser::grammar, UMockdevParser::actions>(input, definitions, umockdev_name);
+#else
       tao::pegtl::parse<UMockdevParser::grammar, UMockdevParser::actions, tao::pegtl::tracer>(input, definitions, umockdev_name);
+#endif
     }
     catch (...) {
       USBGUARD_LOG(Error) << "UMockdevDeviceDefinition: " << "<string>" << ": parsing failed at line <LINE>";
diff --git a/src/Library/public/usbguard/Exception.hpp b/src/Library/public/usbguard/Exception.hpp
index 476c2ae..b8c8687 100644
--- a/src/Library/public/usbguard/Exception.hpp
+++ b/src/Library/public/usbguard/Exception.hpp
@@ -122,11 +122,20 @@ namespace usbguard
       : Exception(context, object, ErrnoException::reasonFromErrno(errno_value))
     {
     }
-  private:
+
     static std::string reasonFromErrno(const int errno_value)
     {
-      char buffer[1024];
-      return std::string(strerror_r(errno_value, buffer, sizeof buffer));
+      char buffer[1024] = "Unknown error";
+      const char* error_message = buffer;
+      // See "man strerror_r" for why we need these two cases:
+#ifndef HAVE_GNU_STRERROR_R
+      // We have the XSI-compliant version of strerror_r with int return
+      strerror_r(errno_value, buffer, sizeof buffer);
+#else
+      // We have the GNU-specific version of strerror_r with char* return
+      error_message = strerror_r(errno_value, buffer, sizeof buffer);
+#endif
+      return std::string(error_message);
     }
   };
 
diff --git a/src/Library/public/usbguard/IPCClient.cpp b/src/Library/public/usbguard/IPCClient.cpp
index 9f76e8a..c86d4d8 100644
--- a/src/Library/public/usbguard/IPCClient.cpp
+++ b/src/Library/public/usbguard/IPCClient.cpp
@@ -87,7 +87,8 @@ namespace usbguard
     return d_pointer->listDevices(query);
   }
 
-  bool IPCClient::checkIPCPermissions(const IPCServer::AccessControl::Section& section, const IPCServer::AccessControl::Privilege& privilege)
+  bool IPCClient::checkIPCPermissions(const IPCServer::AccessControl::Section& section,
+    const IPCServer::AccessControl::Privilege& privilege)
   {
     return d_pointer->checkIPCPermissions(section, privilege);
   }
diff --git a/src/Library/public/usbguard/IPCClient.hpp b/src/Library/public/usbguard/IPCClient.hpp
index 1675d4d..b2ebaf5 100644
--- a/src/Library/public/usbguard/IPCClient.hpp
+++ b/src/Library/public/usbguard/IPCClient.hpp
@@ -163,7 +163,8 @@ namespace usbguard
      * @return True if IPC client has enough permission
      * for (section, privilege), otherwise false.
      */
-    bool checkIPCPermissions(const IPCServer::AccessControl::Section& section, const IPCServer::AccessControl::Privilege& privilege);
+    bool checkIPCPermissions(const IPCServer::AccessControl::Section& section,
+      const IPCServer::AccessControl::Privilege& privilege);
 
     /**
      * @brief Defines algorithm to perform in the case of IPC connection.
@@ -222,6 +223,24 @@ namespace usbguard
       (void)rule_id;
     }
 
+    /**
+     * @brief Defines actions to perform when a USB device
+     * has been inserted, accepted, or rejected.
+     *
+     * @see \link Interface::DevicePolicyApplied()
+     * DevicePolicyApplied()\endlink
+     */
+    virtual void DevicePolicyApplied(uint32_t id,
+      Rule::Target target_new,
+      const std::string& device_rule,
+      uint32_t rule_id) override
+    {
+      (void)id;
+      (void)target_new;
+      (void)device_rule;
+      (void)rule_id;
+    }
+
     /**
      * @brief Defines algorithm to perform in the case that property parameter
      * has been changed.
diff --git a/src/Library/public/usbguard/IPCServer.cpp b/src/Library/public/usbguard/IPCServer.cpp
index e09de44..973eb8b 100644
--- a/src/Library/public/usbguard/IPCServer.cpp
+++ b/src/Library/public/usbguard/IPCServer.cpp
@@ -36,10 +36,8 @@ namespace usbguard
       throw Exception("IPC access control", "name too long", name);
     }
 
-    const std::string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
-
-    if (name.find_first_not_of(valid_chars) != std::string::npos) {
-      throw Exception("IPC access control", "name contains invalid character(s)", name);
+    if (!isValidName(name)) {
+      throw Exception("IPC access control", "invalid name format", name);
     }
   }
 
@@ -159,18 +157,26 @@ namespace usbguard
       throw USBGUARD_BUG("Cannot set privileges for NONE section");
     }
 
+    const uint8_t p = static_cast<uint8_t>(privilege);
+
     if (section == Section::ALL) {
-      for (const auto& value : {
+      for (const auto& s : {
           Section::POLICY,
           Section::PARAMETERS,
           Section::EXCEPTIONS,
           Section::DEVICES
         }) {
-        _access_control[value] |= static_cast<uint8_t>(privilege);
+        _access_control[s] |= p & ~ac_mask(s);
       }
     }
     else {
-      _access_control[section] |= static_cast<uint8_t>(privilege);
+      if (privilege != Privilege::ALL && (p & ac_mask(section))) {
+        throw std::runtime_error("Invalid privilege " +
+          privilegeToString(privilege) + " for section " +
+          sectionToString(section));
+      }
+
+      _access_control[section] |= p & ~ac_mask(section);
     }
   }
 
@@ -254,6 +260,32 @@ namespace usbguard
     merge(access_control);
   }
 
+  uint8_t IPCServer::AccessControl::ac_mask(IPCServer::AccessControl::Section section) const
+  {
+    const uint8_t MODIFY = static_cast<uint8_t>(Privilege::MODIFY);
+    const uint8_t LIST = static_cast<uint8_t>(Privilege::LIST);
+    const uint8_t LISTEN = static_cast<uint8_t>(Privilege::LISTEN);
+
+    switch (section) {
+    case Section::DEVICES:
+      return ~(MODIFY | LIST | LISTEN);
+
+    case Section::POLICY:
+      return ~(MODIFY | LIST);
+
+    case Section::EXCEPTIONS:
+      return ~(LISTEN);
+
+    case Section::PARAMETERS:
+      return ~(MODIFY | LIST | LISTEN);
+
+    case Section::ALL:
+    case Section::NONE:
+    default:
+      return 0xff;
+    }
+  }
+
   IPCServer::IPCServer()
     : d_pointer(usbguard::make_unique<IPCServerPrivate>(*this))
   {
@@ -288,6 +320,14 @@ namespace usbguard
     d_pointer->DevicePolicyChanged(id, target_old, target_new, device_rule, rule_id);
   }
 
+  void IPCServer::DevicePolicyApplied(uint32_t id,
+    Rule::Target target_new,
+    const std::string& device_rule,
+    uint32_t rule_id)
+  {
+    d_pointer->DevicePolicyApplied(id, target_new, device_rule, rule_id);
+  }
+
   void IPCServer::PropertyParameterChanged(const std::string& name,
     const std::string& value_old,
     const std::string& value_new)
diff --git a/src/Library/public/usbguard/IPCServer.hpp b/src/Library/public/usbguard/IPCServer.hpp
index aa13e6b..ddb1d8a 100644
--- a/src/Library/public/usbguard/IPCServer.hpp
+++ b/src/Library/public/usbguard/IPCServer.hpp
@@ -50,9 +50,9 @@ namespace usbguard
     /**
      * @brief Checks whether given name is a valid access control name.
      *
-     * Name is a valid access control name if and only if:
-     *  1. name is not longer then 32 characters.
-     *  2. name consists only from characters from set { A-Z, a-z, 0-9, _ }.
+     * Name is a valid access control name iff:
+     *  1. it is not longer then 32 characters
+     *  2. it matches regex [A-Za-z_][A-Za-z0-9_-]*[$]
      *
      * @param name Name to be verified.
      * @throw Exception If \p name is not a valid access control name.
@@ -277,6 +277,17 @@ namespace usbguard
         }
       };
 
+      /**
+       * @brief Get a privilege mask for given section
+       *
+       * For example, if the section is POLICY that has privileges MODIFY
+       * and LIST, the mask would be ~(MODIFY | LIST)
+       *
+       * @param section Section for which the privilege mask should be returned
+       * @return Privilege mask for section
+       */
+      uint8_t ac_mask(Section section) const;
+
       /**
        * @brief Access control represented by unordered map of
        * tuples (Section, 8b privileges).
@@ -323,6 +334,14 @@ namespace usbguard
       const std::string& device_rule,
       uint32_t rule_id);
 
+    /**
+     * @copydoc Interface::DevicePolicyApplied()
+     */
+    void DevicePolicyApplied(uint32_t id,
+      Rule::Target target_new,
+      const std::string& device_rule,
+      uint32_t rule_id);
+
     /**
      * @copydoc Interface::PropertyParameterChanged()
      */
diff --git a/src/Library/public/usbguard/Interface.hpp b/src/Library/public/usbguard/Interface.hpp
index 8cf1d55..48d8292 100644
--- a/src/Library/public/usbguard/Interface.hpp
+++ b/src/Library/public/usbguard/Interface.hpp
@@ -189,6 +189,32 @@ namespace usbguard
       const std::string& device_rule,
       uint32_t rule_id) = 0;
 
+    /**
+     * @brief Notify about the acceptance or rejection of a device.
+     *
+     * This signal is thrown whenever a device is inserted.
+     * It is also thrown when a device has been allowed or rejected.
+     *
+     * The device attribute dictionary contains the following attributes:
+     * - id (the USB device ID in the form VID:PID)
+     * - name
+     * - serial
+     * - via-port
+     * - hash
+     * - parent-hash
+     * - with-interface
+     *
+     * @param id ID of the device.
+     * @param target_new Current authorization target.
+     * @param device_rule Device specific rule.
+     * @param rule_id Rule ID of the matched rule.
+     * Otherwise a reserved rule ID value is used.
+     */
+    virtual void DevicePolicyApplied(uint32_t id,
+      Rule::Target target_new,
+      const std::string& device_rule,
+      uint32_t rule_id) = 0;
+
     /**
      * @brief Notify about a change of a property parameter.
      *
diff --git a/src/Library/public/usbguard/Policy.cpp b/src/Library/public/usbguard/Policy.cpp
index 8ebf997..136d502 100644
--- a/src/Library/public/usbguard/Policy.cpp
+++ b/src/Library/public/usbguard/Policy.cpp
@@ -114,10 +114,13 @@ namespace usbguard
 
     for (auto ruleset : _rulesets_ptr) {
       uint32_t id = ruleset->upsertRule(match_rule, new_rule, parent_insensitive);
-      if (id == Rule::DefaultID)
-	continue;
-      else
-	return id;
+
+      if (id == Rule::DefaultID) {
+        continue;
+      }
+      else {
+        return id;
+      }
     }
 
     return _rulesets_ptr.back()->appendRule(new_rule, Rule::LastID, /*lock*/true);
diff --git a/src/Library/public/usbguard/RuleParser.cpp b/src/Library/public/usbguard/RuleParser.cpp
index 140bf14..183117c 100644
--- a/src/Library/public/usbguard/RuleParser.cpp
+++ b/src/Library/public/usbguard/RuleParser.cpp
@@ -34,7 +34,11 @@
 #include <stdexcept>
 #include <stdlib.h>
 
-#include <tao/pegtl/contrib/tracer.hpp>
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+  #include <tao/pegtl/contrib/trace.hpp>
+#else
+  #include <tao/pegtl/contrib/tracer.hpp>
+#endif
 
 namespace usbguard
 {
@@ -48,7 +52,11 @@ namespace usbguard
         tao::pegtl::parse<RuleParser::rule_grammar, RuleParser::rule_parser_actions>(input, rule);
       }
       else {
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+        tao::pegtl::complete_trace<RuleParser::rule_grammar, RuleParser::rule_parser_actions>(input, rule);
+#else
         tao::pegtl::parse<RuleParser::rule_grammar, RuleParser::rule_parser_actions, tao::pegtl::tracer>(input, rule);
+#endif
       }
 
       return rule;
@@ -56,7 +64,11 @@ namespace usbguard
     catch (const tao::pegtl::parse_error& ex) {
       RuleParserError error(rule_spec);
       error.setHint(ex.what());
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+      error.setOffset(ex.positions().front().column);
+#else
       error.setOffset(ex.positions[0].byte_in_line);
+#endif
 
       if (!file.empty() || line != 0) {
         error.setFileInfo(file, line);
diff --git a/src/Library/public/usbguard/RuleSet.cpp b/src/Library/public/usbguard/RuleSet.cpp
index f534242..9f681a1 100644
--- a/src/Library/public/usbguard/RuleSet.cpp
+++ b/src/Library/public/usbguard/RuleSet.cpp
@@ -72,6 +72,7 @@ namespace usbguard
   uint32_t RuleSet::appendRule(const Rule& rule, uint32_t parent_id, bool lock)
   {
     std::unique_lock<std::mutex> op_lock(_op_mutex, std::defer_lock);
+    USBGUARD_LOG(Debug) << "appendRule parent:" << parent_id;
 
     if (lock) {
       op_lock.lock();
diff --git a/src/Tests/Fuzzers/Makefile.am b/src/Tests/Fuzzers/Makefile.am
index 423cc73..f926f0e 100644
--- a/src/Tests/Fuzzers/Makefile.am
+++ b/src/Tests/Fuzzers/Makefile.am
@@ -20,7 +20,7 @@ AM_CPPFLAGS=\
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/Library \
 	-I$(top_srcdir)/src/Library/public \
-	-I$(top_srcdir)/src/ThirdParty/PEGTL/include
+	@pegtl_CFLAGS@
 
 AM_LDFLAGS=\
 	-static $(top_builddir)/libusbguard.la -lFuzzingEngine
diff --git a/src/Tests/Fuzzers/Makefile.in b/src/Tests/Fuzzers/Makefile.in
index 2d70197..35cf507 100644
--- a/src/Tests/Fuzzers/Makefile.in
+++ b/src/Tests/Fuzzers/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -347,6 +347,7 @@ protobuf_LIBS = @protobuf_LIBS@
 psdir = @psdir@
 qb_CFLAGS = @qb_CFLAGS@
 qb_LIBS = @qb_LIBS@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 seccomp_CFLAGS = @seccomp_CFLAGS@
 seccomp_LIBS = @seccomp_LIBS@
@@ -365,7 +366,7 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/Library \
 	-I$(top_srcdir)/src/Library/public \
-	-I$(top_srcdir)/src/ThirdParty/PEGTL/include
+	@pegtl_CFLAGS@
 
 AM_LDFLAGS = \
 	-static $(top_builddir)/libusbguard.la -lFuzzingEngine
diff --git a/src/Tests/Fuzzers/fuzzer-rules.cpp b/src/Tests/Fuzzers/fuzzer-rules.cpp
index 5728d43..03fd909 100644
--- a/src/Tests/Fuzzers/fuzzer-rules.cpp
+++ b/src/Tests/Fuzzers/fuzzer-rules.cpp
@@ -20,7 +20,12 @@
 
 #include <cstdint>
 
-#include <tao/pegtl/contrib/tracer.hpp>
+#include <tao/pegtl.hpp>
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+  #include <tao/pegtl/contrib/trace.hpp>
+#else
+  #include <tao/pegtl/contrib/tracer.hpp>
+#endif
 #include <usbguard/Rule.hpp>
 #include <usbguard/RuleParser.hpp>
 
diff --git a/src/Tests/Fuzzers/fuzzer-uevent.cpp b/src/Tests/Fuzzers/fuzzer-uevent.cpp
index aeca009..df74b79 100644
--- a/src/Tests/Fuzzers/fuzzer-uevent.cpp
+++ b/src/Tests/Fuzzers/fuzzer-uevent.cpp
@@ -20,7 +20,12 @@
 
 #include <cstdint>
 
-#include <tao/pegtl/contrib/tracer.hpp>
+#include <tao/pegtl.hpp>
+#if TAO_PEGTL_VERSION_MAJOR >= 3
+  #include <tao/pegtl/contrib/trace.hpp>
+#else
+  #include <tao/pegtl/contrib/tracer.hpp>
+#endif
 #include <UEvent.hpp>
 
 using namespace usbguard;
diff --git a/src/Tests/LDAP/Sanity/ldap-nsswitch.sh b/src/Tests/LDAP/Sanity/ldap-nsswitch.sh
index ea5cb75..51d90d1 100755
--- a/src/Tests/LDAP/Sanity/ldap-nsswitch.sh
+++ b/src/Tests/LDAP/Sanity/ldap-nsswitch.sh
@@ -207,7 +207,7 @@ sudo -n kill $PID
 rckillb=$?
 sudo -n rm -f $PIDFILE
 
-grep_and_fail '(E) NSSwitch parsing: /etc/nsswitch.conf: No such file or directory'
+grep_and_fail '(i) Error when opening nsswitch file: /etc/nsswitch.conf: No such file or directory'
 
 if [ $rckillb -eq "1" ] # expected to fail
 then
diff --git a/src/Tests/LDAP/UseCase/ldap-test-3.sh b/src/Tests/LDAP/UseCase/ldap-test-3.sh
index b6a7131..13b785d 100755
--- a/src/Tests/LDAP/UseCase/ldap-test-3.sh
+++ b/src/Tests/LDAP/UseCase/ldap-test-3.sh
@@ -120,7 +120,7 @@ then
     KILLRC="0"
 fi
 
-grep "Rules: SourceLDAP: usbguard::Exception" $TMPDIR/usbguard.log
+grep "LDAPHandler query: ldap_search_ext_s: No such object" $TMPDIR/usbguard.log
 GREP1=$?
 
 grep -i "Sanitizer" $TMPDIR/usbguard.log
diff --git a/src/Tests/Makefile.am b/src/Tests/Makefile.am
index 4ef2310..e10e845 100644
--- a/src/Tests/Makefile.am
+++ b/src/Tests/Makefile.am
@@ -23,7 +23,8 @@ AM_CPPFLAGS=\
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/Library \
 	-I$(top_srcdir)/src/Library/public \
-	@catch_CFLAGS@
+	@catch_CFLAGS@ \
+	@pegtl_CFLAGS@
 
 EXTRA_DIST=\
 	$(top_srcdir)/src/Tests/custom.supp \
diff --git a/src/Tests/Makefile.in b/src/Tests/Makefile.in
index cdc0b83..2784a30 100644
--- a/src/Tests/Makefile.in
+++ b/src/Tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -661,6 +661,7 @@ protobuf_LIBS = @protobuf_LIBS@
 psdir = @psdir@
 qb_CFLAGS = @qb_CFLAGS@
 qb_LIBS = @qb_LIBS@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 seccomp_CFLAGS = @seccomp_CFLAGS@
 seccomp_LIBS = @seccomp_LIBS@
@@ -680,7 +681,8 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/Library \
 	-I$(top_srcdir)/src/Library/public \
-	@catch_CFLAGS@
+	@catch_CFLAGS@ \
+	@pegtl_CFLAGS@
 
 EXTRA_DIST = \
 	$(top_srcdir)/src/Tests/custom.supp \
diff --git a/src/Tests/Source/CheckScripts/code-style.sh b/src/Tests/Source/CheckScripts/code-style.sh
index 93d688c..4f217bf 100755
--- a/src/Tests/Source/CheckScripts/code-style.sh
+++ b/src/Tests/Source/CheckScripts/code-style.sh
@@ -7,17 +7,22 @@
 #
 set -e -o pipefail
 
-SOURCE_FILEPATH="$1"
-ASTYLE="${PROJECT_ROOT}/scripts/astyle.sh"
-ASTYLERC_PATH="${PROJECT_ROOT}/src/astylerc"
-ASTYLE_VERSION_MAJOR=$(${ASTYLE} -V | sed -n 's|^Artistic Style Version \([0-9]\+\)\.[0-9]\+\.[0-9]\+|\1|p')
+SOURCE_FILEPATH="${1:?needs one filename as an argument}"
+ASTYLE="${PROJECT_ROOT:?not set in the environment}/scripts/astyle.sh"
+ASTYLE_VERSION_MAJOR=$(${ASTYLE} -V | sed -n 's|^Artistic Style Version \([0-9]\+\)\.[0-9]\+\(\.[0-9]\+\)\?|\1|p')
 
 if [[ "$ASTYLE_VERSION_MAJOR" -lt 3 ]]; then
   exit 77
 fi
 
-if [[ -n "$(${ASTYLE} --formatted --dry-run $(< ${ASTYLERC_PATH}) "$SOURCE_FILEPATH")" ]]; then
-   exit 1
-fi
+tempfile="$(mktemp)"
+delete_tempfile() {
+    rm -f "${tempfile}"
+}
+trap delete_tempfile EXIT
+
+"${ASTYLE}" < "${SOURCE_FILEPATH}" > "${tempfile}"
 
-exit 0
+# NOTE: We cannot use "exec" here or the trap callback above will not be run
+# NOTE: This is meant to return code 1 on non-empty diff
+diff -u --color=always "${SOURCE_FILEPATH}" "${tempfile}"
diff --git a/src/Tests/Source/CheckScripts/private-with-build-config.sh b/src/Tests/Source/CheckScripts/private-with-build-config.sh
index 2618f35..dc75e77 100755
--- a/src/Tests/Source/CheckScripts/private-with-build-config.sh
+++ b/src/Tests/Source/CheckScripts/private-with-build-config.sh
@@ -6,6 +6,7 @@
 # check-path-include: *.h
 # check-path-exclude: src/Library/public/*.hpp
 # check-path-exclude: src/Tests/*
+# check-path-exclude: src/test_filesystem.cpp
 #
 SOURCE_FILEPATH="$1"
 
diff --git a/src/Tests/Source/check-driver.sh b/src/Tests/Source/check-driver.sh
index 51127c0..c13d911 100755
--- a/src/Tests/Source/check-driver.sh
+++ b/src/Tests/Source/check-driver.sh
@@ -39,6 +39,8 @@ GLOB_EXCLUDE=(
 *ThirdParty/*
 *build/*
 *m4/*
+*.pb.*
+*build-config.h
 )
 
 ##################
diff --git a/src/Tests/Unit/test_UEvent.cpp b/src/Tests/Unit/test_UEvent.cpp
index d03738f..759ee2c 100644
--- a/src/Tests/Unit/test_UEvent.cpp
+++ b/src/Tests/Unit/test_UEvent.cpp
@@ -17,7 +17,7 @@
 // Authors: Daniel Kopecek <dkopecek@redhat.com>
 //
 #include <catch.hpp>
-#include <UEvent.hpp>
+#include <UEvent.cpp>
 
 using namespace usbguard;
 
diff --git a/src/Tests/Unit/test_UEventParser.cpp b/src/Tests/Unit/test_UEventParser.cpp
index 225baf0..cefbc3b 100644
--- a/src/Tests/Unit/test_UEventParser.cpp
+++ b/src/Tests/Unit/test_UEventParser.cpp
@@ -17,7 +17,7 @@
 // Authors: Daniel Kopecek <dkopecek@redhat.com>
 //
 #include <catch.hpp>
-#include <UEvent.hpp>
+#include <UEventParser.cpp>
 
 using namespace usbguard;
 
diff --git a/src/Tests/Unit/test_UMockdevDeviceDefinition.cpp b/src/Tests/Unit/test_UMockdevDeviceDefinition.cpp
index e611b7a..fbe8bbd 100644
--- a/src/Tests/Unit/test_UMockdevDeviceDefinition.cpp
+++ b/src/Tests/Unit/test_UMockdevDeviceDefinition.cpp
@@ -17,7 +17,7 @@
 // Authors: Daniel Kopecek <dkopecek@redhat.com>
 //
 #include <catch.hpp>
-#include <UMockdevDeviceDefinition.hpp>
+#include <UMockdevDeviceDefinition.cpp>
 
 #include "test_UMockdevDeviceDefinition.data.hpp"
 
diff --git a/src/Tests/UseCase/003_cli_devices_umockdev.sh b/src/Tests/UseCase/003_cli_devices_umockdev.sh
index 43e622f..d0a7dda 100755
--- a/src/Tests/UseCase/003_cli_devices_umockdev.sh
+++ b/src/Tests/UseCase/003_cli_devices_umockdev.sh
@@ -46,29 +46,31 @@ function test_cli_devices()
   local id_dock="$(${USBGUARD} list-devices | sed -n 's|^\([0-9]\+\):.*hash "2y2qS3rcuMr1Ye5knWsbD8CGzPtrs+eiRR/haro7+Ng=".*$|\1|p')"
 
   if [ -z "$id_dock" ]; then
-    echo "Test error: Unable to parse device ID"
+    echo "Test error: Unable to parse device ID (test line ${LINENO})"
     exit 1
   fi
 
 
   ${USBGUARD} block-device "$id_dock"
   ${USBGUARD} allow-device "$id_dock"
+  sleep 1
 
   local id_dock_child="$(${USBGUARD} list-devices | sed -n 's|^\([0-9]\+\):.*hash "D3deklX12Ir3kJPfUZ5AQNwHeZn1bwtPkQkw6e+8B38=".*$|\1|p')"
   
   if [ -z "$id_dock_child" ]; then
-    echo "Test error: Unable to parse device ID"
+    echo "Test error: Unable to parse device ID (test line ${LINENO})"
     exit 1
   fi
 
   ${USBGUARD} block-device "$id_dock_child"
   ${USBGUARD} block-device "$id_dock"
   ${USBGUARD} allow-device "$id_dock"
+  sleep 1
 
   local id_dock_child="$(${USBGUARD} list-devices | sed -n 's|^\([0-9]\+\):.*hash "D3deklX12Ir3kJPfUZ5AQNwHeZn1bwtPkQkw6e+8B38=".*$|\1|p')"
   
   if [ -z "$id_dock_child" ]; then
-    echo "Test error: Unable to parse device ID"
+    echo "Test error: Unable to parse device ID (test line ${LINENO})"
     exit 1
   fi
 
diff --git a/src/build-config.h.in.in b/src/build-config.h.in.in
index d00ceab..63fc9c9 100644
--- a/src/build-config.h.in.in
+++ b/src/build-config.h.in.in
@@ -61,6 +61,14 @@
 /* Define to 1 if you have the `gettimeofday' function. */
 #undef HAVE_GETTIMEOFDAY
 
+/* Wether the GNU version of function basename(3) is available (or just the
+   POSIX one). */
+#undef HAVE_GNU_BASENAME
+
+/* Wether the GNU version of function strerror_r(3) is available (or just the
+   XSI one). */
+#undef HAVE_GNU_STRERROR_R
+
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
diff --git a/src/test_filesystem.cpp b/src/test_filesystem.cpp
new file mode 100644
index 0000000..9ca81a5
--- /dev/null
+++ b/src/test_filesystem.cpp
@@ -0,0 +1,24 @@
+//
+// Copyright (c) 2022 Sebastian Pipping <sebastian@pipping.org>
+//
+// 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 2 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, see <http://www.gnu.org/licenses/>.
+
+#include <filesystem>
+
+int main()
+{
+  return std::filesystem::temp_directory_path().is_absolute();
+}
+
+/* vim: set ts=2 sw=2 et */
diff --git a/usbguard-daemon.conf.in b/usbguard-daemon.conf.in
index 00c5c07..5ea9b5f 100644
--- a/usbguard-daemon.conf.in
+++ b/usbguard-daemon.conf.in
@@ -129,7 +129,7 @@ RestoreControllerDeviceState=false
 DeviceManagerBackend=uevent
 
 #!!! WARNING: It's good practice to set at least one of the !!!
-#!!!          two options bellow. If none of them are set,  !!!
+#!!!          two options below. If none of them are set,   !!!
 #!!!          the daemon will accept IPC connections from   !!!
 #!!!          anyone, thus allowing anyone to modify the    !!!
 #!!!          rule set and (de)authorize USB devices.       !!!
diff --git a/usbguard.service.in b/usbguard.service.in
index 0d7e193..c618618 100644
--- a/usbguard.service.in
+++ b/usbguard.service.in
@@ -5,14 +5,14 @@ Documentation=man:usbguard-daemon(8)
 
 [Service]
 AmbientCapabilities=
-CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER
+CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_AUDIT_WRITE
 DevicePolicy=closed
 ExecStart=%sbindir%/usbguard-daemon -f -s -c %sysconfdir%/usbguard/usbguard-daemon.conf
 IPAddressDeny=any
 LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
-PIDFile=/var/run/usbguard.pid
+PIDFile=/run/usbguard.pid
 PrivateDevices=yes
 PrivateTmp=yes
 ProtectControlGroups=yes