Browse Source

Add the votequorum service

git-svn-id: http://svn.fedorahosted.org/svn/corosync/trunk@1745 fd59a12c-fef9-0310-b244-a6a79926bd2f
Christine Caulfield 17 years ago
parent
commit
a8256ad5fc

+ 2 - 2
Makefile

@@ -122,10 +122,10 @@ lint:
 	(cd $(builddir)lib; echo ==== `pwd` ===; $(call sub_make,lib,lint));
 	(cd $(builddir)lib; echo ==== `pwd` ===; $(call sub_make,lib,lint));
 	(cd $(builddir)tools; echo ==== `pwd` ===; $(call sub_make,tools,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 \
 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
 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 \
 	libcfg.a libcfg.so.2.0.0 \
 	libquorum.a libquorum.so.2.0.0 \
 	libquorum.a libquorum.so.2.0.0 \
 	libpload.a libpload.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
 libcoroutil.a: util.o
 	$(AR) -rc 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
 libquorum.so.2.0.0: util.o quorum.o
 	$(CC) $(DARWIN_OPTS) util.o quorum.o -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 
 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 $@
 	$(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
 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 $@
 	$(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 
 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 $@
 	$(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
 libquorum.a: util.o quorum.o
 	$(AR) -rc 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
 libconfdb.a: util.o confdb.o sa-confdb.o
 	$(AR) -rc libconfdb.a util.o confdb.o sa-confdb.o ../lcr/lcr_ifact.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:	   
 	global:	   
 		quorum_initialize;
 		quorum_initialize;
 		quorum_finalize;
 		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:
 	local:
 		saHandleCreate;
 		saHandleCreate;
@@ -30,17 +24,14 @@ OPENAIS_QUORUM_1.0 {
 		saVersionVerify;
 		saVersionVerify;
 		clustTimeNow;
 		clustTimeNow;
 };
 };
-# Version and symbol export for libcpg.so
+# Version and symbol export for libquorum.so
 
 
 COROSYNC_QUORUM_1.0 {
 COROSYNC_QUORUM_1.0 {
 	global:
 	global:
 		quorum_initialize;
 		quorum_initialize;
 		quorum_finalize;
 		quorum_finalize;
-		quorum_fd_get;
+		quorum_getquorate;
 		quorum_dispatch;
 		quorum_dispatch;
-		quorum_get_quorate;
-		quorum_context_get;
-		quorum_context_set;
 
 
 	local:
 	local:
 		saHandleCreate;
 		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_object_parent_get.3 > html/confdb_object_parent_get.html
 	groff -mandoc -Thtml confdb_overview.8 > html/confdb_overview.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
 	cp index.html html
 
 
 clean:
 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"
 .TH CONFDB_OVERVIEW 8 2006-03-06 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
 .SH OVERVIEW
 .SH OVERVIEW
 The CONFDB library is delivered with the corosync project.  This library is used
 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
 .PP
 The library provides a mechanism to:
 The library provides a mechanism to:
+.PP
 * Create new objects
 * Create new objects
+.PP
 * Create new key/value pairs within those objects
 * Create new key/value pairs within those objects
+.PP
 * Remove existing keys
 * Remove existing keys
+.PP
 * Remove an existing object and all it sub-objects and keys
 * Remove an existing object and all it sub-objects and keys
+.PP
 * Read keys and values
 * Read keys and values
+.PP
 * Iterate keys within an object
 * Iterate keys within an object
+.PP
 * Iterate subobjects within a parent object
 * Iterate subobjects within a parent object
+.PP
 * Find a named object
 * Find a named object
 .PP
 .PP
 .SH BUGS
 .SH BUGS

+ 25 - 0
man/index.html

@@ -85,5 +85,30 @@ Welcome to the corosync project's manual pages.
 <BR>
 <BR>
 <A HREF="confdb_object_parent_get.html">confdb_object_parent_get(3)</A>: Description of the confdb_object_parent_get interface.
 <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>
 </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
 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)
 ifeq (${COROSYNC_COMPAT}, DARWIN)
 
 
@@ -74,6 +74,9 @@ service_cpg.lcrso: cpg.o
 service_pload.lcrso: pload.o
 service_pload.lcrso: pload.o
 	$(CC) $(LDFLAGS) -bundle $(LDFLAGS) -bundle_loader ../exec/corosync -bind_at_load pload.o -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
 testquorum.lcrso: testquorum.o
 	$(CC) $(LDFLAGS) -bundle $(LDFLAGS) -bundle_loader ../exec/corosync -bind_at_load testquorum.o -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
 service_pload.lcrso: pload.o
 	$(CC) -shared -Wl,-soname,service_pload.lcrso pload.o -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
 testquorum.lcrso: testquorum.o
 	$(CC) -shared -Wl,-soname,testquorum.lcrso testquorum.o -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
 	override LDFLAGS += -lnsl -lsocket -lrt
 endif
 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) 
 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 CFLAGS += -I../include
 override LDFLAGS += -L../lib
 override LDFLAGS += -L../lib
@@ -92,9 +93,16 @@ logsysrec: logsysrec.o ../exec/liblogsys.a
 
 
 testquorum1: testquorum1.o $(LIBRARIES)
 testquorum1: testquorum1.o $(LIBRARIES)
 	$(CC) $(LDFLAGS) -o testquorum1 testquorum1.o $(LIBS)
 	$(CC) $(LDFLAGS) -o testquorum1 testquorum1.o $(LIBS)
+
 testquorum2: testquorum2.o $(LIBRARIES)
 testquorum2: testquorum2.o $(LIBRARIES)
 	$(CC) $(LDFLAGS) -o testquorum2 testquorum2.o $(LIBS)
 	$(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
 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)
 	$(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;
+}