diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a3240a084f5f4374c38470467d51462e4d6bf905
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,6 @@
+include:
+- local: debian/pipeline/workflow.yml
+- local: debian/pipeline/variables.yml
+- local: debian/pipeline/source.yml
+- local: debian/pipeline/build.yml
+- local: debian/pipeline/test.yml
diff --git a/configure.ac b/configure.ac
index b43d8b9be966fde68af1dd0ffdd042498574c262..035d3789f98057f870befef19c873491fd212c6b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1646,11 +1646,7 @@ AS_IF([test "$BUILD_DMFILEMAPD" = "yes"], [
 ])
 
 ################################################################################
-AC_PATH_TOOL(MODPROBE_CMD, modprobe, [], [$PATH_SBIN])
-
-AS_IF([test -n "$MODPROBE_CMD"], [
-	AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["$MODPROBE_CMD"], [The path to 'modprobe', if available.])
-])
+AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["/sbin/modprobe"], [The path to 'modprobe', if available.])
 
 SYSCONFDIR="$(eval echo $(eval echo $sysconfdir))"
 
diff --git a/daemons/dmeventd/.exported_symbols b/daemons/dmeventd/.exported_symbols
index fab74dc1d6f3dd8e740a3806d8424855bb399df6..46c14fb5ae4935104891f41e2cd626078baf22f9 100644
--- a/daemons/dmeventd/.exported_symbols
+++ b/daemons/dmeventd/.exported_symbols
@@ -1,4 +1,4 @@
-init_fifos
-fini_fifos
-daemon_talk
+dm_event_daemon_init_fifos
+dm_event_daemon_fini_fifos
+dm_event_daemon_talk
 dm_event_get_version
diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c
index 490768b63cefe0662e18e9cdba91cb8fa693b789..04e1d60a3430daef3215a1334cc02e7c71f0c65b 100644
--- a/daemons/dmeventd/dmeventd.c
+++ b/daemons/dmeventd/dmeventd.c
@@ -1988,7 +1988,7 @@ static int _reinstate_registrations(struct dm_event_fifos *fifos)
 	unsigned long mask_value, timeout_value;
 	int i, ret;
 
-	ret = daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
+	ret = dm_event_daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
 	free(msg.data);
 	msg.data = NULL;
 
@@ -2021,7 +2021,7 @@ static int _reinstate_registrations(struct dm_event_fifos *fifos)
 			continue;
 		}
 
-		if (daemon_talk(fifos, &msg, DM_EVENT_CMD_REGISTER_FOR_EVENT,
+		if (dm_event_daemon_talk(fifos, &msg, DM_EVENT_CMD_REGISTER_FOR_EVENT,
 				dso_name,
 				dev_name,
 				(enum dm_event_mask) mask_value,
@@ -2048,7 +2048,7 @@ static void _restart_dmeventd(void)
 	const char *e;
 
 	/* Get the list of registrations from the running daemon. */
-	if (!init_fifos(&fifos)) {
+	if (!dm_event_daemon_init_fifos(&fifos)) {
 		fprintf(stderr, "WARNING: Could not initiate communication with existing dmeventd.\n");
 		exit(EXIT_FAILURE);
 	}
@@ -2065,7 +2065,7 @@ static void _restart_dmeventd(void)
 		goto bad;
 	}
 
-	if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_STATUS, "-", "-", 0, 0))
+	if (dm_event_daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_STATUS, "-", "-", 0, 0))
 		goto bad;
 
 	message = strchr(msg.data, ' ') + 1;
@@ -2089,7 +2089,7 @@ static void _restart_dmeventd(void)
 	}
 
 	if (version >= 2) {
-		if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_PARAMETERS, "-", "-", 0, 0)) {
+		if (dm_event_daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_PARAMETERS, "-", "-", 0, 0)) {
 			fprintf(stderr, "Failed to acquire parameters from old dmeventd.\n");
 			goto bad;
 		}
@@ -2109,7 +2109,7 @@ static void _restart_dmeventd(void)
 	}
 #endif
 
-	if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0)) {
+	if (dm_event_daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0)) {
 		fprintf(stderr, "Old dmeventd refused to die.\n");
 		goto bad;
 	}
@@ -2125,13 +2125,13 @@ static void _restart_dmeventd(void)
 	}
 
 	if (!_systemd_activation) {
-		fini_fifos(&fifos);
+		dm_event_daemon_fini_fifos(&fifos);
 		return;
 	}
 
 	/* Reopen fifos. */
-	fini_fifos(&fifos);
-	if (!init_fifos(&fifos)) {
+	dm_event_daemon_fini_fifos(&fifos);
+	if (!dm_event_daemon_init_fifos(&fifos)) {
 		fprintf(stderr, "Could not initiate communication with new instance of dmeventd.\n");
 		exit(EXIT_FAILURE);
 	}
@@ -2141,10 +2141,10 @@ static void _restart_dmeventd(void)
 		goto bad;
 	}
 
