summaryrefslogtreecommitdiffstats
path: root/tools/corosync-quorumtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/corosync-quorumtool.c')
-rw-r--r--tools/corosync-quorumtool.c1023
1 files changed, 1023 insertions, 0 deletions
diff --git a/tools/corosync-quorumtool.c b/tools/corosync-quorumtool.c
new file mode 100644
index 0000000..199bd20
--- /dev/null
+++ b/tools/corosync-quorumtool.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (c) 2009-2020 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Authors: Christine Caulfield <ccaulfie@redhat.com>
+ * Fabio M. Di Nitto (fdinitto@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <limits.h>
+
+#include <corosync/totem/totem.h>
+#include <corosync/cfg.h>
+#include <corosync/cmap.h>
+#include <corosync/quorum.h>
+#include <corosync/votequorum.h>
+#include "util.h"
+
+typedef enum {
+ NODEID_FORMAT_DECIMAL,
+ NODEID_FORMAT_HEX
+} nodeid_format_t;
+
+typedef enum {
+ ADDRESS_FORMAT_NAME,
+ ADDRESS_FORMAT_IP
+} name_format_t;
+
+typedef enum {
+ CMD_SHOWNODES,
+ CMD_SHOWSTATUS,
+ CMD_SETVOTES,
+ CMD_SETEXPECTED,
+ CMD_MONITOR,
+ CMD_UNREGISTER_QDEVICE
+} command_t;
+
+typedef enum {
+ SORT_ADDR,
+ SORT_NODEID,
+ SORT_NODENAME
+} sorttype_t;
+
+#define EXIT_NOT_QUORATE 2
+
+/*
+ * global vars
+ */
+
+/*
+ * cmap bits
+ */
+static cmap_handle_t cmap_handle;
+
+/*
+ * quorum bits
+ */
+static void quorum_notification_fn(
+ quorum_handle_t handle,
+ uint32_t quorate,
+ uint64_t ring_id,
+ uint32_t view_list_entries,
+ uint32_t *view_list);
+
+static quorum_handle_t q_handle;
+static uint32_t q_type;
+static quorum_callbacks_t q_callbacks = {
+ .quorum_notify_fn = quorum_notification_fn
+};
+
+/*
+ * quorum call back vars
+ */
+
+/* Containing struct to keep votequorum & normal quorum bits together */
+typedef struct {
+ struct votequorum_info *vq_info; /* Might be NULL if votequorum not present */
+ char *name; /* Might be IP address or NULL */
+ int node_id; /* Always present */
+} view_list_entry_t;
+
+static view_list_entry_t *g_view_list;
+static uint32_t g_quorate;
+static uint64_t g_ring_id;
+static uint32_t g_ring_id_rep_node;
+static uint32_t g_view_list_entries;
+static uint32_t g_called;
+static uint32_t g_vq_called;
+static uint32_t g_show_all_addrs = 0;
+
+/*
+ * votequorum bits
+ */
+static void votequorum_notification_fn(
+ votequorum_handle_t handle,
+ uint64_t context,
+ votequorum_ring_id_t ring_id,
+ uint32_t node_list_entries,
+ uint32_t node_list[]);
+static votequorum_handle_t v_handle;
+static votequorum_callbacks_t v_callbacks = {
+ .votequorum_quorum_notify_fn = NULL,
+ .votequorum_expectedvotes_notify_fn = NULL,
+ .votequorum_nodelist_notify_fn = votequorum_notification_fn,
+};
+static uint32_t our_nodeid = 0;
+
+/*
+ * cfg bits
+ */
+static corosync_cfg_handle_t c_handle;
+static corosync_cfg_callbacks_t c_callbacks = {
+ .corosync_cfg_shutdown_callback = NULL
+};
+
+/*
+ * global
+ */
+static int machine_parsable = 0;
+
+static void show_usage(const char *name)
+{
+ printf("usage: \n");
+ printf("%s <options>\n", name);
+ printf("\n");
+ printf(" options:\n");
+ printf("\n");
+ printf(" -s show quorum status\n");
+ printf(" -m constantly monitor quorum status\n");
+ printf(" -l list nodes\n");
+ printf(" -a show all names or addresses for each node\n");
+ printf(" -p when used with -s or -l, generates machine parsable output\n");
+ printf(" -v <votes> change the number of votes for a node (*)\n");
+ printf(" -n <nodeid> optional nodeid of node for -v\n");
+ printf(" -e <expected> change expected votes for the cluster (*)\n");
+ printf(" -H show nodeids in hexadecimal rather than decimal\n");
+ printf(" -i show node IP addresses instead of the resolved name\n");
+ printf(" -o <a|n|i> order by [a] IP address (default), [n] name, [i] nodeid\n");
+ printf(" -f forcefully unregister a quorum device *DANGEROUS* (*)\n");
+ printf(" -h show this help text\n");
+ printf(" -V show version and exit\n");
+ printf("\n");
+ printf(" (*) Starred items only work if votequorum is the quorum provider for corosync\n");
+ printf("\n");
+}
+
+static int get_quorum_type(char *quorum_type, size_t quorum_type_len)
+{
+ int err;
+ char *str = NULL;
+
+ if ((!quorum_type) || (quorum_type_len <= 0)) {
+ return -1;
+ }
+
+ if (q_type == QUORUM_FREE) {
+ return -1;
+ }
+
+ if ((err = cmap_get_string(cmap_handle, "quorum.provider", &str)) != CS_OK) {
+ goto out;
+ }
+
+ if (!str) {
+ return -1;
+ }
+
+ strncpy(quorum_type, str, quorum_type_len - 1);
+ free(str);
+
+ return 0;
+out:
+ return err;
+}
+
+/*
+ * Returns 1 if 'votequorum' is active. The called then knows that
+ * votequorum calls should work and can provide extra information
+ */
+static int using_votequorum(void)
+{
+ char quorumtype[256];
+ int using_voteq;
+
+ memset(quorumtype, 0, sizeof(quorumtype));
+
+ if (get_quorum_type(quorumtype, sizeof(quorumtype))) {
+ return -1;
+ }
+
+ if (strcmp(quorumtype, "corosync_votequorum") == 0) {
+ using_voteq = 1;
+ } else {
+ using_voteq = 0;
+ }
+
+ return using_voteq;
+}
+
+static int set_votes(uint32_t nodeid, int votes)
+{
+ int err;
+
+ if ((err=votequorum_setvotes(v_handle, nodeid, votes)) != CS_OK) {
+ fprintf(stderr, "Unable to set votes %d for nodeid: " CS_PRI_NODE_ID ": %s\n",
+ votes, nodeid, cs_strerror(err));
+ }
+
+ return (err == CS_OK ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static int set_expected(int expected_votes)
+{
+ int err;
+
+ if ((err=votequorum_setexpected(v_handle, expected_votes)) != CS_OK) {
+ fprintf(stderr, "Unable to set expected votes: %s\n", cs_strerror(err));
+ }
+
+ return (err == CS_OK ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+/*
+ * node name by nodelist
+ */
+
+static const char *node_name_by_nodelist(uint32_t nodeid)
+{
+ cmap_iter_handle_t iter;
+ char key_name[CMAP_KEYNAME_MAXLEN + 1];
+ char tmp_key[CMAP_KEYNAME_MAXLEN + 1];
+ static char ret_buf[_POSIX_HOST_NAME_MAX];
+ char *str = NULL;
+ uint32_t node_pos, cur_nodeid;
+ int res = 0;
+
+ if (cmap_iter_init(cmap_handle, "nodelist.node.", &iter) != CS_OK) {
+ return "";
+ }
+
+ memset(ret_buf, 0, sizeof(ret_buf));
+
+ while ((cmap_iter_next(cmap_handle, iter, key_name, NULL, NULL)) == CS_OK) {
+
+ res = sscanf(key_name, "nodelist.node.%u.%s", &node_pos, tmp_key);
+ if (res != 2) {
+ continue;
+ }
+
+ if (strcmp(tmp_key, "nodeid") != 0) {
+ continue;
+ }
+
+ snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
+ if (cmap_get_uint32(cmap_handle, tmp_key, &cur_nodeid) != CS_OK) {
+ continue;
+ }
+ if (cur_nodeid != nodeid) {
+ continue;
+ }
+ snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.name", node_pos);
+ if (cmap_get_string(cmap_handle, tmp_key, &str) != CS_OK) {
+ continue;
+ }
+ if (!str) {
+ continue;
+ }
+ strncpy(ret_buf, str, sizeof(ret_buf) - 1);
+ free(str);
+ break;
+ }
+ cmap_iter_finalize(cmap_handle, iter);
+
+ return ret_buf;
+}
+
+/*
+ * This resolves the first address assigned to a node
+ * and returns the name or IP address. Use cfgtool if you need more information.
+ */
+static const char *node_name(uint32_t nodeid, name_format_t name_format)
+{
+ int err;
+ int numaddrs;
+ corosync_cfg_node_address_t addrs[INTERFACE_MAX];
+ static char buf[(INET6_ADDRSTRLEN + 1) * KNET_MAX_LINK];
+ const char *nodelist_name = NULL;
+ socklen_t addrlen;
+ struct sockaddr_storage *ss;
+ int start_addr = 0;
+ int i;
+ int bufptr = 0;
+
+ buf[0] = '\0';
+
+ /* If a name is required, always look for the nodelist node0_addr name first */
+ if (name_format == ADDRESS_FORMAT_NAME) {
+ nodelist_name = node_name_by_nodelist(nodeid);
+ }
+ if ((nodelist_name) &&
+ (strlen(nodelist_name) > 0)) {
+ start_addr = 1;
+ assert(strlen(nodelist_name) < sizeof(buf));
+ strcpy(buf, nodelist_name);
+ bufptr = strlen(buf);
+ }
+
+ err = corosync_cfg_get_node_addrs(c_handle, nodeid, INTERFACE_MAX, &numaddrs, addrs);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to get node address for nodeid " CS_PRI_NODE_ID ": %s\n", nodeid, cs_strerror(err));
+ return "";
+ }
+
+ /* Don't show all addressess */
+ if (!g_show_all_addrs) {
+ numaddrs = 1;
+ }
+
+ for (i=start_addr; i<numaddrs; i++) {
+
+ ss = (struct sockaddr_storage *)addrs[i].address;
+
+ if (!ss->ss_family) {
+ continue;
+ }
+
+ if (ss->ss_family == AF_INET6) {
+ addrlen = sizeof(struct sockaddr_in6);
+ } else {
+ addrlen = sizeof(struct sockaddr_in);
+ }
+
+ if (i) {
+ buf[bufptr++] = ',';
+ buf[bufptr++] = ' ';
+ }
+
+ if (!getnameinfo(
+ (struct sockaddr *)addrs[i].address, addrlen,
+ buf+bufptr, sizeof(buf)-bufptr,
+ NULL, 0,
+ (name_format == ADDRESS_FORMAT_IP)?NI_NUMERICHOST:0)) {
+ bufptr += strlen(buf+bufptr);
+ }
+ }
+
+ return buf;
+}
+
+
+static void votequorum_notification_fn(
+ votequorum_handle_t handle,
+ uint64_t context,
+ votequorum_ring_id_t ring_id,
+ uint32_t node_list_entries,
+ uint32_t node_list[])
+{
+ g_ring_id_rep_node = ring_id.nodeid;
+ g_vq_called = 1;
+}
+
+static void quorum_notification_fn(
+ quorum_handle_t handle,
+ uint32_t quorate,
+ uint64_t ring_id,
+ uint32_t view_list_entries,
+ uint32_t *view_list)
+{
+ int i;
+
+ g_called = 1;
+ g_quorate = quorate;
+ g_ring_id = ring_id;
+ g_view_list_entries = view_list_entries;
+ if (g_view_list) {
+ free(g_view_list);
+ }
+ g_view_list = malloc(sizeof(view_list_entry_t) * view_list_entries);
+ if (g_view_list) {
+ for (i=0; i< view_list_entries; i++) {
+ g_view_list[i].node_id = view_list[i];
+ g_view_list[i].name = NULL;
+ g_view_list[i].vq_info = NULL;
+ }
+ }
+}
+
+static void print_string_padded(const char *buf)
+{
+ int len, delta;
+
+ len = strlen(buf);
+ delta = 10 - len;
+ while (delta > 0) {
+ printf(" ");
+ delta--;
+ }
+ printf("%s ", buf);
+}
+
+static void print_uint32_padded(uint32_t value)
+{
+ char buf[12];
+
+ snprintf(buf, sizeof(buf) - 1, "%u", value);
+ print_string_padded(buf);
+}
+
+/* for qsort */
+static int compare_nodeids(const void *one, const void *two)
+{
+ const view_list_entry_t *info1 = one;
+ const view_list_entry_t *info2 = two;
+
+ if (info1->node_id == info2->node_id) {
+ return 0;
+ }
+ if (info1->node_id > info2->node_id) {
+ return 1;
+ }
+ return -1;
+}
+
+static int compare_nodenames(const void *one, const void *two)
+{
+ const view_list_entry_t *info1 = one;
+ const view_list_entry_t *info2 = two;
+
+ return strcmp(info1->name, info2->name);
+}
+
+static void display_nodes_data(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type)
+{
+ int i, display_qdevice = 0;
+ unsigned int our_flags = 0;
+ struct votequorum_info info[g_view_list_entries];
+ /*
+ * cache node info because we need to parse them twice
+ */
+ if (v_handle) {
+ for (i=0; i < g_view_list_entries; i++) {
+ if (votequorum_getinfo(v_handle, g_view_list[i].node_id, &info[i]) != CS_OK) {
+ printf("Unable to get node " CS_PRI_NODE_ID " info\n", g_view_list[i].node_id);
+ }
+ g_view_list[i].vq_info = &info[i];
+ if (info[i].flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED) {
+ display_qdevice = 1;
+ }
+ }
+ }
+
+ /*
+ * Get node names
+ */
+ for (i=0; i < g_view_list_entries; i++) {
+ g_view_list[i].name = strdup(node_name(g_view_list[i].node_id, name_format));
+ }
+
+ printf("\nMembership information\n");
+ printf("----------------------\n");
+
+ print_string_padded("Nodeid");
+ if (v_handle) {
+ print_string_padded("Votes");
+ if ((display_qdevice) || (machine_parsable)) {
+ print_string_padded("Qdevice");
+ }
+ }
+ printf("Name\n");
+
+ /* corosync sends them already sorted by address */
+ if (sort_type == SORT_NODEID) {
+ qsort(g_view_list, g_view_list_entries, sizeof(view_list_entry_t), compare_nodeids);
+ }
+ if (sort_type == SORT_NODENAME) {
+ qsort(g_view_list, g_view_list_entries, sizeof(view_list_entry_t), compare_nodenames);
+ }
+ for (i=0; i < g_view_list_entries; i++) {
+ if (nodeid_format == NODEID_FORMAT_DECIMAL) {
+ print_uint32_padded(g_view_list[i].node_id);
+ } else {
+ printf("0x%08x ", g_view_list[i].node_id);
+ }
+ if (v_handle) {
+ int votes = -1;
+
+ votes = info[i].node_votes;
+ print_uint32_padded(votes);
+
+ if ((display_qdevice) || (machine_parsable)) {
+ if (info[i].flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED) {
+ char buf[10];
+
+ snprintf(buf, sizeof(buf),
+ "%s,%s,%s",
+ info[i].flags & VOTEQUORUM_INFO_QDEVICE_ALIVE?"A":"NA",
+ info[i].flags & VOTEQUORUM_INFO_QDEVICE_CAST_VOTE?"V":"NV",
+ info[i].flags & VOTEQUORUM_INFO_QDEVICE_MASTER_WINS?"MW":"NMW");
+ print_string_padded(buf);
+ } else {
+ print_string_padded("NR");
+ }
+ }
+ }
+ printf("%s", g_view_list[i].name);
+ if (g_view_list[i].node_id == our_nodeid) {
+ printf(" (local)");
+ if (v_handle) {
+ our_flags = info[i].flags;
+ }
+ }
+ printf("\n");
+ }
+
+ if (g_view_list_entries) {
+ for (i=0; i < g_view_list_entries; i++) {
+ free(g_view_list[i].name);
+ }
+ free(g_view_list);
+ g_view_list = NULL;
+ }
+
+ if (display_qdevice) {
+ if (nodeid_format == NODEID_FORMAT_DECIMAL) {
+ print_uint32_padded(VOTEQUORUM_QDEVICE_NODEID);
+ } else {
+ printf("0x%08x ", VOTEQUORUM_QDEVICE_NODEID);
+ }
+ /* If the quorum device is inactive on this node then show votes as 0
+ so that the display is not confusing */
+ if (our_flags & VOTEQUORUM_INFO_QDEVICE_CAST_VOTE) {
+ print_uint32_padded(info[0].qdevice_votes);
+ }
+ else {
+ print_uint32_padded(0);
+ }
+ printf(" %s", info[0].qdevice_name);
+ if (our_flags & VOTEQUORUM_INFO_QDEVICE_CAST_VOTE) {
+ printf("\n");
+ }
+ else {
+ printf(" (votes %d)\n", info[0].qdevice_votes);
+ }
+ }
+
+}
+
+static int display_quorum_data(int is_quorate,
+ nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type,
+ int loop)
+{
+ struct votequorum_info info;
+ int err;
+ char quorumtype[256];
+ time_t t;
+
+ memset(quorumtype, 0, sizeof(quorumtype));
+
+ printf("Quorum information\n");
+ printf("------------------\n");
+ time(&t);
+ printf("Date: %s", ctime((const time_t *)&t));
+
+ if (get_quorum_type(quorumtype, sizeof(quorumtype))) {
+ strncpy(quorumtype, "Not configured", sizeof(quorumtype) - 1);
+ }
+ printf("Quorum provider: %s\n", quorumtype);
+ printf("Nodes: %d\n", g_view_list_entries);
+ if (nodeid_format == NODEID_FORMAT_DECIMAL) {
+ printf("Node ID: " CS_PRI_NODE_ID "\n", our_nodeid);
+ } else {
+ printf("Node ID: 0x%08x\n", our_nodeid);
+ }
+
+ if (v_handle) {
+ printf("Ring ID: " CS_PRI_RING_ID "\n", g_ring_id_rep_node, g_ring_id);
+ }
+ else {
+ printf("Ring ID: " CS_PRI_RING_ID_SEQ "\n", g_ring_id);
+ }
+ printf("Quorate: %s\n", is_quorate?"Yes":"No");
+
+ if (!v_handle) {
+ return CS_OK;
+ }
+
+ err=votequorum_getinfo(v_handle, our_nodeid, &info);
+ if ((err == CS_OK) || (err == CS_ERR_NOT_EXIST)) {
+ printf("\nVotequorum information\n");
+ printf("----------------------\n");
+ 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 %s\n", info.quorum, info.flags & VOTEQUORUM_INFO_QUORATE?" ":"Activity blocked");
+ printf("Flags: ");
+ if (info.flags & VOTEQUORUM_INFO_TWONODE) printf("2Node ");
+ if (info.flags & VOTEQUORUM_INFO_QUORATE) printf("Quorate ");
+ if (info.flags & VOTEQUORUM_INFO_WAIT_FOR_ALL) printf("WaitForAll ");
+ if (info.flags & VOTEQUORUM_INFO_LAST_MAN_STANDING) printf("LastManStanding ");
+ if (info.flags & VOTEQUORUM_INFO_AUTO_TIE_BREAKER) printf("AutoTieBreaker ");
+ if (info.flags & VOTEQUORUM_INFO_ALLOW_DOWNSCALE) printf("AllowDownscale ");
+ if (info.flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED) printf("Qdevice ");
+ printf("\n");
+ } else {
+ fprintf(stderr, "Unable to get node info: %s\n", cs_strerror(err));
+ }
+
+ display_nodes_data(nodeid_format, name_format, sort_type);
+
+ return err;
+}
+
+/*
+ * return EXIT_SUCCESS if quorate
+ * EXIT_NOT_QUORATE if not quorate
+ * EXIT_FAILURE on error
+ */
+static int show_status(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type)
+{
+ int is_quorate;
+ int err;
+
+ err=quorum_getquorate(q_handle, &is_quorate);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to get cluster quorate status: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+
+ err=quorum_trackstart(q_handle, CS_TRACK_CURRENT);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to start quorum status tracking: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+
+ g_called = 0;
+ while (g_called == 0 && err == CS_OK) {
+ err = quorum_dispatch(q_handle, CS_DISPATCH_ONE);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to dispatch quorum status: %s\n", cs_strerror(err));
+ }
+ }
+
+ if (quorum_trackstop(q_handle) != CS_OK) {
+ fprintf(stderr, "Unable to stop quorum status tracking: %s\n", cs_strerror(err));
+ }
+
+ if (using_votequorum()) {
+
+ if ( (err=votequorum_trackstart(v_handle, 0LL, CS_TRACK_CURRENT)) != CS_OK) {
+ fprintf(stderr, "Unable to start votequorum status tracking: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+
+ g_vq_called = 0;
+ while (g_vq_called == 0 && err == CS_OK) {
+ err = votequorum_dispatch(v_handle, CS_DISPATCH_ONE);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to dispatch votequorum status: %s\n", cs_strerror(err));
+ }
+ }
+ }
+
+quorum_err:
+ if (err != CS_OK) {
+ return EXIT_FAILURE;
+ }
+
+ err = display_quorum_data(is_quorate, nodeid_format, name_format, sort_type, 0);
+ if (err != CS_OK) {
+ return EXIT_FAILURE;
+ }
+
+ return (is_quorate ? EXIT_SUCCESS : EXIT_NOT_QUORATE);
+}
+
+static int monitor_status(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type) {
+ int err;
+ int loop = 0;
+
+ if (q_type == QUORUM_FREE) {
+ printf("\nQuorum is not configured - cannot monitor\n");
+ return show_status(nodeid_format, name_format, sort_type);
+ }
+
+ err=quorum_trackstart(q_handle, CS_TRACK_CHANGES);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to start quorum status tracking: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+
+ if (using_votequorum()) {
+ if ( (err=votequorum_trackstart(v_handle, 0LL, CS_TRACK_CHANGES)) != CS_OK) {
+ fprintf(stderr, "Unable to start votequorum status tracking: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+ }
+
+
+ while (1) {
+ err = quorum_dispatch(q_handle, CS_DISPATCH_ONE);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to dispatch quorum status: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+ if (using_votequorum()) {
+ g_vq_called = 0;
+ while (!g_vq_called) {
+ err = votequorum_dispatch(v_handle, CS_DISPATCH_ONE);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to dispatch votequorum status: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+ }
+ }
+
+ err = display_quorum_data(g_quorate, nodeid_format, name_format, sort_type, loop);
+ printf("\n");
+ loop = 1;
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to display quorum data: %s\n", cs_strerror(err));
+ goto quorum_err;
+ }
+ }
+
+quorum_err:
+ return EXIT_FAILURE;
+}
+
+static int show_nodes(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type)
+{
+ int err;
+ int result = EXIT_FAILURE;
+
+ err = quorum_trackstart(q_handle, CS_TRACK_CURRENT);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to start quorum status tracking: %s\n", cs_strerror(err));
+ goto err_exit;
+ }
+
+ g_called = 0;
+ while (g_called == 0) {
+ err = quorum_dispatch(q_handle, CS_DISPATCH_ONE);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to dispatch quorum status: %s\n", cs_strerror(err));
+ goto err_exit;
+ }
+ }
+
+ display_nodes_data(nodeid_format, name_format, sort_type);
+
+ result = EXIT_SUCCESS;
+err_exit:
+ return result;
+}
+
+static int unregister_qdevice(void)
+{
+ int err;
+ struct votequorum_info info;
+ int result;
+
+ result = EXIT_FAILURE;
+
+ err = votequorum_getinfo(v_handle, our_nodeid, &info);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to get quorum device info: %s\n", cs_strerror(err));
+ goto err_exit;
+ }
+
+ if (!(info.flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED)) {
+ result = EXIT_SUCCESS;
+ goto err_exit;
+ }
+
+ err = votequorum_qdevice_unregister(v_handle, info.qdevice_name);
+ if (err != CS_OK) {
+ fprintf(stderr, "Unable to unregister quorum device: %s\n", cs_strerror(err));
+ goto err_exit;
+ }
+
+ result = EXIT_SUCCESS;
+err_exit:
+ return result;
+}
+
+/*
+ * return -1 on error
+ * 0 if OK
+ */
+
+static int init_all(void) {
+ cmap_handle = 0;
+ q_handle = 0;
+ v_handle = 0;
+ c_handle = 0;
+
+ if (cmap_initialize(&cmap_handle) != CS_OK) {
+ fprintf(stderr, "Cannot initialize CMAP service\n");
+ cmap_handle = 0;
+ goto out;
+ }
+
+ if (quorum_initialize(&q_handle, &q_callbacks, &q_type) != CS_OK) {
+ fprintf(stderr, "Cannot initialize QUORUM service\n");
+ q_handle = 0;
+ goto out;
+ }
+
+ if (corosync_cfg_initialize(&c_handle, &c_callbacks) != CS_OK) {
+ fprintf(stderr, "Cannot initialise CFG service\n");
+ c_handle = 0;
+ goto out;
+ }
+
+ if (using_votequorum() <= 0) {
+ return 0;
+ }
+
+ if (votequorum_initialize(&v_handle, &v_callbacks) != CS_OK) {
+ fprintf(stderr, "Cannot initialise VOTEQUORUM service\n");
+ v_handle = 0;
+ goto out;
+ }
+
+ if (cmap_get_uint32(cmap_handle, "runtime.votequorum.this_node_id", &our_nodeid) != CS_OK) {
+ fprintf(stderr, "Unable to retrieve this_node_id\n");
+ goto out;
+ }
+
+ return 0;
+out:
+ return -1;
+}
+
+static void close_all(void) {
+ if (cmap_handle) {
+ cmap_finalize(cmap_handle);
+ }
+ if (q_handle) {
+ quorum_finalize(q_handle);
+ }
+ if (c_handle) {
+ corosync_cfg_finalize(c_handle);
+ }
+ if (v_handle) {
+ votequorum_finalize(v_handle);
+ }
+}
+
+int main (int argc, char *argv[]) {
+ const char *options = "VHaslpmfe:v:hin:o:";
+ int opt;
+ int votes = 0;
+ int ret = 0;
+ uint32_t nodeid = 0;
+ uint32_t nodeid_set = 0;
+ nodeid_format_t nodeid_format = NODEID_FORMAT_DECIMAL;
+ name_format_t address_format = ADDRESS_FORMAT_NAME;
+ command_t command_opt = CMD_SHOWSTATUS;
+ sorttype_t sort_opt = SORT_ADDR;
+ long long int l;
+
+ while ( (opt = getopt(argc, argv, options)) != -1 ) {
+ switch (opt) {
+ case 'f':
+ command_opt = CMD_UNREGISTER_QDEVICE;
+ break;
+ case 's':
+ command_opt = CMD_SHOWSTATUS;
+ break;
+ case 'a':
+ g_show_all_addrs = 1;
+ break;
+ case 'm':
+ command_opt = CMD_MONITOR;
+ break;
+ case 'i':
+ address_format = ADDRESS_FORMAT_IP;
+ break;
+ case 'H':
+ nodeid_format = NODEID_FORMAT_HEX;
+ break;
+ case 'l':
+ command_opt = CMD_SHOWNODES;
+ break;
+ case 'p':
+ machine_parsable = 1;
+ break;
+ case 'e':
+ if (util_strtonum(optarg, 1, INT_MAX, &l) == -1) {
+ fprintf(stderr, "New expected votes value was not valid, try a positive number\n");
+ exit(EXIT_FAILURE);
+ }
+ votes = l;
+ command_opt = CMD_SETEXPECTED;
+ break;
+ case 'n':
+ if (util_strtonum(optarg, 1, UINT_MAX, &l) == -1) {
+ fprintf(stderr, "The nodeid was not valid, try a positive number\n");
+ exit(EXIT_FAILURE);
+ }
+ nodeid = l;
+ nodeid_set = 1;
+ break;
+ case 'v':
+ if (util_strtonum(optarg, 0, INT_MAX, &l) == -1) {
+ fprintf(stderr, "New votes value was not valid, try a positive number or zero\n");
+ exit(EXIT_FAILURE);
+ }
+ votes = l;
+ command_opt = CMD_SETVOTES;
+ break;
+ case 'o':
+ if (strcmp(optarg, "a") == 0) {
+ sort_opt = SORT_ADDR;
+ } else if (strcmp(optarg, "i") == 0) {
+ sort_opt = SORT_NODEID;
+ } else if (strcmp(optarg, "n") == 0) {
+ sort_opt = SORT_NODENAME;
+ } else {
+ fprintf(stderr, "Invalid ordering option. valid orders are a(address), i(node ID) or n(name)\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'V':
+ printf("corosync-quorumtool version: %s\n", VERSION);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ show_usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case ':':
+ case '?':
+ default:
+ show_usage(argv[0]);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if (init_all()) {
+ close_all();
+ exit(EXIT_FAILURE);
+ }
+
+ switch (command_opt) {
+ case CMD_SHOWNODES:
+ ret = show_nodes(nodeid_format, address_format, sort_opt);
+ break;
+ case CMD_SHOWSTATUS:
+ ret = show_status(nodeid_format, address_format, sort_opt);
+ break;
+ case CMD_SETVOTES:
+ if (using_votequorum() > 0) {
+ if (!nodeid_set) {
+ nodeid = our_nodeid;
+ }
+ ret = set_votes(nodeid, votes);
+ } else {
+ fprintf(stderr, "You cannot change node votes, corosync is not using votequorum\n");
+ ret = EXIT_FAILURE;
+ }
+ break;
+ case CMD_SETEXPECTED:
+ if (using_votequorum() > 0) {
+ ret = set_expected(votes);
+ } else {
+ fprintf(stderr, "You cannot change expected votes, corosync is not using votequorum\n");
+ ret = EXIT_FAILURE;
+ }
+ break;
+ case CMD_MONITOR:
+ ret = monitor_status(nodeid_format, address_format, sort_opt);
+ break;
+ case CMD_UNREGISTER_QDEVICE:
+ if (using_votequorum() > 0) {
+ ret = unregister_qdevice();
+ } else {
+ fprintf(stderr, "You cannot unregister quorum device, corosync is not using votequorum\n");
+ ret = EXIT_FAILURE;
+ }
+ break;
+ }
+
+ close_all();
+
+ return (ret);
+}