autofs-5.1.8 - add command pipe handling functions

From: Ian Kent <raven@themaw.net>

In order to use a single file handle for a command pipe the pipe needs
to be independent of the kernel message packet handling function.

Add most of the functions needed for this as preperation.

Signed-off-by: Ian Kent <raven@themaw.net>
---
 CHANGELOG          |    1 
 daemon/automount.c |  269 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 270 insertions(+)

diff --git a/CHANGELOG b/CHANGELOG
index 86326a8d..09b5b157 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -66,6 +66,7 @@
 - eliminate last remaining state_pipe usage.
 - add function master_find_mapent_by_devid().
 - use device id to locate autofs_point when setting log priotity.
+- add command pipe handling functions.
 
 19/10/2021 autofs-5.1.8
 - add xdr_exports().
diff --git a/daemon/automount.c b/daemon/automount.c
index 1164198e..33301121 100644
--- a/daemon/automount.c
+++ b/daemon/automount.c
@@ -70,6 +70,14 @@ unsigned int nfs_mount_uses_string_options = 0;
 static struct nfs_mount_vers vers, check = {1, 1, 1};
 
 #define FIFO_BUF_SIZE		25
+static int cmd_pipe_fifo = -1;
+
+/* autofs cmd fifo name */
+#define FIFO_NAME "autofs.cmd.fifo"
+const char *cmd_pipe_name = AUTOFS_FIFO_DIR "/" FIFO_NAME;
+
+int start_cmd_pipe_handler(void);
+void finish_cmd_pipe_handler(void);
 
 /* autofs fifo name prefix */
 #define FIFO_NAME_PREFIX "autofs.fifo"
@@ -1687,6 +1695,267 @@ static void *signal_handler(void *arg)
 	}
 }
 
