Blame SOURCES/0001-multipathd-warn-when-configuration-has-been-changed.patch

8444ee
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
8444ee
From: Benjamin Marzinski <bmarzins@redhat.com>
8444ee
Date: Thu, 19 Sep 2019 13:46:03 -0500
8444ee
Subject: [PATCH] multipathd: warn when configuration has been changed.
8444ee
8444ee
It would be helpful if multipathd could log a message when
8444ee
multipath.conf or files in the config_dir have been written to, both so
8444ee
that it can be used to send a notification to users, and to help with
8444ee
determining after the fact if multipathd was running with an older
8444ee
config, when the logs of multipathd's behaviour don't match with the
8444ee
current multipath.conf.
8444ee
8444ee
To do this, the multipathd uxlsnr thread now sets up inotify watches on
8444ee
both /etc/multipath.conf and the config_dir to watch if the files are
8444ee
deleted or closed after being opened for writing.  In order to keep
8444ee
uxlsnr from polling repeatedly if the multipath.conf or the config_dir
8444ee
aren't present, it will only set up the watches once per reconfigure.
8444ee
However, since multipath.conf is far more likely to be replaced by a
8444ee
text editor than modified in place, if it gets removed, multipathd will
8444ee
immediately try to restart the watch on it (which will succeed if the
8444ee
file was simply replaced by a new copy).  This does mean that if
8444ee
multipath.conf or the config_dir are actually removed and then later
8444ee
re-added, multipathd won't log any more messages for changes until the
8444ee
next reconfigure. But that seems like a fair trade-off to avoid
8444ee
repeatedly polling for files that aren't likely to appear.
8444ee
8444ee
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
8444ee
---
8444ee
 libmultipath/config.h |   1 +
8444ee
 multipathd/main.c     |   1 +
8444ee
 multipathd/uxlsnr.c   | 134 ++++++++++++++++++++++++++++++++++++++++--
8444ee
 3 files changed, 130 insertions(+), 6 deletions(-)