-	fini_fifos(&fifos);
+	dm_event_daemon_fini_fifos(&fifos);
 	exit(EXIT_SUCCESS);
 bad:
-	fini_fifos(&fifos);
+	dm_event_daemon_fini_fifos(&fifos);
 	exit(EXIT_FAILURE);
 }
 
diff --git a/daemons/dmeventd/dmeventd.h b/daemons/dmeventd/dmeventd.h
index afe0b0c4b66740bb021719bb54c5c426080ba7cf..a3dad10a0c5a14005a5ef095ff8698ccb1cdccd2 100644
--- a/daemons/dmeventd/dmeventd.h
+++ b/daemons/dmeventd/dmeventd.h
@@ -65,12 +65,12 @@ struct dm_event_fifos {
 
 /* Implemented in libdevmapper-event.c, but not part of public API. */
 // FIXME  misuse of bitmask as enum
-int daemon_talk(struct dm_event_fifos *fifos,
+int dm_event_daemon_talk(struct dm_event_fifos *fifos,
 		struct dm_event_daemon_message *msg, int cmd,
 		const char *dso_name, const char *dev_name,
 		enum dm_event_mask evmask, uint32_t timeout);
-int init_fifos(struct dm_event_fifos *fifos);
-void fini_fifos(struct dm_event_fifos *fifos);
+int dm_event_daemon_init_fifos(struct dm_event_fifos *fifos);
+void dm_event_daemon_fini_fifos(struct dm_event_fifos *fifos);
 int dm_event_get_version(struct dm_event_fifos *fifos, int *version);
 
 #endif /* __DMEVENTD_DOT_H__ */
diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c
index b5ae37fbca2aaf56d712ea9c277fe038afb7e70c..b4f71d35f1d0bfd2c6fe0f9adb5f8dc32a80d87a 100644
--- a/daemons/dmeventd/libdevmapper-event.c
+++ b/daemons/dmeventd/libdevmapper-event.c
@@ -349,7 +349,7 @@ static int _daemon_write(struct dm_event_fifos *fifos,
 	return bytes == size;
 }
 
-int daemon_talk(struct dm_event_fifos *fifos,
+int dm_event_daemon_talk(struct dm_event_fifos *fifos,
 		struct dm_event_daemon_message *msg, int cmd,
 		const char *dso_name, const char *dev_name,
 		enum dm_event_mask evmask, uint32_t timeout)
@@ -499,7 +499,7 @@ start_server:
 	return ret;
 }
 
-int init_fifos(struct dm_event_fifos *fifos)
+int dm_event_daemon_init_fifos(struct dm_event_fifos *fifos)
 {
 	/* FIXME? Is fifo the most suitable method? Why not share
 	   comms/daemon code with something else e.g. multipath? */
@@ -537,10 +537,10 @@ static int _init_client(char *dmeventd_path, struct dm_event_fifos *fifos)
 	if (!_start_daemon(dmeventd_path, fifos))
 		return_0;
 
-	return init_fifos(fifos);
+	return dm_event_daemon_init_fifos(fifos);
 }
 
-void fini_fifos(struct dm_event_fifos *fifos)
+void dm_event_daemon_fini_fifos(struct dm_event_fifos *fifos)
 {
 	if (fifos->client >= 0 && close(fifos->client))
 		log_sys_debug("close", fifos->client_path);
@@ -627,16 +627,16 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag
 		goto_out;
 	}
 
-	ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
+	ret = dm_event_daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
 
 	free(msg->data);
 	msg->data = 0;
 
 	if (!ret)
-		ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
+		ret = dm_event_daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
 out:
 	/* what is the opposite of init? */
-	fini_fifos(&fifos);
+	dm_event_daemon_fini_fifos(&fifos);
 
 	return ret;
 }
@@ -845,7 +845,7 @@ int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
 	char *p;
 	struct dm_event_daemon_message msg = { 0 };
 
-	if (daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0))
+	if (dm_event_daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0))
 		return 0;
 	p = msg.data;
 	*version = 0;