+static pthread_mutex_t cmd_pipe_mutex = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int done = 0;
+static pthread_t cmd_pipe_thid;
+
+void cmd_pipe_mutex_lock(void)
+{
+	int status = pthread_mutex_lock(&cmd_pipe_mutex);
+	if (status)
+		fatal(status);
+}
+
+void cmd_pipe_mutex_unlock(void)
+{
+	int status = pthread_mutex_unlock(&cmd_pipe_mutex);
+	if (status)
+		fatal(status);
+}
+
+static int create_cmd_pipe_fifo(void)
+{
+	char buf[MAX_ERR_BUF];
+	int ret = -1;
+	int fd;
+
+	if (cmd_pipe_fifo != -1)
+		return 0;
+
+	ret = unlink(cmd_pipe_name);
+	if (ret != 0 && errno != ENOENT) {
+		fprintf(stderr,
+			"%s: failed to unlink command pipe. Is the "
+			"automount daemon already running?", program);
+		return ret;
+	}
+
+	ret = mkfifo(cmd_pipe_name, S_IRUSR|S_IWUSR);
+	if (ret != 0 && errno != EEXIST) {
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		fprintf(stderr, "%s: mkfifo for %s failed: %s",
+			program, cmd_pipe_name, estr);
+		return ret;
+	}
+
+	fd = open_fd(cmd_pipe_name, O_RDWR|O_NONBLOCK);
+	if (fd < 0) {
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		unlink(cmd_pipe_name);
+		fprintf(stderr, "%s: failed to open cwcommand pipe %s: %s",
+			program, cmd_pipe_name, estr);
+		return -1;
+	}
+
+	cmd_pipe_fifo = fd;
+
+	return 0;
+}
+
+static int destroy_cmd_pipe_fifo(void)
+{
+	char buf[MAX_ERR_BUF];
+	int ret = -1;
+
+	if (cmd_pipe_fifo == -1)
+		return 0;
+
+	ret = close(cmd_pipe_fifo);
+	if (ret != 0) {
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		warn(LOGOPT_ANY,
+		     "close for command pipe %s: %s", cmd_pipe_name, estr);
+	}
+
+	cmd_pipe_fifo = -1;
+
+	ret = unlink(cmd_pipe_name);
+	if (ret != 0) {
+		warn(LOGOPT_ANY,
+		     "failed to unlink FIFO. Was the fifo created OK?");
+	}
+
+	return 0;
+}
+
+static void handle_cmd_pipe_fifo_message(int fd)
+{
+	struct autofs_point *ap;
+	char buffer[PIPE_BUF];
+	char *end, *sep;
+	char buf[MAX_ERR_BUF];
+	dev_t devid;
+	int ret;
+	long pri;
+
+	memset(buffer, 0, sizeof(buffer));
+	ret = read(fd, &buffer, sizeof(buffer));
+	if (ret < 0) {
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		warn(LOGOPT_ANY,
+		     "read on command pipe returned error: %s", estr);
+		return;
+	}
+
+	sep = strrchr(buffer, ' ');
+	if (!sep) {
+		error(LOGOPT_ANY,
+		      "incorrect command pipe message format %s.", buffer);
+		return;
+	}
+	sep++;
+
+	errno = 0;
+	devid = strtol(buffer, &end, 10);
+	if ((devid == LONG_MIN || devid == LONG_MAX) && errno == ERANGE) {
+		debug(LOGOPT_ANY, "strtol reported a range error.");
+		error(LOGOPT_ANY, "invalid command pipe message format %s.", buffer);
+		return;
+	}
+
+	if ((devid == 0 && errno == EINVAL) || end == buffer) {
+		debug(LOGOPT_ANY, "devid id is expected to be a integer.");
+		return;
+	}
+
+	ap = master_find_mapent_by_devid(master_list, devid);
+	if (!ap) {
+		error(LOGOPT_ANY, "can't locate autofs_point for device id %ld.", devid);
+		return;
+	}
+
+	errno = 0;
+	pri = strtol(sep, &end, 10);
+	if ((pri == LONG_MIN || pri == LONG_MAX) && errno == ERANGE) {
+		error(ap->logopt, "failed to set log priority.");
+		error(ap->logopt, "strtol reported an %s.",
+		      pri == LONG_MIN ? "underflow" : "overflow");
+		return;
+	}
+
+	if ((pri == 0 && errno == EINVAL) || end == sep) {
+		debug(ap->logopt, "priority is expected to be an integer "
+		      "in the range 0-7 inclusive.");
+		return;
+	}
+
+	if (pri > LOG_DEBUG || pri < LOG_EMERG) {
+		debug(ap->logopt,
+		      "invalid log priority (%ld) received on fifo", pri);
+		return;
+	}
+
+	/*
+	 * OK, the message passed all of the sanity checks.  The
+	 * automounter actually only supports three log priorities.
+	 * Everything is logged at log level debug, deamon messages
+	 * and everything except debug messages are logged with the
+	 * verbose setting and only error and critical messages are
+	 * logged when debugging isn't enabled.
+	 */
+	if (pri >= LOG_WARNING) {
+		if (pri == LOG_DEBUG) {
+			set_log_debug_ap(ap);
+			info(ap->logopt, "debug logging set for %s", ap->path);
+		} else {
+			set_log_verbose_ap(ap);
+			info(ap->logopt, "verbose logging set for %s", ap->path);
+		}
+	} else {
+		if (ap->logopt & LOGOPT_ANY)
+			info(ap->logopt, "basic logging set for %s", ap->path);
+		set_log_norm_ap(ap);
+	}
+}
+
+static void cmd_pipe_dummy(int sig)
+{
+}
+
+static void *cmd_pipe_handler(void *arg)
+{
+	struct sigaction sa;
+	sigset_t signalset;
+	struct pollfd fds[1];
+	int pollfds = 1;
+	char buf[MAX_ERR_BUF];
+	char *estr;
+
+	if (create_cmd_pipe_fifo())
+		return NULL;
+
+	fds[0].fd = cmd_pipe_fifo;
+	fds[0].events = POLLIN;
+
+	sa.sa_handler = cmd_pipe_dummy;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = 0;
+	if (sigaction(SIGPIPE, &sa, NULL) == -1) {
+		error(LOGOPT_ANY, "failed to set signal handler %d", errno);
+		return NULL;
+	}
+
+	sigfillset(&signalset);
+	sigdelset(&signalset, SIGPIPE);
+
+	while (1) {
+		cmd_pipe_mutex_lock();
+		if (done) {
+			cmd_pipe_mutex_unlock();
+			break;
+		}
+		cmd_pipe_mutex_unlock();
+
+		errno = 0;
+		if (ppoll(fds, pollfds, NULL, &signalset) == -1) {
+			if (errno == EINTR)
+				continue;
+			estr = strerror_r(errno, buf, MAX_ERR_BUF);
+			logerr("poll failed: %s", estr);
+			return NULL;
+		}
+
+		if (fds[0].revents & POLLIN) {
+			debug(LOGOPT_ANY, "message pending on control fifo.");
+			handle_cmd_pipe_fifo_message(fds[0].fd);
+		}
+	}
+	destroy_cmd_pipe_fifo();
+	return NULL;
+}
+
+int start_cmd_pipe_handler(void)
+{
+	pthread_t thid;
+	pthread_attr_t attrs;
+	pthread_attr_t *pattrs = &attrs;
+	int status;
+
+	status = pthread_attr_init(pattrs);
+	if (status)
+		pattrs = NULL;
+	else
+		pthread_attr_setdetachstate(pattrs, PTHREAD_CREATE_DETACHED);
+
+	status = pthread_create(&thid, pattrs, cmd_pipe_handler, NULL);
+
+	if (pattrs)
+		pthread_attr_destroy(pattrs);
+
+	if (!status)
+		cmd_pipe_thid = thid;
+
+	return !status;
+}
+
+void finish_cmd_pipe_handler(void)
+{
+	cmd_pipe_mutex_lock();
+	done = 1;
+	pthread_kill(cmd_pipe_thid, SIGPIPE);
+	cmd_pipe_mutex_unlock();
+}
+
 static void return_start_status(void *arg)
 {
 	struct startup_cond *sc;