8444ee
8444ee
diff --git a/libmultipath/config.h b/libmultipath/config.h
8444ee
index ffec3103..e69aa07c 100644
8444ee
--- a/libmultipath/config.h
8444ee
+++ b/libmultipath/config.h
8444ee
@@ -188,6 +188,7 @@ struct config {
8444ee
 	int find_multipaths_timeout;
8444ee
 	int marginal_pathgroups;
8444ee
 	unsigned int version[3];
8444ee
+	unsigned int sequence_nr;
8444ee
 
8444ee
 	char * multipath_dir;
8444ee
 	char * selector;
8444ee
diff --git a/multipathd/main.c b/multipathd/main.c
8444ee
index 34a57689..7b364cfe 100644
8444ee
--- a/multipathd/main.c
8444ee
+++ b/multipathd/main.c
8444ee
@@ -2618,6 +2618,7 @@ reconfigure (struct vectors * vecs)
8444ee
 	uxsock_timeout = conf->uxsock_timeout;
8444ee
 
8444ee
 	old = rcu_dereference(multipath_conf);
8444ee
+	conf->sequence_nr = old->sequence_nr + 1;
8444ee
 	rcu_assign_pointer(multipath_conf, conf);
8444ee
 	call_rcu(&old->rcu, rcu_free_config);
8444ee
 
8444ee
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
8444ee
index bc71679e..92d9a79a 100644
8444ee
--- a/multipathd/uxlsnr.c
8444ee
+++ b/multipathd/uxlsnr.c
8444ee
@@ -23,6 +23,7 @@
8444ee
 #include <sys/time.h>
8444ee
 #include <signal.h>
8444ee
 #include <stdbool.h>
8444ee
+#include <sys/inotify.h>
8444ee
 #include "checkers.h"
8444ee
 #include "memory.h"
8444ee
 #include "debug.h"
8444ee
@@ -51,6 +52,8 @@ struct client {
8444ee
 LIST_HEAD(clients);
8444ee
 pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
8444ee
 struct pollfd *polls;
8444ee
+int notify_fd = -1;
8444ee
+char *config_dir;
8444ee
 
8444ee
 static bool _socket_client_is_root(int fd);
8444ee
 
8444ee
@@ -151,6 +154,8 @@ void uxsock_cleanup(void *arg)
8444ee
 	long ux_sock = (long)arg;
8444ee
 
8444ee
 	close(ux_sock);
8444ee
+	close(notify_fd);
8444ee
+	free(config_dir);
8444ee
 
8444ee
 	pthread_mutex_lock(&client_lock);
8444ee
 	list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
8444ee
@@ -162,6 +167,106 @@ void uxsock_cleanup(void *arg)
8444ee
 	free_polls();
8444ee
 }
8444ee
 
8444ee
+/* failing to set the watch descriptor is o.k. we just miss a warning
8444ee
+ * message */
8444ee
+void reset_watch(int notify_fd, int *wds, unsigned int *sequence_nr)
8444ee
+{
8444ee
+	struct config *conf;
8444ee
+	int dir_reset = 0;
8444ee
+	int conf_reset = 0;
8444ee
+
8444ee
+	if (notify_fd == -1)
8444ee
+		return;
8444ee
+
8444ee
+	conf = get_multipath_config();
8444ee
+	/* instead of repeatedly try to reset the inotify watch if
8444ee
+	 * the config directory or multipath.conf isn't there, just
8444ee
+	 * do it once per reconfigure */
8444ee
+	if (*sequence_nr != conf->sequence_nr) {
8444ee
+		*sequence_nr = conf->sequence_nr;
8444ee
+		if (wds[0] == -1)
8444ee
+			conf_reset = 1;
8444ee
+		if (!config_dir || !conf->config_dir ||
8444ee
+		    strcmp(config_dir, conf->config_dir)) {
8444ee
+			dir_reset = 1;
8444ee
+			if (config_dir)
8444ee
+				free(config_dir);
8444ee
+			if (conf->config_dir)
8444ee
+				config_dir = strdup(conf->config_dir);
8444ee
+			else
8444ee
+				config_dir = NULL;
8444ee
+		} else if (wds[1] == -1)
8444ee
+			dir_reset = 1;
8444ee
+	}
8444ee
+	put_multipath_config(conf);
8444ee
+
8444ee
+	if (dir_reset) {
8444ee
+		if (wds[1] != -1) {
8444ee
+			inotify_rm_watch(notify_fd, wds[1]);
8444ee
+			wds[1] = -1;
8444ee
+		}
8444ee
+		if (config_dir) {
8444ee
+			wds[1] = inotify_add_watch(notify_fd, config_dir,
8444ee
+						   IN_CLOSE_WRITE | IN_DELETE |
8444ee
+						   IN_ONLYDIR);
8444ee
+			if (wds[1] == -1)
8444ee
+				condlog(3, "didn't set up notifications on %s: %s", config_dir, strerror(errno));
8444ee
+		}
8444ee
+	}
8444ee
+	if (conf_reset) {
8444ee
+		wds[0] = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE,
8444ee
+					   IN_CLOSE_WRITE);
8444ee
+		if (wds[0] == -1)
8444ee
+			condlog(3, "didn't set up notifications on /etc/multipath.conf: %s", strerror(errno));
8444ee
+	}
8444ee
+	return;
8444ee
+}
8444ee
+
8444ee
+void handle_inotify(int fd, int  *wds)
8444ee
+{
8444ee
+	char buff[1024]
8444ee
+		__attribute__ ((aligned(__alignof__(struct inotify_event))));
8444ee
+	const struct inotify_event *event;
8444ee
+	ssize_t len;
8444ee
+	char *ptr;
8444ee
+	int i, got_notify = 0;
8444ee
+
8444ee
+	for (;;) {
8444ee
+		len = read(fd, buff, sizeof(buff));
8444ee
+		if (len <= 0) {
8444ee
+			if (len < 0 && errno != EAGAIN) {
8444ee
+				condlog(3, "error reading from inotify_fd");
8444ee
+				for (i = 0; i < 2; i++) {
8444ee
+					if (wds[i] != -1) {
8444ee
+						inotify_rm_watch(fd, wds[i]);
8444ee
+						wds[i] = -1;
8444ee
+					}
8444ee
+				}
8444ee
+			}
8444ee
+			break;
8444ee
+		}
8444ee
+
8444ee
+		got_notify = 1;
8444ee
+		for (ptr = buff; ptr < buff + len;
8444ee
+		     ptr += sizeof(struct inotify_event) + event->len) {
8444ee
+			event = (const struct inotify_event *) ptr;
8444ee
+
8444ee
+			if (event->mask & IN_IGNORED) {
8444ee
+				/* multipathd.conf may have been overwritten.
8444ee
+				 * Try once to reset the notification */
8444ee
+				if (wds[0] == event->wd)
8444ee
+					wds[0] = inotify_add_watch(notify_fd,
8444ee
+							DEFAULT_CONFIGFILE,
8444ee
+							IN_CLOSE_WRITE);
8444ee
+				else if (wds[1] == event->wd)
8444ee
+					wds[1] = -1;
8444ee
+			}
8444ee
+		}
8444ee
+	}
8444ee
+	if (got_notify)
8444ee
+		condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
8444ee
+}
8444ee
+
8444ee
 /*
8444ee
  * entry point
8444ee
  */
