[Ocfs2-tools-devel] [PATCH 17/39] ocfs2_controld: Add mountgroups and mount client communication.

Joel Becker joel.becker at oracle.com
Fri Mar 14 16:52:40 PDT 2008


The daemon now accepts the MOUNT, MRESULT, and UNMOUNT messages as
specified in the protocol.  It builds mountgroup structures as well as
the associated mountpoints.  It properly handles secondary mountpoints
as well as all but one client death scenario.

Currently it fakes joining and leaving groups.  That's the next step.

Signed-off-by: Joel Becker <joel.becker at oracle.com>
---
 ocfs2_controld/Makefile         |    2 +-
 ocfs2_controld/cman.c           |   17 +-
 ocfs2_controld/cpg.c            |    6 +-
 ocfs2_controld/main.c           |  118 +++++---
 ocfs2_controld/mount.c          |  645 +++++++++++++++++++++++++++++++++++++++
 ocfs2_controld/ocfs2_controld.h |   19 +-
 6 files changed, 751 insertions(+), 56 deletions(-)
 create mode 100644 ocfs2_controld/mount.c

diff --git a/ocfs2_controld/Makefile b/ocfs2_controld/Makefile
index e5476e8..1888728 100644
--- a/ocfs2_controld/Makefile
+++ b/ocfs2_controld/Makefile
@@ -30,7 +30,7 @@ DEFINES = -DOCFS2_FLAT_INCLUDES -DO2DLM_FLAT_INCLUDES \
 
 UNINST_HFILES = ocfs2_controld.h
 
-DAEMON_CFILES = main.c cman.c cpg.c
+DAEMON_CFILES = main.c cman.c cpg.c mount.c
 TEST_CFILES = test_client.c
 
 DAEMON_OBJS = $(subst .c,.o,$(DAEMON_CFILES))
diff --git a/ocfs2_controld/cman.c b/ocfs2_controld/cman.c
index d1815eb..520bf99 100644
--- a/ocfs2_controld/cman.c
+++ b/ocfs2_controld/cman.c
@@ -96,6 +96,19 @@ char *nodeid2name(int nodeid)
 	return cn->cn_name;
 }
 
+int validate_cluster(const char *cluster)
+{
+	if (!clustername) {
+		log_error("Trying to validate before cman is alive");
+		return 0;
+	}
+
+	if (!cluster)
+		return 0;
+
+	return !strcmp(cluster, clustername);
+}
+
 /* keep track of the nodes */
 static void statechange(void)
 {
@@ -163,7 +176,7 @@ static void dead_cman(int ci)
 
 	log_error("cman connection died");
 	shutdown_daemon();
-	client_dead(ci);
+	connection_dead(ci);
 }
 
 static void process_cman(int ci)
@@ -235,7 +248,7 @@ int setup_cman(void)
 	/* Fill the node list */
 	statechange();
 
-	cman_ci = client_add(fd, process_cman, dead_cman);
+	cman_ci = connection_add(fd, process_cman, dead_cman);
 	if (cman_ci < 0) {
 		rv = cman_ci;
 		log_error("Unable to add cman client: %s",
diff --git a/ocfs2_controld/cpg.c b/ocfs2_controld/cpg.c
index 80fb42b..cd29d02 100644
--- a/ocfs2_controld/cpg.c
+++ b/ocfs2_controld/cpg.c
@@ -217,7 +217,7 @@ static void dead_cpg(int ci)
 		daemon_group.cg_handle = 0;
 	}
 
-	client_dead(ci);
+	connection_dead(ci);
 }
 
 int setup_cpg(void)
@@ -231,8 +231,8 @@ int setup_cpg(void)
 	}
 
 	cpg_fd_get(daemon_group.cg_handle, &daemon_group.cg_fd);
