Просмотр исходного кода

quorum: Add support for nodelist callback

Current quorum callback contains only actual view list and there is no
way how to find out joined/left nodes. This cannot be emulated by user
app, because when corosync restarts before other nodes notices then view
list is unchanged (ring id is changed tho).

Solution is to implement similar callback as for cpg which contains ring
id, member list, joined list and left list.

To implement such callback and keep backwards compatibility,
quorum_model_initialize is introduced. Its behavior is similar to
cpg_model_initialize. This allows passing model v1, which contains
enhanced quorum (full ring id is passed instead of just seq number)
and nodelist callbacks.

To find out which events should be sent by corosync daemon, new message
MESSAGE_REQ_QUORUM_MODEL_GETTYPE is used. Quorum library on init was
sending MESSAGE_REQ_QUORUM_GETTYPE. Whem model v1 is requested the
MESSAGE_REQ_QUORUM_MODEL_GETTYPE is used, which contains model number
so corosync knows that client is using model v1 and can send enhanced
quorum and nodelist events.

Nodelist event is (for now) send both in case of change of membership
and also when requested, also when CS_TRACK_CURRENT is requested, but
then left_list and joined_list is left empty, because they don't make
too much sense there.

New test application testquorummodel is added as an example of new API
usage.

Also during patch developement, I found few bugs here and there, which
are also fixed:
- quorum_initialize was never returning error code returned by
  MESSAGE_REQ_QUORUM_GETTYPE call (always returned CS_OK)
- Allocated memory in send_library_notification was based
  on sizeof(unsigned int) instead of mar_uint32_t. That's not wrong,
  but   it make more sense to use sizeof(mar_uint32_t) instead

(big thanks to Chrissie for englishify the man pages)

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
Reviewed-by: Christine Caulfield <ccaulfie@redhat.com>
Jan Friesse 5 лет назад
Родитель
Сommit
4eb3629728