8444ee
@@ -173,13 +278,19 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
8444ee
 	char *reply;
8444ee
 	sigset_t mask;
8444ee
 	int old_clients = MIN_POLLS;
8444ee
+	/* conf->sequence_nr will be 1 when uxsock_listen is first called */
8444ee
+	unsigned int sequence_nr = 0;
8444ee
+	int wds[2] = { -1, -1 };
8444ee
 
8444ee
 	condlog(3, "uxsock: startup listener");
8444ee
-	polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd));
8444ee
+	polls = (struct pollfd *)MALLOC((MIN_POLLS + 2) * sizeof(struct pollfd));
8444ee
 	if (!polls) {
8444ee
 		condlog(0, "uxsock: failed to allocate poll fds");
8444ee
 		exit_daemon();
8444ee
 	}
8444ee
+	notify_fd = inotify_init1(IN_NONBLOCK);
8444ee
+	if (notify_fd == -1) /* it's fine if notifications fail */
8444ee
+		condlog(3, "failed to start up configuration notifications");
8444ee
 	sigfillset(&mask);
8444ee
 	sigdelset(&mask, SIGINT);
8444ee
 	sigdelset(&mask, SIGTERM);
8444ee
@@ -198,18 +309,18 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
8444ee
 		if (num_clients != old_clients) {
8444ee
 			struct pollfd *new;
8444ee
 			if (num_clients <= MIN_POLLS && old_clients > MIN_POLLS) {
8444ee
-				new = REALLOC(polls, (1 + MIN_POLLS) *
8444ee
+				new = REALLOC(polls, (2 + MIN_POLLS) *
8444ee
 						sizeof(struct pollfd));
8444ee
 			} else if (num_clients <= MIN_POLLS && old_clients <= MIN_POLLS) {
8444ee
 				new = polls;
8444ee
 			} else {
8444ee
-				new = REALLOC(polls, (1+num_clients) *
8444ee
+				new = REALLOC(polls, (2 + num_clients) *
8444ee
 						sizeof(struct pollfd));
8444ee
 			}
8444ee
 			if (!new) {
8444ee
 				pthread_mutex_unlock(&client_lock);
8444ee
 				condlog(0, "%s: failed to realloc %d poll fds",
8444ee
-					"uxsock", 1 + num_clients);
8444ee
+					"uxsock", 2 + num_clients);
8444ee
 				sched_yield();
8444ee
 				continue;
8444ee
 			}
8444ee
@@ -219,8 +330,15 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
8444ee
 		polls[0].fd = ux_sock;
8444ee
 		polls[0].events = POLLIN;
8444ee
 
8444ee
+		reset_watch(notify_fd, wds, &sequence_nr);
8444ee
+		if (notify_fd == -1 || (wds[0] == -1 && wds[1] == -1))
8444ee
+			polls[1].fd = -1;
8444ee
+		else
8444ee
+			polls[1].fd = notify_fd;
8444ee
+		polls[1].events = POLLIN;
8444ee
+
8444ee
 		/* setup the clients */
8444ee
-		i = 1;
8444ee
+		i = 2;
8444ee
 		list_for_each_entry(c, &clients, node) {
8444ee
 			polls[i].fd = c->fd;
8444ee
 			polls[i].events = POLLIN;
8444ee
@@ -262,7 +380,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
8444ee
 		}
8444ee
 
8444ee
 		/* see if a client wants to speak to us */
8444ee
-		for (i = 1; i < num_clients + 1; i++) {
8444ee
+		for (i = 2; i < num_clients + 2; i++) {
8444ee
 			if (polls[i].revents & POLLIN) {
8444ee
 				struct timespec start_time;
8444ee
 
8444ee
@@ -321,6 +439,10 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
8444ee
 		if (polls[0].revents & POLLIN) {
8444ee
 			new_client(ux_sock);
8444ee
 		}
8444ee
+
8444ee
+		/* handle inotify events on config files */
8444ee
+		if (polls[1].revents & POLLIN)
8444ee
+			handle_inotify(notify_fd, wds);
8444ee
 	}
8444ee
 
8444ee
 	return NULL;
8444ee
-- 
8444ee
2.17.2
8444ee