-	daemon_group.cg_ci = client_add(daemon_group.cg_fd,
-					process_cpg, dead_cpg);
+	daemon_group.cg_ci = connection_add(daemon_group.cg_fd,
+					    process_cpg, dead_cpg);
 	if (daemon_group.cg_ci < 0) {
 		log_error("Unable to add cpg client: %s",
 			  strerror(-daemon_group.cg_ci));
diff --git a/ocfs2_controld/main.c b/ocfs2_controld/main.c
index f86376d..c5515f4 100644
--- a/ocfs2_controld/main.c
+++ b/ocfs2_controld/main.c
@@ -87,7 +87,7 @@ static void handler(int signum)
 static void dead_sigpipe(int ci)
 {
 	log_error("Error on the signal pipe");
-	client_dead(ci);
+	connection_dead(ci);
 	shutdown_daemon();
 }
 
@@ -119,19 +119,15 @@ static void handle_signal(int ci)
 		case SIGTERM:
 		case SIGINT:
 		case SIGHUP:
-#if 0
-			if (list_empty(&mounts)) {
-#endif
-				log_error("Caught signal %d, exiting",
-					  caught_sig);
-				rc = 1;
-#if 0
-			} else {
+			if (have_mounts()) {
 				log_error("Caught signal %d, but mounts exist.  Ignoring.",
 					  caught_sig);
 				rc = 0;
+			} else {
+				log_error("Caught signal %d, exiting",
+					  caught_sig);
+				rc = 1;
 			}
-#endif
 			break;
 
 		case SIGSEGV:
@@ -195,7 +191,7 @@ static int setup_sigpipe(void)
 		goto out;
 	}
 
-	rc = client_add(signal_pipe[0], handle_signal, dead_sigpipe);
+	rc = connection_add(signal_pipe[0], handle_signal, dead_sigpipe);
 	if (rc < 0)
 		log_error("Unable to add signal pipe: %s", strerror(-rc));
 
@@ -241,7 +237,50 @@ int do_write(int fd, void *buf, size_t count)
 	return 0;
 }
 
-void client_dead(int ci)
+static int do_mount(int ci, int fd, const char *fstype, const char *uuid,
+		    const char *cluster, const char *device,
+		    const char *mountpoint)
+{
+	char *error_msg;
+
+	if (!fstype || strcmp(fstype, OCFS2_FS_NAME)) {
+		error_msg = "Invalid filesystem type";
+		goto fail;
+	}
+
+	if (!validate_cluster(cluster)) {
+		error_msg = "Invalid cluster name";
+		goto fail;
+	}
+
+	return start_mount(ci, fd, uuid, device, mountpoint);
+
+fail:
+	return send_message(fd, CM_STATUS, EINVAL, error_msg);
+}
+
+static int do_mount_result(int ci, int fd, const char *fstype,
+			   const char *uuid, const char *errcode,
+			   const char *mountpoint)
+{
+	if (!fstype || strcmp(fstype, OCFS2_FS_NAME))
+		return send_message(fd, CM_STATUS, EINVAL,
+				    "Invalid filesystem type");
+
+	return complete_mount(ci, fd, uuid, errcode, mountpoint);
+}
+
+static int do_unmount(int ci, int fd, const char *fstype, const char *uuid,
+		      const char *mountpoint)
+{
+	if (!fstype || strcmp(fstype, OCFS2_FS_NAME))
+		return send_message(fd, CM_STATUS, EINVAL,
+				    "Invalid filesystem type");
+
+	return remove_mount(ci, fd, uuid, mountpoint);
+}
+
+void connection_dead(int ci)
 {
 	log_debug("client %d fd %d dead", ci, client[ci].fd);
 	close(client[ci].fd);
@@ -289,7 +328,7 @@ static int client_alloc(void)
 	return 0;
 }
 
