Ver código fonte

Add the votequorum service

git-svn-id: http://svn.fedorahosted.org/svn/corosync/trunk@1745 fd59a12c-fef9-0310-b244-a6a79926bd2f
Christine Caulfield 17 anos atrás
pai
commit
a8256ad5fc

+ 2 - 2
Makefile

@@ -122,10 +122,10 @@ lint:
 	(cd $(builddir)lib; echo ==== `pwd` ===; $(call sub_make,lib,lint));
 	(cd $(builddir)tools; echo ==== `pwd` ===; $(call sub_make,tools,lint));
 
-COROSYNC_LIBS	= evs cpg cfg coroutil confdb quorum
+COROSYNC_LIBS	= evs cpg cfg coroutil confdb quorum votequorum
 
 COROSYNC_HEADERS	= cpg.h cfg.h evs.h ipc_gen.h mar_gen.h swab.h \
-		  ais_util.h confdb.h quorum.h list.h corotypes.h
+		  ais_util.h confdb.h quorum.h list.h corotypes.h votequorum.h
 
 EXEC_LIBS	= totem_pg logsys
 

+ 149 - 0
include/corosync/ipc_votequorum.h

@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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 CONTIBUTORS "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.
+ */
+#ifndef IPC_VOTEQUORUM_H_DEFINED
+#define IPC_VOTEQUORUM_H_DEFINED
+
+#include "corosync/corotypes.h"
+#include "corosync/ipc_gen.h"
+
+// ILLEGAL value!!
+#define VOTEQUORUM_SERVICE 15
+
+#define VOTEQUORUM_MAX_QDISK_NAME_LEN 255
+
+
+enum req_votequorum_types {
+	MESSAGE_REQ_VOTEQUORUM_GETINFO = 0,
+	MESSAGE_REQ_VOTEQUORUM_SETEXPECTED,
+	MESSAGE_REQ_VOTEQUORUM_SETVOTES,
+	MESSAGE_REQ_VOTEQUORUM_QDISK_REGISTER,
+	MESSAGE_REQ_VOTEQUORUM_QDISK_UNREGISTER,
+	MESSAGE_REQ_VOTEQUORUM_QDISK_POLL,
+	MESSAGE_REQ_VOTEQUORUM_QDISK_GETINFO,
+	MESSAGE_REQ_VOTEQUORUM_SETSTATE,
+	MESSAGE_REQ_VOTEQUORUM_LEAVING,
+	MESSAGE_REQ_VOTEQUORUM_TRACKSTART,
+	MESSAGE_REQ_VOTEQUORUM_TRACKSTOP
+};
+
+enum res_votequorum_types {
+	MESSAGE_RES_VOTEQUORUM_STATUS = 0,
+	MESSAGE_RES_VOTEQUORUM_GETINFO,
+	MESSAGE_RES_VOTEQUORUM_QDISK_GETINFO,
+	MESSAGE_RES_VOTEQUORUM_TRACKSTART,
+	MESSAGE_RES_VOTEQUORUM_NOTIFICATION
+};
+
+struct req_lib_votequorum_setvotes {
+        mar_req_header_t header __attribute__((aligned(8)));
+	unsigned int votes;
+	int nodeid;
+};
+
+struct req_lib_votequorum_qdisk_register {
+        mar_req_header_t header __attribute__((aligned(8)));
+	unsigned int votes;
+	char name[VOTEQUORUM_MAX_QDISK_NAME_LEN];
+};
+
+struct req_lib_votequorum_qdisk_poll {
+        mar_req_header_t header __attribute__((aligned(8)));
+	int state;
+};
+
+struct req_lib_votequorum_setexpected {
+        mar_req_header_t header __attribute__((aligned(8)));
+	unsigned int expected_votes;
+};
+
+struct req_lib_votequorum_trackstart {
+        mar_req_header_t header __attribute__((aligned(8)));
+	uint64_t context;
+	unsigned int track_flags;
+};
+
+struct req_lib_votequorum_general {
+        mar_req_header_t header __attribute__((aligned(8)));
+};
+
+#define VOTEQUORUM_REASON_KILL_REJECTED    1
+#define VOTEQUORUM_REASON_KILL_APPLICATION 2
+#define VOTEQUORUM_REASON_KILL_REJOIN      3
+
+struct req_lib_votequorum_getinfo {
+        mar_req_header_t header __attribute__((aligned(8)));
+	int nodeid;
+};
+
+struct res_lib_votequorum_status {
+        mar_res_header_t header __attribute__((aligned(8)));
+};
+
+#define VOTEQUORUM_INFO_FLAG_HASSTATE   1
+#define VOTEQUORUM_INFO_FLAG_DISALLOWED 2
+#define VOTEQUORUM_INFO_FLAG_TWONODE    4
+#define VOTEQUORUM_INFO_FLAG_QUORATE    8
+
+struct res_lib_votequorum_getinfo {
+        mar_res_header_t header __attribute__((aligned(8)));
+	int nodeid;
+	unsigned int votes;
+	unsigned int expected_votes;
+	unsigned int highest_expected;
+	unsigned int total_votes;
+	unsigned int quorum;
+	unsigned int flags;
+};
+
+struct res_lib_votequorum_qdisk_getinfo {
+        mar_res_header_t header __attribute__((aligned(8)));
+	unsigned int votes;
+	unsigned int state;
+	char name[VOTEQUORUM_MAX_QDISK_NAME_LEN];
+};
+
+struct votequorum_node {
+	mar_uint32_t nodeid;
+	mar_uint32_t state;
+};
+
+struct res_lib_votequorum_notification {
+	mar_res_header_t header __attribute__((aligned(8)));
+	mar_uint32_t quorate __attribute__((aligned(8)));
+	mar_uint64_t context __attribute__((aligned(8)));
+	mar_uint32_t node_list_entries __attribute__((aligned(8)));
+	struct votequorum_node node_list[] __attribute__((aligned(8)));
+};
+
+#endif

+ 198 - 0
include/corosync/votequorum.h

@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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 CONTIBUTORS "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.
+ */
+
+#ifndef COROSYNC_VOTEQUORUM_H_DEFINED
+#define COROSYNC_VOTEQUORUM_H_DEFINED
+
+typedef uint64_t votequorum_handle_t;
+
+
+#define VOTEQUORUM_MAX_QDISK_NAME_LEN 255
+
+#define VOTEQUORUM_INFO_FLAG_HASSTATE   1
+#define VOTEQUORUM_INFO_FLAG_DISALLOWED 2
+#define VOTEQUORUM_INFO_FLAG_TWONODE    4
+#define VOTEQUORUM_INFO_FLAG_QUORATE    8
+
+#define NODESTATE_JOINING    1
+#define NODESTATE_MEMBER     2
+#define NODESTATE_DEAD       3
+#define NODESTATE_LEAVING    4
+#define NODESTATE_DISALLOWED 5
+
+
+/** @} */
+
+struct votequorum_info {
+	unsigned int node_id;
+	unsigned int node_votes;
+	unsigned int node_expected_votes;
+	unsigned int highest_expected;
+	unsigned int total_votes;
+	unsigned int quorum;
+	unsigned int flags;
+};
+
+struct votequorum_qdisk_info {
+	unsigned int votes;
+	unsigned int state;
+	char name[VOTEQUORUM_MAX_QDISK_NAME_LEN];
+};
+
+typedef struct {
+	uint32_t nodeid;
+	uint32_t state;
+} votequorum_node_t;
+
+
+typedef void (*votequorum_notification_fn_t) (
+	votequorum_handle_t handle,
+	uint64_t context,
+	uint32_t quorate,
+	uint32_t node_list_entries,
+	votequorum_node_t node_list[]
+	);
+
+typedef struct {
+	votequorum_notification_fn_t votequorum_notify_fn;
+} votequorum_callbacks_t;
+
+
+/*
+ * Create a new quorum connection
+ */
+cs_error_t votequorum_initialize (
+	votequorum_handle_t *handle,
+	votequorum_callbacks_t *callbacks);
+
+/*
+ * Close the quorum handle
+ */
+cs_error_t votequorum_finalize (
+	votequorum_handle_t handle);
+
+
+/*
+ * Dispatch messages and configuration changes
+ */
+cs_error_t votequorum_dispatch (
+	votequorum_handle_t handle,
+	cs_dispatch_flags_t dispatch_types);
+
+
+/*
+ * Get quorum information.
+ */
+cs_error_t votequorum_getinfo (
+	votequorum_handle_t handle,
+	unsigned int nodeid,
+	struct votequorum_info *info);
+
+/*
+ * set expected_votes
+ */
+cs_error_t votequorum_setexpected (
+	votequorum_handle_t handle,
+	unsigned int expected_votes);
+
+/*
+ * set votes for a node
+ */
+cs_error_t votequorum_setvotes (
+	votequorum_handle_t handle,
+	unsigned int nodeid,
+	unsigned int votes);
+
+/*
+ * Register a quorum device
+ * it will be DEAD until polled
+ */
+cs_error_t votequorum_qdisk_register (
+	votequorum_handle_t handle,
+	char *name,
+	unsigned int votes);
+
+/*
+ * Unregister a quorum device
+ */
+cs_error_t votequorum_qdisk_unregister (
+	votequorum_handle_t handle);
+
+/*
+ * Poll a quorum device
+ */
+cs_error_t votequorum_qdisk_poll (
+	votequorum_handle_t handle,
+	unsigned int state);
+
+/*
+ * Get quorum device information
+ */
+cs_error_t votequorum_qdisk_getinfo (
+	votequorum_handle_t handle,
+	struct votequorum_qdisk_info *info);
+
+/*
+ * Set the "hasstate" bit for this node
+ */
+cs_error_t votequorum_setstate (
+	votequorum_handle_t handle);
+
+/* Track node and quorum changes */
+cs_error_t votequorum_trackstart (
+	votequorum_handle_t handle,
+	uint64_t context,
+	unsigned int flags );
+
+cs_error_t votequorum_trackstop (
+	votequorum_handle_t handle);
+
+/*
+ * Set our LEAVING flag. we should exit soon after this
+ */
+cs_error_t votequorum_leaving (
+	votequorum_handle_t handle);
+
+/*
+ * Save and retrieve private data/context
+ */
+cs_error_t votequorum_context_get (
+	votequorum_handle_t handle,
+	void **context);
+
+cs_error_t votequorum_context_set (
+	votequorum_handle_t handle,
+	void *context);
+
+#endif /* COROSYNC_VOTEQUORUM_H_DEFINED */

+ 11 - 1
lib/Makefile

@@ -43,7 +43,8 @@ all: libcpg.a libcpg.so.2.0.0 \
 	libcfg.a libcfg.so.2.0.0 \
 	libquorum.a libquorum.so.2.0.0 \
 	libpload.a libpload.so.2.0.0 \
-	libcoroutil.a libcoroutil.so.2.0.0
+	libcoroutil.a libcoroutil.so.2.0.0 \
+	libvotequorum.a libvotequorum.so.2.0.0
 
 libcoroutil.a: util.o
 	$(AR) -rc libcoroutil.a util.o
@@ -63,6 +64,9 @@ libcpg.so.2.0.0: util.o cpg.o
 libquorum.so.2.0.0: util.o quorum.o
 	$(CC) $(DARWIN_OPTS) util.o quorum.o -o $@
 
+libvotequorum.so.2.0.0: util.o votequorum.o
+	$(CC) $(DARWIN_OPTS) util.o votequorum.o -o $@
+
 libconfdb.so.2.0.0: util.o confdb.o sa-confdb.o 
 	$(CC) $(LDFLAGS) $(DARWIN_OPTS) util.o confdb.o sa-confdb.o ../lcr/lcr_ifact.o -o $@
 
@@ -86,6 +90,9 @@ libcpg.so.2.0.0: util.o cpg.o
 libquorum.so.2.0.0: util.o quorum.o
 	$(CC) -shared -Wl,-soname,libquorum.so.2,-version-script=$(srcdir)$(subdir)libquorum.versions util.o quorum.o -o $@
 
+libvotequorum.so.2.0.0: util.o votequorum.o
+	$(CC) -shared -Wl,-soname,libvotequorum.so.2,-version-script=$(srcdir)$(subdir)libvotequorum.versions util.o votequorum.o -o $@
+
 libconfdb.so.2.0.0: util.o confdb.o sa-confdb.o 
 	$(CC) $(LDFLAGS) -shared -Wl,-soname,libconfdb.so.2,-version-script=$(srcdir)$(subdir)libconfdb.versions util.o confdb.o sa-confdb.o ../lcr/lcr_ifact.o -o $@
 
@@ -106,6 +113,9 @@ libcpg.a: util.o cpg.o
 libquorum.a: util.o quorum.o
 	$(AR) -rc libquorum.a util.o quorum.o
 
+libvotequorum.a: util.o votequorum.o
+	$(AR) -rc libvotequorum.a util.o votequorum.o
+
 libconfdb.a: util.o confdb.o sa-confdb.o
 	$(AR) -rc libconfdb.a util.o confdb.o sa-confdb.o ../lcr/lcr_ifact.o
 

+ 6 - 15
lib/libquorum.versions