diff --git a/daemons/lvmdbusd/Makefile.in b/daemons/lvmdbusd/Makefile.in
index 9c262927956350b305b33489f57c97bee280ce5f..4b09b5746eaa6d77ef264a340515f910e2e6c3d6 100644
--- a/daemons/lvmdbusd/Makefile.in
+++ b/daemons/lvmdbusd/Makefile.in
@@ -58,8 +58,6 @@ install_lvmdbusd: $(LVMDBUSD)
 	$(Q) $(INSTALL_DIR) $(lvmdbusdir) $(lvmdbusdir)/__pycache__
 	$(Q) (cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(lvmdbusdir))
 	$(Q) $(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(lvmdbusdir)
-	$(Q) PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbuspydir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES)
-	$(Q) $(CHMOD) 444 $(lvmdbusdir)/__pycache__/*.py[co]
 
 install_lvm2: install_lvmdbusd
 
diff --git a/make.tmpl.in b/make.tmpl.in
index fcb62f78a8cc4f837b0f5ee0009d6bc6e6075a16..b8cbecee5b99269e008384991af98f105f80686e 100644
--- a/make.tmpl.in
+++ b/make.tmpl.in
@@ -495,7 +495,7 @@ DEFS+=-D_FILE_OFFSET_BITS=64
 %.o: %.cpp $(DEPS)
 	@echo "    [CXX] $(<F)"
 	@mkdir -p $(@D)
-	$(Q) $(CXX) $(DEPFLAGS) -c $(INCLUDES) $(VALGRIND_CFLAGS) $(DEFS) $(DEFS_$@) $(WFLAGS) $(CXXFLAGS) $(CXXFLAGS_$@) $< -o $@
+	$(Q) $(CXX) $(DEPFLAGS) -c $(INCLUDES) $(VALGRIND_CFLAGS) $(DEFS) $(DEFS_$@) $(WFLAGS) $(CXXFLAGS) $(CXXFLAGS_$@) $(CPPFLAGS) $< -o $@
 
 %.pot: %.c $(DEPS)
 	@echo "    [CC] $(<F)"
diff --git a/udev/69-dm-lvm.rules.in b/udev/69-dm-lvm.rules.in
index ff15681456403f52c9743e54901372c9fafa0e54..7390f1269e302da2c5c7ca644346739b12537cf4 100644
--- a/udev/69-dm-lvm.rules.in
+++ b/udev/69-dm-lvm.rules.in
@@ -76,8 +76,14 @@ LABEL="lvm_scan"
 # it's better suited to appearing in the journal.
 
 IMPORT{program}="(LVM_EXEC)/lvm pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event --udevoutput --journal=output $env{DEVNAME}"
+TEST!="/run/systemd/system", GOTO="lvm_direct_vgchange"
+
 ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="(SYSTEMDRUN) --no-block --property DefaultDependencies=no --unit lvm-activate-$env{LVM_VG_NAME_COMPLETE} (LVM_EXEC)/lvm vgchange -aay --autoactivation event $env{LVM_VG_NAME_COMPLETE}"
 GOTO="lvm_end"
 
+LABEL="lvm_direct_vgchange"
+ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="(LVM_EXEC)/lvm vgchange -aay --autoactivation event $env{LVM_VG_NAME_COMPLETE}"
+GOTO="lvm_end"
+
 LABEL="lvm_end"
 
diff --git a/udev/Makefile.in b/udev/Makefile.in
index e592c534d384363a964c4b543b6bda7e7aff5c53..285188f9c94015f0a9d57230a094188707579531 100644
--- a/udev/Makefile.in
+++ b/udev/Makefile.in
@@ -17,8 +17,8 @@ top_builddir = @top_builddir@
 
 include $(top_builddir)/make.tmpl
 
-DM_RULES=10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
-LVM_RULES=11-dm-lvm.rules 69-dm-lvm.rules
+DM_RULES=55-dm.rules 60-persistent-storage-dm.rules 95-dm-notify.rules
+LVM_RULES=56-lvm.rules 69-lvm.rules
 
 DM_DIR=$(shell $(AWK) '/\#define DM_DIR/ {print $$3}' $(top_srcdir)/libdm/misc/dm-ioctl.h)
 
@@ -46,6 +46,18 @@ endif
 %.rules: $(srcdir)/%.rules.in
 	$(Q) $(SED) -e "s+(DM_DIR)+$(DM_DIR)+;s+(SYSTEMDRUN)+$(SYSTEMDRUN)+;s+(BLKID_RULE)+$(BLKID_RULE)+;s+(DM_EXEC_RULE)+$(DM_EXEC_RULE)+;s+(DM_EXEC)+$(DM_EXEC)+;s+(LVM_EXEC_RULE)+$(LVM_EXEC_RULE)+;s+(LVM_EXEC)+$(LVM_EXEC)+;" $< >$@
 
+55-dm.rules: 10-dm.rules
+	ln -s $< $@
+
+56-lvm.rules: 11-dm-lvm.rules
+	ln -s $< $@
+
+60-persistent-storage-dm.rules: 13-dm-disk.rules
+	ln -s $< $@
+
+69-lvm.rules: 69-dm-lvm.rules
+	ln -s $< $@
+
 %_install: %.rules
 	@echo "    [INSTALL] $<"
 	$(Q) $(INSTALL_DATA) -D $< $(udevdir)/$(<F)