-int client_add(int fd, void (*work)(int ci), void (*dead)(int ci))
+int connection_add(int fd, void (*work)(int ci), void (*dead)(int ci))
 {
 	int i;
 
@@ -304,7 +343,7 @@ int client_add(int fd, void (*work)(int ci), void (*dead)(int ci))
 			if (client[i].fd == -1) {
 				client[i].fd = fd;
 				client[i].work = work;
-				client[i].dead = dead ? dead : client_dead;
+				client[i].dead = dead ? dead : connection_dead;
 				pollfd[i].fd = fd;
 				pollfd[i].events = POLLIN;
 				if (i > client_maxi)
@@ -335,11 +374,14 @@ static int dump_debug(int ci)
 	return 0;
 }
 
+static void dead_client(int ci)
+{
+	dead_mounter(ci, client[ci].fd);
+	connection_dead(ci);
+}
+
 static void process_client(int ci)
 {
-#if 0
-	struct mountgroup *mg;
-#endif
 	client_message message;
 	char *argv[OCFS2_CONTROLD_MAXARGS + 1];
 	char buf[OCFS2_CONTROLD_MAXLINE];
@@ -349,7 +391,7 @@ static void process_client(int ci)
 	/* receive_message ensures we have the proper number of arguments */
 	rv = receive_message(fd, buf, &message, argv);
 	if (rv == -EPIPE) {
-		client_dead(ci);
+		dead_client(ci);
 		return;
 	}
 
@@ -364,34 +406,20 @@ static void process_client(int ci)
 
 	switch (message) {
 		case CM_MOUNT:
-#if 0
 		rv = do_mount(ci, fd, argv[0], argv[1], argv[2], argv[3],
-			      argv[4], &mg);
+			      argv[4]);
 		fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
-		if (!rv || rv == -EALREADY) {
-			client[ci].another_mount = rv;
-			client[ci].mg = mg;
-			mg->mount_client_fd = fd;
-		}
-#endif
 		break;
 
 		case CM_MRESULT:
-#if 0
-		rv = do_mount_result(client[ci].mg, ci,
-				     client[ci].another_mount,
-				     argv[0], argv[1], argv[2], argv[3]);
-#endif
+		rv = do_mount_result(ci, fd, argv[0], argv[1], argv[2],
+				     argv[3]);
 		break;
 
 		case CM_UNMOUNT:
-#if 0
 		rv = do_unmount(ci, fd, argv[0], argv[1], argv[2]);
-		if (!rv) {
-			client[ci].mg = mg;
-			mg->mount_client_fd = fd;
-		}
-#endif
+		if (!rv)
+			hack_leave(argv[1]);
 		break;
 
 		case CM_STATUS:
@@ -451,7 +479,7 @@ static void process_listener(int ci)
 		return;
 	}
 
-	i = client_add(fd, process_client, NULL);
+	i = connection_add(fd, process_client, NULL);
 	if (i < 0) {
 		log_error("Error adding client: %s", strerror(-i));
 		close(fd);
@@ -462,7 +490,7 @@ static void process_listener(int ci)
 static void dead_listener(int ci)
 {
 	log_error("Error on the listening socket");
-	client_dead(ci);
+	connection_dead(ci);
 	shutdown_daemon();
 }
 
@@ -477,7 +505,7 @@ static int setup_listener(void)
 		return fd;
 	}
 
-	i = client_add(fd, process_listener, dead_listener);
+	i = connection_add(fd, process_listener, dead_listener);
 	if (i < 0) {
 		log_error("Unable to add listening socket: %s",
 			  strerror(-i));
@@ -541,10 +569,8 @@ static int loop(void)
 	}
 
 stop:
-#if 0
-	if (!rv && !list_empty(&mounts))
+	if (!rv && have_mounts())
 		rv = 1;
-#endif
 
 #if 0
 	bail_on_mounts();
@@ -709,10 +735,8 @@ int main(int argc, char **argv)
 {
 	errcode_t err;
 	prog_name = argv[0];
-#if 0
-	INIT_LIST_HEAD(&mounts);
-#endif
-	/* INIT_LIST_HEAD(&withdrawn_mounts); */
+
+	init_mounts();
 
 	initialize_o2cb_error_table();
 	err = o2cb_init();
diff --git a/ocfs2_controld/mount.c b/ocfs2_controld/mount.c
new file mode 100644
index 0000000..d849cc8
--- /dev/null
+++ b/ocfs2_controld/mount.c
@@ -0,0 +1,645 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * Copyright (C) 2007 Oracle.  All rights reserved.
+ *
+ *  This copyrighted material is made available to anyone wishing to use,
+ *  modify, copy, or redistribute it subject to the terms and conditions
+ *  of the GNU General Public License v.2.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <syslog.h>
+#include <time.h>
+
+#include "ocfs2-kernel/kernel-list.h"
+#include "ocfs2-kernel/sparse_endian_types.h"
+#include "ocfs2-kernel/ocfs2_fs.h"
+#include "o2cb/o2cb_client_proto.h"
+
+#include "ocfs2_controld.h"
+
+
+struct mountpoint {
+	struct list_head	mp_list;
+	char			mp_mountpoint[PATH_MAX + 1];
+};
+
+struct mountgroup {
+	struct list_head	mg_list;
+	struct cgroup		*mg_group;
+	int			mg_leave_on_join;
+
+	char			mg_uuid[OCFS2_VOL_UUID_LEN + 1];
+	char			mg_device[PATH_MAX + 1];
+
+	struct list_head	mg_mountpoints;
+	struct mountpoint	*mg_mp_in_progress;
+
+	/* Communication with mount/umount.ocfs2 */
+	int			mg_mount_ci;
+	int			mg_mount_fd;
+	int			mg_mount_notified;
+
+	int			mg_error;
+	char			mg_error_msg[128];
+};
+
+
+static struct list_head mounts;
+
+static void fill_error(struct mountgroup *mg, int error, char *errfmt, ...)
+{
+	int rc;
+	va_list args;
+
+	/* Don't overwrite an error */
+	if (mg->mg_error)
+		return;
+
+	mg->mg_error = error;
+	va_start(args, errfmt);
+	rc = vsnprintf(mg->mg_error_msg, sizeof(mg->mg_error_msg),
+		       errfmt, args);
+	va_end(args);
+
+	if (rc >= sizeof(mg->mg_error_msg)) {
+		log_debug("Error message truncated");
+		mg->mg_error_msg[sizeof(mg->mg_error_msg) - 1] = '\0';
+	}
+}
+
+int have_mounts(void)
+{
+	return !list_empty(&mounts);
+}
+
+static struct mountgroup *find_mg_by_uuid(const char *uuid)
+{
+	struct list_head *p;
+	struct mountgroup *mg;
+
+	list_for_each(p, &mounts) {
+		mg = list_entry(p, struct mountgroup, mg_list);
+		if ((strlen(mg->mg_uuid) == strlen(uuid)) &&
+		    !strncmp(mg->mg_uuid, uuid, strlen(uuid)))
+			return mg;
+	}
+
+	return NULL;
+}
+
+static struct mountgroup *find_mg_by_client(int ci)
+{
+	struct list_head *p;
+	struct mountgroup *mg;
+
+	if (ci < 0)
+		return NULL;
+
+	list_for_each(p, &mounts) {
+		mg = list_entry(p, struct mountgroup, mg_list);
+		if (mg->mg_mount_ci == ci)
+			return mg;
+	}
+
+	return NULL;
+}
+
+static struct mountgroup *create_mg(const char *uuid, const char *device)
+{
+	struct mountgroup *mg = NULL;
+
+	if (strlen(uuid) > OCFS2_VOL_UUID_LEN) {
+		log_error("uuid too long!");
+		goto out;
+	}
+
+	mg = malloc(sizeof(struct mountgroup));
+	if (!mg)
+		goto out;
+
+	memset(mg, 0, sizeof(struct mountgroup));
+	INIT_LIST_HEAD(&mg->mg_mountpoints);
+	mg->mg_mount_ci = -1;
+	mg->mg_mount_fd = -1;
+	strncpy(mg->mg_uuid, uuid, sizeof(mg->mg_uuid));
+	strncpy(mg->mg_device, device, sizeof(mg->mg_device));
+	list_add(&mg->mg_list, &mounts);
+
+out:
+	return mg;
+}
+
+static void notify_mount_client(struct mountgroup *mg)
+{
+	int error = mg->mg_error;
+	char *error_msg = "OK";
+
+	if (error) {
+		if (mg->mg_error_msg[0])
+			error_msg = mg->mg_error_msg;
+		else
+			error_msg = strerror(error);
+		mg->mg_error = 0;
+	}
+
+	log_debug("notify_mount_client sending %d \"%s\"", error,
+		  error_msg);
+
+	if (mg->mg_mount_fd < 0) {
+		log_debug("not sending - client went away");
+		return;
+	}
+
+	error = send_message(mg->mg_mount_fd, CM_STATUS, error, error_msg);
+	if (error)
+		log_error("Unable to notify client, send_message failed with %d: %s",
+			  -error, strerror(-error));
+	else
+		mg->mg_mount_notified = 1;
+
+	/*
+	 * XXX If we failed to notify the client, what can we do?  I'm
+	 * guessing that our main loop will get POLLHUP and we'll clean
+	 * up.
+	 */
+}
+
+static struct mountpoint *find_mountpoint(struct mountgroup *mg,
+					  const char *mountpoint)
+{
+	struct list_head *p;
+	struct mountpoint *mp;
+
+	list_for_each(p, &mg->mg_mountpoints) {
+		mp = list_entry(p, struct mountpoint, mp_list);
+		if ((strlen(mp->mp_mountpoint) == strlen(mountpoint)) &&
+		    !strcmp(mp->mp_mountpoint, mountpoint))
+			return mp;
+	}
+
+	return NULL;
+}
+
+static void remove_mountpoint(struct mountgroup *mg,
+			      const char *mountpoint)
+{
+	struct mountpoint *mp;
+
+	mp = find_mountpoint(mg, mountpoint);
+
+	if (!mp) {
+		log_error("mountpoint \"%s\" not found for mountgroup \"%s\"",
+			  mountpoint, mg->mg_uuid);
+		return;
+	}
+
+	list_del(&mp->mp_list);
+
+	/*
+	 * We must clear the list here so that dead_mounter()
+	 * knows we're in the middle of a LEAVE.
+	 */
+	INIT_LIST_HEAD(&mp->mp_list);
+
+	if (list_empty(&mg->mg_mountpoints)) {
+		/* Set in-progress for leave */
+		mg->mg_mp_in_progress = mp;
+
+		log_debug("time to leave group %s", mg->mg_uuid);
+		if (mg->mg_group) {
+			log_debug("calling LEAVE for group %s",
+				  mg->mg_uuid);
+			/* XXX leave the group */
+		} else {
+			/*
+			 * Join is in progress, let's leave when we get
+			 * there.
+			 */
+			log_debug("Not joined %s, so set leave_on_join",
+				  mg->mg_uuid);
+			mg->mg_leave_on_join = 1;
+		}
+	} else
+		free(mp);
+}
+
+void hack_leave(char *uuid)
+{
+	struct mountgroup *mg;
+	struct mountpoint *mp;
+
+	mg = find_mg_by_uuid(uuid);
+	if (!mg) {
+		log_error("Unable to find mg for \"%s\"", uuid);
+		return;
+	}
+
+	if (!list_empty(&mg->mg_mountpoints))
+		return;
+
+	mp = mg->mg_mp_in_progress;
+	if (!mp) {
+		log_error("No mp in progress for \"%s\"", uuid);
+		return;
+	}
+
+	if (!mg->mg_leave_on_join) {
+		log_error("leave_on_join not set on \"%s\"", uuid);
+		return;
+	}
+
+	log_debug("leaving group %s", uuid);
+	free(mp);
+	list_del(&mg->mg_list);
+	free(mg);
+}
+
+static void add_mountpoint(struct mountgroup *mg, const char *device,
+			   const char *mountpoint, int ci, int fd)
+{
+	struct mountpoint *mp;
+
+	log_debug("Adding mountpoint %s to device %s uuid %s",
+		  mountpoint, device, mg->mg_uuid);
+
+	if (strcmp(mg->mg_device, device)) {
+		fill_error(mg, EINVAL,
+			   "Trying to mount fs %s on device %s, but it is already mounted from device %s",
+			   mg->mg_uuid, device, mg->mg_device);
+		return;
+	}
+
+	if (find_mountpoint(mg, mountpoint)) {
+		fill_error(mg, EBUSY,
+			   "Filesystem %s is already mounted on %s",
+			   mg->mg_uuid, mountpoint);
+		return;
+	}
+
+	if (mg->mg_mp_in_progress) {
+		fill_error(mg, EBUSY, "Another mount is in progress");
+		return;
+	}
+
+	if ((mg->mg_mount_ci != -1) ||
+	    (mg->mg_mount_fd != -1)) {
+		log_error("adding a mountpoint, but ci/fd are set: %d %d",
+			  mg->mg_mount_ci, mg->mg_mount_fd);
+	}
+
+	mp = malloc(sizeof(struct mountpoint));
+	if (!mp) {
+		fill_error(mg, ENOMEM,
+			   "Unable to allocate mountpoint structure");
+		return;
+	}
+
+	memset(mp, 0, sizeof(struct mountpoint));
+	strncpy(mp->mp_mountpoint, mountpoint, sizeof(mp->mp_mountpoint));
+	mg->mg_mount_ci = ci;
+	mg->mg_mount_fd = fd;
+	mg->mg_mp_in_progress = mp;
+
+	/*
+	 * This special error is returned to mount.ocfs2 when the filesystem
+	 * is already mounted elsewhere.  The group is already joined, and
+	 * no additional work is required from ocfs2_controld.  When
+	 * mount.ocfs2 sees this error, it will just clal mount(2).
+	 */
+	if (!list_empty(&mg->mg_mountpoints))
+		fill_error(mg, EALREADY, "Already mounted, go ahead");
+
+	list_add(&mp->mp_list, &mg->mg_mountpoints);
+}
+
+int start_mount(int ci, int fd, const char *uuid, const char *device,
+		const char *mountpoint)
+{
+	int rc = 0;
+	struct mountgroup mg_error = { /* Until we have a real mg */
+		.mg_error	= 0,
+	};
+	struct mountgroup *mg = &mg_error;
+
+	log_debug("start_mount: uuid \"%s\", device \"%s\", mountpoint \"%s\"",
+		  uuid, device, mountpoint);
+
+	if (strlen(uuid) > OCFS2_VOL_UUID_LEN) {
+		fill_error(mg, ENAMETOOLONG, "UUID too long: %s", uuid);
+		goto out;
+	}
+
+	mg = find_mg_by_uuid(uuid);
+	if (mg) {
+		add_mountpoint(mg, device, mountpoint, ci, fd);
+		goto out;
+	}
+
+	/* Here we stop using &mg_error and start using the real one */
+	mg = create_mg(uuid, device);
+	if (!mg) {
+		mg = &mg_error;  /* Well, almost did */
+		fill_error(mg, ENOMEM,
+			   "Unable to allocate mountgroup structure");
+		goto out;
+	}
+
+	add_mountpoint(mg, device, mountpoint, ci, fd);
+	if (mg->mg_error)
+		goto out;
+
+	/* XXX This is where we do the asynchronous join
+	 *
+	 * Here we fire off a group join.  The cpg infrastructure will
+	 * let us know when the group is joined, at which point we
+	 * notify_mount_client().  If there's a failure, we notify as well.
+	 *
+	 * XXX: For now, let's pretend :-)
+	 */
+	notify_mount_client(mg);
+
+out:
+	/*
+	 * Only reply on error.  If we're doing OK, the reply is delayed
+	 * until join completes (notify_mount_client()).
+	 *
+	 * This reply includes -EALREADY, which tells the mount client that
+	 * we're doing an additional mount - it can just go ahead.
+	 */
+	if (mg->mg_error) {
+		rc = -mg->mg_error;
+		send_message(fd, CM_STATUS, mg->mg_error, mg->mg_error_msg);
+		mg->mg_error = 0;
+
+		if (mg->mg_error == EALREADY)
+			mg->mg_mount_notified = 1;
+		else {
+			log_error("mount: %s", mg->mg_error_msg);
+
+			if ((mg != &mg_error) &&
+			    list_empty(&mg->mg_mountpoints)) {
+				log_debug("mount: freeing failed mountgroup");
+				list_del(&mg->mg_list);
+				free(mg);
+			}
+		}
+	}
+
+	log_debug("start_mount returns %d", rc);
+
+	return rc;
+}
+
+int complete_mount(int ci, int fd, const char *uuid, const char *errcode,
+		   const char *mountpoint)
+{
+	int rc = 0;
+	int reply = 1;
+	struct mountgroup mg_error = { /* Until we have a real mg */
+		.mg_error	= 0,
+	};
+	struct mountgroup *mg;
+	struct mountpoint *mp;
+	long err;
+	char *ptr = NULL;
+
+	log_debug("complete_mount: uuid \"%s\", errcode \"%s\", mountpoint \"%s\"",
+		  uuid, errcode, mountpoint);
+
+	mg = find_mg_by_client(ci);
+	if (!mg) {
+		mg = &mg_error;
+		fill_error(mg, EINVAL,
+			   "Client is not attached to a mountgroup");
+		goto out;
+	}
+
+	if (mg->mg_mount_fd != fd) {
+		fill_error(mg, EINVAL,
+			   "Client file descriptor does not match");
+		goto out;
+	}
+
+	if (strlen(uuid) > OCFS2_VOL_UUID_LEN) {
+		fill_error(mg, EINVAL,
+			   "UUID too long: %s", uuid);
+		goto out;
+	}
+
+	if (strcmp(uuid, mg->mg_uuid)) {
+		fill_error(mg, EINVAL,
+			   "UUID %s does not match mountgroup %s", uuid,
+			   mg->mg_uuid);
+		goto out;
+	}
+
+	if (!mg->mg_mp_in_progress) {
+		fill_error(mg, ENOENT,
+			   "No mount in progress for filesystem %s",
+			   mg->mg_uuid);
+		goto out;
+	}
+
+	mp = find_mountpoint(mg, mountpoint);
+	if (!mp) {
+		fill_error(mg, ENOENT,
+			   "Unknown mountpoint %s for filesystem %s",
+			   mountpoint, mg->mg_uuid);
+		goto out;
+	}
+
+	if (mp != mg->mg_mp_in_progress) {
+		fill_error(mg, EINVAL, "Mountpoint %s is not in progress",
+			   mountpoint);
+		goto out;
+	}
+
+	err = strtol(errcode, &ptr, 10);
+	if (ptr && *ptr != '\0') {
+		fill_error(mg, EINVAL, "Invalid error code string: %s",
+			   errcode);
+		goto out;
+	}
+	if ((err == LONG_MIN) || (err == LONG_MAX) ||
+	    (err < INT_MIN) || (err > INT_MAX)) {
+		fill_error(mg, ERANGE, "Error code %ld is out of range",
+			   err);
+		goto out;
+	}
+
+	/*
+	 * Clear the in-progress pointer and store off the reply fd.  If
+	 * there was an error, remove_mountpoint may reset the in-progress
+	 * pointer.
+	 */
+	mg->mg_mp_in_progress = NULL;
+
+	if (!err) {
+		mg->mg_mount_fd = -1;
+		mg->mg_mount_ci = -1;
+	} else {
+		/*
+		 * remove_mountpoint() will kick off a leave if this was
+		 * the last mountpoint.  As part of the leave, it will add
+		 * reset mp_in_progress.
+		 */
+		remove_mountpoint(mg, mountpoint);
+
+		/*
+		 * We don't pass err onto mg->mg_error because it came
+		 * from mount.ocfs2.  We actually respond with 0, as we
+		 * successfully processed the MRESULT.  Unless
+		 * remove_mountpoint() set mg_error.
+		 */
+	}
+
+	if (mg->mg_mp_in_progress)
+		reply = 0;
+
+out:
+	if (reply)
+		send_message(fd, CM_STATUS, mg->mg_error,
+			     mg->mg_error ? mg->mg_error_msg : "OK");
+
+	return rc;
+}
+
+int remove_mount(int ci, int fd, const char *uuid, const char *mountpoint)
+{
+	int rc = 0;
+	int reply = 1;
+	struct mountgroup mg_error = {
+		.mg_error	= 0,
+	};
+	struct mountgroup *mg = NULL;
+	struct mountpoint *mp;
+
+	log_debug("remove_mount: uuid \"%s\", mountpoint \"%s\"",
+		  uuid, mountpoint);
+
+	if (strlen(uuid) > OCFS2_VOL_UUID_LEN) {
+		fill_error(&mg_error, ENAMETOOLONG, "UUID too long: %s",
+			   uuid);
+		goto out;
+	}
+
+	mg = find_mg_by_uuid(uuid);
+	if (!mg) {
+		fill_error(&mg_error, ENOENT, "Unknown filesystem %s",
+			   uuid);
+		goto out;
+	}
+
+	/* find_mg() should fail if the uuid isn't mounted *somewhere* */
+	if (list_empty(&mg->mg_mountpoints))
+		log_error("Mountpoint list is empty!");
+
+	mp = find_mountpoint(mg, mountpoint);
+	if (!mp) {
+		fill_error(&mg_error, ENOENT,
+			   "Filesystem %s is not mounted on %s", uuid,
+			   mountpoint);
+		goto out;
+	}
+
+	if (mg->mg_mp_in_progress) {
+		fill_error(&mg_error, EBUSY,
+			   "Another mount is in progress");
+		goto out;;
+	}
+
+	if ((mg->mg_mount_ci != -1) ||
+	    (mg->mg_mount_fd != -1)) {
+		log_error("removing a mountpoint, but ci/fd are set: %d %d",
+			  mg->mg_mount_ci, mg->mg_mount_fd);
+	}
+
+	remove_mountpoint(mg, mountpoint);
+	if (mg->mg_mp_in_progress) {
+		/*
+		 * remove_mountpoint() kicked off a LEAVE.  It needs the
+		 * umount.ocfs2 client connection information.  It will
+		 * handle replying via notify_mount_client().
+		 */
+		mg->mg_mount_ci = ci;
+		mg->mg_mount_fd = fd;
+		reply = 0;
+	} else if (mg->mg_error) {
+		fill_error(&mg_error, mg->mg_error, "%s", mg->mg_error_msg);
+	}
+
+out:
+	if (reply)
+		send_message(fd, CM_STATUS, mg_error.mg_error,
+			     mg_error.mg_error ? mg_error.mg_error_msg : "OK");
+
+	if (mg_error.mg_error)
+		rc = -mg_error.mg_error;
+
+	return rc;
+}
+
+void dead_mounter(int ci, int fd)
+{
+	struct mountgroup *mg;
+	struct mountpoint *mp;
+
+	/* If there's no matching mountgroup, nothing to do. */
+	mg = find_mg_by_client(ci);
+	if (!mg)
+		return;
+
+	mp = mg->mg_mp_in_progress;
+
+	/* If we have nothing in progress, nothing to do. */
+	if (!mp)
+		return;
+
+	mg->mg_mount_ci = -1;
+	mg->mg_mount_fd = -1;
+
+	/*
+	 * If mp_list is empty, the daemon is in the process
+	 * of leaving the group.  We need that to complete whether we
+	 * have a client or not.
+	 */
+	if (list_empty(&mp->mp_list))
+		return;
+
+	/*
+	 * We haven't notified the client yet.  Thus, the client can't have
+	 * called mount(2).  Let's just abort this mountpoint.  If this was
+	 * the last mountpoint, we'll plan to leave the group.
+	 */
+	if (!mg->mg_mount_notified)
+		remove_mountpoint(mg, mp->mp_mountpoint);
+
+	/*
+	 * XXX
+	 *
+	 * This is the hard one.  If we've notified the client, we're
+	 * expecting the client to call mount(2).  But the client died.
+	 * We don't know if that happened, so we can't leave the group.
+	 *
+	 * That's not totally true, btw.  Eventually we'll be opening the
+	 * sysfs file.  Once we have that info, we'll do better here.
+	 */
+}
+
+void init_mounts(void)
+{
+	INIT_LIST_HEAD(&mounts);
+}
+
diff --git a/ocfs2_controld/ocfs2_controld.h b/ocfs2_controld/ocfs2_controld.h
index e7a719d..426528a 100644
--- a/ocfs2_controld/ocfs2_controld.h
+++ b/ocfs2_controld/ocfs2_controld.h
@@ -28,8 +28,9 @@
 #define DUMP_SIZE			(1024 * 1024)
 
 
+struct cgroup;
+
 extern char *prog_name;
-extern struct list_head mounts;
 extern int daemon_debug_opt;
 extern char daemon_debug_buf[1024];
 extern char dump_buf[DUMP_SIZE];
@@ -63,16 +64,28 @@ do { \
 } while (0)
 
 
-int client_add(int fd, void (*work)(int ci), void (*dead)(int ci));
-void client_dead(int ci);
+int connection_add(int fd, void (*work)(int ci), void (*dead)(int ci));
+void connection_dead(int ci);
 void shutdown_daemon(void);
 
 int setup_cman(void);
 char *nodeid2name(int nodeid);
+int validate_cluster(const char *cluster);
 int kill_cman(int nodeid);
 void exit_cman(void);
 
 int setup_cpg(void);
 void exit_cpg(void);
 
+void init_mounts(void);
+int have_mounts(void);
+int start_mount(int ci, int fd, const char *uuid, const char *device,
+	     const char *mountpoint);
+int complete_mount(int ci, int fd, const char *uuid, const char *errcode,
+		   const char *mountpoint);
+int remove_mount(int ci, int fd, const char *uuid, const char *mountpoint);
+void dead_mounter(int ci, int fd);
+
+/* This is a hack to test umount until cpg leave happens */
+void hack_leave(char *uuid);
 #endif
-- 
1.5.3.8




More information about the Ocfs2-tools-devel mailing list