@@ -4,16 +4,10 @@ OPENAIS_QUORUM_1.0 {
 	global:	   
 		quorum_initialize;
 		quorum_finalize;
-		quorum_getinfo;
-		quorum_setexpected;
-		quorum_setvotes;
-		quorum_qdisk_register;
-		quorum_qdisk_unregister;
-		quorum_qdisk_poll;
-		quorum_qdisk_getinfo;
-		quorum_setdirty;
-		quorum_killnode;
-		quorum_leaving;
+		quorum_getquorate;
+		quorum_initialize;
+		quorum_finalize;
+		quorum_dispatch;
 
 	local:
 		saHandleCreate;
@@ -30,17 +24,14 @@ OPENAIS_QUORUM_1.0 {
 		saVersionVerify;
 		clustTimeNow;
 };
-# Version and symbol export for libcpg.so
+# Version and symbol export for libquorum.so
 
 COROSYNC_QUORUM_1.0 {
 	global:
 		quorum_initialize;
 		quorum_finalize;
-		quorum_fd_get;
+		quorum_getquorate;
 		quorum_dispatch;
-		quorum_get_quorate;
-		quorum_context_get;
-		quorum_context_set;
 
 	local:
 		saHandleCreate;

+ 71 - 0
lib/libvotequorum.versions

@@ -0,0 +1,71 @@
+# Version and symbol export for libvotequorum.so
+
+OPENAIS_VOTEQUORUM_1.0 {
+	global:	   
+		votequorum_initialize;
+		votequorum_finalize;
+		votequorum_getinfo;
+		votequorum_setexpected;
+		votequorum_setvotes;
+		votequorum_qdisk_register;
+		votequorum_qdisk_unregister;
+		votequorum_qdisk_poll;
+		votequorum_qdisk_getinfo;
+		votequorum_setstate;
+		votequorum_leaving;
+		votequorum_trackstart;
+		votequorum_trackstop;
+		votequorum_context_get;
+		votequorum_context_set;
+
+	local:
+		saHandleCreate;
+		saHandleDestroy;
+		saHandleInstanceGet;
+		saHandleInstancePut;
+		saRecvRetry;
+		saSelectRetry;
+		saSendMsgReceiveReply;
+		saSendMsgRetry;
+		saSendReceiveReply;
+		saSendRetry;
+		saServiceConnect;
+		saVersionVerify;
+		clustTimeNow;
+};
+# Version and symbol export for libvotequorum.so
+
+COROSYNC_VOTEQUORUM_1.0 {
+	global:
+		votequorum_initialize;
+		votequorum_finalize;
+		votequorum_getinfo;
+		votequorum_setexpected;
+		votequorum_setvotes;
+		votequorum_qdisk_register;
+		votequorum_qdisk_unregister;
+		votequorum_qdisk_poll;
+		votequorum_qdisk_getinfo;
+		votequorum_setdirty;
+		votequorum_killnode;
+		votequorum_leaving;
+		votequorum_trackstart;
+		votequorum_trackstop;
+		votequorum_context_get;
+		votequorum_context_set;
+
+	local:
+		saHandleCreate;
+		saHandleDestroy;
+		saHandleInstanceGet;
+		saHandleInstancePut;
+		saRecvRetry;
+		saSelectRetry;
+		saSendMsgReceiveReply;
+		saSendMsgRetry;
+		saSendReceiveReply;
+		saSendRetry;
+		saServiceConnect;
+		saVersionVerify;
+		clustTimeNow;
+};

+ 821 - 0
lib/votequorum.c

@@ -0,0 +1,821 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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 CONTIBUTORS "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.
+ */
+
+/*
+ * Provides a quorum API using the corosync executive
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <corosync/mar_gen.h>
+#include <corosync/ipc_gen.h>
+#include <corosync/ais_util.h>
+#include "corosync/votequorum.h"
+#include "corosync/ipc_votequorum.h"
+
+struct votequorum_inst {
+	int response_fd;
+	int dispatch_fd;
+	int finalize;
+	void *context;
+	votequorum_callbacks_t callbacks;
+	pthread_mutex_t response_mutex;
+	pthread_mutex_t dispatch_mutex;
+};
+
+static void votequorum_instance_destructor (void *instance);
+
+static struct saHandleDatabase votequorum_handle_t_db = {
+	.handleCount		        = 0,
+	.handles			= 0,
+	.mutex				= PTHREAD_MUTEX_INITIALIZER,
+	.handleInstanceDestructor	= votequorum_instance_destructor
+};
+
+/*
+ * Clean up function for a quorum instance (votequorum_initialize) handle
+ */
+static void votequorum_instance_destructor (void *instance)
+{
+	struct votequorum_inst *votequorum_inst = instance;
+
+	pthread_mutex_destroy (&votequorum_inst->response_mutex);
+}
+
+cs_error_t votequorum_initialize (
+	votequorum_handle_t *handle,
+	votequorum_callbacks_t *callbacks)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+
+	error = saHandleCreate (&votequorum_handle_t_db, sizeof (struct votequorum_inst), handle);
+	if (error != CS_OK) {
+		goto error_no_destroy;
+	}
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, *handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		goto error_destroy;
+	}
+
+	error = saServiceConnect (&votequorum_inst->dispatch_fd,
+				  &votequorum_inst->response_fd,
+				  VOTEQUORUM_SERVICE);
+	if (error != CS_OK) {
+		goto error_put_destroy;
+	}
+
+	pthread_mutex_init (&votequorum_inst->response_mutex, NULL);
+	pthread_mutex_init (&votequorum_inst->dispatch_mutex, NULL);
+	if (callbacks)
+		memcpy(&votequorum_inst->callbacks, callbacks, sizeof (callbacks));
+	else
+		memset(&votequorum_inst->callbacks, 0, sizeof (callbacks));
+
+	saHandleInstancePut (&votequorum_handle_t_db, *handle);
+
+	return (CS_OK);
+
+error_put_destroy:
+	saHandleInstancePut (&votequorum_handle_t_db, *handle);
+error_destroy:
+	saHandleDestroy (&votequorum_handle_t_db, *handle);
+error_no_destroy:
+	return (error);
+}
+
+cs_error_t votequorum_finalize (
+	votequorum_handle_t handle)
+{
+	struct votequorum_inst *votequorum_inst;
+	cs_error_t error;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	/*
+	 * Another thread has already started finalizing
+	 */
+	if (votequorum_inst->finalize) {
+		pthread_mutex_unlock (&votequorum_inst->response_mutex);
+		saHandleInstancePut (&votequorum_handle_t_db, handle);
+		return (CS_ERR_BAD_HANDLE);
+	}
+
+	votequorum_inst->finalize = 1;
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	saHandleDestroy (&votequorum_handle_t_db, handle);
+
+	/*
+	 * Disconnect from the server
+	 */
+	if (votequorum_inst->response_fd != -1) {
+		shutdown(votequorum_inst->response_fd, 0);
+		close(votequorum_inst->response_fd);
+	}
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (CS_OK);
+}
+
+
+cs_error_t votequorum_getinfo (
+	votequorum_handle_t handle,
+	unsigned int nodeid,
+	struct votequorum_info *info)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_getinfo req_lib_votequorum_getinfo;
+	struct res_lib_votequorum_getinfo res_lib_votequorum_getinfo;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_getinfo.header.size = sizeof (struct req_lib_votequorum_getinfo);
+	req_lib_votequorum_getinfo.header.id = MESSAGE_REQ_VOTEQUORUM_GETINFO;
+	req_lib_votequorum_getinfo.nodeid = nodeid;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_getinfo;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_getinfo);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_getinfo, sizeof (struct res_lib_votequorum_getinfo));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_getinfo.header.error;
+
+	info->node_id = res_lib_votequorum_getinfo.nodeid;
+	info->node_votes = res_lib_votequorum_getinfo.votes;
+	info->node_expected_votes = res_lib_votequorum_getinfo.expected_votes;
+	info->highest_expected = res_lib_votequorum_getinfo.highest_expected;
+	info->total_votes = res_lib_votequorum_getinfo.total_votes;
+	info->quorum = res_lib_votequorum_getinfo.quorum;
+	info->flags = res_lib_votequorum_getinfo.flags;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_setexpected (
+	votequorum_handle_t handle,
+	unsigned int expected_votes)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_setexpected req_lib_votequorum_setexpected;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_setexpected.header.size = sizeof (struct req_lib_votequorum_setexpected);
+	req_lib_votequorum_setexpected.header.id = MESSAGE_REQ_VOTEQUORUM_SETEXPECTED;
+	req_lib_votequorum_setexpected.expected_votes = expected_votes;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_setexpected;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_setexpected);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_setvotes (
+	votequorum_handle_t handle,
+	unsigned int nodeid,
+	unsigned int votes)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_setvotes req_lib_votequorum_setvotes;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_setvotes.header.size = sizeof (struct req_lib_votequorum_setvotes);
+	req_lib_votequorum_setvotes.header.id = MESSAGE_REQ_VOTEQUORUM_SETVOTES;
+	req_lib_votequorum_setvotes.nodeid = nodeid;
+	req_lib_votequorum_setvotes.votes = votes;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_setvotes;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_setvotes);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_qdisk_register (
+	votequorum_handle_t handle,
+	char *name,
+	unsigned int votes)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_qdisk_register req_lib_votequorum_qdisk_register;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	if (strlen(name) > VOTEQUORUM_MAX_QDISK_NAME_LEN)
+		return CS_ERR_INVALID_PARAM;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_qdisk_register.header.size = sizeof (struct req_lib_votequorum_qdisk_register);
+	req_lib_votequorum_qdisk_register.header.id = MESSAGE_REQ_VOTEQUORUM_QDISK_REGISTER;
+	strcpy(req_lib_votequorum_qdisk_register.name, name);
+	req_lib_votequorum_qdisk_register.votes = votes;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_qdisk_register;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_qdisk_register);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_qdisk_poll (
+	votequorum_handle_t handle,
+	unsigned int state)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_qdisk_poll req_lib_votequorum_qdisk_poll;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_qdisk_poll.header.size = sizeof (struct req_lib_votequorum_qdisk_poll);
+	req_lib_votequorum_qdisk_poll.header.id = MESSAGE_REQ_VOTEQUORUM_QDISK_POLL;
+	req_lib_votequorum_qdisk_poll.state = state;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_qdisk_poll;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_qdisk_poll);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_qdisk_unregister (
+	votequorum_handle_t handle)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_general req_lib_votequorum_general;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_general.header.size = sizeof (struct req_lib_votequorum_general);
+	req_lib_votequorum_general.header.id = MESSAGE_REQ_VOTEQUORUM_QDISK_UNREGISTER;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_general;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_general);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+
+
+cs_error_t votequorum_qdisk_getinfo (
+	votequorum_handle_t handle,
+	struct votequorum_qdisk_info *qinfo)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_general req_lib_votequorum_general;
+	struct res_lib_votequorum_qdisk_getinfo res_lib_votequorum_qdisk_getinfo;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_general.header.size = sizeof (struct req_lib_votequorum_general);
+	req_lib_votequorum_general.header.id = MESSAGE_REQ_VOTEQUORUM_QDISK_GETINFO;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_general;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_general);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_qdisk_getinfo, sizeof (struct res_lib_votequorum_qdisk_getinfo));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_qdisk_getinfo.header.error;
+
+	qinfo->votes = res_lib_votequorum_qdisk_getinfo.votes;
+	qinfo->state = res_lib_votequorum_qdisk_getinfo.state;
+	strcpy(qinfo->name, res_lib_votequorum_qdisk_getinfo.name);
+
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_setstate (
+	votequorum_handle_t handle)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_general req_lib_votequorum_general;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_general.header.size = sizeof (struct req_lib_votequorum_general);
+	req_lib_votequorum_general.header.id = MESSAGE_REQ_VOTEQUORUM_SETSTATE;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_general;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_general);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_leaving (
+	votequorum_handle_t handle)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_general req_lib_votequorum_general;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_general.header.size = sizeof (struct req_lib_votequorum_general);
+	req_lib_votequorum_general.header.id = MESSAGE_REQ_VOTEQUORUM_LEAVING;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_general;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_general);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_trackstart (
+	votequorum_handle_t handle,
+	uint64_t context,
+	unsigned int flags )
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_trackstart req_lib_votequorum_trackstart;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_trackstart.header.size = sizeof (struct req_lib_votequorum_trackstart);
+	req_lib_votequorum_trackstart.header.id = MESSAGE_REQ_VOTEQUORUM_TRACKSTART;
+	req_lib_votequorum_trackstart.track_flags = flags;
+	req_lib_votequorum_trackstart.context = context;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_trackstart;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_trackstart);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+cs_error_t votequorum_trackstop (
+	votequorum_handle_t handle)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+	struct iovec iov[2];
+	struct req_lib_votequorum_general req_lib_votequorum_general;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&votequorum_inst->response_mutex);
+
+	req_lib_votequorum_general.header.size = sizeof (struct req_lib_votequorum_general);
+	req_lib_votequorum_general.header.id = MESSAGE_REQ_VOTEQUORUM_TRACKSTOP;
+
+	iov[0].iov_base = (char *)&req_lib_votequorum_general;
+	iov[0].iov_len = sizeof (struct req_lib_votequorum_general);
+
+	error = saSendMsgReceiveReply (votequorum_inst->response_fd, iov, 1,
+		&res_lib_votequorum_status, sizeof (struct res_lib_votequorum_status));
+
+	pthread_mutex_unlock (&votequorum_inst->response_mutex);
+
+	if (error != CS_OK) {
+		goto error_exit;
+	}
+
+	error = res_lib_votequorum_status.header.error;
+
+error_exit:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (error);
+}
+
+
+cs_error_t votequorum_context_get (
+	votequorum_handle_t handle,
+	void **context)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	*context = votequorum_inst->context;
+
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (CS_OK);
+}
+
+cs_error_t votequorum_context_set (
+	votequorum_handle_t handle,
+	void *context)
+{
+	cs_error_t error;
+	struct votequorum_inst *votequorum_inst;
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle, (void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	votequorum_inst->context = context;
+
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+
+	return (CS_OK);
+}
+
+
+struct res_overlay {
+	mar_res_header_t header __attribute__((aligned(8)));
+	char data[512000];
+};
+
+cs_error_t votequorum_dispatch (
+	votequorum_handle_t handle,
+	cs_dispatch_flags_t dispatch_types)
+{
+	struct pollfd ufds;
+	int timeout = -1;
+	cs_error_t error;
+	int cont = 1; /* always continue do loop except when set to 0 */
+	int dispatch_avail;
+	struct votequorum_inst *votequorum_inst;
+	votequorum_callbacks_t callbacks;
+	struct res_overlay dispatch_data;
+	struct res_lib_votequorum_notification *res_lib_votequorum_notification;
+
+	if (dispatch_types != CS_DISPATCH_ONE &&
+		dispatch_types != CS_DISPATCH_ALL &&
+		dispatch_types != CS_DISPATCH_BLOCKING) {
+
+		return (CS_ERR_INVALID_PARAM);
+	}
+
+	error = saHandleInstanceGet (&votequorum_handle_t_db, handle,
+		(void *)&votequorum_inst);
+	if (error != CS_OK) {
+		return (error);
+	}
+
+	/*
+	 * Timeout instantly for CS_DISPATCH_ONE or CS_DISPATCH_ALL and
+	 * wait indefinately for CS_DISPATCH_BLOCKING
+	 */
+	if (dispatch_types == CS_DISPATCH_ALL) {
+		timeout = 0;
+	}
+
+	do {
+		ufds.fd = votequorum_inst->dispatch_fd;
+		ufds.events = POLLIN;
+		ufds.revents = 0;
+
+		pthread_mutex_lock (&votequorum_inst->dispatch_mutex);
+
+		error = saPollRetry (&ufds, 1, timeout);
+		if (error != CS_OK) {
+			goto error_unlock;
+		}
+
+		/*
+		 * Handle has been finalized in another thread
+		 */
+		if (votequorum_inst->finalize == 1) {
+			error = CS_OK;
+			goto error_unlock;
+		}
+
+		if ((ufds.revents & (POLLERR|POLLHUP|POLLNVAL)) != 0) {
+			error = CS_ERR_BAD_HANDLE;
+			goto error_unlock;
+		}
+
+		dispatch_avail = ufds.revents & POLLIN;
+		if (dispatch_avail == 0 && dispatch_types == CS_DISPATCH_ALL) {
+			pthread_mutex_unlock (&votequorum_inst->dispatch_mutex);
+			break; /* exit do while cont is 1 loop */
+		} else
+		if (dispatch_avail == 0) {
+			pthread_mutex_unlock (&votequorum_inst->dispatch_mutex);
+			continue; /* next poll */
+		}
+
+		if (ufds.revents & POLLIN) {
+			error = saRecvRetry (votequorum_inst->dispatch_fd, &dispatch_data.header,
+				sizeof (mar_res_header_t));
+			if (error != CS_OK) {
+				goto error_unlock;
+			}
+			if (dispatch_data.header.size > sizeof (mar_res_header_t)) {
+				error = saRecvRetry (votequorum_inst->dispatch_fd, &dispatch_data.data,
+					dispatch_data.header.size - sizeof (mar_res_header_t));
+				if (error != CS_OK) {
+					goto error_unlock;
+				}
+			}
+		} else {
+			pthread_mutex_unlock (&votequorum_inst->dispatch_mutex);
+			continue;
+		}
+
+		/*
+		 * Make copy of callbacks, message data, unlock instance, and call callback
+		 * A risk of this dispatch method is that the callback routines may
+		 * operate at the same time that votequorum_finalize has been called in another thread.
+		 */
+		memcpy (&callbacks, &votequorum_inst->callbacks, sizeof (votequorum_callbacks_t));
+		pthread_mutex_unlock (&votequorum_inst->dispatch_mutex);
+
+		/*
+		 * Dispatch incoming message
+		 */
+		switch (dispatch_data.header.id) {
+
+		case MESSAGE_RES_VOTEQUORUM_NOTIFICATION:
+			if (callbacks.votequorum_notify_fn == NULL) {
+				continue;
+			}
+			res_lib_votequorum_notification = (struct res_lib_votequorum_notification *)&dispatch_data;
+
+			callbacks.votequorum_notify_fn ( handle,
+							 res_lib_votequorum_notification->context,
+							 res_lib_votequorum_notification->quorate,
+							 res_lib_votequorum_notification->node_list_entries,
+							 (votequorum_node_t *)res_lib_votequorum_notification->node_list );
+				;
+			break;
+
+		default:
+			error = CS_ERR_LIBRARY;
+			goto error_put;
+			break;
+		}
+
+		/*
+		 * Determine if more messages should be processed
+		 * */
+		switch (dispatch_types) {
+		case CS_DISPATCH_ONE:
+			cont = 0;
+			break;
+		case CS_DISPATCH_ALL:
+			break;
+		case CS_DISPATCH_BLOCKING:
+			break;
+		}
+	} while (cont);
+
+	goto error_put;
+
+error_unlock:
+	pthread_mutex_unlock (&votequorum_inst->dispatch_mutex);
+
+error_put:
+	saHandleInstancePut (&votequorum_handle_t_db, handle);
+	return (error);
+}

+ 13 - 0
man/Makefile

@@ -74,6 +74,19 @@ html:
 	groff -mandoc -Thtml confdb_object_parent_get.3 > html/confdb_object_parent_get.html
 	groff -mandoc -Thtml confdb_overview.8 > html/confdb_overview.html
 
+	groff -mandoc -Thtml votequorum_overview.8 > html/votequorum_overview.html
+	groff -mandoc -Thtml votequorum_initialize.3 > html/votequorum_initialize.html
+	groff -mandoc -Thtml votequorum_finalize.3 > html/votequorum_finalize.html
+	groff -mandoc -Thtml votequorum_fd_get.3 > html/votequorum_fd_get.html
+	groff -mandoc -Thtml votequorum_dispatch.3 > html/votequorum_dispatch.html
+	groff -mandoc -Thtml votequorum_getinfo.3 > html/votequorum_getinfo.html
+	groff -mandoc -Thtml votequorum_leaving.3 > html/votequorum_leaving.html
+	groff -mandoc -Thtml votequorum_setexpected.3 > html/votequorum_setexpected.html
+	groff -mandoc -Thtml votequorum_setvotes.3 > html/votequorum_setvotes.html
+	groff -mandoc -Thtml votequorum_qdisk_register.3 > html/votequorum_qdisk_register.html
+	groff -mandoc -Thtml votequorum_qdisk_unregister.3 > html/votequorum_qdisk_unregister.html
+	groff -mandoc -Thtml votequorum_qdisk_poll.3 > html/votequorum_poll.html
+	groff -mandoc -Thtml votequorum_qdisk_getinfo.3 > html/votequorum_qdisk_getinfo.html
 	cp index.html html
 
 clean:

+ 9 - 1
man/confdb_overview.8

@@ -34,16 +34,24 @@
 .TH CONFDB_OVERVIEW 8 2006-03-06 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
 .SH OVERVIEW
 The CONFDB library is delivered with the corosync project.  This library is used
-to examine manipulate the configuratin databser used by corosync.
+to examine manipulate the configuration database used by corosync.
 .PP
 The library provides a mechanism to:
+.PP
 * Create new objects
+.PP
 * Create new key/value pairs within those objects
+.PP
 * Remove existing keys
+.PP
 * Remove an existing object and all it sub-objects and keys
+.PP
 * Read keys and values
+.PP
 * Iterate keys within an object
+.PP
 * Iterate subobjects within a parent object
+.PP
 * Find a named object
 .PP
 .SH BUGS

+ 25 - 0
man/index.html

@@ -85,5 +85,30 @@ Welcome to the corosync project's manual pages.
 <BR>
 <A HREF="confdb_object_parent_get.html">confdb_object_parent_get(3)</A>: Description of the confdb_object_parent_get interface.
 
+<BR>
+<A HREF="votequorum_overview.html">votequorum_overview(8)</A>: An overview of the vote-based quorum service
+<BR>
+<A HREF="votequorum_initialize.html">votequorum_initialize(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_finalize.html">votequorum_finalize(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_fd_get.html">votequorum_fd_get(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_getinfo.html">votequorum_getinfo(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_leaving.html">votequorum_leaving(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_setexpected.html">votequorum_setexpected(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_setvotes.html">votequorum_setvotes(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_qdisk_register.html">votequorum_qdisk_register(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_qdisk_unregister.html">votequorum_qdisk_unregister(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_qdisk_poll.html">votequorum_qdisk_poll(3)</A>: Description of the votequorum interface.
+<BR>
+<A HREF="votequorum_qdisk_getinfo.html">votequorum_qdisk_getinfo(3)</A>: Description of the votequorum interface.
+<BR>
 
 </body>

+ 96 - 0
man/votequorum_dispatch.3

@@ -0,0 +1,96 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_DISPATCH 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_dispatch \- Dispatches callbacks from the votequorum service
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_dispatch(votequorum_handle_t " handle ", votequorum_dispatch_t *" dispatch_types ");"
+.SH DESCRIPTION
+The
+.B votequorum_dispatch
+function is used to dispatch configuration changes.
+.PP
+Each application may have several connections to the votequorum API.  Each  application
+uses the 
+.I handle
+argument to uniquely identify the connection.
+.PP
+The
+.I dispatch_types
+argument is used to identify the type of dispatch to execute.  The possible types are
+defined by the structure:
+
+.IP
+.RS
+.ne 18
+.nf
+.ta 4n 30n 33n
+typedef enum {
+        CS_DISPATCH_ONE,
+        CS_DISPATCH_ALL,
+        CS_DISPATCH_BLOCKING
+} votequorum_dispatch_t;
+.ta
+.fi
+.RE
+.IP
+.PP
+.PP
+The dispatch values have the following meanings:
+.TP
+.B CS_DISPATCH_ONE
+Dispatch at least one callback, blocking until the callback is dispatched.
+.TP
+.B CS_DISPATCH_ALL
+Dispatch all waiting callbacks without blocking to wait for any callbacks.
+.TP
+.B CS_DISPATCH_BLOCKING
+Dispatch all callbacks blocking indefinitely.  This is used in a threaded
+program where a thread is created, and then votequorum_dispatch() is called immediately
+from the created thread to execute callbacks.
+
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_fd_get (3),
+
+.PP

+ 64 - 0
man/votequorum_fd_get.3

@@ -0,0 +1,64 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_FD_GET 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_fd_get \- Dispatches callbacks from the votequorum service
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_fd_get(votequorum_handle_t " handle ", int *" fd ");"
+.SH DESCRIPTION
+The
+.B votequorum_fd_get
+function is used to retrieve the file descriptor that may be used with the poll
+system call to determine when
+.B votequorum_dispatch(3)
+won't block.  The
+.I handle
+argument may not be used directly with
+.B poll
+because it is not the file descriptor, but instead an internal identifier used
+by the votequorum library.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.PP

+ 61 - 0
man/votequorum_finalize.3

@@ -0,0 +1,61 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_FINALIZE 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_finalize \- Terminate a connection to the votequorum service
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_finalize(votequorum_handle_t " handle ");"
+.SH DESCRIPTION
+The
+.B votequorum_finalize
+function is used to close a connection to the configuration dabatase API.
+Once the connection is finalized, the handle may not be used again by applications.
+No more callbacks will be dispatched from the
+.B votequorum_dispatch function.
+.PP
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+
+.PP

+ 91 - 0
man/votequorum_getinfo.3

@@ -0,0 +1,91 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_GETINFO 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_getinfo \- Get information about the VoteQuorum service
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_getinfo(votequorum_handle_t *" handle ", unsigned int " nodeid ", struct votequorum_info *" info ");
+.SH DESCRIPTION
+The
+.B votequorum_getinfo
+function is used to get information about the voteing system and its nodes. 
+
+The votequorum_info structure is defined as follows:
+.PP
+.PP
+.IP
+.RS
+.ne 18
+.nf
+.ta 4n 20n 32n
+
+struct votequorum_info {
+	unsigned int node_id;
+	unsigned int node_votes;
+	unsigned int node_expected_votes;
+	unsigned int highest_expected;
+	unsigned int total_votes;
+	unsigned int quorum;
+	unsigned int flags;
+};
+
+#define VOTEQUORUM_INFO_FLAG_DIRTY      1
+#define VOTEQUORUM_INFO_FLAG_DISALLOWED 2
+#define VOTEQUORUM_INFO_FLAG_TWONODE    4
+#define VOTEQUORUM_INFO_FLAG_QUORATE    8
+
+.ta
+.fi
+.RE
+.IP
+.PP
+.PP
+The members starting node_ hold information specific to the requested nodeid, the other are
+general to the voting system.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH BUGS
+Callbacks are not support at the moment.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_finalize (3),
+.BR votequorum_fd_get (3),
+.BR votequorum_dispatch (3),
+.PP

+ 106 - 0
man/votequorum_initialize.3

@@ -0,0 +1,106 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_INITIALIZE 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_initialize \- Create a new connection to the VoteQuorum service
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_initialize(votequorum_handle_t *" handle ", votequorum_callbacks_t *" callbacks ");
+.SH DESCRIPTION
+The
+.B votequorum_initialize
+function is used to initialize a connection to the vote-based quorum database API.
+.PP
+Each application may have several connections to the votequorum 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 votequorum service.
+.PP
+Every time the voting configuraton changes (eg a node joins or leave the cluster), the callback is called.
+The callback function is described by the following type definitions:
+
+typedef void (*votequorum_notification_fn_t) (
+	votequorum_handle_t handle,
+	uint64_t context,
+	uint32_t quorate,
+	uint32_t node_list_entries,
+	votequorum_node_t node_list[]
+	);
+
+.ta
+.fi
+.RE
+.IP
+.PP
+.PP
+The
+.I callbacks
+argument is of the type:
+.IP
+.RS
+.ne 18
+.nf
+.PP
+typedef struct {
+	votequorum_notification_fn_t votequorum_notify_fn;
+} votequorum_callbacks_t;
+
+.ta
+.fi
+.RE
+.IP
+.PP
+When a configuration change occurs, the callback
+is called from the
+.B votequorum_dispatch()
+function. 
+.PP
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH BUGS
+Callbacks are not support at the moment.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_finalize (3),
+.BR votequorum_fd_get (3),
+.BR votequorum_dispatch (3),
+.PP

+ 67 - 0
man/votequorum_leaving.3

@@ -0,0 +1,67 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_LEAVING 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_leaving \- Tell other nodes that we are leaving the cluster
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_leaving(votequorum_handle_t " handle ");"
+.SH DESCRIPTION
+The
+.B votequorum_leaving
+function is used to tell the other nodes in the cluster that this node is leaving. They
+will (when the node actually leaves) reduce quorum to keep the cluster running without
+this node.
+.PP
+This function should only be called if it is known that the node is being shut down for
+a known reason and could be out of the cluster for an extended period of time. 
+.PP
+Normal behaviour is for the cluster to reduce the total number of votes, but NOT expected_votes
+when a node leave the cluster, so the cluster could become inquorate. This is correct behaviour
+and is ther eto prevent split-brain.
+.PP
+Do NOT call this function unless you know what you are doing.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.PP

+ 82 - 0
man/votequorum_overview.8

@@ -0,0 +1,82 @@
+.\"/*
+.\" * Copyright (c) 2008 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_OVERVIEW 8 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH OVERVIEW
+The votequuorum library is delivered with the corosync project. It is the external interface to 
+the vote-based quorum service. This service is optionally loaded into all ndes in a corosync cluster
+to avoid split-brain situations. It does this by having a number of votes assigned to each system
+in the cluster and ensuring that only when a majority of the votes are present, cluster operations are
+allowed to proceed.
+.PP
+The library provides a mechanism to:
+* Query the quorum status
+.PP
+* Get a list of nodes known to the quorum service
+.PP
+* Receive notifications of quorum state changes
+.PP
+* Change the number of votes assigned to a node
+.PP
+* Change the number of expected votes for a cluster to be quorate
+.PP
+* Connect an additional quorum device to allow small clusters to remain quorate during node outages.
+.PP
+.B votequorum 
+reads its configuration from the objdb. The following keys are read when it starts up:
+.PP
+* quorum.expected_votes
+.br
+* quorum.votes
+.br
+* quorum.quorumdev_poll
+.br
+* quorum.disallowed
+.br
+* quorum.two_node
+.PP
+Most of those values can be changed while corosync is running with the following exceptions:
+.B quorum.disallowed 
+cannot be changed, and
+.B two_node
+cannot be set on-the-fly, though it can be cleared. ie you can start with two nodes in the cluster
+and add a third without rebooting all the nodes.
+.PP
+.SH BUGS
+This software is not yet production, so there may still be some bugs.
+.SH "SEE ALSO"
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_fd_get (3),
+.BR votequorum_dispatch (3),
+.PP

+ 80 - 0
man/votequorum_qdisk_getinfo.3

@@ -0,0 +1,80 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_QDISK_GETINFO 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_qdisk_getinfo \- Get details of the quorum device
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_qdisk_getinfo(votequorum_handle_t " handle ", struct votequorum_qdisk_info " *info ");"
+.SH DESCRIPTION
+The
+.B votequorum_qdisk_getinfo
+Returns information about the quorum device in the following structure:
+.PP
+.PP
+.IP
+.RS
+.ne 18
+.nf
+.ta 4n 20n 32n
+
+struct votequorum_qdisk_info {
+	unsigned int votes;
+	unsigned int state;
+	char name[VOTEQUORUM_MAX_QDISK_NAME_LEN];
+};
+
+.ta
+.fi
+.RE
+.IP
+.PP
+.PP
+
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.BR votequorum_qdisk_poll (3),
+.BR votequorum_qdisk_unregister (3),
+.BR votequorum_qdisk_getinfo (3),
+.PP

+ 69 - 0
man/votequorum_qdisk_poll.3

@@ -0,0 +1,69 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_QDISK_POLL 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_qdisk_poll \- Tells votequorum the result of the quorum device poll
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_qdisk_poll(votequorum_handle_t " handle ", unsigned int " state ");"
+.SH DESCRIPTION
+The
+.B votequorum_qdisk_poll
+is called by the quorum device subsyetem (not provided as part of votequorum) to tell
+the voting system if the qurum device is present/active or not. If 
+.B state
+is 1 then the votes for the device are included in the quorum calculation, otherwise not.
+This routine should be called at regular intervals to ensure that the device status
+is always known to votequorum. If 
+.B votequorum_qdisk_poll
+is not called after (default) 10 seconds then the device will be deeded to be dead and 
+its votes removed from the cluster. This does not unregister the device.
+The default poll time can be changed by setting the object database variable
+quorum.quorumdev_poll.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.BR votequorum_qdisk_poll (3),
+.BR votequorum_qdisk_unregister (3),
+.BR votequorum_qdisk_getinfo (3),
+.PP

+ 68 - 0
man/votequorum_qdisk_register.3

@@ -0,0 +1,68 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_QDISK_REGISTER 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_qdisk_register \- Registers a new quorum device
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_qdisk_register(votequorum_handle_t " handle ", char * " name ", unsigned int " votes ");"
+.SH DESCRIPTION
+The
+.B votequorum_qdisk_register
+is used to register a new quorum device. A quorum device is an external way of adding votes to a small 
+cluster. The quorum device is, in effect, a pseudo node in the cluster that provide votes based on some
+external device, usually a shared disk partition or perhaps a network router.
+.br
+This call creates the device but does not mark it active.
+.B votequorum_qdisk_poll
+must be called for the votes to be included in the quorum calculation.
+.br
+Note that it is the responsibility of the quorum device subsystem (not provided as part of votequorum)
+to keep all nodes informed of the quorum device status.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.BR votequorum_qdisk_poll (3),
+.BR votequorum_qdisk_unregister (3),
+.BR votequorum_qdisk_getinfo (3),
+.PP

+ 60 - 0
man/votequorum_qdisk_unregister.3

@@ -0,0 +1,60 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_QDISK_UNREGISTER 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_qdisk_unregister \- Unregisters a new quorum device
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_qdisk_unregister(votequorum_handle_t " handle ");"
+.SH DESCRIPTION
+The
+.B votequorum_qdisk_unregister
+unregisters a quorum device. Any votes it had will be removed from the cluster. Not that this could
+make the cluster inquorate.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.BR votequorum_qdisk_poll (3),
+.BR votequorum_qdisk_unregister (3),
+.BR votequorum_qdisk_getinfo (3),
+.PP

+ 60 - 0
man/votequorum_setexpected.3

@@ -0,0 +1,60 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_SETEXPECTED 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_setexpected \- Sets the expected votes for the cluster
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_setexpected(votequorum_handle_t " handle ", int " expected_votes ");"
+.SH DESCRIPTION
+The
+.B votequorum_setexpected
+function is used to change the expected votes in the cluster. Expected votes is used to calculate
+quorum and should normally be the total number of votes that will exist when all the expected nodes
+are joined. Quorum will usually be half of this (rounded up).
+.br
+It is not possible to set expected votes up so that it makes the cluster inquorate using this command.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.PP

+ 57 - 0
man/votequorum_setvotes.3

@@ -0,0 +1,57 @@
+.\"/*
+.\" * Copyright (c) 2009 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Christine Caulfield <ccaulfie@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 VOTEQUORUM_VOTES 3 2009-01-26 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
+.SH NAME
+votequorum_setvotes \- Sets the number of votes for a node
+.SH SYNOPSIS
+.B #include <corosync/votequorum.h>
+.sp
+.BI "int votequorum_setexpected(votequorum_handle_t " handle ", unsigned int " nodeid ", int " votes ");"
+.SH DESCRIPTION
+The
+.B votequorum_setvotes
+is used to change the number of votes that a node has. Note that it is not possible, using this function, 
+to change the number of node votes such that the cluster goes inquorate.
+.SH RETURN VALUE
+This call returns the CS_OK value if successful, otherwise an error is returned.
+.PP
+.SH ERRORS
+The errors are undocumented.
+.SH "SEE ALSO"
+.BR votequorum_overview (8),
+.BR votequorum_initialize (3),
+.BR votequorum_finalize (3),
+.BR votequorum_dispatch (3),
+.BR votequorum_fd_get (3),
+.PP

+ 7 - 1
services/Makefile

@@ -55,7 +55,7 @@ LCR_OBJS = evs.o cfg.o cpg.o confdb.o $(AMF_OBJS) pload.o
 
 override CFLAGS += -fPIC
 
-all: service_evs.lcrso service_cfg.lcrso service_cpg.lcrso service_confdb.lcrso service_pload.lcrso testquorum.lcrso
+all: service_evs.lcrso service_cfg.lcrso service_cpg.lcrso service_confdb.lcrso service_pload.lcrso testquorum.lcrso service_votequorum.lcrso
 
 ifeq (${COROSYNC_COMPAT}, DARWIN)
 
@@ -74,6 +74,9 @@ service_cpg.lcrso: cpg.o
 service_pload.lcrso: pload.o
 	$(CC) $(LDFLAGS) -bundle $(LDFLAGS) -bundle_loader ../exec/corosync -bind_at_load pload.o -o $@
 
+service_votequorum.lcrso: votequorum.o
+	$(CC) $(LDFLAGS) -bundle $(LDFLAGS) -bundle_loader ../exec/corosync -bind_at_load votequorum.o -o $@
+
 testquorum.lcrso: testquorum.o
 	$(CC) $(LDFLAGS) -bundle $(LDFLAGS) -bundle_loader ../exec/corosync -bind_at_load testquorum.o -o $@
 
@@ -94,6 +97,9 @@ service_cpg.lcrso: cpg.o
 service_pload.lcrso: pload.o
 	$(CC) -shared -Wl,-soname,service_pload.lcrso pload.o -o $@
 
+service_votequorum.lcrso: votequorum.o
+	$(CC) -shared -Wl,-soname,service_votequorum.lcrso votequorum.o -o $@
+
 testquorum.lcrso: testquorum.o
 	$(CC) -shared -Wl,-soname,testquorum.lcrso testquorum.o -o $@
 

+ 1619 - 0
services/votequorum.c

@@ -0,0 +1,1619 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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 CONTIBUTORS "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.
+ */
+#include <sys/types.h>
+#ifndef COROSYNC_BSD
+#include <alloca.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <corosync/corotypes.h>
+#include <corosync/cfg.h>
+#include <corosync/list.h>
+#include <corosync/lcr/lcr_comp.h>
+#include <corosync/engine/logsys.h>
+#include <corosync/ipc_gen.h>
+#include <corosync/engine/coroapi.h>
+#include <corosync/engine/quorum.h>
+#include <corosync/ipc_votequorum.h>
+#include <corosync/list.h>
+
+#define VOTEQUORUM_MAJOR_VERSION 6
+#define VOTEQUORUM_MINOR_VERSION 3
+#define VOTEQUORUM_PATCH_VERSION 0
+
+ /* Silly default to prevent accidents! */
+#define DEFAULT_EXPECTED   1024
+#define DEFAULT_QDEV_POLL 10000
+
+LOGSYS_DECLARE_SUBSYS ("VOTEQ", LOG_INFO);
+
+enum quorum_message_req_types {
+	MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO  = 0,
+	MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE = 1,
+	MESSAGE_REQ_EXEC_VOTEQUORUM_KILLNODE = 2,
+};
+
+#define NODE_FLAGS_BEENDOWN         1
+#define NODE_FLAGS_SEESDISALLOWED   8
+#define NODE_FLAGS_HASSTATE        16
+#define NODE_FLAGS_QDISK           32
+#define NODE_FLAGS_REMOVED         64
+#define NODE_FLAGS_US             128
+
+
+typedef enum { NODESTATE_JOINING=1, NODESTATE_MEMBER,
+	       NODESTATE_DEAD, NODESTATE_LEAVING, NODESTATE_DISALLOWED } nodestate_t;
+
+
+/* This structure is tacked onto the start of a cluster message packet for our
+ * own nefarious purposes. */
+struct q_protheader {
+	unsigned char  tgtport; /* Target port number */
+	unsigned char  srcport; /* Source (originating) port number */
+	unsigned short pad;
+	unsigned int   flags;
+	int            srcid;	/* Node ID of the sender */
+	int            tgtid;	/* Node ID of the target */
+} __attribute__((packed));
+
+struct cluster_node {
+	int flags;
+	int node_id;
+	unsigned int expected_votes;
+	unsigned int votes;
+	time_t join_time;
+
+	nodestate_t state;
+
+	struct timeval last_hello; /* Only used for quorum devices */
+
+	struct list_head list;
+};
+
+static int quorum_flags;
+#define VOTEQUORUM_FLAG_FEATURE_DISALLOWED 1
+#define VOTEQUORUM_FLAG_FEATURE_TWONODE 1
+
+static int quorum;
+static int cluster_is_quorate;
+static int first_trans = 1;
+static unsigned int quorumdev_poll = DEFAULT_QDEV_POLL;
+
+static struct cluster_node *us;
+static struct cluster_node *quorum_device = NULL;
+static char quorum_device_name[VOTEQUORUM_MAX_QDISK_NAME_LEN];
+static corosync_timer_handle_t quorum_device_timer;
+static struct list_head cluster_members_list;
+static struct corosync_api_v1 *corosync_api;
+static struct list_head trackers_list;
+static unsigned int quorum_members[PROCESSOR_COUNT_MAX+1];
+static int quorum_members_entries = 0;
+static struct memb_ring_id quorum_ringid;
+static cs_tpg_handle group_handle;
+
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+static struct cluster_node *find_node_by_nodeid(int nodeid);
+static struct cluster_node *allocate_node(int nodeid);
+static char *kill_reason(int reason);
+
+static struct corosync_tpg_group quorum_group[1] = {
+        { .group          = "VOTEQ", .group_len      = 5},
+};
+
+#define list_iterate(v, head) \
+        for (v = (head)->next; v != head; v = v->next)
+
+struct quorum_pd {
+	unsigned char track_flags;
+	int tracking_enabled;
+	uint64_t tracking_context;
+	struct list_head list;
+	void *conn;
+};
+
+/*
+ * Service Interfaces required by service_message_handler struct
+ */
+
+static void votequorum_init(struct corosync_api_v1 *api,
+			    quorum_set_quorate_fn_t report);
+
+static void quorum_confchg_fn (
+	enum totem_configuration_type configuration_type,
+	unsigned int *member_list, int member_list_entries,
+	unsigned int *left_list, int left_list_entries,
+	unsigned int *joined_list, int joined_list_entries,
+	struct memb_ring_id *ring_id);
+
+static void quorum_deliver_fn(unsigned int nodeid, struct iovec *iovec, int iov_len,
+			      int endian_conversion_required);
+
+static int votequorum_exec_init_fn (struct corosync_api_v1 *corosync_api);
+
+static int quorum_lib_init_fn (void *conn);
+
+static int quorum_lib_exit_fn (void *conn);
+
+static void message_handler_req_exec_quorum_nodeinfo (
+	void *message,
+	unsigned int nodeid);
+
+static void message_handler_req_exec_quorum_reconfigure (
+	void *message,
+	unsigned int nodeid);
+
+static void message_handler_req_exec_quorum_killnode (
+	void *message,
+	unsigned int nodeid);
+
+
+static void message_handler_req_lib_votequorum_getinfo (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_setexpected (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_setvotes (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_qdisk_register (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_qdisk_unregister (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_qdisk_poll (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_qdisk_getinfo (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_setstate (void *conn, void *message);
+
+static void message_handler_req_lib_votequorum_leaving (void *conn, void *message);
+static void message_handler_req_lib_votequorum_trackstart (void *conn, void *msg);
+static void message_handler_req_lib_votequorum_trackstop (void *conn, void *msg);
+
+static int quorum_exec_send_nodeinfo(void);
+static int quorum_exec_send_reconfigure(int param, int nodeid, int value);
+static int quorum_exec_send_killnode(int nodeid, unsigned int reason);
+
+static void add_votequorum_config_notification(unsigned int quorum_object_handle);
+
+
+/*
+ * Library Handler Definition
+ */
+static struct corosync_lib_handler quorum_lib_service[] =
+{
+	{ /* 0 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_getinfo,
+		.response_size				= sizeof (struct res_lib_votequorum_getinfo),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_GETINFO,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 1 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_setexpected,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 2 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_setvotes,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 3 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_qdisk_register,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 4 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_qdisk_unregister,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 5 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_qdisk_poll,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 6 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_qdisk_getinfo,
+		.response_size				= sizeof (struct res_lib_votequorum_qdisk_getinfo),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_QDISK_GETINFO,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 7 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_setstate,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 8 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_leaving,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 9 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_trackstart,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	},
+	{ /* 10 */
+		.lib_handler_fn				= message_handler_req_lib_votequorum_trackstop,
+		.response_size				= sizeof (struct res_lib_votequorum_status),
+		.response_id				= MESSAGE_RES_VOTEQUORUM_STATUS,
+		.flow_control				= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
+	}
+};
+
+static quorum_set_quorate_fn_t set_quorum;
+/*
+ * lcrso object definition
+ */
+static struct quorum_services_api_ver1 votequorum_iface_ver0 = {
+	.init				= votequorum_init
+};
+
+static struct corosync_service_engine quorum_service_handler = {
+	.name				        = "corosync votes quorum service v0.90",
+	.id					= VOTEQUORUM_SERVICE,
+	.private_data_size			= sizeof (struct quorum_pd),
+	.allow_inquorate			= CS_LIB_ALLOW_INQUORATE,
+	.flow_control				= COROSYNC_LIB_FLOW_CONTROL_REQUIRED,
+	.lib_init_fn				= quorum_lib_init_fn,
+	.lib_exit_fn				= quorum_lib_exit_fn,
+	.lib_engine				= quorum_lib_service,
+	.lib_engine_count			= sizeof (quorum_lib_service) / sizeof (struct corosync_lib_handler),
+	.exec_init_fn				= votequorum_exec_init_fn,
+	.exec_engine				= NULL,
+	.exec_engine_count		        = 0,
+	.confchg_fn                             = NULL,
+};
+
+/*
+ * Dynamic loader definition
+ */
+static struct corosync_service_engine *quorum_get_service_handler_ver0 (void);
+
+static struct corosync_service_engine_iface_ver0 quorum_service_handler_iface = {
+	.corosync_get_service_engine_ver0 = quorum_get_service_handler_ver0
+};
+
+static struct lcr_iface corosync_quorum_ver0[2] = {
+	{
+		.name				= "corosync_votequorum",
+		.version			= 0,
+		.versions_replace		= 0,
+		.versions_replace_count         = 0,
+		.dependencies			= 0,
+		.dependency_count		= 0,
+		.constructor			= NULL,
+		.destructor			= NULL,
+		.interfaces			= (void **)(void *)&votequorum_iface_ver0
+	},
+	{
+		.name				= "corosync_votequorum_iface",
+		.version			= 0,
+		.versions_replace		= 0,
+		.versions_replace_count         = 0,
+		.dependencies			= 0,
+		.dependency_count		= 0,
+		.constructor			= NULL,
+		.destructor			= NULL,
+		.interfaces			= NULL
+	}
+};
+
+static struct lcr_comp quorum_comp_ver0 = {
+	.iface_count			= 2,
+	.ifaces			        = corosync_quorum_ver0
+};
+
+
+static struct corosync_service_engine *quorum_get_service_handler_ver0 (void)
+{
+	return (&quorum_service_handler);
+}
+
+__attribute__ ((constructor)) static void quorum_comp_register (void) {
+        lcr_interfaces_set (&corosync_quorum_ver0[0], &votequorum_iface_ver0);
+	lcr_interfaces_set (&corosync_quorum_ver0[1], &quorum_service_handler_iface);
+	lcr_component_register (&quorum_comp_ver0);
+}
+
+static void votequorum_init(struct corosync_api_v1 *api,
+			    quorum_set_quorate_fn_t report)
+{
+	ENTER();
+	set_quorum = report;
+
+	/* Load the library-servicing part of this module */
+	api->service_link_and_init(api, "corosync_votequorum_iface", 0);
+
+	LEAVE();
+}
+
+/* Message types */
+#define VOTEQUORUM_MSG_NODEINFO     5
+#define VOTEQUORUM_MSG_KILLNODE     6
+#define VOTEQUORUM_MSG_RECONFIGURE  8
+
+struct req_exec_quorum_nodeinfo {
+	unsigned char cmd;
+	unsigned char first_trans;
+	unsigned int  votes;
+	unsigned int  expected_votes;
+
+	unsigned int   major_version;	/* Not backwards compatible */
+	unsigned int   minor_version;	/* Backwards compatible */
+	unsigned int   patch_version;	/* Backwards/forwards compatible */
+	unsigned int   config_version;
+	unsigned int   flags;
+
+} __attribute__((packed));
+
+/* Parameters for RECONFIG command */
+#define RECONFIG_PARAM_EXPECTED_VOTES 1
+#define RECONFIG_PARAM_NODE_VOTES     2
+#define RECONFIG_PARAM_LEAVING        3
+
+struct req_exec_quorum_reconfigure {
+	unsigned char  cmd;
+	unsigned char  param;
+	unsigned short pad;
+	int            nodeid;
+	unsigned int   value;
+};
+
+struct req_exec_quorum_killnode {
+	unsigned char cmd;
+	unsigned char pad1;
+	uint16_t reason;
+	int nodeid;
+};
+
+/* These just make the access a little neater */
+static inline int objdb_get_string(struct corosync_api_v1 *corosync, unsigned int object_service_handle,
+				   char *key, char **value)
+{
+	int res;
+
+	*value = NULL;
+	if ( !(res = corosync_api->object_key_get(object_service_handle,
+					      key,
+					      strlen(key),
+					      (void *)value,
+					      NULL))) {
+		if (*value)
+			return 0;
+	}
+	return -1;
+}
+
+static inline void objdb_get_int(struct corosync_api_v1 *corosync, unsigned int object_service_handle,
+				 char *key, unsigned int *intvalue, unsigned int default_value)
+{
+	char *value = NULL;
+
+	*intvalue = default_value;
+
+	if (!corosync_api->object_key_get(object_service_handle, key, strlen(key),
+				 (void *)&value, NULL)) {
+		if (value) {
+			*intvalue = atoi(value);
+		}
+	}
+}
+
+static int votequorum_send_message(void *message, int len)
+{
+	struct iovec iov[2];
+	struct q_protheader header;
+
+	header.tgtport = 0;
+	header.srcport = 0;
+	header.flags   = 0;
+	header.srcid   = us->node_id;
+	header.tgtid   = 0;
+
+	iov[0].iov_base = &header;
+	iov[0].iov_len  = sizeof(header);
+	iov[1].iov_base = message;
+	iov[1].iov_len  = len;
+
+	return corosync_api->tpg_joined_mcast(group_handle, iov, 2, TOTEM_AGREED);
+}
+
+static void read_quorum_config(unsigned int quorum_handle)
+{
+	unsigned int value = 0;
+	int cluster_members = 0;
+	struct list_head *tmp;
+	struct cluster_node *node;
+
+	log_printf(LOG_INFO, "Reading configuration\n");
+
+	objdb_get_int(corosync_api, quorum_handle, "expected_votes", &us->expected_votes, DEFAULT_EXPECTED);
+	objdb_get_int(corosync_api, quorum_handle, "votes", &us->votes, 1);
+	objdb_get_int(corosync_api, quorum_handle, "quorumdev_poll", &quorumdev_poll, DEFAULT_QDEV_POLL);
+	objdb_get_int(corosync_api, quorum_handle, "disallowed", &value, 0);
+	if (value)
+		quorum_flags |= VOTEQUORUM_FLAG_FEATURE_DISALLOWED;
+	else
+		quorum_flags &= ~VOTEQUORUM_FLAG_FEATURE_DISALLOWED;
+
+	objdb_get_int(corosync_api, quorum_handle, "two_node", &value, 0);
+	if (value)
+		quorum_flags |= VOTEQUORUM_FLAG_FEATURE_TWONODE;
+	else
+		quorum_flags &= ~VOTEQUORUM_FLAG_FEATURE_TWONODE;
+
+	/*
+	 * two_node mode is invalid if there are more than 2 nodes in the cluster!
+	 */
+	list_iterate(tmp, &cluster_members_list) {
+		node = list_entry(tmp, struct cluster_node, list);
+		cluster_members++;
+        }
+
+	if (quorum_flags & VOTEQUORUM_FLAG_FEATURE_TWONODE && cluster_members > 2) {
+		log_printf(LOG_WARNING, "quorum.two_node was set but there are more than 2 nodes in the cluster. It will be ignored.");
+		quorum_flags &= ~VOTEQUORUM_FLAG_FEATURE_TWONODE;
+	}
+}
+
+static int votequorum_exec_init_fn (struct corosync_api_v1 *api)
+{
+	unsigned int object_handle;
+	unsigned int find_handle;
+
+	ENTER();
+
+	corosync_api = api;
+
+	list_init(&cluster_members_list);
+	list_init(&trackers_list);
+
+	/* Allocate a cluster_node for us */
+	us = allocate_node(corosync_api->totem_nodeid_get());
+	if (!us)
+		return (1);
+
+	us->flags |= NODE_FLAGS_US;
+	us->state = NODESTATE_MEMBER;
+	us->expected_votes = DEFAULT_EXPECTED;
+	us->votes = 1;
+	time(&us->join_time);
+
+	/* Get configuration variables */
+	corosync_api->object_find_create(OBJECT_PARENT_HANDLE, "quorum", strlen("quorum"), &find_handle);
+
+	if (corosync_api->object_find_next(find_handle, &object_handle) == 0) {
+		read_quorum_config(object_handle);
+	}
+	/* Listen for changes */
+	add_votequorum_config_notification(object_handle);
+	corosync_api->object_find_destroy(find_handle);
+
+	api->tpg_init(&group_handle, quorum_deliver_fn, quorum_confchg_fn);
+	api->tpg_join(group_handle, quorum_group, 1);
+
+	LEAVE();
+	return (0);
+}
+
+static int quorum_lib_exit_fn (void *conn)
+{
+	struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
+
+	ENTER();
+	if (quorum_pd->tracking_enabled) {
+		list_del (&quorum_pd->list);
+		list_init (&quorum_pd->list);
+	}
+	LEAVE();
+	return (0);
+}
+
+
+static int send_quorum_notification(void *conn, uint64_t context)
+{
+	struct res_lib_votequorum_notification *res_lib_votequorum_notification;
+	struct list_head *tmp;
+	struct cluster_node *node;
+	int cluster_members = 0;
+	int i = 0;
+	int size;
+	char *buf;
+
+	ENTER();
+	list_iterate(tmp, &cluster_members_list) {
+		node = list_entry(tmp, struct cluster_node, list);
+		cluster_members++;
+        }
+	if (quorum_device)
+		cluster_members++;
+
+	size = sizeof(struct res_lib_votequorum_notification) + sizeof(struct votequorum_node) * cluster_members;
+	buf = alloca(size);
+	if (!buf) {
+		LEAVE();
+		return -1;
+	}
+
+	res_lib_votequorum_notification = (struct res_lib_votequorum_notification *)buf;
+	res_lib_votequorum_notification->quorate = cluster_is_quorate;
+	res_lib_votequorum_notification->node_list_entries = cluster_members;
+	res_lib_votequorum_notification->context = context;
+	list_iterate(tmp, &cluster_members_list) {
+		node = list_entry(tmp, struct cluster_node, list);
+		res_lib_votequorum_notification->node_list[i].nodeid = node->node_id;
+		res_lib_votequorum_notification->node_list[i++].state = node->state;
+        }
+	if (quorum_device) {
+		res_lib_votequorum_notification->node_list[i].nodeid = 0;
+		res_lib_votequorum_notification->node_list[i++].state = quorum_device->state | 0x80;
+	}
+	res_lib_votequorum_notification->header.id = MESSAGE_RES_VOTEQUORUM_NOTIFICATION;
+	res_lib_votequorum_notification->header.size = size;
+	res_lib_votequorum_notification->header.error = CS_OK;
+
+	/* Send it to all interested parties */
+	if (conn) {
+		int ret = corosync_api->ipc_conn_send_response(conn, buf, size);
+		LEAVE();
+		return ret;
+	}
+	else {
+		struct quorum_pd *qpd;
+
+		list_iterate(tmp, &trackers_list) {
+			qpd = list_entry(tmp, struct quorum_pd, list);
+			res_lib_votequorum_notification->context = qpd->tracking_context;
+			corosync_api->ipc_conn_send_response(corosync_api->ipc_conn_partner_get(qpd->conn), buf, size);
+		}
+	}
+	LEAVE();
+	return 0;
+}
+
+static void set_quorate(int total_votes)
+{
+	int quorate;
+
+	ENTER();
+	if (quorum > total_votes) {
+		quorate = 0;
+	}
+	else {
+		quorate = 1;
+	}
+
+	if (cluster_is_quorate && !quorate)
+		log_printf(LOG_INFO, "quorum lost, blocking activity\n");
+	if (!cluster_is_quorate && quorate)
+		log_printf(LOG_INFO, "quorum regained, resuming activity\n");
+
+	/* If we are newly quorate, then kill any DISALLOWED nodes */
+	if (!cluster_is_quorate && quorate) {
+		struct cluster_node *node = NULL;
+		struct list_head *tmp;
+
+		list_iterate(tmp, &cluster_members_list) {
+			node = list_entry(tmp, struct cluster_node, list);
+			if (node->state == NODESTATE_DISALLOWED)
+				quorum_exec_send_killnode(node->node_id, VOTEQUORUM_REASON_KILL_REJOIN);
+		}
+	}
+
+	cluster_is_quorate = quorate;
+	set_quorum(quorum_members, quorum_members_entries, quorate, &quorum_ringid);
+	ENTER();
+}
+
+static int calculate_quorum(int allow_decrease, int max_expected, unsigned int *ret_total_votes)
+{
+	struct list_head *nodelist;
+	struct cluster_node *node;
+	unsigned int total_votes = 0;
+	unsigned int highest_expected = 0;
+	unsigned int newquorum, q1, q2;
+	unsigned int total_nodes = 0;
+	unsigned int leaving = 0;
+
+	ENTER();
+	list_iterate(nodelist, &cluster_members_list) {
+		node = list_entry(nodelist, struct cluster_node, list);
+
+		log_printf(LOG_DEBUG, "node %x state=%d, votes=%d, expected=%d\n",
+			   node->node_id, node->state, node->votes, node->expected_votes);
+
+		if (node->state == NODESTATE_MEMBER) {
+			if (max_expected)
+				node->expected_votes = max_expected;
+			else
+				highest_expected = max(highest_expected, node->expected_votes);
+			total_votes += node->votes;
+			total_nodes++;
+		}
+		if (node->state == NODESTATE_LEAVING) {
+			leaving = 1;
+		}
+	}
+
+	if (quorum_device && quorum_device->state == NODESTATE_MEMBER)
+		total_votes += quorum_device->votes;
+
+	if (max_expected > 0)
+		highest_expected = max_expected;
+
+	/* This quorum calculation is taken from the OpenVMS Cluster Systems
+	 * manual, but, then, you guessed that didn't you */
+	q1 = (highest_expected + 2) / 2;
+	q2 = (total_votes + 2) / 2;
+	newquorum = max(q1, q2);
+
+	/* Normally quorum never decreases but the system administrator can
+	 * force it down by setting expected votes to a maximum value */
+	if (!allow_decrease)
+		newquorum = max(quorum, newquorum);
+
+	/* The special two_node mode allows each of the two nodes to retain
+	 * quorum if the other fails.  Only one of the two should live past
+	 * fencing (as both nodes try to fence each other in split-brain.)
+	 * Also: if there are more than two nodes, force us inquorate to avoid
+	 * any damage or confusion.
+	 */
+	if ((quorum_flags & VOTEQUORUM_FLAG_FEATURE_TWONODE) && total_nodes <= 2)
+		newquorum = 1;
+
+	if (ret_total_votes)
+		*ret_total_votes = total_votes;
+
+	LEAVE();
+	return newquorum;
+}
+
+/* Recalculate cluster quorum, set quorate and notify changes */
+static void recalculate_quorum(int allow_decrease)
+{
+	unsigned int total_votes;
+
+	ENTER();
+	quorum = calculate_quorum(allow_decrease, 0, &total_votes);
+	set_quorate(total_votes);
+	send_quorum_notification(NULL, 0L);
+	LEAVE();
+}
+
+static int have_disallowed(void)
+{
+	struct cluster_node *node;
+	struct list_head *tmp;
+
+	list_iterate(tmp, &cluster_members_list) {
+		node = list_entry(tmp, struct cluster_node, list);
+		if (node->state == NODESTATE_DISALLOWED)
+			return 1;
+	}
+
+	return 0;
+}
+
+static void node_add_ordered(struct cluster_node *newnode)
+{
+	struct cluster_node *node = NULL;
+	struct list_head *tmp;
+	struct list_head *newlist = &newnode->list;
+
+	list_iterate(tmp, &cluster_members_list) {
+		node = list_entry(tmp, struct cluster_node, list);
+
+                if (newnode->node_id < node->node_id)
+                        break;
+        }
+
+        if (!node)
+		list_add(&newnode->list, &cluster_members_list);
+        else {
+                newlist->prev = tmp->prev;
+                newlist->next = tmp;
+                tmp->prev->next = newlist;
+                tmp->prev = newlist;
+        }
+}
+
+static struct cluster_node *allocate_node(int nodeid)
+{
+	struct cluster_node *cl;
+
+	cl = malloc(sizeof(struct cluster_node));
+	if (cl) {
+		memset(cl, 0, sizeof(struct cluster_node));
+		cl->node_id = nodeid;
+		if (nodeid)
+			node_add_ordered(cl);
+	}
+	return cl;
+}
+
+static struct cluster_node *find_node_by_nodeid(int nodeid)
+{
+	struct cluster_node *node;
+	struct list_head *tmp;
+
+	list_iterate(tmp, &cluster_members_list) {
+		node = list_entry(tmp, struct cluster_node, list);
+		if (node->node_id == nodeid)
+			return node;
+	}
+	return NULL;
+}
+
+
+static int quorum_exec_send_nodeinfo()
+{
+	struct req_exec_quorum_nodeinfo req_exec_quorum_nodeinfo;
+	int ret;
+
+	ENTER();
+
+	req_exec_quorum_nodeinfo.cmd = VOTEQUORUM_MSG_NODEINFO;
+	req_exec_quorum_nodeinfo.expected_votes = us->expected_votes;
+	req_exec_quorum_nodeinfo.votes = us->votes;
+	req_exec_quorum_nodeinfo.major_version = VOTEQUORUM_MAJOR_VERSION;
+	req_exec_quorum_nodeinfo.minor_version = VOTEQUORUM_MINOR_VERSION;
+	req_exec_quorum_nodeinfo.patch_version = VOTEQUORUM_PATCH_VERSION;
+	req_exec_quorum_nodeinfo.flags = us->flags;
+	req_exec_quorum_nodeinfo.first_trans = first_trans;
+	if (have_disallowed())
+		req_exec_quorum_nodeinfo.flags |= NODE_FLAGS_SEESDISALLOWED;
+
+	ret = votequorum_send_message(&req_exec_quorum_nodeinfo, sizeof(req_exec_quorum_nodeinfo));
+	LEAVE();
+	return ret;
+}
+
+
+static int quorum_exec_send_reconfigure(int param, int nodeid, int value)
+{
+	struct req_exec_quorum_reconfigure req_exec_quorum_reconfigure;
+	int ret;
+
+	ENTER();
+
+	req_exec_quorum_reconfigure.cmd = VOTEQUORUM_MSG_RECONFIGURE;
+	req_exec_quorum_reconfigure.param = param;
+	req_exec_quorum_reconfigure.nodeid = nodeid;
+	req_exec_quorum_reconfigure.value = value;
+
+	ret = votequorum_send_message(&req_exec_quorum_reconfigure, sizeof(req_exec_quorum_reconfigure));
+	LEAVE();
+	return ret;
+}
+
+static int quorum_exec_send_killnode(int nodeid, unsigned int reason)
+{
+	struct req_exec_quorum_killnode req_exec_quorum_killnode;
+	int ret;
+
+	ENTER();
+
+	req_exec_quorum_killnode.cmd = VOTEQUORUM_MSG_KILLNODE;
+	req_exec_quorum_killnode.nodeid = nodeid;
+	req_exec_quorum_killnode.reason = reason;
+
+	ret = votequorum_send_message(&req_exec_quorum_killnode, sizeof(req_exec_quorum_killnode));
+	LEAVE();
+	return ret;
+}
+
+static void quorum_confchg_fn (
+	enum totem_configuration_type configuration_type,
+	unsigned int *member_list, int member_list_entries,
+	unsigned int *left_list, int left_list_entries,
+	unsigned int *joined_list, int joined_list_entries,
+	struct memb_ring_id *ring_id)
+{
+	int i;
+	int leaving = 0;
+	struct cluster_node *node;
+
+	ENTER();
+	if (member_list_entries > 1)
+		first_trans = 0;
+
+	if (left_list_entries) {
+		for (i = 0; i< left_list_entries; i++) {
+			node = find_node_by_nodeid(left_list[i]);
+			if (node) {
+				if (node->state == NODESTATE_LEAVING)
+					leaving = 1;
+				node->state = NODESTATE_DEAD;
+				node->flags |= NODE_FLAGS_BEENDOWN;
+			}
+		}
+		recalculate_quorum(leaving);
+	}
+
+	if (member_list_entries) {
+		memcpy(quorum_members, member_list, sizeof(unsigned int) * member_list_entries);
+		quorum_members_entries = member_list_entries;
+		if (quorum_device) {
+			quorum_members[quorum_members_entries++] = 0;
+		}
+		quorum_exec_send_nodeinfo();
+	}
+
+	memcpy(&quorum_ringid, ring_id, sizeof(*ring_id));
+	LEAVE();
+}
+
+static void exec_quorum_nodeinfo_endian_convert (void *msg)
+{
+	struct req_exec_quorum_nodeinfo *nodeinfo = (struct req_exec_quorum_nodeinfo *)msg;
+
+	nodeinfo->votes = swab32(nodeinfo->votes);
+	nodeinfo->expected_votes = swab32(nodeinfo->expected_votes);
+	nodeinfo->major_version = swab32(nodeinfo->major_version);
+	nodeinfo->minor_version = swab32(nodeinfo->minor_version);
+	nodeinfo->patch_version = swab32(nodeinfo->patch_version);
+	nodeinfo->config_version = swab32(nodeinfo->config_version);
+	nodeinfo->flags = swab32(nodeinfo->flags);
+}
+
+static void exec_quorum_reconfigure_endian_convert (void *msg)
+{
+	struct req_exec_quorum_reconfigure *reconfigure = (struct req_exec_quorum_reconfigure *)msg;
+	reconfigure->nodeid = swab32(reconfigure->nodeid);
+	reconfigure->value = swab32(reconfigure->value);
+}
+
+static void exec_quorum_killnode_endian_convert (void *msg)
+{
+	struct req_exec_quorum_killnode *killnode = (struct req_exec_quorum_killnode *)msg;
+	killnode->reason = swab16(killnode->reason);
+	killnode->nodeid = swab32(killnode->nodeid);
+}
+
+static void quorum_deliver_fn(unsigned int nodeid, struct iovec *iovec, int iov_len,
+			      int endian_conversion_required)
+{
+	struct q_protheader *header = iovec->iov_base;
+	char *buf;
+
+	ENTER();
+
+	if (endian_conversion_required) {
+		header->srcid = swab32(header->srcid);
+		header->tgtid = swab32(header->tgtid);
+		header->flags = swab32(header->flags);
+	}
+
+	/* Only pass on messages for us or everyone */
+	if (header->tgtport == 0 &&
+	    (header->tgtid == us->node_id ||
+	     header->tgtid == 0)) {
+		buf = iovec->iov_base + sizeof(struct q_protheader);
+		switch (*buf) {
+
+		case VOTEQUORUM_MSG_NODEINFO:
+			if (endian_conversion_required)
+				exec_quorum_nodeinfo_endian_convert(buf);
+			message_handler_req_exec_quorum_nodeinfo (buf, header->srcid);
+			break;
+		case VOTEQUORUM_MSG_RECONFIGURE:
+			if (endian_conversion_required)
+				exec_quorum_reconfigure_endian_convert(buf);
+			message_handler_req_exec_quorum_reconfigure (buf, header->srcid);
+			break;
+		case VOTEQUORUM_MSG_KILLNODE:
+			if (endian_conversion_required)
+				exec_quorum_killnode_endian_convert(buf);
+			message_handler_req_exec_quorum_killnode (buf, header->srcid);
+			break;
+
+			/* Just ignore other messages */
+		}
+	}
+	LEAVE();
+}
+
+static void message_handler_req_exec_quorum_nodeinfo (
+	void *message,
+	unsigned int nodeid)
+{
+	struct req_exec_quorum_nodeinfo *req_exec_quorum_nodeinfo = (struct req_exec_quorum_nodeinfo *)message;
+	struct cluster_node *node;
+	int old_votes;
+	int old_expected;
+	nodestate_t old_state;
+	int new_node = 0;
+
+	ENTER();
+	log_printf(LOG_LEVEL_DEBUG, "got nodeinfo message from cluster node %d\n", nodeid);
+
+	node = find_node_by_nodeid(nodeid);
+	if (!node) {
+		node = allocate_node(nodeid);
+		new_node = 1;
+	}
+	if (!node) {
+		corosync_api->error_memory_failure();
+		return;
+	}
+
+	/*
+	 * If the node sending the message sees disallowed nodes and we don't, then
+	 * we have to leave
+	 */
+	if (req_exec_quorum_nodeinfo->flags & NODE_FLAGS_SEESDISALLOWED && !have_disallowed()) {
+		/* Must use syslog directly here or the message will never arrive */
+		syslog(LOG_CRIT, "[VOTEQ]: Joined a cluster with disallowed nodes. must die");
+		corosync_api->fatal_error(2, __FILE__, __LINE__);
+		exit(2);
+	}
+	old_votes = node->votes;
+	old_expected = node->expected_votes;
+	old_state = node->state;
+
+	/* Update node state */
+	if (req_exec_quorum_nodeinfo->minor_version >= 2)
+		node->votes = req_exec_quorum_nodeinfo->votes;
+	node->expected_votes = req_exec_quorum_nodeinfo->expected_votes;
+	node->state = NODESTATE_MEMBER;
+
+	/* Check flags for disallowed (if enabled) */
+	if (quorum_flags & VOTEQUORUM_FLAG_FEATURE_DISALLOWED) {
+		if ((req_exec_quorum_nodeinfo->flags & NODE_FLAGS_HASSTATE && node->flags & NODE_FLAGS_BEENDOWN) ||
+		    (req_exec_quorum_nodeinfo->flags & NODE_FLAGS_HASSTATE && req_exec_quorum_nodeinfo->first_trans && !(node->flags & NODE_FLAGS_US))) {
+			if (node->state != NODESTATE_DISALLOWED) {
+				if (cluster_is_quorate) {
+					log_printf(LOG_CRIT, "Killing node %d because it has rejoined the cluster with existing state", node->node_id);
+					node->state = NODESTATE_DISALLOWED;
+					quorum_exec_send_killnode(nodeid, VOTEQUORUM_REASON_KILL_REJOIN);
+				}
+				else {
+					log_printf(LOG_CRIT, "Node %d not joined to quorum because it has existing state", node->node_id);
+					node->state = NODESTATE_DISALLOWED;
+				}
+			}
+		}
+	}
+	node->flags &= ~NODE_FLAGS_BEENDOWN;
+
+	if (new_node || old_votes != node->votes || old_expected != node->expected_votes || old_state != node->state)
+		recalculate_quorum(0);
+	LEAVE();
+}
+
+static void message_handler_req_exec_quorum_killnode (
+	void *message,
+	unsigned int nodeid)
+{
+	struct req_exec_quorum_killnode *req_exec_quorum_killnode = (struct req_exec_quorum_killnode *)message;
+
+	if (req_exec_quorum_killnode->nodeid == corosync_api->totem_nodeid_get()) {
+		log_printf(LOG_CRIT, "Killed by node %d: %s\n", nodeid, kill_reason(req_exec_quorum_killnode->reason));
+
+		corosync_api->fatal_error(1, __FILE__, __LINE__);
+		exit(1);
+	}
+}
+
+static void message_handler_req_exec_quorum_reconfigure (
+	void *message,
+	unsigned int nodeid)
+{
+	struct req_exec_quorum_reconfigure *req_exec_quorum_reconfigure = (struct req_exec_quorum_reconfigure *)message;
+	struct cluster_node *node;
+	struct list_head *nodelist;
+
+	log_printf(LOG_LEVEL_DEBUG, "got reconfigure message from cluster node %d\n", nodeid);
+
+	node = find_node_by_nodeid(req_exec_quorum_reconfigure->nodeid);
+	if (!node)
+		return;
+
+	switch(req_exec_quorum_reconfigure->param)
+	{
+	case RECONFIG_PARAM_EXPECTED_VOTES:
+		node->expected_votes = req_exec_quorum_reconfigure->value;
+
+		list_iterate(nodelist, &cluster_members_list) {
+			node = list_entry(nodelist, struct cluster_node, list);
+			if (node->state == NODESTATE_MEMBER &&
+			    node->expected_votes > req_exec_quorum_reconfigure->value) {
+				node->expected_votes = req_exec_quorum_reconfigure->value;
+			}
+		}
+		recalculate_quorum(1);  /* Allow decrease */
+		break;
+
+	case RECONFIG_PARAM_NODE_VOTES:
+		node->votes = req_exec_quorum_reconfigure->value;
+		recalculate_quorum(1);  /* Allow decrease */
+		break;
+
+	case RECONFIG_PARAM_LEAVING:
+		node->state = NODESTATE_LEAVING;
+		break;
+	}
+}
+
+static int quorum_lib_init_fn (void *conn)
+{
+	struct quorum_pd *pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
+
+	ENTER();
+
+	list_init (&pd->list);
+	pd->conn = conn;
+
+	LEAVE();
+	return (0);
+}
+
+/* Message from the library */
+static void message_handler_req_lib_votequorum_getinfo (void *conn, void *message)
+{
+	struct req_lib_votequorum_getinfo *req_lib_votequorum_getinfo = (struct req_lib_votequorum_getinfo *)message;
+	struct res_lib_votequorum_getinfo res_lib_votequorum_getinfo;
+	struct cluster_node *node;
+	unsigned int highest_expected = 0;
+	unsigned int total_votes = 0;
+	cs_error_t error = CS_OK;
+
+	log_printf(LOG_LEVEL_DEBUG, "got getinfo request on %p for node %d\n", conn, req_lib_votequorum_getinfo->nodeid);
+
+	if (req_lib_votequorum_getinfo->nodeid) {
+		node = find_node_by_nodeid(req_lib_votequorum_getinfo->nodeid);
+	}
+	else {
+		node = us;
+	}
+
+	if (node) {
+		struct cluster_node *iternode;
+		struct list_head *nodelist;
+
+		list_iterate(nodelist, &cluster_members_list) {
+			iternode = list_entry(nodelist, struct cluster_node, list);
+
+			if (node->state == NODESTATE_MEMBER) {
+				highest_expected =
+					max(highest_expected, node->expected_votes);
+				total_votes += node->votes;
+			}
+		}
+
+		if (quorum_device && quorum_device->state == NODESTATE_MEMBER) {
+			total_votes += quorum_device->votes;
+		}
+
+		res_lib_votequorum_getinfo.votes = us->votes;
+		res_lib_votequorum_getinfo.expected_votes = us->expected_votes;
+		res_lib_votequorum_getinfo.highest_expected = highest_expected;
+
+		res_lib_votequorum_getinfo.quorum = quorum;
+		res_lib_votequorum_getinfo.total_votes = total_votes;
+		res_lib_votequorum_getinfo.flags = 0;
+		res_lib_votequorum_getinfo.nodeid = node->node_id;
+
+		if (us->flags & NODE_FLAGS_HASSTATE)
+			res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_FLAG_HASSTATE;
+		if (quorum_flags & VOTEQUORUM_FLAG_FEATURE_TWONODE)
+			res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_FLAG_TWONODE;
+		if (cluster_is_quorate)
+			res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_FLAG_QUORATE;
+		if (us->flags & NODE_FLAGS_SEESDISALLOWED)
+			res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_FLAG_DISALLOWED;
+	}
+	else {
+		error = CS_ERR_NOT_EXIST;
+	}
+
+	res_lib_votequorum_getinfo.header.size = sizeof(res_lib_votequorum_getinfo);
+	res_lib_votequorum_getinfo.header.id = MESSAGE_RES_VOTEQUORUM_GETINFO;
+	res_lib_votequorum_getinfo.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_getinfo, sizeof(res_lib_votequorum_getinfo));
+	log_printf(LOG_LEVEL_DEBUG, "getinfo response error: %d\n", error);
+}
+
+/* Message from the library */
+static void message_handler_req_lib_votequorum_setexpected (void *conn, void *message)
+{
+	struct req_lib_votequorum_setexpected *req_lib_votequorum_setexpected = (struct req_lib_votequorum_setexpected *)message;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	cs_error_t error = CS_OK;
+	unsigned int newquorum;
+	unsigned int total_votes;
+
+	ENTER();
+
+	/*
+	 * If there are disallowed nodes, then we can't allow the user
+	 * to bypass them by fiddling with expected votes.
+	 */
+	if (quorum_flags & VOTEQUORUM_FLAG_FEATURE_DISALLOWED && have_disallowed()) {
+		error = CS_ERR_EXIST;
+		goto error_exit;
+	}
+
+	/* Validate new expected votes */
+	newquorum = calculate_quorum(1, req_lib_votequorum_setexpected->expected_votes, &total_votes);
+	if (newquorum < total_votes / 2
+	    || newquorum > total_votes) {
+		error = CS_ERR_INVALID_PARAM;
+		goto error_exit;
+	}
+
+	quorum_exec_send_reconfigure(RECONFIG_PARAM_EXPECTED_VOTES, us->node_id, req_lib_votequorum_setexpected->expected_votes);
+
+	/* send status */
+error_exit:
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+	LEAVE();
+}
+
+/* Message from the library */
+static void message_handler_req_lib_votequorum_setvotes (void *conn, void *message)
+{
+	struct req_lib_votequorum_setvotes *req_lib_votequorum_setvotes = (struct req_lib_votequorum_setvotes *)message;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	struct cluster_node *node;
+	unsigned int newquorum;
+	unsigned int total_votes;
+	unsigned int saved_votes;
+	cs_error_t error = CS_OK;
+
+	ENTER();
+
+	node = find_node_by_nodeid(req_lib_votequorum_setvotes->nodeid);
+	if (!node) {
+		error = CS_ERR_NAME_NOT_FOUND;
+		goto error_exit;
+	}
+
+	/* Check votes is valid */
+	saved_votes = node->votes;
+	node->votes = req_lib_votequorum_setvotes->votes;
+
+	newquorum = calculate_quorum(1, 0, &total_votes);
+
+	if (newquorum < total_votes / 2 || newquorum > total_votes) {
+		node->votes = saved_votes;
+		error = CS_ERR_INVALID_PARAM;
+		goto error_exit;
+	}
+
+	if (!req_lib_votequorum_setvotes->nodeid)
+		req_lib_votequorum_setvotes->nodeid = corosync_api->totem_nodeid_get();
+
+	quorum_exec_send_reconfigure(RECONFIG_PARAM_NODE_VOTES, req_lib_votequorum_setvotes->nodeid, req_lib_votequorum_setvotes->votes);
+
+error_exit:
+	/* send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+	LEAVE();
+}
+
+static void message_handler_req_lib_votequorum_leaving (void *conn, void *message)
+{
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	cs_error_t error = CS_OK;
+
+	ENTER();
+
+	quorum_exec_send_reconfigure(RECONFIG_PARAM_LEAVING, us->node_id, 0);
+
+	/* send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+	LEAVE();
+}
+
+static void quorum_device_timer_fn(void *arg)
+{
+	struct timeval now;
+
+	ENTER();
+	if (!quorum_device || quorum_device->state == NODESTATE_DEAD)
+		return;
+	gettimeofday(&now, NULL);
+	if (quorum_device->last_hello.tv_sec + quorumdev_poll/1000 < now.tv_sec) {
+		quorum_device->state = NODESTATE_DEAD;
+		log_printf(LOG_INFO, "lost contact with quorum device\n");
+		recalculate_quorum(0);
+	}
+	else {
+		corosync_api->timer_add_duration((unsigned long long)quorumdev_poll*1000000, quorum_device,
+						 quorum_device_timer_fn, &quorum_device_timer);
+	}
+	LEAVE();
+}
+
+
+static void message_handler_req_lib_votequorum_qdisk_register (void *conn, void *message)
+{
+	struct req_lib_votequorum_qdisk_register *req_lib_votequorum_qdisk_register = (struct req_lib_votequorum_qdisk_register *)message;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	cs_error_t error = CS_OK;
+
+	ENTER();
+
+	if (quorum_device) {
+		error = CS_ERR_EXIST;
+	}
+	else {
+		quorum_device = allocate_node(0);
+		quorum_device->state = NODESTATE_DEAD;
+		quorum_device->votes = req_lib_votequorum_qdisk_register->votes;
+		strcpy(quorum_device_name, req_lib_votequorum_qdisk_register->name);
+		list_add(&quorum_device->list, &cluster_members_list);
+	}
+
+	/* send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+	LEAVE();
+}
+
+static void message_handler_req_lib_votequorum_qdisk_unregister (void *conn, void *message)
+{
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	cs_error_t error = CS_OK;
+
+	ENTER();
+
+	if (quorum_device) {
+		struct cluster_node *node = quorum_device;
+
+		quorum_device = NULL;
+		list_del(&node->list);
+		free(node);
+		recalculate_quorum(0);
+	}
+	else {
+		error = CS_ERR_NOT_EXIST;
+	}
+
+	/* send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+	LEAVE();
+}
+
+static void message_handler_req_lib_votequorum_qdisk_poll (void *conn, void *message)
+{
+	struct req_lib_votequorum_qdisk_poll *req_lib_votequorum_qdisk_poll = (struct req_lib_votequorum_qdisk_poll *)message;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	cs_error_t error = CS_OK;
+
+	ENTER();
+
+	if (quorum_device) {
+		if (req_lib_votequorum_qdisk_poll->state) {
+			gettimeofday(&quorum_device->last_hello, NULL);
+			if (quorum_device->state == NODESTATE_DEAD) {
+				quorum_device->state = NODESTATE_MEMBER;
+				recalculate_quorum(0);
+
+				corosync_api->timer_add_duration((unsigned long long)quorumdev_poll*1000000, quorum_device,
+								 quorum_device_timer_fn, &quorum_device_timer);
+			}
+		}
+		else {
+			if (quorum_device->state == NODESTATE_MEMBER) {
+				quorum_device->state = NODESTATE_DEAD;
+				recalculate_quorum(0);
+				corosync_api->timer_delete(quorum_device_timer);
+			}
+		}
+	}
+	else {
+		error = CS_ERR_NOT_EXIST;
+	}
+
+	/* send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+
+	LEAVE();
+}
+
+static void message_handler_req_lib_votequorum_qdisk_getinfo (void *conn, void *message)
+{
+	struct res_lib_votequorum_qdisk_getinfo res_lib_votequorum_qdisk_getinfo;
+	cs_error_t error = CS_OK;
+
+	ENTER();
+
+	if (quorum_device) {
+		log_printf(LOG_LEVEL_DEBUG, "got qdisk_getinfo state %d\n", quorum_device->state);
+		res_lib_votequorum_qdisk_getinfo.votes = quorum_device->votes;
+		if (quorum_device->state == NODESTATE_MEMBER)
+			res_lib_votequorum_qdisk_getinfo.state = 1;
+		else
+			res_lib_votequorum_qdisk_getinfo.state = 0;
+		strcpy(res_lib_votequorum_qdisk_getinfo.name, quorum_device_name);
+	}
+	else {
+		error = CS_ERR_NOT_EXIST;
+	}
+
+	/* send status */
+	res_lib_votequorum_qdisk_getinfo.header.size = sizeof(res_lib_votequorum_qdisk_getinfo);
+	res_lib_votequorum_qdisk_getinfo.header.id = MESSAGE_RES_VOTEQUORUM_GETINFO;
+	res_lib_votequorum_qdisk_getinfo.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_qdisk_getinfo, sizeof(res_lib_votequorum_qdisk_getinfo));
+
+	LEAVE();
+}
+
+static void message_handler_req_lib_votequorum_setstate (void *conn, void *message)
+{
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	cs_error_t error = CS_OK;
+
+	ENTER();
+
+	us->flags |= NODE_FLAGS_HASSTATE;
+
+	/* send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+
+	LEAVE();
+}
+
+static void message_handler_req_lib_votequorum_trackstart (void *conn, void *msg)
+{
+	struct req_lib_votequorum_trackstart *req_lib_votequorum_trackstart = (struct req_lib_votequorum_trackstart *)msg;
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
+
+	ENTER();
+	/*
+	 * If an immediate listing of the current cluster membership
+	 * is requested, generate membership list
+	 */
+	if (req_lib_votequorum_trackstart->track_flags & CS_TRACK_CURRENT ||
+	    req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES) {
+		log_printf(LOG_LEVEL_DEBUG, "sending initial status to %p\n", conn);
+		send_quorum_notification(corosync_api->ipc_conn_partner_get (conn), req_lib_votequorum_trackstart->context);
+	}
+
+	/*
+	 * Record requests for tracking
+	 */
+	if (req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES ||
+	    req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES_ONLY) {
+
+		quorum_pd->track_flags = req_lib_votequorum_trackstart->track_flags;
+		quorum_pd->tracking_enabled = 1;
+		quorum_pd->tracking_context = req_lib_votequorum_trackstart->context;
+
+		list_add (&quorum_pd->list, &trackers_list);
+	}
+
+	/* Send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = CS_OK;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+
+	LEAVE();
+}
+
+static void message_handler_req_lib_votequorum_trackstop (void *conn, void *msg)
+{
+	struct res_lib_votequorum_status res_lib_votequorum_status;
+	struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
+	int error = CS_OK;
+
+	ENTER();
+
+	if (quorum_pd->tracking_enabled) {
+		error = CS_OK;
+		quorum_pd->tracking_enabled = 0;
+		list_del (&quorum_pd->list);
+		list_init (&quorum_pd->list);
+	} else {
+		error = CS_ERR_NOT_EXIST;
+	}
+
+	/* send status */
+	res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
+	res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
+	res_lib_votequorum_status.header.error = error;
+	corosync_api->ipc_conn_send_response(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
+
+	LEAVE();
+}
+
+
+static char *kill_reason(int reason)
+{
+	static char msg[1024];
+
+	switch (reason)
+	{
+	case VOTEQUORUM_REASON_KILL_REJECTED:
+		return "our membership application was rejected";
+
+	case VOTEQUORUM_REASON_KILL_APPLICATION:
+		return "we were killed by an application request";
+
+	case VOTEQUORUM_REASON_KILL_REJOIN:
+		return "we rejoined the cluster without a full restart";
+
+	default:
+		sprintf(msg, "we got kill message number %d", reason);
+		return msg;
+	}
+}
+
+static void reread_config(unsigned int object_handle)
+{
+	unsigned int old_votes;
+	unsigned int old_expected;
+
+	old_votes = us->votes;
+	old_expected = us->expected_votes;
+
+	/*
+	 * Reload the configuration
+	 */
+	read_quorum_config(object_handle);
+
+	/*
+	 * Check for fundamental changes that we need to propogate
+	 */
+	if (old_votes != us->votes) {
+		quorum_exec_send_reconfigure(RECONFIG_PARAM_NODE_VOTES, us->node_id, us->votes);
+	}
+	if (old_expected != us->expected_votes) {
+		quorum_exec_send_reconfigure(RECONFIG_PARAM_EXPECTED_VOTES, us->node_id, us->expected_votes);
+	}
+}
+
+static void quorum_key_change_notify(object_change_type_t change_type,
+				     unsigned int parent_object_handle,
+				     unsigned int object_handle,
+				     void *object_name_pt, int object_name_len,
+				     void *key_name_pt, int key_len,
+				     void *key_value_pt, int key_value_len,
+				     void *priv_data_pt)
+{
+	if (memcmp(object_name_pt, "quorum", object_name_len) == 0)
+		reread_config(object_handle);
+}
+
+
+/* Called when the objdb is reloaded */
+static void votequorum_objdb_reload_notify(
+	objdb_reload_notify_type_t type, int flush,
+	void *priv_data_pt)
+{
+	/*
+	 * A new quorum {} key might exist, cancel the
+	 * existing notification at the start of reload,
+	 * and start a new one on the new object when
+	 * it's all settled.
+	 */
+
+	if (type == OBJDB_RELOAD_NOTIFY_START) {
+		corosync_api->object_track_stop(
+			quorum_key_change_notify,
+			NULL,
+			NULL,
+			NULL,
+			NULL);
+	}
+
+	if (type == OBJDB_RELOAD_NOTIFY_END ||
+	    type == OBJDB_RELOAD_NOTIFY_FAILED) {
+		unsigned int find_handle;
+		unsigned int object_handle;
+
+		corosync_api->object_find_create(OBJECT_PARENT_HANDLE, "quorum", strlen("quorum"), &find_handle);
+		if (corosync_api->object_find_next(find_handle, &object_handle) == 0) {
+			add_votequorum_config_notification(object_handle);
+
+			reread_config(object_handle);
+		}
+		else {
+			log_printf(LOG_LEVEL_ERROR, "votequorum objdb tracking stopped, cannot find quorum{} handle in objdb\n");
+		}
+	}
+}
+
+
+static void add_votequorum_config_notification(
+	unsigned int quorum_object_handle)
+{
+
+	corosync_api->object_track_start(quorum_object_handle,
+					 1,
+					 quorum_key_change_notify,
+					 NULL,
+					 NULL,
+					 NULL,
+					 NULL);
+
+	/*
+	 * Reload notify must be on the parent object
+	 */
+	corosync_api->object_track_start(OBJECT_PARENT_HANDLE,
+					 1,
+					 NULL,
+					 NULL,
+					 NULL,
+					 votequorum_objdb_reload_notify,
+					 NULL);
+}

+ 10 - 2
test/Makefile

@@ -42,9 +42,10 @@ ifeq (${COROSYNC_COMPAT}, SOLARIS)
 	override LDFLAGS += -lnsl -lsocket -lrt
 endif
 
-LIBRARIES= ../lib/libevs.a ../lib/libcpg.a ../lib/libcfg.a ../lib/libconfdb.a ../lib/libquorum.a
+LIBRARIES= ../lib/libevs.a ../lib/libcpg.a ../lib/libcfg.a ../lib/libconfdb.a ../lib/libquorum.a ../lib/libvotequorum.a
 LIBS = $(LIBRARIES) 
-BINARIES= testevs evsbench evsverify testcpg testcpg2 cpgbench testconfdb logsysbench logsysrec testquorum
+BINARIES= testevs evsbench evsverify testcpg testcpg2 cpgbench testconfdb logsysbench logsysrec testquorum \
+	testvotequorum1 testvotequorum2
 
 override CFLAGS += -I../include
 override LDFLAGS += -L../lib
@@ -92,9 +93,16 @@ logsysrec: logsysrec.o ../exec/liblogsys.a
 
 testquorum1: testquorum1.o $(LIBRARIES)
 	$(CC) $(LDFLAGS) -o testquorum1 testquorum1.o $(LIBS)
+
 testquorum2: testquorum2.o $(LIBRARIES)
 	$(CC) $(LDFLAGS) -o testquorum2 testquorum2.o $(LIBS)
 
+testvotequorum1: testvotequorum1.o $(LIBRARIES)
+	$(CC) $(LDFLAGS) -o testvotequorum1 testvotequorum1.o $(LIBS)
+
+testvotequorum2: testvotequorum2.o $(LIBRARIES)
+	$(CC) $(LDFLAGS) -o testvotequorum2 testvotequorum2.o $(LIBS)
+
 logsys_s: logsys_s.o logsys_s1.o logsys_s2.o ../exec/liblogsys.a
 	$(CC) -o logsys_s logsys_s.o logsys_s1.o logsys_s2.o ../exec/liblogsys.a $(LDFLAGS)
 

+ 159 - 0
test/testvotequorum1.c

@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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 CONTIBUTORS "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.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <corosync/corotypes.h>
+#include <corosync/votequorum.h>
+
+static votequorum_handle_t handle;
+
+static char *node_state(int state)
+{
+	switch (state) {
+	case NODESTATE_JOINING:
+		return "Joining";
+		break;
+	case NODESTATE_MEMBER:
+		return "Member";
+		break;
+	case NODESTATE_DEAD:
+		return "Dead";
+		break;
+	case NODESTATE_LEAVING:
+		return "Leaving";
+		break;
+	case NODESTATE_DISALLOWED:
+		return "Disallowed";
+		break;
+	default:
+		return "UNKNOWN";
+		break;
+	}
+}
+
+static void votequorum_notification_fn(
+	votequorum_handle_t handle,
+	uint64_t context,
+	uint32_t quorate,
+	uint32_t node_list_entries,
+	votequorum_node_t node_list[]
+	)
+{
+	int i;
+
+	printf("votequorum notification called \n");
+	printf("  quorate         = %d\n", quorate);
+	printf("  number of nodes = %d\n", node_list_entries);
+
+	for (i = 0; i< node_list_entries; i++) {
+		printf("      %d: %s\n", node_list[i].nodeid, node_state(node_list[i].state));
+	}
+	printf("\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct votequorum_info info;
+	votequorum_callbacks_t callbacks;
+	int err;
+
+	if (argc > 1 && strcmp(argv[1], "-h")==0) {
+		fprintf(stderr, "usage: %s [new-expected] [new-votes]\n", argv[0]);
+		return 0;
+	}
+
+	callbacks.votequorum_notify_fn = votequorum_notification_fn;
+	if ( (err=votequorum_initialize(&handle, &callbacks)) != CS_OK)
+		fprintf(stderr, "votequorum_initialize FAILED: %d\n", err);
+
+	if ( (err = votequorum_trackstart(handle, handle, CS_TRACK_CHANGES)) != CS_OK)
+		fprintf(stderr, "votequorum_trackstart FAILED: %d\n", err);
+
+	if ( (err=votequorum_getinfo(handle, 0, &info)) != CS_OK)
+		fprintf(stderr, "votequorum_getinfo FAILED: %d\n", err);
+	else {
+		printf("node votes       %d\n", info.node_votes);
+		printf("expected votes   %d\n", info.node_expected_votes);
+		printf("highest expected %d\n", info.highest_expected);
+		printf("total votes      %d\n", info.total_votes);
+		printf("quorum           %d\n", info.quorum);
+		printf("flags            ");
+		if (info.flags & VOTEQUORUM_INFO_FLAG_HASSTATE) printf("HasState ");
+		if (info.flags & VOTEQUORUM_INFO_FLAG_DISALLOWED) printf("Disallowed ");
+		if (info.flags & VOTEQUORUM_INFO_FLAG_TWONODE) printf("2Node ");
+		if (info.flags & VOTEQUORUM_INFO_FLAG_QUORATE) printf("Quorate ");
+		printf("\n");
+	}
+
+	if (argc >= 2 && atoi(argv[1])) {
+		if ( (err=votequorum_setexpected(handle, atoi(argv[1]))) != CS_OK)
+			fprintf(stderr, "set expected votes FAILED: %d\n", err);
+	}
+	if (argc >= 3 && atoi(argv[2])) {
+		if ( (err=votequorum_setvotes(handle, 0, atoi(argv[2]))) != CS_OK)
+			fprintf(stderr, "set votes FAILED: %d\n", err);
+	}
+
+	if (argc >= 2) {
+		if ( (err=votequorum_getinfo(handle, 0, &info)) != CS_OK)
+			fprintf(stderr, "votequorum_getinfo2 FAILED: %d\n", err);
+		else {
+			printf("-------------------\n");
+			printf("node votes       %d\n", info.node_votes);
+			printf("expected votes   %d\n", info.node_expected_votes);
+			printf("highest expected %d\n", info.highest_expected);
+			printf("total votes      %d\n", info.total_votes);
+			printf("votequorum           %d\n", info.quorum);
+			printf("flags            ");
+			if (info.flags & VOTEQUORUM_INFO_FLAG_HASSTATE) printf("HasState ");
+			if (info.flags & VOTEQUORUM_INFO_FLAG_DISALLOWED) printf("Disallowed ");
+			if (info.flags & VOTEQUORUM_INFO_FLAG_TWONODE) printf("2Node ");
+			if (info.flags & VOTEQUORUM_INFO_FLAG_QUORATE) printf("Quorate ");
+			printf("\n");
+		}
+	}
+
+	printf("Waiting for votequorum events, press ^C to finish\n");
+	printf("-------------------\n");
+
+	while (1)
+		votequorum_dispatch(handle, CS_DISPATCH_ALL);
+
+	return 0;
+}

+ 96 - 0
test/testvotequorum2.c

@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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 CONTIBUTORS "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.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <corosync/corotypes.h>
+#include <corosync/votequorum.h>
+
+static votequorum_handle_t handle;
+
+
+static void print_info(int ok_to_fail)
+{
+	struct votequorum_qdisk_info qinfo;
+	int err;
+
+	if ( (err=votequorum_qdisk_getinfo(handle, &qinfo)) != CS_OK)
+		fprintf(stderr, "votequorum_qdisk_getinfo error %d: %s\n", err, ok_to_fail?"OK":"FAILED");
+	else {
+		printf("qdisk votes  %d\n", qinfo.votes);
+		printf("state        %d\n", qinfo.state);
+		printf("name         %s\n", qinfo.name);
+		printf("\n");
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	int pollcount=0, polltime=1;
+	int err;
+
+	if ( (err=votequorum_initialize(&handle, NULL)) != CS_OK) {
+		fprintf(stderr, "votequorum_initialize FAILED: %d\n", err);
+		return -1;
+	}
+
+	print_info(1);
+
+	if (argc >= 2 && atoi(argv[1])) {
+		pollcount = atoi(argv[1]);
+	}
+	if (argc >= 3 && atoi(argv[2])) {
+		polltime = atoi(argv[2]);
+	}
+
+	if (argc >= 2) {
+		if ( (err=votequorum_qdisk_register(handle, "QDISK", 4)) != CS_OK)
+			fprintf(stderr, "qdisk_register FAILED: %d\n", err);
+
+		while (pollcount--) {
+			print_info(0);
+			if ((err=votequorum_qdisk_poll(handle, 1)) != CS_OK)
+				fprintf(stderr, "qdisk poll FAILED: %d\n", err);
+			print_info(0);
+			sleep(polltime);
+		}
+		if ((err= votequorum_qdisk_unregister(handle)) != CS_OK)
+			fprintf(stderr, "qdisk unregister FAILED: %d\n", err);
+	}
+	print_info(1);
+
+	return 0;
+}