+ 332 - 15
exec/vsf_quorum.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2015 Red Hat, Inc.
+ * Copyright (c) 2008-2020 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -77,6 +77,7 @@ struct quorum_pd {
 	int tracking_enabled;
 	struct qb_list_head list;
 	void *conn;
+	enum lib_quorum_model model;
 };
 
 struct internal_callback_pd {
@@ -85,6 +86,19 @@ struct internal_callback_pd {
 	void *context;
 };
 
+static void quorum_sync_init (
+	const unsigned int *trans_list,
+	size_t trans_list_entries,
+	const unsigned int *member_list,
+	size_t member_list_entries,
+	const struct memb_ring_id *ring_id);
+
+static int quorum_sync_process (void);
+
+static void quorum_sync_activate (void);
+
+static void quorum_sync_abort (void);
+
 static void message_handler_req_lib_quorum_getquorate (void *conn,
 						       const void *msg);
 static void message_handler_req_lib_quorum_trackstart (void *conn,
@@ -93,8 +107,11 @@ static void message_handler_req_lib_quorum_trackstop (void *conn,
 						      const void *msg);
 static void message_handler_req_lib_quorum_gettype (void *conn,
 						       const void *msg);
+static void message_handler_req_lib_quorum_model_gettype (void *conn,
+						       const void *msg);
 static void send_library_notification(void *conn);
 static void send_internal_notification(void);
+static void send_nodelist_library_notification(void *conn, int send_joined_left_list);
 static char *quorum_exec_init_fn (struct corosync_api_v1 *api);
 static int quorum_lib_init_fn (void *conn);
 static int quorum_lib_exit_fn (void *conn);
@@ -105,12 +122,24 @@ static struct corosync_api_v1 *corosync_api;
 static struct qb_list_head lib_trackers_list;
 static struct qb_list_head internal_trackers_list;
 static struct memb_ring_id quorum_ring_id;
+static struct memb_ring_id last_sync_ring_id;
 static size_t quorum_view_list_entries = 0;
 static int quorum_view_list[PROCESSOR_COUNT_MAX];
 struct quorum_services_api_ver1 *quorum_iface = NULL;
+
 static char view_buf[64];
 
-static void log_view_list(const unsigned int *view_list, size_t view_list_entries)
+static unsigned int my_member_list[PROCESSOR_COUNT_MAX];
+static size_t my_member_list_entries;
+static unsigned int my_old_member_list[PROCESSOR_COUNT_MAX];
+static size_t my_old_member_list_entries = 0;
+static unsigned int my_left_list[PROCESSOR_COUNT_MAX];
+static size_t my_left_list_entries;
+static unsigned int my_joined_list[PROCESSOR_COUNT_MAX];
+static size_t my_joined_list_entries;
+
+static void log_view_list(const unsigned int *view_list, size_t view_list_entries,
+    const char *view_list_type_str)
 {
 	int total = (int)view_list_entries;
 	int len, pos, ret;
@@ -127,8 +156,8 @@ static void log_view_list(const unsigned int *view_list, size_t view_list_entrie
 				break;
 			pos += ret;
 		}
-		log_printf (LOGSYS_LEVEL_NOTICE, "Members[%d]:%s%s",
-			    total, view_buf, i < total ? "\\" : "");
+		log_printf (LOGSYS_LEVEL_NOTICE, "%s[%d]:%s%s",
+			    view_list_type_str, total, view_buf, i < total ? "\\" : "");
 
 		if (i == total)
 			break;
@@ -153,7 +182,7 @@ static void quorum_api_set_quorum(const unsigned int *view_list,
 	memcpy(&quorum_ring_id, ring_id, sizeof (quorum_ring_id));
 	memcpy(quorum_view_list, view_list, sizeof(unsigned int)*view_list_entries);
 
-	log_view_list(view_list, view_list_entries);
+	log_view_list(view_list, view_list_entries, "Members");
 
 	/* Tell internal listeners */
 	send_internal_notification();
@@ -179,6 +208,10 @@ static struct corosync_lib_handler quorum_lib_service[] =
 	{ /* 3 */
 		.lib_handler_fn				= message_handler_req_lib_quorum_gettype,
 		.flow_control				= CS_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 4 */
+		.lib_handler_fn				= message_handler_req_lib_quorum_model_gettype,
+		.flow_control				= CS_LIB_FLOW_CONTROL_NOT_REQUIRED
 	}
 };
 
@@ -193,6 +226,10 @@ static struct corosync_service_engine quorum_service_handler = {
 	.lib_exit_fn				= quorum_lib_exit_fn,
 	.lib_engine				= quorum_lib_service,
 	.exec_init_fn				= quorum_exec_init_fn,
+	.sync_init				= quorum_sync_init,
+	.sync_process				= quorum_sync_process,
+	.sync_activate				= quorum_sync_activate,
+	.sync_abort				= quorum_sync_abort,
 	.lib_engine_count			= sizeof (quorum_lib_service) / sizeof (struct corosync_lib_handler)
 };
 
@@ -251,6 +288,134 @@ static struct quorum_callin_functions callins = {
 
 /* --------------------------------------------------------------------- */
 
+static void quorum_sync_init (
+	const unsigned int *trans_list,
+	size_t trans_list_entries,
+	const unsigned int *member_list,
+	size_t member_list_entries,
+	const struct memb_ring_id *ring_id)
+{
+	int found;
+	int i, j;
+	int entries;
+	int node_joined;
+
+	memcpy (my_member_list, member_list, member_list_entries *
+	    sizeof (unsigned int));
+	my_member_list_entries = member_list_entries;
+
+	last_sync_ring_id = *ring_id;
+
+	/*
+	 * Determine left list of nodeids
+	 */
+	entries = 0;
+	for (i = 0; i < my_old_member_list_entries; i++) {
+		found = 0;
+		for (j = 0; j < trans_list_entries; j++) {
+			if (my_old_member_list[i] == trans_list[j]) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (found == 0) {
+			my_left_list[entries++] = my_old_member_list[i];
+		} else {
+			/*
+			 * Check it is really in new membership
+			 */
+			found = 0;
+
+			for (j = 0; j < my_member_list_entries; j++) {
+				if (my_old_member_list[i] == my_member_list[j]) {
+					found = 1;
+					break;
+				}
+			}
+
+			/*
+			 * Node is in both old_member_list and trans list but not in my_member_list.
+			 * (This shouldn't really happen).
+			 */
+			if (!found) {
+				my_left_list[entries++] = my_old_member_list[i];
+			}
+		}
+	}
+	my_left_list_entries = entries;
+
+	/*
+	 * Determine joined list of nodeids
+	 */
+	entries = 0;
+	for (i = 0; i < my_member_list_entries; i++) {
+		node_joined = 1;
+		for (j = 0; j < my_old_member_list_entries; j++) {
+			if (my_member_list[i] == my_old_member_list[j]) {
+				/*
+				 * Node is in member list and also in my_old_member list -> check
+				 * if it is in left_list.
+				 */
+				node_joined = 0;
+				break;
+			}
+		}
+
+		if (!node_joined) {
+			/*
+			 * Check if node is in left list.
+			 */
+			for (j = 0; j < my_left_list_entries; j++) {
+				if (my_member_list[i] == my_left_list[j]) {
+					/*
+					 * Node is both in left and also in member list -> joined
+					 */
+					node_joined = 1;
+					break;
+				}
+			}
+		}
+
+		if (node_joined) {
+			my_joined_list[entries++] = my_member_list[i];
+		}
+	}
+	my_joined_list_entries = entries;
+
+	log_view_list(my_member_list, my_member_list_entries, "Sync members");
+
+	if (my_joined_list_entries > 0) {
+		log_view_list(my_joined_list, my_joined_list_entries, "Sync joined");
+	}
+
+	if (my_left_list_entries > 0) {
+		log_view_list(my_left_list, my_left_list_entries, "Sync left");
+	}
+}
+
+static int quorum_sync_process (void)
+{
+
+	return (0);
+}
+
+static void quorum_sync_activate (void)
+{
+
+	memcpy (my_old_member_list, my_member_list,
+	    my_member_list_entries * sizeof (unsigned int));
+	my_old_member_list_entries = my_member_list_entries;
+
+	/* Tell IPC listeners */
+	send_nodelist_library_notification(NULL, 1);
+}
+
+static void quorum_sync_abort (void)
+{
+
+}
+
 static char *quorum_exec_init_fn (struct corosync_api_v1 *api)
 {
 	char *quorum_module = NULL;
@@ -316,6 +481,7 @@ static int quorum_lib_init_fn (void *conn)
 
 	qb_list_init (&pd->list);
 	pd->conn = conn;
+	pd->model = LIB_QUORUM_MODEL_V0;
 
 	return (0);
 }
@@ -346,16 +512,11 @@ static void send_internal_notification(void)
 	}
 }
 
-static void send_library_notification(void *conn)
+static void prepare_library_notification_v0(char *buf, size_t size)
 {
-	int size = sizeof(struct res_lib_quorum_notification) + sizeof(unsigned int)*quorum_view_list_entries;
-	char buf[size];
 	struct res_lib_quorum_notification *res_lib_quorum_notification = (struct res_lib_quorum_notification *)buf;
-	struct qb_list_head *tmp;
 	int i;
 
-	log_printf(LOGSYS_LEVEL_DEBUG, "sending quorum notification to %p, length = %d", conn, size);
-
 	res_lib_quorum_notification->quorate = primary_designated;
 	res_lib_quorum_notification->ring_seq = quorum_ring_id.seq;
 	res_lib_quorum_notification->view_list_entries = quorum_view_list_entries;
@@ -366,21 +527,147 @@ static void send_library_notification(void *conn)
 	res_lib_quorum_notification->header.id = MESSAGE_RES_QUORUM_NOTIFICATION;
 	res_lib_quorum_notification->header.size = size;
 	res_lib_quorum_notification->header.error = CS_OK;
+}
+
+static void prepare_library_notification_v1(char *buf, size_t size)
+{
+	struct res_lib_quorum_v1_quorum_notification *res_lib_quorum_v1_quorum_notification =
+	    (struct res_lib_quorum_v1_quorum_notification *)buf;
+	int i;
+
+	res_lib_quorum_v1_quorum_notification->quorate = primary_designated;
+	res_lib_quorum_v1_quorum_notification->ring_id.nodeid = quorum_ring_id.nodeid;
+	res_lib_quorum_v1_quorum_notification->ring_id.seq = quorum_ring_id.seq;
+	res_lib_quorum_v1_quorum_notification->view_list_entries = quorum_view_list_entries;
+	for (i=0; i<quorum_view_list_entries; i++) {
+		res_lib_quorum_v1_quorum_notification->view_list[i] = quorum_view_list[i];
+	}
+
+	res_lib_quorum_v1_quorum_notification->header.id = MESSAGE_RES_QUORUM_V1_QUORUM_NOTIFICATION;
+	res_lib_quorum_v1_quorum_notification->header.size = size;
+	res_lib_quorum_v1_quorum_notification->header.error = CS_OK;
+}
+
+static void send_library_notification(void *conn)
+{
+	int size_v0 = sizeof(struct res_lib_quorum_notification) +
+	    sizeof(mar_uint32_t) * quorum_view_list_entries;
+	int size_v1 = sizeof(struct res_lib_quorum_v1_quorum_notification) +
+	    sizeof(mar_uint32_t)*quorum_view_list_entries;
+
+	char buf_v0[size_v0];
+	char buf_v1[size_v1];
+
+	struct res_lib_quorum_notification *res_lib_quorum_notification =
+	    (struct res_lib_quorum_notification *)buf_v0;
+	struct res_lib_quorum_v1_quorum_notification *res_lib_quorum_v1_quorum_notification =
+	    (struct res_lib_quorum_v1_quorum_notification *)buf_v1;
+
+	struct quorum_pd *qpd;
+	struct qb_list_head *tmp;
+
+	log_printf(LOGSYS_LEVEL_DEBUG, "sending quorum notification to %p, length = %u/%u", conn, size_v0, size_v1);
+
+	prepare_library_notification_v0(buf_v0, size_v0);
+	prepare_library_notification_v1(buf_v1, size_v1);
 
 	/* Send it to all interested parties */
 	if (conn) {
-		corosync_api->ipc_dispatch_send(conn, res_lib_quorum_notification, size);
+		qpd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
+
+		if (qpd->model == LIB_QUORUM_MODEL_V0) {
+			corosync_api->ipc_dispatch_send(conn, res_lib_quorum_notification, size_v0);
+		} else if (qpd->model == LIB_QUORUM_MODEL_V1) {
+			corosync_api->ipc_dispatch_send(conn, res_lib_quorum_v1_quorum_notification, size_v1);
+		}
 	}
 	else {
-		struct quorum_pd *qpd;
+		qb_list_for_each(tmp, &lib_trackers_list) {
+			qpd = qb_list_entry(tmp, struct quorum_pd, list);
+
+			if (qpd->model == LIB_QUORUM_MODEL_V0) {
+				corosync_api->ipc_dispatch_send(qpd->conn,
+				     res_lib_quorum_notification, size_v0);
+			} else if (qpd->model == LIB_QUORUM_MODEL_V1) {
+				corosync_api->ipc_dispatch_send(qpd->conn,
+				     res_lib_quorum_v1_quorum_notification, size_v1);
+			}
+		}
+	}
+	return;
+}
+
+static void send_nodelist_library_notification(void *conn, int send_joined_left_list)
+{
+	int size = sizeof(struct res_lib_quorum_v1_nodelist_notification) +
+	    sizeof(mar_uint32_t) * my_member_list_entries;
+	char *buf;
+	struct res_lib_quorum_v1_nodelist_notification *res_lib_quorum_v1_nodelist_notification;
+	struct quorum_pd *qpd;
+	struct qb_list_head *tmp;
+	mar_uint32_t *ptr;
+	int i;
+
+	if (send_joined_left_list) {
+		size += sizeof(mar_uint32_t) * my_joined_list_entries;
+		size += sizeof(mar_uint32_t) * my_left_list_entries;
+	}
+
+	buf = alloca(size);
+	memset(buf, 0, size);
+
+	res_lib_quorum_v1_nodelist_notification = (struct res_lib_quorum_v1_nodelist_notification *)buf;
+
+	res_lib_quorum_v1_nodelist_notification->ring_id.nodeid = last_sync_ring_id.nodeid;
+	res_lib_quorum_v1_nodelist_notification->ring_id.seq = last_sync_ring_id.seq;
+	res_lib_quorum_v1_nodelist_notification->member_list_entries = my_member_list_entries;
+
+	if (send_joined_left_list) {
+		res_lib_quorum_v1_nodelist_notification->joined_list_entries = my_joined_list_entries;
+		res_lib_quorum_v1_nodelist_notification->left_list_entries = my_left_list_entries;
+	}
+
+	ptr = res_lib_quorum_v1_nodelist_notification->member_list;
+
+	for (i=0; i<my_member_list_entries; i++, ptr++) {
+		*ptr = my_member_list[i];
+	}
 
+	if (send_joined_left_list) {
+		for (i=0; i<my_joined_list_entries; i++, ptr++) {
+			*ptr = my_joined_list[i];
+		}
+
+		for (i=0; i<my_left_list_entries; i++, ptr++) {
+			*ptr = my_left_list[i];
+		}
+	}
+
+	res_lib_quorum_v1_nodelist_notification->header.id = MESSAGE_RES_QUORUM_V1_NODELIST_NOTIFICATION;
+	res_lib_quorum_v1_nodelist_notification->header.size = size;
+	res_lib_quorum_v1_nodelist_notification->header.error = CS_OK;
+
+	log_printf(LOGSYS_LEVEL_DEBUG, "sending nodelist notification to %p, length = %u", conn, size);
+
+	/* Send it to all interested parties */
+	if (conn) {
+		qpd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
+
+		if (qpd->model == LIB_QUORUM_MODEL_V1) {
+			corosync_api->ipc_dispatch_send(conn, res_lib_quorum_v1_nodelist_notification, size);
+		}
+	}
+	else {
 		qb_list_for_each(tmp, &lib_trackers_list) {
 			qpd = qb_list_entry(tmp, struct quorum_pd, list);
 
-			corosync_api->ipc_dispatch_send(qpd->conn,
-			     res_lib_quorum_notification, size);
+			if (qpd->model == LIB_QUORUM_MODEL_V1) {
+				corosync_api->ipc_dispatch_send(qpd->conn,
+				     res_lib_quorum_v1_nodelist_notification, size);
+			}
 		}
 	}
+
 	return;
 }
 
@@ -416,6 +703,7 @@ static void message_handler_req_lib_quorum_trackstart (void *conn,
 	if (req_lib_quorum_trackstart->track_flags & CS_TRACK_CURRENT ||
 	    req_lib_quorum_trackstart->track_flags & CS_TRACK_CHANGES) {
 		log_printf(LOGSYS_LEVEL_DEBUG, "sending initial status to %p", conn);
+		send_nodelist_library_notification(conn, 0);
 		send_library_notification(conn);
 	}
 
@@ -482,3 +770,32 @@ static void message_handler_req_lib_quorum_gettype (void *conn,
 	corosync_api->ipc_response_send(conn, &res_lib_quorum_gettype, sizeof(res_lib_quorum_gettype));
 }
 
+static void message_handler_req_lib_quorum_model_gettype (void *conn,
+						       const void *msg)
+{
+	const struct req_lib_quorum_model_gettype *req_lib_quorum_model_gettype = msg;
+	struct res_lib_quorum_model_gettype res_lib_quorum_model_gettype;
+	struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
+	cs_error_t ret_err;
+
+	log_printf(LOGSYS_LEVEL_DEBUG, "got quorum_model_type request on %p", conn);
+
+	ret_err = CS_OK;
+
+	if (req_lib_quorum_model_gettype->model != LIB_QUORUM_MODEL_V0 &&
+	    req_lib_quorum_model_gettype->model != LIB_QUORUM_MODEL_V1) {
+		log_printf(LOGSYS_LEVEL_ERROR, "quorum_model_type request for unsupported model %u",
+		    req_lib_quorum_model_gettype->model);
+
+		ret_err = CS_ERR_INVALID_PARAM;
+	} else {
+		quorum_pd->model = req_lib_quorum_model_gettype->model;
+	}
+
+	/* send status */
+	res_lib_quorum_model_gettype.quorum_type = quorum_type;
+	res_lib_quorum_model_gettype.header.size = sizeof(res_lib_quorum_model_gettype);
+	res_lib_quorum_model_gettype.header.id = MESSAGE_RES_QUORUM_MODEL_GETTYPE;
+	res_lib_quorum_model_gettype.header.error = ret_err;
+	corosync_api->ipc_response_send(conn, &res_lib_quorum_model_gettype, sizeof(res_lib_quorum_model_gettype));
+}

+ 49 - 3
include/corosync/ipc_quorum.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2011 Red Hat, Inc.
+ * Copyright (c) 2008-2020 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -44,7 +44,8 @@ enum req_quorum_types {
 	MESSAGE_REQ_QUORUM_GETQUORATE = 0,
 	MESSAGE_REQ_QUORUM_TRACKSTART,
 	MESSAGE_REQ_QUORUM_TRACKSTOP,
-	MESSAGE_REQ_QUORUM_GETTYPE
+	MESSAGE_REQ_QUORUM_GETTYPE,
+	MESSAGE_REQ_QUORUM_MODEL_GETTYPE
 };
 
 /**
@@ -55,9 +56,25 @@ enum res_quorum_types {
 	MESSAGE_RES_QUORUM_TRACKSTART,
 	MESSAGE_RES_QUORUM_TRACKSTOP,
 	MESSAGE_RES_QUORUM_NOTIFICATION,
-	MESSAGE_RES_QUORUM_GETTYPE
+	MESSAGE_RES_QUORUM_GETTYPE,
+	MESSAGE_RES_QUORUM_MODEL_GETTYPE,
+	MESSAGE_RES_QUORUM_V1_QUORUM_NOTIFICATION,
+	MESSAGE_RES_QUORUM_V1_NODELIST_NOTIFICATION
 };
 
+/*
+ * Must be in sync with definition in quorum.h
+ */
+enum lib_quorum_model {
+	LIB_QUORUM_MODEL_V0 = 0,
+	LIB_QUORUM_MODEL_V1 = 1,
+};
+
+typedef struct {
+	mar_uint32_t nodeid __attribute__((aligned(8)));
+	mar_uint64_t seq __attribute__((aligned(8)));
+} mar_quorum_ring_id_t;
+
 /**
  * @brief The req_lib_quorum_trackstart struct
  */
@@ -85,6 +102,25 @@ struct res_lib_quorum_notification {
 	mar_uint32_t view_list[];
 };
 
+struct res_lib_quorum_v1_quorum_notification {
+	struct qb_ipc_response_header header __attribute__((aligned(8)));
+	mar_int32_t quorate __attribute__((aligned(8)));
+	mar_quorum_ring_id_t ring_id __attribute__((aligned(8)));
+	mar_uint32_t view_list_entries __attribute__((aligned(8)));
+	mar_uint32_t view_list[];
+};
+
+struct res_lib_quorum_v1_nodelist_notification {
+	struct qb_ipc_response_header header __attribute__((aligned(8)));
+	mar_quorum_ring_id_t ring_id __attribute__((aligned(8)));
+	mar_uint32_t member_list_entries __attribute__((aligned(8)));
+	mar_uint32_t joined_list_entries __attribute__((aligned(8)));
+	mar_uint32_t left_list_entries __attribute__((aligned(8)));
+	mar_uint32_t member_list[];
+//	mar_uint32_t joined_list[];
+//	mar_uint32_t left_list[];
+};
+
 /**
  * @brief The res_lib_quorum_gettype struct
  */
@@ -93,4 +129,14 @@ struct res_lib_quorum_gettype {
 	mar_uint32_t quorum_type;
 };
 
+struct req_lib_quorum_model_gettype {
+        struct qb_ipc_request_header header __attribute__((aligned(8)));
+        mar_uint32_t model __attribute__((aligned(8)));
+};
+
+struct res_lib_quorum_model_gettype {
+	struct qb_ipc_response_header header __attribute__((aligned(8)));
+	mar_uint32_t quorum_type __attribute__((aligned(8)));
+};
+
 #endif

+ 48 - 1
include/corosync/quorum.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2012 Red Hat, Inc.
+ * Copyright (c) 2008-2020 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -40,11 +40,21 @@
 extern "C" {
 #endif
 
+typedef enum {
+	QUORUM_MODEL_V0 = 0,
+	QUORUM_MODEL_V1 = 1,
+} quorum_model_t;
+
 /**
  * @brief quorum_handle_t
  */
 typedef uint64_t quorum_handle_t;
 
+struct quorum_ring_id {
+	uint32_t nodeid;
+	uint64_t seq;
+};
+
 /**
  * @brief The quorum_notification_fn_t callback
  */
@@ -56,6 +66,21 @@ typedef void (*quorum_notification_fn_t) (
 	uint32_t *view_list
 	);
 
+typedef void (*quorum_v1_quorum_notification_fn_t) (
+	quorum_handle_t handle,
+	uint32_t quorate,
+	struct quorum_ring_id ring_id,
+	uint32_t member_list_entries, const uint32_t *member_list
+);
+
+typedef void (*quorum_v1_nodelist_notification_fn_t) (
+	quorum_handle_t handle,
+	struct quorum_ring_id ring_id,
+	uint32_t member_list_entries, const uint32_t *member_list,
+	uint32_t joined_list_entries, const uint32_t *joined_list,
+	uint32_t left_list_entries, const uint32_t *left_list
+);
+
 /**
  * @brief The quorum_callbacks_t struct
  */
@@ -63,6 +88,21 @@ typedef struct {
 	quorum_notification_fn_t quorum_notify_fn;
 } quorum_callbacks_t;
 
+typedef struct {
+	quorum_model_t model;
+} quorum_model_data_t;
+
+typedef struct {
+	quorum_model_t model;
+	quorum_notification_fn_t quorum_notify_fn;
+} quorum_model_v0_data_t;
+
+typedef struct {
+	quorum_model_t model;
+	quorum_v1_quorum_notification_fn_t quorum_notify_fn;
+	quorum_v1_nodelist_notification_fn_t nodelist_notify_fn;
+} quorum_model_v1_data_t;
+
 #define QUORUM_FREE	0
 #define QUORUM_SET	1
 
@@ -78,6 +118,13 @@ cs_error_t quorum_initialize (
 	quorum_callbacks_t *callbacks,
 	uint32_t *quorum_type);
 
+cs_error_t quorum_model_initialize (
+	quorum_handle_t *handle,
+	quorum_model_t model,
+	quorum_model_data_t *model_data,
+	uint32_t *quorum_type,
+	void *context);
+
 /**
  * @brief Close the quorum handle
  * @param handle

+ 1 - 1
lib/Makefile.am

@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2009 Red Hat, Inc.
+# Copyright (c) 2009-2020 Red Hat, Inc.
 #
 # Authors: Andrew Beekhof
 #	   Steven Dake (sdake@redhat.com)

+ 1 - 1
lib/libquorum.verso

@@ -1 +1 @@
-5.0.0
+5.1.0

+ 170 - 45
lib/quorum.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2012 Red Hat, Inc.
+ * Copyright (c) 2008-2020 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -59,7 +59,11 @@ struct quorum_inst {
 	qb_ipcc_connection_t *c;
 	int finalize;
 	const void *context;
-	quorum_callbacks_t callbacks;
+	union {
+		quorum_model_data_t model_data;
+		quorum_model_v0_data_t model_v0_data;
+		quorum_model_v1_data_t model_v1_data;
+	};
 };
 
 static void quorum_inst_free (void *inst);
@@ -70,12 +74,39 @@ cs_error_t quorum_initialize (
 	quorum_handle_t *handle,
 	quorum_callbacks_t *callbacks,
 	uint32_t *quorum_type)
+{
+	quorum_model_v0_data_t model_v0_data;
+
+	memset (&model_v0_data, 0, sizeof(quorum_model_v0_data_t));
+
+	if (callbacks) {
+		model_v0_data.quorum_notify_fn = callbacks->quorum_notify_fn;
+	}
+
+	return (quorum_model_initialize(handle, QUORUM_MODEL_V0,
+	    (quorum_model_data_t *)&model_v0_data, quorum_type, NULL));
+}
+
+cs_error_t quorum_model_initialize (
+	quorum_handle_t *handle,
+	quorum_model_t model,
+	quorum_model_data_t *model_data,
+	uint32_t *quorum_type,
+	void *context)
 {
 	cs_error_t error;
 	struct quorum_inst *quorum_inst;
 	struct iovec iov;
-	struct qb_ipc_request_header req;
+	struct qb_ipc_request_header quorum_gettype_req;
+	struct req_lib_quorum_model_gettype quorum_model_gettype_req;
 	struct res_lib_quorum_gettype res_lib_quorum_gettype;
+	struct res_lib_quorum_model_gettype res_lib_quorum_model_gettype;
+	uint32_t local_quorum_type;
+
+	if (model != QUORUM_MODEL_V0 && model != QUORUM_MODEL_V1) {
+		error = CS_ERR_INVALID_PARAM;
+		goto error_no_destroy;
+	}
 
 	error = hdb_error_to_cs(hdb_handle_create (&quorum_handle_t_db, sizeof (struct quorum_inst), handle));
 	if (error != CS_OK) {
@@ -95,35 +126,71 @@ cs_error_t quorum_initialize (
 		goto error_put_destroy;
 	}
 
-	req.size = sizeof (req);
-	req.id = MESSAGE_REQ_QUORUM_GETTYPE;
+	switch (model) {
+	case QUORUM_MODEL_V0:
+		quorum_gettype_req.size = sizeof (quorum_gettype_req);
+		quorum_gettype_req.id = MESSAGE_REQ_QUORUM_GETTYPE;
 
-	iov.iov_base = (char *)&req;
-	iov.iov_len = sizeof (req);
+		iov.iov_base = (char *)&quorum_gettype_req;
+		iov.iov_len = sizeof (quorum_gettype_req);
 
-	error = qb_to_cs_error(qb_ipcc_sendv_recv (
-		quorum_inst->c,
-		&iov,
-		1,
-		&res_lib_quorum_gettype,
-		sizeof (struct res_lib_quorum_gettype), -1));
+		error = qb_to_cs_error(qb_ipcc_sendv_recv (
+			quorum_inst->c,
+			&iov,
+			1,
+			&res_lib_quorum_gettype,
+			sizeof(res_lib_quorum_gettype), -1));
 
-	if (error != CS_OK) {
-		goto error_put_destroy;
+		if (error != CS_OK) {
+			goto error_put_destroy;
+		}
+		error = res_lib_quorum_gettype.header.error;
+		local_quorum_type = res_lib_quorum_gettype.quorum_type;
+		break;
+	case QUORUM_MODEL_V1:
+		quorum_model_gettype_req.header.size = sizeof (quorum_model_gettype_req);
+		quorum_model_gettype_req.header.id = MESSAGE_REQ_QUORUM_MODEL_GETTYPE;
+		quorum_model_gettype_req.model = model;
+
+		iov.iov_base = (char *)&quorum_model_gettype_req;
+		iov.iov_len = sizeof (quorum_model_gettype_req);
+
+		error = qb_to_cs_error(qb_ipcc_sendv_recv (
+			quorum_inst->c,
+			&iov,
+			1,
+			&res_lib_quorum_model_gettype,
+			sizeof(res_lib_quorum_model_gettype), -1));
+
+		if (error != CS_OK) {
+			goto error_put_destroy;
+		}
+		error = res_lib_quorum_model_gettype.header.error;
+		local_quorum_type = res_lib_quorum_model_gettype.quorum_type;
+		break;
 	}
 
-	error = res_lib_quorum_gettype.header.error;
+	if (quorum_type != NULL) {
+		*quorum_type = local_quorum_type;
+	}
 
-	*quorum_type = res_lib_quorum_gettype.quorum_type;
+	if (model_data != NULL) {
+		switch (model) {
+		case QUORUM_MODEL_V0:
+			memcpy(&quorum_inst->model_v0_data, model_data, sizeof(quorum_model_v0_data_t));
+			break;
+		case QUORUM_MODEL_V1:
+			memcpy(&quorum_inst->model_v1_data, model_data, sizeof(quorum_model_v1_data_t));
+			break;
+		}
+	}
 
-	if (callbacks)
-		memcpy(&quorum_inst->callbacks, callbacks, sizeof (*callbacks));
-	else
-		memset(&quorum_inst->callbacks, 0, sizeof (*callbacks));
+	quorum_inst->model_data.model = model;
+	quorum_inst->context = context;
 
 	(void)hdb_handle_put (&quorum_handle_t_db, *handle);
 
-	return (CS_OK);
+	return (error);
 
 error_put_destroy:
 	(void)hdb_handle_put (&quorum_handle_t_db, *handle);
@@ -356,10 +423,15 @@ cs_error_t quorum_dispatch (
 	cs_error_t error;
 	int cont = 1; /* always continue do loop except when set to 0 */
 	struct quorum_inst *quorum_inst;
-	quorum_callbacks_t callbacks;
+	struct quorum_inst quorum_inst_copy;
 	struct qb_ipc_response_header *dispatch_data;
 	char dispatch_buf[IPC_DISPATCH_SIZE];
 	struct res_lib_quorum_notification *res_lib_quorum_notification;
+	struct res_lib_quorum_v1_quorum_notification *res_lib_quorum_v1_quorum_notification;
+	struct res_lib_quorum_v1_nodelist_notification *res_lib_quorum_v1_nodelist_notification;
+	struct quorum_ring_id ring_id;
+	mar_uint32_t *joined_list;
+	mar_uint32_t *left_list;
 
 	if (dispatch_types != CS_DISPATCH_ONE &&
 		dispatch_types != CS_DISPATCH_ALL &&
@@ -417,29 +489,82 @@ cs_error_t quorum_dispatch (
 		 * A risk of this dispatch method is that the callback routines may
 		 * operate at the same time that quorum_finalize has been called in another thread.
 		 */
-		memcpy (&callbacks, &quorum_inst->callbacks, sizeof (quorum_callbacks_t));
-		/*
-		 * Dispatch incoming message
-		 */
-		switch (dispatch_data->id) {
-
-		case MESSAGE_RES_QUORUM_NOTIFICATION:
-			if (callbacks.quorum_notify_fn == NULL) {
+		memcpy (&quorum_inst_copy, quorum_inst, sizeof(quorum_inst_copy));
+		switch (quorum_inst_copy.model_data.model) {
+		case QUORUM_MODEL_V0:
+			/*
+			 * Dispatch incoming message
+			 */
+			switch (dispatch_data->id) {
+			case MESSAGE_RES_QUORUM_NOTIFICATION:
+				if (quorum_inst_copy.model_v0_data.quorum_notify_fn == NULL) {
+					break;
+				}
+				res_lib_quorum_notification = (struct res_lib_quorum_notification *)dispatch_data;
+
+				quorum_inst_copy.model_v0_data.quorum_notify_fn ( handle,
+					res_lib_quorum_notification->quorate,
+					res_lib_quorum_notification->ring_seq,
+					res_lib_quorum_notification->view_list_entries,
+					res_lib_quorum_notification->view_list);
 				break;
-			}
-			res_lib_quorum_notification = (struct res_lib_quorum_notification *)dispatch_data;
-
-			callbacks.quorum_notify_fn ( handle,
-				res_lib_quorum_notification->quorate,
-				res_lib_quorum_notification->ring_seq,
-				res_lib_quorum_notification->view_list_entries,
-				res_lib_quorum_notification->view_list);
-			break;
-
-		default:
-			error = CS_ERR_LIBRARY;
-			goto error_put;
-			break;
+			default:
+				error = CS_ERR_LIBRARY;
+				goto error_put;
+				break;
+			} /* switch (dispatch_data->id) */
+			break; /* case QUORUM_MODEL_V0 */
+		case QUORUM_MODEL_V1:
+			/*
+			 * Dispatch incoming message
+			 */
+			switch (dispatch_data->id) {
+			case MESSAGE_RES_QUORUM_V1_QUORUM_NOTIFICATION:
+				if (quorum_inst_copy.model_v1_data.quorum_notify_fn == NULL) {
+					break;
+				}
+				res_lib_quorum_v1_quorum_notification =
+					(struct res_lib_quorum_v1_quorum_notification *)dispatch_data;
+
+				ring_id.nodeid = res_lib_quorum_v1_quorum_notification->ring_id.nodeid;
+				ring_id.seq = res_lib_quorum_v1_quorum_notification->ring_id.seq;
+
+				quorum_inst_copy.model_v1_data.quorum_notify_fn ( handle,
+					res_lib_quorum_v1_quorum_notification->quorate,
+					ring_id,
+					res_lib_quorum_v1_quorum_notification->view_list_entries,
+					res_lib_quorum_v1_quorum_notification->view_list);
+				break;
+			case MESSAGE_RES_QUORUM_V1_NODELIST_NOTIFICATION:
+				if (quorum_inst_copy.model_v1_data.nodelist_notify_fn == NULL) {
+					break;
+				}
+				res_lib_quorum_v1_nodelist_notification =
+					(struct res_lib_quorum_v1_nodelist_notification *)dispatch_data;
+
+				ring_id.nodeid = res_lib_quorum_v1_nodelist_notification->ring_id.nodeid;
+				ring_id.seq = res_lib_quorum_v1_nodelist_notification->ring_id.seq;
+
+				joined_list = res_lib_quorum_v1_nodelist_notification->member_list +
+				    res_lib_quorum_v1_nodelist_notification->member_list_entries;
+				left_list = joined_list +
+				    res_lib_quorum_v1_nodelist_notification->joined_list_entries;
+
+				quorum_inst_copy.model_v1_data.nodelist_notify_fn ( handle,
+					ring_id,
+					res_lib_quorum_v1_nodelist_notification->member_list_entries,
+					res_lib_quorum_v1_nodelist_notification->member_list,
+					res_lib_quorum_v1_nodelist_notification->joined_list_entries,
+					joined_list,
+					res_lib_quorum_v1_nodelist_notification->left_list_entries,
+					left_list);
+				break;
+			default:
+				error = CS_ERR_LIBRARY;
+				goto error_put;
+				break;
+			} /* switch (dispatch_data->id) */
+			break; /* case QUORUM_MODEL_V1 */
 		}
 		if (quorum_inst->finalize) {
 			/*

+ 1 - 0
man/Makefile.am

@@ -60,6 +60,7 @@ autogen_man		= cpg_context_get.3 \
 			  cpg_iteration_initialize.3 \
 			  cpg_iteration_next.3 \
 			  quorum_initialize.3 \
+			  quorum_model_initialize.3 \
 			  quorum_finalize.3 \
 			  quorum_fd_get.3 \
 			  quorum_dispatch.3 \

+ 2 - 1
man/quorum_context_get.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -51,6 +51,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),

+ 2 - 1
man/quorum_context_set.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -53,6 +53,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),

+ 2 - 1
man/quorum_dispatch.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -87,6 +87,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),

+ 2 - 1
man/quorum_fd_get.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -58,6 +58,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),

+ 2 - 1
man/quorum_finalize.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -55,6 +55,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),
 .BR quorum_trackstop (3),

+ 2 - 1
man/quorum_getquorate.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -51,6 +51,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_trackstart (3),
 .BR quorum_trackstop (3),

+ 2 - 1
man/quorum_initialize.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -102,6 +102,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 @COMMONIPCERRORS@
 .SH "SEE ALSO"
 .BR quorum_overview (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),

+ 165 - 0
man/quorum_model_initialize.3.in

@@ -0,0 +1,165 @@
+.\"/*
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Authors: Jan Friesse <jfriesse@redhat.com>
+.\" *          Fabio M. Di Nitto <fdinitto@redhat.com>
+.\" *
+.\" * This software licensed under BSD license, the text of which follows:
+.\" *
+.\" * Redistribution and use in source and binary forms, with or without
+.\" * modification, are permitted provided that the following conditions are met:
+.\" *
+.\" * - Redistributions of source code must retain the above copyright notice,
+.\" *   this list of conditions and the following disclaimer.
+.\" * - Redistributions in binary form must reproduce the above copyright notice,
+.\" *   this list of conditions and the following disclaimer in the documentation
+.\" *   and/or other materials provided with the distribution.
+.\" * - Neither the name of the MontaVista Software, Inc. nor the names of its
+.\" *   contributors may be used to endorse or promote products derived from this
+.\" *   software without specific prior written permission.
+.\" *
+.\" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" * THE POSSIBILITY OF SUCH DAMAGE.
+.\" */
+.TH QUORUM_MODEL_INITIALIZE 3 @BUILDDATE@ "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+quorum_model_initialize \- Create a new connection to the Quorum service
+.SH SYNOPSIS
+.B #include <corosync/quorum.h>
+.sp
+.BI "cs_error_t quorum_model_initialize(quorum_handle_t *" handle ", quorum_model_t " model ", quorum_model_data_t *" model_data ", uint32_t *" quorum_type ",void *" context ");"
+.SH DESCRIPTION
+The
+.B quorum_model_initialize
+function is an enhanced way to initialize a connection to the quorum API.
+.PP
+Each application may have several connections to the quorum API.  Each application
+uses the
+.I handle
+argument to uniquely identify the connection.  The
+.I handle
+argument is then used in other function calls to identify the connection to be used
+for communication with the quorum service.
+.PP
+The
+.I model
+is used to explicitly choose set of callbacks and internal parameters. Currently two models
+.I QUORUM_MODEL_V0
+and
+.I QUORUM_MODEL_V1
+are defined.
+.I QUORUM_MODEL_V0
+exists only for compatibility reasons with
+.B quorum_initialize(3)
+function and it is not recommended to be used as an argument for
+.B quorum_model_initialize(3).
+
+The Following description is focused only on
+.I QUORUM_MODEL_V1
+model.
+
+.PP
+Every time the voting configuration changes (eg a node joins or leave the cluster) or the quorum status changes
+the quorum callback is called.
+The quorum callback function is described by the following type definitions:
+
+.nf
+typedef void (*quorum_v1_quorum_notification_fn_t) (
+        quorum_handle_t handle,
+        uint32_t quorate,
+        struct quorum_ring_id ring_id,
+        uint32_t member_list_entries,
+        const uint32_t *member_list
+);
+.fi
+.PP
+Also every time when membership configuration changes (eg a node joins or leave the cluster) the node list change
+callback is called before the quorum callback.
+The node list change callback function is described by the following type definitions:
+
+.nf
+typedef void (*quorum_v1_nodelist_notification_fn_t) (
+        quorum_handle_t handle,
+        struct quorum_ring_id ring_id,
+        uint32_t member_list_entries,
+        const uint32_t *member_list,
+        uint32_t joined_list_entries,
+        const uint32_t *joined_list,
+        uint32_t left_list_entries,
+        const uint32_t *left_list
+);
+.fi
+.PP
+The
+.I model_data
+argument for
+.I QUORUM_MODEL_V1
+is of the type:
+
+.nf
+typedef struct {
+        quorum_model_t model;
+        quorum_v1_quorum_notification_fn_t quorum_notify_fn;
+        quorum_v1_nodelist_notification_fn_t nodelist_notify_fn;
+} quorum_model_v1_data_t;
+.fi
+
+It's not required (nor recommended) to set
+.I model
+field in the structure. It is also fine if only some of notification callbacks are
+used (only these events will be delivered then).
+
+.PP
+The
+.I quorum_type
+argument is set to:
+
+.nf
+#define QUORUM_FREE     0
+#define QUORUM_SET      1
+.fi
+.PP
+.I QUORUM_FREE
+value means that no quorum algorithm is loaded and that no callbacks will take place.
+.PP
+.I QUORUM_SET
+value means that one quorum algorithm is configured and that callbacks will take place.
+.PP
+The
+.I context
+argument sets context same way as
+.B quorum_context_set(3).
+.PP
+When a configuration change occurs, the callback
+is called from the
+.B quorum_dispatch(3)
+function.
+.PP
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+@COMMONIPCERRORS@
+.SH "SEE ALSO"
+.BR quorum_overview (3),
+.BR quorum_initialize (3),
+.BR quorum_finalize (3),
+.BR quorum_getquorate (3),
+.BR quorum_trackstart (3),
+.BR quorum_trackstop (3),
+.BR quorum_fd_get (3),
+.BR quorum_dispatch (3),
+.BR quorum_context_set (3),
+.BR quorum_context_get (3)
+.PP

+ 5 - 2
man/quorum_overview.3

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2008, 2012 Red Hat, Inc.
+.\" * Copyright (c) 2008-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -32,7 +32,7 @@
 .\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" * THE POSSIBILITY OF SUCH DAMAGE.
 .\" */
-.TH QUORUM_OVERVIEW 3 2012-02-09 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.TH QUORUM_OVERVIEW 3 2020-02-14 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
 .SH NAME
 quorum_overview \- Quorum Library Overview
 .SH OVERVIEW
@@ -46,6 +46,8 @@ The library provides a mechanism to:
 * Query the quorum status
 .PP
 * Receive notifications of quorum state changes
+.PP
+* Receive notifications of membership changes
 .SH BUGS
 No known bugs at the time of writing. The authors are from outerspace. Deal with it.
 .SH "SEE ALSO"
@@ -53,6 +55,7 @@ No known bugs at the time of writing. The authors are from outerspace. Deal with
 .BR corosync.conf (5),
 .BR votequorum (5),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),

+ 2 - 1
man/quorum_trackstart.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -67,6 +67,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstop (3),

+ 2 - 1
man/quorum_trackstop.3.in

@@ -1,5 +1,5 @@
 .\"/*
-.\" * Copyright (c) 2012 Red Hat, Inc.
+.\" * Copyright (c) 2012-2020 Red Hat, Inc.
 .\" *
 .\" * All rights reserved.
 .\" *
@@ -51,6 +51,7 @@ This call returns the CS_OK value if successful, otherwise an error is returned.
 .SH "SEE ALSO"
 .BR quorum_overview (3),
 .BR quorum_initialize (3),
+.BR quorum_model_initialize (3),
 .BR quorum_finalize (3),
 .BR quorum_getquorate (3),
 .BR quorum_trackstart (3),

+ 1 - 0
test/.gitignore

@@ -12,6 +12,7 @@ stress_cpgfdget
 testcpg
 testcpg2
 testquorum
+testquorummodel
 testsam
 testvotequorum1
 testvotequorum2

+ 4 - 2
test/Makefile.am

@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2009 Red Hat, Inc.
+# Copyright (c) 2009-2020 Red Hat, Inc.
 #
 # Authors: Andrew Beekhof
 #	   Steven Dake (sdake@redhat.com)
@@ -37,7 +37,8 @@ EXTRA_DIST		= ploadstart.sh
 noinst_PROGRAMS		= testcpg testcpg2 cpgbench \
 			  testquorum testvotequorum1 testvotequorum2	\
 			  stress_cpgfdget stress_cpgcontext cpgbound testsam \
-			  testcpgzc cpgbenchzc testzcgc stress_cpgzc
+			  testcpgzc cpgbenchzc testzcgc stress_cpgzc \
+			  testquorummodel
 
 noinst_SCRIPTS		= ploadstart
 
@@ -49,6 +50,7 @@ stress_cpgzc_LDADD	= $(LIBQB_LIBS) $(top_builddir)/lib/libcpg.la
 stress_cpgfdget_LDADD	= $(LIBQB_LIBS) $(top_builddir)/lib/libcpg.la
 stress_cpgcontext_LDADD	= $(LIBQB_LIBS) $(top_builddir)/lib/libcpg.la
 testquorum_LDADD	= $(LIBQB_LIBS) $(top_builddir)/lib/libquorum.la
+testquorummodel_LDADD	= $(LIBQB_LIBS) $(top_builddir)/lib/libquorum.la
 testvotequorum1_LDADD	= $(LIBQB_LIBS) $(top_builddir)/lib/libvotequorum.la
 testvotequorum2_LDADD	= $(LIBQB_LIBS) $(top_builddir)/lib/libvotequorum.la
 cpgbound_LDADD		= $(LIBQB_LIBS) $(top_builddir)/lib/libcpg.la

+ 103 - 0
test/testquorummodel.c

@@ -0,0 +1,103 @@
+#include <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <corosync/corotypes.h>
+#include <corosync/quorum.h>
+
+static quorum_handle_t g_handle;
+
+static void quorum_notification_fn(
+	quorum_handle_t handle,
+	uint32_t quorate,
+	struct quorum_ring_id ring_id,
+	uint32_t view_list_entries,
+	const uint32_t *view_list)
+{
+	int i;
+
+	printf("quorum notification called \n");
+	printf("  quorate   = %lu\n", (long unsigned int) quorate);
+	printf("  ring id   = " CS_PRI_RING_ID "\n", ring_id.nodeid, ring_id.seq);
+	printf("  num nodes = %lu ", (long unsigned int) view_list_entries);
+
+	for (i=0; i<view_list_entries; i++) {
+		printf(" " CS_PRI_NODE_ID, view_list[i]);
+	}
+	printf("\n");
+}
+
+static void nodelist_notification_fn(
+	quorum_handle_t handle,
+	struct quorum_ring_id ring_id,
+	uint32_t member_list_entries, const uint32_t *member_list,
+	uint32_t joined_list_entries, const uint32_t *joined_list,
+	uint32_t left_list_entries, const uint32_t *left_list)
+{
+	int i;
+
+	printf("nodelist notification called \n");
+	printf("  ring id            = " CS_PRI_RING_ID "\n", ring_id.nodeid, ring_id.seq);
+	printf("  num members        = %" PRIu32 " ", member_list_entries);
+
+	for (i=0; i<member_list_entries; i++) {
+		printf(" " CS_PRI_NODE_ID, member_list[i]);
+	}
+	printf("\n");
+
+	printf("  num joined members = %" PRIu32 " ", joined_list_entries);
+	for (i=0; i<joined_list_entries; i++) {
+		printf(" " CS_PRI_NODE_ID, joined_list[i]);
+	}
+	printf("\n");
+
+	printf("  num left members   = %" PRIu32 " ", left_list_entries);
+	for (i=0; i<left_list_entries; i++) {
+		printf(" " CS_PRI_NODE_ID, left_list[i]);
+	}
+	printf("\n");
+
+}
+
+int main(int argc, char *argv[])
+{
+	int quorate;
+	quorum_model_v1_data_t model_data;
+	uint32_t quorum_type;
+	int err;
+
+	memset(&model_data, 0, sizeof(model_data));
+	model_data.quorum_notify_fn = quorum_notification_fn;
+	model_data.nodelist_notify_fn = nodelist_notification_fn;
+
+	if ( (err=quorum_model_initialize (&g_handle, QUORUM_MODEL_V1,
+	    (quorum_model_data_t *)&model_data, &quorum_type, NULL)) != CS_OK) {
+		fprintf(stderr, "quorum_initialize FAILED: %d\n", err);
+		exit(1);
+	}
+
+	if ( (err=quorum_trackstart(g_handle, CS_TRACK_CHANGES)) != CS_OK)
+		fprintf(stderr, "quorum_trackstart FAILED: %d\n", err);
+
+	if ( (err=quorum_getquorate(g_handle, &quorate)) != CS_OK)
+		fprintf(stderr, "quorum_getquorate FAILED: %d\n", err);
+	else {
+		printf("quorate   %d\n", quorate);
+	}
+
+	printf("Waiting for quorum events, press ^C to finish\n");
+	printf("-------------------\n");
+
+	while (1)
+		if (quorum_dispatch(g_handle, CS_DISPATCH_ALL) != CS_OK) {
+			fprintf(stderr, "Error from quorum_dispatch\n");
+			return -1;
+		}
+
+	return 0;
+}