public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* master - config: move ccs/ccs_tool to config/tools/ccs_tool
@ 2008-08-14 16:00 Fabio M. Di Nitto
0 siblings, 0 replies; only message in thread
From: Fabio M. Di Nitto @ 2008-08-14 16:00 UTC (permalink / raw)
To: cluster-cvs-relay
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=c84e65d62b7ff3fffbf02985a39f187bf59eb541
Commit: c84e65d62b7ff3fffbf02985a39f187bf59eb541
Parent: 3fc1b89bcf0f0b2bfd53da2541e023865619cf24
Author: Fabio M. Di Nitto <fdinitto@redhat.com>
AuthorDate: Thu Aug 7 06:40:01 2008 +0200
Committer: Fabio M. Di Nitto <fdinitto@redhat.com>
CommitterDate: Thu Aug 14 15:18:02 2008 +0200
config: move ccs/ccs_tool to config/tools/ccs_tool
ccs_tool is now a generic tool for handling configuration.
Move it to a proper location.
Signed-off-by: Fabio M. Di Nitto <fdinitto@redhat.com>
---
ccs/Makefile | 2 +-
ccs/ccs_tool/Makefile | 56 --
ccs/ccs_tool/ccs_tool.c | 353 -----------
ccs/ccs_tool/editconf.c | 1261 --------------------------------------
ccs/ccs_tool/editconf.h | 8 -
ccs/ccs_tool/update.c | 673 --------------------
ccs/ccs_tool/update.h | 6 -
ccs/man/Makefile | 8 +-
ccs/man/ccs_test.8 | 132 ----
ccs/man/ccs_tool.8 | 185 ------
config/tools/Makefile | 2 +-
config/tools/ccs_tool/Makefile | 56 ++
config/tools/ccs_tool/ccs_tool.c | 353 +++++++++++
config/tools/ccs_tool/editconf.c | 1261 ++++++++++++++++++++++++++++++++++++++
config/tools/ccs_tool/editconf.h | 8 +
config/tools/ccs_tool/update.c | 673 ++++++++++++++++++++
config/tools/ccs_tool/update.h | 6 +
config/tools/man/Makefile | 8 +-
config/tools/man/ccs_test.8 | 132 ++++
config/tools/man/ccs_tool.8 | 185 ++++++
20 files changed, 2684 insertions(+), 2684 deletions(-)
diff --git a/ccs/Makefile b/ccs/Makefile
index cb36c8c..ec46665 100644
--- a/ccs/Makefile
+++ b/ccs/Makefile
@@ -1,4 +1,4 @@
include ../make/defines.mk
include $(OBJDIR)/make/passthrough.mk
-SUBDIRS=libccscompat daemon ccsais ccs_tool man
+SUBDIRS=libccscompat daemon ccsais man
diff --git a/ccs/ccs_tool/Makefile b/ccs/ccs_tool/Makefile
deleted file mode 100644
index ff77b9f..0000000
--- a/ccs/ccs_tool/Makefile
+++ /dev/null
@@ -1,56 +0,0 @@
-TARGET1 = ccs_tool
-TARGET2 = ccs_test
-
-SBINDIRT = $(TARGET1)
-SBINSYMT = $(TARGET2)
-
-include ../../make/defines.mk
-
-ifdef legacy_code
-all: depends ${TARGET1} ${TARGET2}
-else
-all: ${TARGET1} ${TARGET2}
-endif
-
-include $(OBJDIR)/make/cobj.mk
-include $(OBJDIR)/make/clean.mk
-include $(OBJDIR)/make/install.mk
-include $(OBJDIR)/make/uninstall.mk
-
-OBJS = ccs_tool.o \
- editconf.o
-
-ifdef legacy_code
-OBJS += update.o
-endif
-
-CFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
-CFLAGS += -I${cmanincdir} `xml2-config --cflags`
-CFLAGS += -I${ccsincdir} -I$(S)/../include
-CFLAGS += -I$(S)/../libccscompat
-CFLAGS += -I${incdir}
-
-LDFLAGS += -L${cmanlibdir} -lcman
-ifdef legacy_code
-LDFLAGS += -L../libccscompat -lccscompat
-LDDEPS += ../libccscompat/libccscompat.a
-else
-LDFLAGS += -L${ccslibdir} -lccs
-endif
-LDFLAGS += `xml2-config --libs`
-LDFLAGS += -L${libdir}
-
-${TARGET1}: ${OBJS} ${LDDEPS}
- $(CC) -o $@ $^ $(LDFLAGS)
-
-${TARGET2}: ${TARGET1}
- ln -sf ${TARGET1} ${TARGET2}
-
-ifdef legacy_code
-depends:
- $(MAKE) -C ../libccscompat all
-endif
-
-clean: generalclean
-
--include $(OBJS:.o=.d)
diff --git a/ccs/ccs_tool/ccs_tool.c b/ccs/ccs_tool/ccs_tool.c
deleted file mode 100644
index c05a07e..0000000
--- a/ccs/ccs_tool/ccs_tool.c
+++ /dev/null
@@ -1,353 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <string.h>
-#include <errno.h>
-
-#include "copyright.cf"
-#include "editconf.h"
-#ifdef LEGACY_CODE
-#include "update.h"
-#include "libccscompat.h"
-#else
-#include "ccs.h"
-#endif
-
-
-/*
- * Old libccs retruned -error (mostly!) but didn't set errno (sigh)
- * New libccs sets errno correctly
- */
-static char *errstring(int retcode)
-{
-#ifdef LEGACY_CODE
- return strerror(retcode);
-#else
- return strerror(errno);
-#endif
-}
-
-static void tool_print_usage(FILE *stream);
-
-int globalverbose=0;
-
-static void test_print_usage(FILE *stream);
-
-static int test_main(int argc, char *argv[], int old_format){
- int desc=0;
- int i=0;
- int error = 0;
- int force = 0, blocking = 0;
- char *str=NULL;
- char *cluster_name = NULL;
-
- if(argc <= 1){
- test_print_usage(stderr);
- exit(EXIT_FAILURE);
- }
-
- for(i=1; i < argc; i++){
- if(!strcmp(argv[i], "-h")){
- test_print_usage(stdout);
- exit(EXIT_SUCCESS);
- }
- if(!strcmp(argv[i], "-V")){
- printf("%s %s (built %s %s)\n", argv[0], RELEASE_VERSION, __DATE__, __TIME__);
- printf("%s\n", REDHAT_COPYRIGHT);
- exit(EXIT_SUCCESS);
- }
- }
-
- if(!strcmp(argv[1], "connect")){
- for(i=2; i < argc; i++){
- if(!strcmp(argv[i], "force")){
- printf("Force is set.\n");
- force = 1;
- } else if(!strcmp(argv[i], "block")){
- printf("Blocking is set.\n");
- blocking = 1;
- } else {
- cluster_name = argv[i];
- printf("Setting cluster name to %s\n", cluster_name);
- }
- }
- if(blocking && !force){
- fprintf(stderr, "Blocking can only be used with \"force\".\n");
- exit(EXIT_FAILURE);
- }
- if(force){
- desc = ccs_force_connect(cluster_name, blocking);
- } else {
- if(cluster_name){
- fprintf(stderr, "A cluster name can only be specified when using 'force'.\n");
- exit(EXIT_FAILURE);
- }
- desc = ccs_connect();
- }
- if(desc < 0){
- fprintf(stderr, "ccs_connect failed: %s\n", errstring(-desc));
- exit(EXIT_FAILURE);
- } else {
- printf("Connect successful.\n");
- printf(" Connection descriptor = %d\n", desc);
-#ifndef LEGACY_CODE
- ccs_disconnect(desc);
-#endif
- }
- }
- else if(!strcmp(argv[1], "disconnect")){
- if(argc < 3){
- fprintf(stderr, "Wrong number of arguments.\n");
- exit(EXIT_FAILURE);
- }
-#ifdef LEGACY_CODE
- desc = atoi(argv[2]);
-#else
- desc = ccs_connect();
-#endif
- if((error = ccs_disconnect(desc))){
- fprintf(stderr, "ccs_disconnect failed: %s\n", errstring(-error));
- exit(EXIT_FAILURE);
- } else {
- printf("Disconnect successful.\n");
- }
- }
- else if(!strcmp(argv[1], "get")){
- if(argc < 4){
- fprintf(stderr, "Wrong number of arguments.\n");
- exit(EXIT_FAILURE);
- }
-#ifdef LEGACY_CODE
- desc = atoi(argv[2]);
-#else
- desc = ccs_connect();
-#endif
- if((desc < 0) || (error = ccs_get(desc, argv[3], &str))){
- fprintf(stderr, "ccs_get failed: %s\n", errstring(-error));
- exit(EXIT_FAILURE);
- } else {
- if (old_format) {
- printf("Get successful.\n");
- printf(" Value = <%s>\n", str);
- }
- else {
- printf("%s\n", str);
- }
- if(str)free(str);
-#ifndef LEGACY_CODE
- ccs_disconnect(desc);
-#endif
- }
- }
- else {
- fprintf(stderr, "Unknown command: %s\n", argv[1]);
- exit(EXIT_FAILURE);
- }
-
- exit(EXIT_SUCCESS);
-}
-
-static void test_print_usage(FILE *stream)
-{
- fprintf(stream,
- "Usage:\n"
- "\n"
- "ccs_test [Options] <Command>\n"
- "\n"
- "Options:\n"
- " -h Print usage.\n"
- " -V Print version information.\n"
- "\n"
- "Commands:\n"
- " connect <force> <block> Connect to CCS and return connection descriptor.\n"
- " disconnect <desc> Disconnect from CCS.\n"
- " get <desc> <request> Get a value from CCS.\n"
- );
-}
-
-#ifndef LEGACY_CODE
-static int xpath_query(int argc, char **argv)
-{
- int handle;
- char *ret;
- int i;
-
- if (argc < 2) {
- fprintf(stderr,
- "Usage:\n"
- "\n"
- "ccs_tool query <xpath query>\n");
- return 1;
- }
-
- /* Tell the library we want full XPath parsing */
- fullxpath = 1;
-
- handle = ccs_connect();
-
- /* Process all the queries on the command-line */
- for (i=1; i<argc; i++) {
- if (!ccs_get(handle, argv[1], &ret)) {
- printf("%s\n", ret);
- free(ret);
- }
- else {
- fprintf(stderr, "Query failed: %s\n", strerror(errno));
- ccs_disconnect(handle);
- return -1;
- }
- }
- ccs_disconnect(handle);
- return 0;
-}
-#endif
-
-static int tool_main(int argc, char *argv[])
-{
- optind = 1;
-
- if (argc < 2 || !strcmp(argv[optind], "-h")) {
- tool_print_usage(stdout);
- exit(EXIT_SUCCESS);
- }
- if (!strcmp(argv[optind], "-V")) {
- printf("%s %s (built %s %s)\n", argv[0], RELEASE_VERSION,
- __DATE__, __TIME__);
- printf("%s\n", REDHAT_COPYRIGHT);
- exit(EXIT_SUCCESS);
- }
-
- if(optind < argc){
- if(!strcmp(argv[optind], "-verbose")){
- optind++;
- globalverbose=1;
- }
- if(!strcmp(argv[optind], "help")){
- tool_print_usage(stdout);
- exit(EXIT_SUCCESS);
- }
-#ifdef LEGACY_CODE
- /* Update is meaningless now */
- else if(!strcmp(argv[optind], "update")){
- if(optind+1 >= argc){
- fprintf(stderr, "Too few arguments.\n"
- "Try 'ccs_tool help' for help.\n");
- exit(EXIT_FAILURE);
- }
- if(update(argv[optind+1])){
- fprintf(stderr, "\nFailed to update config file.\n");
- exit(EXIT_FAILURE);
- }
- printf("\nUpdate complete.\n");
- }
- /* Do old ccs queries */
- else if(!strcmp(argv[optind], "query")){
- char *new_argv[argc+2];
- int i;
-
- new_argv[0] = "ccs_test";
- new_argv[1] = "get";
- new_argv[2] = "0"; /* Dummy connection ID */
- for (i=2; i<argc; i++)
- new_argv[1+i] = argv[i];
-
- return test_main(argc+1, new_argv, 0);
- }
-#else
- else if(!strcmp(argv[optind], "query")){
- return xpath_query(argc-1, argv+1);
- }
-#endif
- else if(!strcmp(argv[optind], "addnode")){
- add_node(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
- else if(!strcmp(argv[optind], "delnode")){
- del_node(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
- else if(!strcmp(argv[optind], "addfence")){
- add_fence(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
- else if(!strcmp(argv[optind], "delfence")){
- del_fence(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
- else if(!strcmp(argv[optind], "lsnode")){
- list_nodes(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
- else if(!strcmp(argv[optind], "lsfence")){
- list_fences(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
- else if(!strcmp(argv[optind], "create")){
- create_skeleton(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
- else if(!strcmp(argv[optind], "addnodeids")){
- add_nodeids(argc-1, argv+1);
- exit(EXIT_SUCCESS);
- }
-
- else {
- fprintf(stderr, "Unknown command, %s.\n"
- "Try 'ccs_tool help' for help.\n", argv[optind]);
- exit(EXIT_FAILURE);
- }
- } else {
- fprintf(stderr, "Too few arguments.\n"
- "Try 'ccs_tool help' for help.\n");
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
-}
-
-static void tool_print_usage(FILE *stream){
- fprintf(stream,
- "Usage:\n"
- " ccs_tool [options] <command>\n"
- "\n"
- "Options:\n"
- " -verbose Make some operations print more details.\n"
- " -h Print this usage and exit.\n"
- " -V Print version information and exit.\n"
- "\n"
- "Commands:\n"
- " help Print this usage and exit.\n"
-#ifdef LEGACY_CODE
- " update <xml file> Tells ccsd to upgrade to new config file version.\n"
- " query <ccs query> Query the cluster configuration.\n"
-#else
- " query <xpath query> Query the cluster configuration.\n"
-#endif
- " addnode <node> Add a node\n"
- " delnode <node> Delete a node\n"
- " lsnode List nodes\n"
- " lsfence List fence devices\n"
- " addfence <fencedev> Add a new fence device\n"
- " delfence <fencedev> Delete a fence device\n"
- " create Create a skeleton config file\n"
- " addnodeids Assign node ID numbers to all nodes\n"
- "\n");
-}
-
-
-int main(int argc, char *argv[])
-{
- char *name = strdup(argv[0]);
-
- /*
- * Don't be anal about the binary name.
- * We expect either 'ccs_tool' or 'ccs_test',
- * but interpret anything other than 'ccs_test'
- * as 'ccs_tool'.
- * That's not a bug, it's a feature.
- */
-
- if (strcmp(basename(name), "ccs_test") == 0)
- return test_main(argc, argv, 1);
- else
- return tool_main(argc, argv);
-}
diff --git a/ccs/ccs_tool/editconf.c b/ccs/ccs_tool/editconf.c
deleted file mode 100644
index 53b0ed6..0000000
--- a/ccs/ccs_tool/editconf.c
+++ /dev/null
@@ -1,1261 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <getopt.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
-#include <libxml/tree.h>
-
-#ifdef LEGACY_CODE
-#include "update.h"
-#endif
-
-#define MAX_NODES 256
-
-char *prog_name = "ccs_tool";
-
-#define die(fmt, args...) \
-do { \
- fprintf(stderr, "%s: ", prog_name); \
- fprintf(stderr, fmt "\n", ##args); \
- exit(EXIT_FAILURE); \
-} while (0)
-
-
-struct option_info
-{
- char *name;
- char *altname;
- char *votes;
- char *nodeid;
- char *mcast_addr;
- char *fence_type;
- char *configfile;
- char *outputfile;
- int do_delete;
- int tell_ccsd;
- int force_ccsd;
-};
-
-static void config_usage(int rw)
-{
- fprintf(stderr, " -c --configfile Name of configuration file (" DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE ")\n");
- if (rw)
- {
- fprintf(stderr, " -o --outputfile Name of output file (defaults to same as --configfile)\n");
- fprintf(stderr, " -C --no_ccs Don't tell CCSD about this change\n");
- fprintf(stderr, " default: run \"ccs_tool update\" if file is updated in place)\n");
- fprintf(stderr, " -F --force_ccs Force \"ccs_tool upgrade\" even if input & output files differ\n");
- }
-}
-
-static void help_usage(void)
-{
- fprintf(stderr, " -h --help Display this help text\n");
-}
-
-static void list_usage(const char *name)
-{
- fprintf(stderr, "Usage: %s %s [options]\n", prog_name, name);
- fprintf(stderr, " -v --verbose Print all properties of the item\n");
- config_usage(0);
- help_usage();
-
- exit(0);
-}
-
-static void create_usage(const char *name)
-{
- fprintf(stderr, "Usage: %s %s [-2] <clustername>\n", prog_name, name);
- fprintf(stderr, " -2 Create a 2-node cman cluster config file\n");
- config_usage(0);
- help_usage();
- fprintf(stderr, "\n"
- "Note that \"create\" on its own will not create a valid configuration file.\n"
- "Fence agents and nodes will need to be added to it before handing it over\n"
- "to ccsd.\n"
- "\n"
- "eg:\n"
- " ccs_tool create MyCluster\n"
- " ccs_tool addfence apc fence_apc ipaddr=apc.domain.net user=apc password=apc\n"
- " ccs_tool addnode node1 -n 1 -f apc port=1\n"
- " ccs_tool addnode node2 -n 2 -f apc port=2\n"
- " ccs_tool addnode node3 -n 3 -f apc port=3\n"
- " ccs_tool addnode node4 -n 4 -f apc port=4\n"
- "\n");
-
- exit(0);
-}
-
-static void addfence_usage(const char *name)
-{
- fprintf(stderr, "Usage: %s %s [options] <name> <agent> [param=value]\n", prog_name, name);
- config_usage(1);
- help_usage();
-
- exit(0);
-}
-
-static void delfence_usage(const char *name)
-{
- fprintf(stderr, "Usage: %s %s [options] <name>\n", prog_name, name);
- config_usage(1);
- help_usage();
- fprintf(stderr, "\n");
- fprintf(stderr, "%s will allow you to remove a fence device that is in use by nodes.\n", name);
- fprintf(stderr, "This is to allow changes to be made, but be aware that it may produce an\n");
- fprintf(stderr, "invalid configuration file if you don't add it back in again.\n");
-
- exit(0);
-}
-
-static void delnode_usage(const char *name)
-{
- fprintf(stderr, "Usage: %s %s [options] <name>\n", prog_name, name);
- config_usage(1);
- help_usage();
-
- exit(0);
-}
-
-static void addnodeid_usage(const char *name)
-{
- fprintf(stderr, "Add node IDs to all nodes in the config file that don't have them.\n");
- fprintf(stderr, "Nodes with IDs will not be afftected, so you can run this as many times\n");
- fprintf(stderr, "as you like without doing any harm.\n");
- fprintf(stderr, "It will optionally add a multicast address to the cluster config too.\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "Usage: %s %s [options] <name>\n", prog_name, name);
- fprintf(stderr, " -n --nodeid Nodeid to start with (default 1)\n");
- fprintf(stderr, " -m --multicast Set or change the multicast address\n");
- fprintf(stderr, " -v --verbose Print nodeids that are assigned\n");
- config_usage(1);
- help_usage();
-
- exit(0);
-}
-
-static void addnode_usage(const char *name)
-{
- fprintf(stderr, "Usage: %s %s [options] <nodename> [<fencearg>=<value>]...\n", prog_name, name);
- fprintf(stderr, " -n --nodeid Nodeid (required)\n");
- fprintf(stderr, " -v --votes Number of votes for this node (default 1)\n");
- fprintf(stderr, " -a --altname Alternative name/interface for multihomed hosts\n");
- fprintf(stderr, " -f --fence_type Type of fencing to use\n");
- config_usage(1);
- help_usage();
-
- fprintf(stderr, "\n");
- fprintf(stderr, "Examples:\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "Add a new node to default configuration file:\n");
- fprintf(stderr, " %s %s -n 1 -f manual ipaddr=newnode\n", prog_name, name);
- fprintf(stderr, "\n");
- fprintf(stderr, "Add a new node and dump config file to stdout rather than save it\n");
- fprintf(stderr, " %s %s -n 2 -f apc -o- newnode.temp.net port=1\n", prog_name, name);
-
- exit(0);
-}
-
-/* Is it really ?
- * Actually, we don't check that this is a valid multicast address(!),
- * merely that it is a valid IP[46] address.
- */
-static int valid_mcast_addr(char *mcast)
-{
- struct addrinfo *ainfo;
- struct addrinfo ahints;
- int ret;
-
- memset(&ahints, 0, sizeof(ahints));
-
- ret = getaddrinfo(mcast, NULL, &ahints, &ainfo);
- if (ret) {
- freeaddrinfo(ainfo);
- return 0;
- }
- return 1;
-}
-
-static void save_file(xmlDoc *doc, struct option_info *ninfo)
-{
- char tmpfile[strlen(ninfo->outputfile)+5];
- char oldfile[strlen(ninfo->outputfile)+5];
- int using_stdout = 0;
- mode_t old_mode;
- int ret;
-
- old_mode = umask(026);
-
- if (strcmp(ninfo->outputfile, "-") == 0)
- using_stdout = 1;
-
- /*
- * Save it to a temp file before moving the old one out of the way
- */
- if (!using_stdout)
- {
- snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", ninfo->outputfile);
- snprintf(oldfile, sizeof(oldfile), "%s.old", ninfo->outputfile);
- }
- else
- {
- strcpy(tmpfile, ninfo->outputfile);
- }
-
- xmlKeepBlanksDefault(0);
- ret = xmlSaveFormatFile(tmpfile, doc, 1);
- if (ret == -1)
- die("Error writing new config file %s", ninfo->outputfile);
-
- if (!using_stdout)
- {
- if (rename(ninfo->outputfile, oldfile) == -1 && errno != ENOENT)
- die("Can't move old config file out of the way\n");
-
- if (rename(tmpfile, ninfo->outputfile))
- {
- perror("Error renaming new file to its real filename");
-
- /* Drat, that failed, try to put the old one back */
- if (rename(oldfile, ninfo->outputfile))
- die("Can't move old config fileback in place - clean up after me please\n");
- }
- }
-
-#ifdef LEGACY_CODE
- /* Try to tell ccsd if needed */
- if ((strcmp(ninfo->configfile, ninfo->outputfile) == 0 && ninfo->tell_ccsd) ||
- ninfo->force_ccsd)
- {
- printf("running ccs_tool update...\n");
- update(ninfo->outputfile);
- }
-#endif
-
- /* free the document */
- xmlFreeDoc(doc);
-
- umask(old_mode);
-}
-
-static void validate_int_arg(char argopt, char *arg)
-{
- char *tmp;
- int val;
-
- val = strtol(arg, &tmp, 10);
- if (tmp == arg || tmp != arg + strlen(arg))
- die("argument to %c (%s) is not an integer", argopt, arg);
-
- if (val < 0)
- die("argument to %c cannot be negative", argopt);
-}
-
-/* Get the config_version string from the file */
-static xmlChar *find_version(xmlNode *root)
-{
- if (xmlHasProp(root, BAD_CAST "config_version"))
- {
- xmlChar *ver;
-
- ver = xmlGetProp(root, BAD_CAST "config_version");
- return ver;
- }
- return NULL;
-}
-
-/* Get the cluster name string from the file */
-static xmlChar *cluster_name(xmlNode *root)
-{
- if (xmlHasProp(root, BAD_CAST "name"))
- {
- xmlChar *ver;
-
- ver = xmlGetProp(root, BAD_CAST "name");
- return ver;
- }
- return NULL;
-}
-
-static void increment_version(xmlNode *root_element)
-{
- int ver;
- unsigned char *version_string;
- char newver[32];
-
- /* Increment version */
- version_string = find_version(root_element);
- if (!version_string)
- die("Can't find \"config_version\" in config file\n");
-
- ver = atoi((char *)version_string);
- snprintf(newver, sizeof(newver), "%d", ++ver);
- xmlSetProp(root_element, BAD_CAST "config_version", BAD_CAST newver);
-}
-
-static xmlNode *findnode(xmlNode *root, char *name)
-{
- xmlNode *cur_node;
-
- for (cur_node = root->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, name)==0)
- {
- return cur_node;
- }
- }
- return NULL;
-}
-
-/* Return the fence type name (& node) for a cluster node */
-static xmlChar *get_fence_type(xmlNode *clusternode, xmlNode **fencenode)
-{
- xmlNode *f;
-
- f = findnode(clusternode, "fence");
- if (f)
- {
- f = findnode(f, "method");
- if (f)
- {
- f = findnode(f, "device");
- *fencenode = f;
- return xmlGetProp(f, BAD_CAST "name");
- }
- }
- return NULL;
-}
-
-/* Check the fence type exists under <fencedevices> */
-static xmlNode *valid_fence_type(xmlNode *root, char *fencetype)
-{
- xmlNode *devs;
- xmlNode *cur_node;
-
- devs = findnode(root, "fencedevices");
- if (!devs)
- return NULL;
-
- for (cur_node = devs->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "fencedevice") == 0)
- {
- xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
- if (strcmp((char *)name, fencetype) == 0)
- return cur_node;
- }
- }
- return NULL;
-}
-
-/* Check the nodeid is not already in use by another node */
-static xmlNode *get_by_nodeid(xmlNode *root, int nodeid)
-{
- xmlNode *cnodes;
- xmlNode *cur_node;
-
- cnodes = findnode(root, "clusternodes");
- if (!cnodes)
- return NULL;
-
- for (cur_node = cnodes->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
- {
- xmlChar *idstring = xmlGetProp(cur_node, BAD_CAST "nodeid");
- if (idstring && atoi((char *)idstring) == nodeid)
- return cur_node;
- }
- }
- return NULL;
-}
-
-
-/* Get the multicast address node.
- */
-static xmlNode *find_multicast_addr(xmlNode *clusternodes)
-{
- xmlNode *clnode = findnode(clusternodes, "cman");
- if (clnode)
- {
- xmlNode *mcast = findnode(clnode, "multicast");
- return mcast;
- }
- return NULL;
-}
-
-static xmlNode *find_node(xmlNode *clusternodes, char *nodename)
-{
- xmlNode *cur_node;
-
- for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
- {
- xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
- if (strcmp((char *)name, nodename) == 0)
- return cur_node;
- }
- }
- return NULL;
-}
-
-/* Print name=value pairs for a (n XML) node.
- * "ignore" is a string to ignore if present as a property (probably already printed on the main line)
- */
-static int print_properties(xmlNode *node, char *prefix, char *ignore, char *ignore2)
-{
- xmlAttr *attr;
- int done_prefix = 0;
-
- for (attr = node->properties; attr; attr = attr->next)
- {
- /* Don't print "name=" */
- if (strcmp((char *)attr->name, "name") &&
- strcmp((char *)attr->name, ignore) &&
- strcmp((char *)attr->name, ignore2)
- )
- {
- if (!done_prefix)
- {
- done_prefix = 1;
- printf("%s", prefix);
- }
- printf(" %s=%s", attr->name, xmlGetProp(node, attr->name));
- }
- }
- if (done_prefix)
- printf("\n");
- return done_prefix;
-}
-
-/* Add name=value pairs from the commandline as properties to a node */
-static void add_fence_args(xmlNode *fencenode, int argc, char **argv, int optind)
-{
- int i;
-
- for (i = optind; i<argc; i++)
- {
- char *prop;
- char *value;
- char *equals;
-
- prop = strdup(argv[i]);
- equals = strchr(prop, '=');
- if (!equals)
- die("option '%s' is not opt=value pair\n", prop);
-
- value = equals+1;
- *equals = '\0';
-
- /* "name" is used for the fence type itself, so this is just
- * to protect the user from their own stupidity
- */
- if (strcmp(prop, "name") == 0)
- die("Can't use \"name\" as a fence argument name\n");
-
- xmlSetProp(fencenode, BAD_CAST prop, BAD_CAST value);
- free(prop);
- }
-}
-
-static void add_clusternode(xmlNode *root_element, struct option_info *ninfo,
- int argc, char **argv, int optind)
-{
- xmlNode *clusternodes;
- xmlNode *newnode;
-
- xmlNode *newfence;
- xmlNode *newfencemethod;
- xmlNode *newfencedevice;
-
- clusternodes = findnode(root_element, "clusternodes");
- if (!clusternodes)
- die("Can't find \"clusternodes\" in %s\n", ninfo->configfile);
-
- /* Don't allow duplicate node names */
- if (find_node(clusternodes, ninfo->name))
- die("node %s already exists in %s\n", ninfo->name, ninfo->configfile);
-
- /* Check for duplicate node ID */
- if (!ninfo->nodeid)
- die("nodeid not specified\n");
-
- if (get_by_nodeid(root_element, atoi((char *)ninfo->nodeid)))
- die("nodeid %s already in use\n", ninfo->nodeid);
-
- /* Don't allow random fence types */
- if (!valid_fence_type(root_element, ninfo->fence_type))
- die("fence type '%s' not known\n", ninfo->fence_type);
-
- /* Add the new node */
- newnode = xmlNewNode(NULL, BAD_CAST "clusternode");
- xmlSetProp(newnode, BAD_CAST "name", BAD_CAST ninfo->name);
- xmlSetProp(newnode, BAD_CAST "votes", BAD_CAST ninfo->votes);
- xmlSetProp(newnode, BAD_CAST "nodeid", BAD_CAST ninfo->nodeid);
- xmlAddChild(clusternodes, newnode);
-
- if (ninfo->altname)
- {
- xmlNode *altnode;
-
- altnode = xmlNewNode(NULL, BAD_CAST "altname");
- xmlSetProp(altnode, BAD_CAST "name", BAD_CAST ninfo->altname);
- xmlAddChild(newnode, altnode);
- }
-
- /* Add the fence attributes */
- newfence = xmlNewNode(NULL, BAD_CAST "fence");
- newfencemethod = xmlNewNode(NULL, BAD_CAST "method");
- xmlSetProp(newfencemethod, BAD_CAST "name", BAD_CAST "single");
-
- newfencedevice = xmlNewNode(NULL, BAD_CAST "device");
- xmlSetProp(newfencedevice, BAD_CAST "name", BAD_CAST ninfo->fence_type);
-
- /* Add name=value options */
- add_fence_args(newfencedevice, argc, argv, optind+1);
-
- xmlAddChild(newnode, newfence);
- xmlAddChild(newfence, newfencemethod);
- xmlAddChild(newfencemethod, newfencedevice);
-}
-
-static xmlDoc *open_configfile(struct option_info *ninfo)
-{
- xmlDoc *doc;
-
- /* Init libxml */
- xmlInitParser();
- LIBXML_TEST_VERSION;
-
- if (!ninfo->configfile)
- ninfo->configfile = DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE;
- if (!ninfo->outputfile)
- ninfo->outputfile = ninfo->configfile;
-
- /* Load XML document */
- doc = xmlParseFile(ninfo->configfile);
- if (doc == NULL)
- die("Error: unable to parse requested configuration file\n");
-
- return doc;
-
-}
-
-static void del_clusternode(xmlNode *root_element, struct option_info *ninfo)
-{
- xmlNode *clusternodes;
- xmlNode *oldnode;
-
- clusternodes = findnode(root_element, "clusternodes");
- if (!clusternodes)
- {
- fprintf(stderr, "Can't find \"clusternodes\" in %s\n", ninfo->configfile);
- exit(1);
- }
-
- oldnode = find_node(clusternodes, ninfo->name);
- if (!oldnode)
- {
- fprintf(stderr, "node %s does not exist in %s\n", ninfo->name, ninfo->configfile);
- exit(1);
- }
-
- xmlUnlinkNode(oldnode);
-}
-
-struct option addnode_options[] =
-{
- { "votes", required_argument, NULL, 'v'},
- { "nodeid", required_argument, NULL, 'n'},
- { "altname", required_argument, NULL, 'a'},
- { "fence_type", required_argument, NULL, 'f'},
- { "outputfile", required_argument, NULL, 'o'},
- { "configfile", required_argument, NULL, 'c'},
- { "no_ccs", no_argument, NULL, 'C'},
- { "force_ccs", no_argument, NULL, 'F'},
- { NULL, 0, NULL, 0 },
-};
-
-struct option delnode_options[] =
-{
- { "outputfile", required_argument, NULL, 'o'},
- { "configfile", required_argument, NULL, 'c'},
- { "no_ccs", no_argument, NULL, 'C'},
- { "force_ccs", no_argument, NULL, 'F'},
- { NULL, 0, NULL, 0 },
-};
-
-struct option addfence_options[] =
-{
- { "outputfile", required_argument, NULL, 'o'},
- { "configfile", required_argument, NULL, 'c'},
- { "no_ccs", no_argument, NULL, 'C'},
- { "force_ccs", no_argument, NULL, 'F'},
- { NULL, 0, NULL, 0 },
-};
-
-struct option addnodeid_options[] =
-{
- { "outputfile", required_argument, NULL, 'o'},
- { "configfile", required_argument, NULL, 'c'},
- { "multicast", required_argument, NULL, 'm'},
- { "nodeid", no_argument, NULL, 'n'},
- { "verbose", no_argument, NULL, 'v'},
- { NULL, 0, NULL, 0 },
-};
-
-struct option list_options[] =
-{
- { "configfile", required_argument, NULL, 'c'},
- { "verbose", no_argument, NULL, 'v'},
- { NULL, 0, NULL, 0 },
-};
-
-
-static int next_nodeid(int startid, int *nodeids, int nodecount)
-{
- int i;
- int nextid = startid;
-
-retry:
- for (i=0; i<nodecount; i++)
- {
- if (nodeids[i] == nextid)
- {
- nextid++;
- goto retry;
- }
- }
-
- return nextid;
-}
-
-void add_nodeids(int argc, char **argv)
-{
- struct option_info ninfo;
- unsigned char *nodenames[MAX_NODES];
- xmlDoc *doc;
- xmlNode *root_element;
- xmlNode *clusternodes;
- xmlNode *cur_node;
- xmlNode *mcast;
- int verbose = 0;
- int opt;
- int i;
- int nodenumbers[MAX_NODES];
- int nodeidx;
- int totalnodes;
- int nextid;
-
- memset(nodenames, 0, sizeof(nodenames));
- memset(nodenumbers, 0, sizeof(nodenumbers));
- memset(&ninfo, 0, sizeof(ninfo));
- ninfo.nodeid = "1";
-
- while ( (opt = getopt_long(argc, argv, "n:o:c:m:vh?", addnodeid_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'n':
- validate_int_arg(opt, optarg);
- ninfo.nodeid = strdup(optarg);
- break;
-
- case 'c':
- ninfo.configfile = strdup(optarg);
- break;
-
- case 'o':
- ninfo.outputfile = strdup(optarg);
- break;
-
- case 'm':
- if (!valid_mcast_addr(optarg)) {
- fprintf(stderr, "%s is not a valid multicast address\n", optarg);
- return;
- }
- ninfo.mcast_addr = strdup(optarg);
- break;
-
- case 'v':
- verbose++;
- break;
-
- case '?':
- default:
- addnodeid_usage(argv[0]);
- }
- }
-
- doc = open_configfile(&ninfo);
-
- root_element = xmlDocGetRootElement(doc);
-
- increment_version(root_element);
-
- /* Warn if the cluster doesn't have a multicast address */
- mcast = find_multicast_addr(root_element);
- if (!mcast & !ninfo.mcast_addr) {
- fprintf(stderr, "\nWARNING: The cluster does not have a multicast address.\n");
- fprintf(stderr, "A default will be assigned a run-time which might not suit your installation\n\n");
- }
-
- if (ninfo.mcast_addr) {
- if (!mcast) {
- xmlNode *cman = xmlNewNode(NULL, BAD_CAST "cman");
- mcast = xmlNewNode(NULL, BAD_CAST "multicast");
-
- xmlAddChild(cman, mcast);
- xmlAddChild(root_element, cman);
- }
- xmlSetProp(mcast, BAD_CAST "addr", BAD_CAST ninfo.mcast_addr);
- }
-
- /* Get a list of nodes that /do/ have nodeids so we don't generate
- any duplicates */
- nodeidx=0;
- clusternodes = findnode(root_element, "clusternodes");
- if (!clusternodes)
- die("Can't find \"clusternodes\" in %s\n", ninfo.configfile);
-
-
- for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
- {
- xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
- xmlChar *nodeid = xmlGetProp(cur_node, BAD_CAST "nodeid");
- nodenames[nodeidx] = name;
- if (nodeid)
- nodenumbers[nodeidx] = atoi((char*)nodeid);
- nodeidx++;
- }
- }
- totalnodes = nodeidx;
-
- /* Loop round nodes adding nodeIDs where they don't exist. */
- nextid = next_nodeid(atoi(ninfo.nodeid), nodenumbers, totalnodes);
- for (i=0; i<totalnodes; i++)
- {
- if (nodenumbers[i] == 0)
- {
- nodenumbers[i] = nextid;
- nextid = next_nodeid(nextid, nodenumbers, totalnodes);
- if (verbose)
- fprintf(stderr, "Node %s now has id %d\n", nodenames[i], nodenumbers[i]);
- }
- }
-
- /* Now write them into the tree */
- nodeidx = 0;
- for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
- {
- char tmp[80];
- xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
-
- assert(strcmp((char*)nodenames[nodeidx], (char*)name) == 0);
-
- sprintf(tmp, "%d", nodenumbers[nodeidx]);
- xmlSetProp(cur_node, BAD_CAST "nodeid", BAD_CAST tmp);
- nodeidx++;
- }
- }
-
-
- /* Write it out */
- save_file(doc, &ninfo);
-
- /* Shutdown libxml */
- xmlCleanupParser();
-}
-
-void add_node(int argc, char **argv)
-{
- struct option_info ninfo;
- int opt;
- xmlDoc *doc;
- xmlNode *root_element;
-
- memset(&ninfo, 0, sizeof(ninfo));
- ninfo.tell_ccsd = 1;
- ninfo.votes = "1";
-
- while ( (opt = getopt_long(argc, argv, "v:n:a:f:o:c:CFh?", addnode_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'v':
- validate_int_arg(opt, optarg);
- ninfo.votes = optarg;
- break;
-
- case 'n':
- validate_int_arg(opt, optarg);
- ninfo.nodeid = optarg;
- break;
-
- case 'a':
- ninfo.altname = strdup(optarg);
- break;
-
- case 'f':
- ninfo.fence_type = strdup(optarg);
- break;
-
- case 'c':
- ninfo.configfile = strdup(optarg);
- break;
-
- case 'o':
- ninfo.outputfile = strdup(optarg);
- break;
-
- case 'C':
- ninfo.tell_ccsd = 0;
- break;
-
- case 'F':
- ninfo.force_ccsd = 1;
- break;
-
- case '?':
- default:
- addnode_usage(argv[0]);
- }
- }
-
- /* Get node name parameter */
- if (optind < argc)
- ninfo.name = strdup(argv[optind]);
- else
- addnode_usage(argv[0]);
-
- if (!ninfo.fence_type)
- addnode_usage(argv[0]);
-
-
- doc = open_configfile(&ninfo);
-
- root_element = xmlDocGetRootElement(doc);
-
- increment_version(root_element);
-
- add_clusternode(root_element, &ninfo, argc, argv, optind);
-
- /* Write it out */
- save_file(doc, &ninfo);
- /* Shutdown libxml */
- xmlCleanupParser();
-
-}
-
-void del_node(int argc, char **argv)
-{
- struct option_info ninfo;
- int opt;
- xmlDoc *doc;
- xmlNode *root_element;
-
- memset(&ninfo, 0, sizeof(ninfo));
- ninfo.tell_ccsd = 1;
-
- while ( (opt = getopt_long(argc, argv, "o:c:CFh?", delnode_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'c':
- ninfo.configfile = strdup(optarg);
- break;
-
- case 'o':
- ninfo.outputfile = strdup(optarg);
- break;
-
- case 'C':
- ninfo.tell_ccsd = 0;
- break;
-
- case 'F':
- ninfo.force_ccsd = 1;
- break;
-
- case '?':
- default:
- delnode_usage(argv[0]);
- }
- }
-
- /* Get node name parameter */
- if (optind < argc)
- ninfo.name = strdup(argv[optind]);
- else
- delnode_usage(argv[0]);
-
- doc = open_configfile(&ninfo);
-
- root_element = xmlDocGetRootElement(doc);
-
- increment_version(root_element);
-
- del_clusternode(root_element, &ninfo);
-
- /* Write it out */
- save_file(doc, &ninfo);
-}
-
-void list_nodes(int argc, char **argv)
-{
- xmlNode *cur_node;
- xmlNode *root_element;
- xmlNode *clusternodes;
- xmlNode *fencenode = NULL;
- xmlDocPtr doc;
- xmlNode *mcast;
- struct option_info ninfo;
- int opt;
- int verbose = 0;
-
- memset(&ninfo, 0, sizeof(ninfo));
-
- while ( (opt = getopt_long(argc, argv, "c:vh?", list_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'c':
- ninfo.configfile = strdup(optarg);
- break;
- case 'v':
- verbose++;
- break;
- case '?':
- default:
- list_usage(argv[0]);
- }
- }
- doc = open_configfile(&ninfo);
-
- root_element = xmlDocGetRootElement(doc);
-
-
- printf("\nCluster name: %s, config_version: %s\n\n",
- (char *)cluster_name(root_element),
- (char *)find_version(root_element));
-
- clusternodes = findnode(root_element, "clusternodes");
- if (!clusternodes)
- die("Can't find \"clusternodes\" in %s\n", ninfo.configfile);
-
- mcast = find_multicast_addr(root_element);
- if (mcast)
- printf("Multicast address for cluster: %s\n\n", xmlGetProp(mcast, BAD_CAST "addr"));
-
- printf("Nodename Votes Nodeid Fencetype\n");
- for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
- {
- xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
- xmlChar *votes = xmlGetProp(cur_node, BAD_CAST "votes");
- xmlChar *nodeid = xmlGetProp(cur_node, BAD_CAST "nodeid");
- xmlChar *ftype = get_fence_type(cur_node, &fencenode);
-
- if (!nodeid)
- nodeid=(unsigned char *)"0";
- if (!votes)
- votes = (unsigned char *)"1";
-
- printf("%-32s %3d %3d %s\n", name, atoi((char *)votes),
- atoi((char *)nodeid),
- ftype?ftype:(xmlChar *)"");
- if (verbose)
- {
- xmlNode *a = findnode(cur_node, "altname");
- if (a)
- {
- printf(" altname %s=%s", "name", xmlGetProp(a, BAD_CAST "name"));
- if (!print_properties(a, "","",""))
- printf("\n");
- }
- print_properties(cur_node, " Node properties: ", "votes", "nodeid");
- print_properties(fencenode, " Fence properties: ", "agent", "");
- }
-
- }
- }
-}
-
-void create_skeleton(int argc, char **argv)
-{
- xmlNode *root_element;
- xmlNode *fencedevices;
- xmlNode *clusternodes;
- xmlNode *rm;
- xmlNode *rm1;
- xmlNode *rm2;
- xmlDocPtr doc;
- char *clustername;
- struct option_info ninfo;
- struct stat st;
- int twonode = 0;
- int opt;
-
- memset(&ninfo, 0, sizeof(ninfo));
-
- while ( (opt = getopt_long(argc, argv, "c:2h?", list_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'c':
- ninfo.outputfile = strdup(optarg);
- break;
-
- case '2':
- twonode = 1;
- break;
-
- case '?':
- default:
- create_usage(argv[0]);
- }
- }
- if (!ninfo.outputfile)
- ninfo.outputfile = DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE;
- ninfo.configfile = "-";
-
- if (argc - optind < 1)
- create_usage(argv[0]);
-
- clustername = argv[optind];
-
- if (stat(ninfo.outputfile, &st) == 0)
- die("%s already exists", ninfo.outputfile);
-
- /* Init libxml */
- xmlInitParser();
- LIBXML_TEST_VERSION;
-
- doc = xmlNewDoc(BAD_CAST "1.0");
- root_element = xmlNewNode(NULL, BAD_CAST "cluster");
- xmlDocSetRootElement(doc, root_element);
-
- xmlSetProp(root_element, BAD_CAST "name", BAD_CAST clustername);
- xmlSetProp(root_element, BAD_CAST "config_version", BAD_CAST "1");
-
- /* Generate extra bits for a 2node cman cluster */
- if (twonode) {
-
- xmlNode *cman = xmlNewNode(NULL, BAD_CAST "cman");
- xmlSetProp(cman, BAD_CAST "two_node", BAD_CAST "1");
- xmlSetProp(cman, BAD_CAST "expected_votes", BAD_CAST "1");
- xmlAddChild(root_element, cman);
- }
-
- clusternodes = xmlNewNode(NULL, BAD_CAST "clusternodes");
- fencedevices = xmlNewNode(NULL, BAD_CAST "fencedevices");
- rm = xmlNewNode(NULL, BAD_CAST "rm");
- rm1 = xmlNewNode(NULL, BAD_CAST "failoverdomains");
-
- xmlAddChild(root_element, clusternodes);
- xmlAddChild(root_element, fencedevices);
- xmlAddChild(root_element, rm);
-
- /* Create empty resource manager sections to keep GUI happy */
- rm2 = xmlNewNode(NULL, BAD_CAST "resources");
- xmlAddChild(rm, rm1);
- xmlAddChild(rm, rm2);
-
- save_file(doc, &ninfo);
-
-}
-
-void add_fence(int argc, char **argv)
-{
- xmlNode *root_element;
- xmlNode *fencedevices;
- xmlNode *fencenode = NULL;
- xmlDocPtr doc;
- char *fencename;
- char *agentname;
- struct option_info ninfo;
- int opt;
-
- memset(&ninfo, 0, sizeof(ninfo));
- ninfo.tell_ccsd = 1;
-
- while ( (opt = getopt_long(argc, argv, "c:o:CFh?", list_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'c':
- ninfo.configfile = strdup(optarg);
- break;
- case 'o':
- ninfo.outputfile = strdup(optarg);
- break;
-
- case 'C':
- ninfo.tell_ccsd = 0;
- break;
-
- case 'F':
- ninfo.force_ccsd = 1;
- break;
-
- case '?':
- default:
- addfence_usage(argv[0]);
- }
- }
-
- if (argc - optind < 2)
- addfence_usage(argv[0]);
-
- doc = open_configfile(&ninfo);
- root_element = xmlDocGetRootElement(doc);
-
- increment_version(root_element);
-
- fencedevices = findnode(root_element, "fencedevices");
- if (!fencedevices)
- die("Can't find \"fencedevices\" %s\n", ninfo.configfile);
-
- /* First param is the fence name - check it doesn't already exist */
- fencename = argv[optind++];
-
- if (valid_fence_type(root_element, fencename))
- die("fence type %s already exists\n", fencename);
-
- agentname = argv[optind++];
-
- /* Add it */
- fencenode = xmlNewNode(NULL, BAD_CAST "fencedevice");
- xmlSetProp(fencenode, BAD_CAST "name", BAD_CAST fencename);
- xmlSetProp(fencenode, BAD_CAST "agent", BAD_CAST agentname);
-
- /* Add name=value options */
- add_fence_args(fencenode, argc, argv, optind);
-
- xmlAddChild(fencedevices, fencenode);
-
- save_file(doc, &ninfo);
-}
-
-void del_fence(int argc, char **argv)
-{
- xmlNode *root_element;
- xmlNode *fencedevices;
- xmlNode *fencenode;
- xmlDocPtr doc;
- char *fencename;
- struct option_info ninfo;
- int opt;
-
- memset(&ninfo, 0, sizeof(ninfo));
- ninfo.tell_ccsd = 1;
-
- while ( (opt = getopt_long(argc, argv, "c:o:CFhv?", list_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'c':
- ninfo.configfile = strdup(optarg);
- break;
- case 'o':
- ninfo.outputfile = strdup(optarg);
- break;
-
- case 'C':
- ninfo.tell_ccsd = 0;
- break;
-
- case 'F':
- ninfo.force_ccsd = 1;
- break;
-
- case '?':
- default:
- delfence_usage(argv[0]);
- }
- }
-
- if (argc - optind < 1)
- delfence_usage(argv[0]);
-
- fencename = argv[optind];
-
- doc = open_configfile(&ninfo);
- root_element = xmlDocGetRootElement(doc);
- increment_version(root_element);
-
- fencedevices = findnode(root_element, "fencedevices");
- if (!fencedevices)
- die("Can't find \"fencedevices\" in %s\n", ninfo.configfile);
-
- fencenode = valid_fence_type(root_element, fencename);
- if (!fencenode)
- die("fence type %s does not exist\n", fencename);
-
- xmlUnlinkNode(fencenode);
-
- save_file(doc, &ninfo);
-}
-
-void list_fences(int argc, char **argv)
-{
- xmlNode *cur_node;
- xmlNode *root_element;
- xmlNode *fencedevices;
- xmlDocPtr doc;
- struct option_info ninfo;
- int opt;
- int verbose=0;
-
- memset(&ninfo, 0, sizeof(ninfo));
-
- while ( (opt = getopt_long(argc, argv, "c:hv?", list_options, NULL)) != EOF)
- {
- switch(opt)
- {
- case 'c':
- ninfo.configfile = strdup(optarg);
- break;
- case 'v':
- verbose++;
- break;
- case '?':
- default:
- list_usage(argv[0]);
- }
- }
- doc = open_configfile(&ninfo);
- root_element = xmlDocGetRootElement(doc);
-
- fencedevices = findnode(root_element, "fencedevices");
- if (!fencedevices)
- die("Can't find \"fencedevices\" in %s\n", ninfo.configfile);
-
-
- printf("Name Agent\n");
- for (cur_node = fencedevices->children; cur_node; cur_node = cur_node->next)
- {
- if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "fencedevice") == 0)
- {
- xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
- xmlChar *agent = xmlGetProp(cur_node, BAD_CAST "agent");
-
- printf("%-16s %s\n", name, agent);
- if (verbose)
- print_properties(cur_node, " Properties: ", "agent", "");
- }
- }
-}
-
diff --git a/ccs/ccs_tool/editconf.h b/ccs/ccs_tool/editconf.h
deleted file mode 100644
index 1847e2c..0000000
--- a/ccs/ccs_tool/editconf.h
+++ /dev/null
@@ -1,8 +0,0 @@
-void add_node(int argc, char **argv);
-void add_nodeids(int argc, char **argv);
-void add_fence(int argc, char **argv);
-void del_node(int argc, char **argv);
-void del_fence(int argc, char **argv);
-void list_nodes(int argc, char **argv);
-void list_fences(int argc, char **argv);
-void create_skeleton(int argc, char **argv);
diff --git a/ccs/ccs_tool/update.c b/ccs/ccs_tool/update.c
deleted file mode 100644
index d8c1308..0000000
--- a/ccs/ccs_tool/update.c
+++ /dev/null
@@ -1,673 +0,0 @@
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-
-#include "comm_headers.h"
-#include "libccscompat.h"
-#include "libcman.h"
-
-typedef struct member_list {
- int count;
- int pad;
- cman_node_t *nodes;
-} member_list_t;
-
-static member_list_t *get_member_list(cman_handle_t handle);
-static void free_member_list(member_list_t *list);
-
-static int select_retry(int max_fd, fd_set *rfds, fd_set *wfds, fd_set *xfds,
- struct timeval *timeout);
-
-static ssize_t read_retry(int fd, void *buf, int count, struct timeval *timeout);
-
-static int ccs_open(cman_node_t node, uint16_t baseport, int timeout);
-static int ipv4_connect(struct in_addr *addr, uint16_t port, int timeout);
-static int ipv6_connect(struct in6_addr *addr, uint16_t port, int timeout);
-static int connect_nb(int fd, struct sockaddr *addr, socklen_t len, int timeout);
-
-extern int globalverbose;
-
-int cluster_base_port = 50008;
-
-static int get_doc_version(xmlDocPtr ldoc)
-{
- int i;
- int error = 0;
-
- xmlXPathObjectPtr obj = NULL;
- xmlXPathContextPtr ctx = NULL;
- xmlNodePtr node = NULL;
-
- ctx = xmlXPathNewContext(ldoc);
-
- if (!ctx) {
- fprintf(stderr, "Unable to create new XPath context.\n");
- error = -EIO; /* ATTENTION -- what should this be? */
- goto fail;
- }
-
- obj = xmlXPathEvalExpression((xmlChar *)"/cluster/@config_version", ctx);
-
- if (!obj || !obj->nodesetval || (obj->nodesetval->nodeNr != 1)) {
- fprintf(stderr, "Error while retrieving config_version.\n");
- error = -ENODATA;
- goto fail;
- }
-
- node = obj->nodesetval->nodeTab[0];
-
- if (node->type != XML_ATTRIBUTE_NODE) {
- fprintf(stderr, "Object returned is not of attribute type.\n");
- error = -ENODATA;
- goto fail;
- }
-
- if (!node->children->content || !strlen((char *)node->children->content)) {
- error = -ENODATA;
- goto fail;
- }
-
- for (i = 0; i < strlen((char *)node->children->content); i++) {
- if (!isdigit(node->children->content[i])) {
- fprintf(stderr, "config_version is not a valid integer.\n");
- error = -EINVAL;
- goto fail;
- }
- }
-
- error = atoi((char *)node->children->content);
-
-fail:
-
- if (ctx) {
- xmlXPathFreeContext(ctx);
- }
-
- if (obj) {
- xmlXPathFreeObject(obj);
- }
-
- return error;
-}
-
-
-int update(char *location)
-{
- int error = 0;
- int i, fd;
- int cluster_fd = -1;
- char true_location[256];
- xmlDocPtr doc = NULL;
- xmlChar *mem_doc;
- int doc_size = 0;
- char *buffer = NULL;
- comm_header_t *ch = NULL, rch;
- member_list_t *members = NULL;
- cman_handle_t handle = NULL;
- int desc;
- char *v1_str,*v3_str;
- int v1, v2, v3;
-
- struct timeval tv;
-
- if (location[0] != '/') {
- memset(true_location, 0, 256);
- if (!getcwd(true_location, 256)) {
- fprintf(stderr, "Unable to get the current working directory.\n");
- return -errno;
- }
- true_location[strlen(true_location)] = '/';
- strncpy(true_location+strlen(true_location), location, 256-strlen(true_location));
- } else {
- strncpy(true_location, location, 256);
- }
-
- desc = ccs_connect();
-
- if (desc < 0) {
- fprintf(stderr, "Unable to connect to the CCS daemon: %s\n", strerror(-desc));
- return desc;
- }
-
- if ((error = ccs_get(desc, "/cluster/@config_version", &v1_str))) {
- fprintf(stderr, "Unable to get current config_version: %s\n", strerror(-error));
- ccs_disconnect(desc);
- return error;
- }
-
- ccs_disconnect(desc);
-
- for (i = 0; i < strlen(v1_str); i++) {
- if (!isdigit(v1_str[i])) {
- fprintf(stderr, "config_version is not a valid integer.\n");
- free(v1_str);
- return -EINVAL;
- }
- }
-
- v1 = atoi(v1_str);
- free(v1_str);
-
- doc = xmlParseFile(true_location);
-
- if (!doc) {
- fprintf(stderr, "Unable to parse %s\n", true_location);
- return -EINVAL;
- }
-
- v2 = get_doc_version(doc);
-
- if (v2 < 0) {
- fprintf(stderr, "Unable to get the config_version from %s\n", location);
- xmlFreeDoc(doc);
- return -EINVAL;
- }
-
- if (v2 <= v1) {
- fprintf(stderr,
- "Proposed updated config file does not have greater version number.\n"
- " Current config_version :: %d\n"
- " Proposed config_version:: %d\n", v1, v2);
- xmlFreeDoc(doc);
- return -EINVAL;
- }
-
- xmlDocDumpFormatMemory(doc, &mem_doc, &doc_size, 0);
-
- if (!mem_doc) {
- fprintf(stderr, "Unable to allocate memory for update document.\n");
- xmlFreeDoc(doc);
- return -ENOMEM;
- }
-
- xmlFreeDoc(doc);
-
- buffer = malloc(doc_size + sizeof(comm_header_t));
-
- if (!buffer) {
- fprintf(stderr, "Unable to allocate memory for transfer buffer.\n");
- free(mem_doc);
- return -ENOMEM;
- }
-
- memset(buffer, 0, (doc_size + sizeof(comm_header_t)));
- ch = (comm_header_t *)buffer;
-
- memcpy(buffer+sizeof(comm_header_t), mem_doc, doc_size);
- free(mem_doc);
-
- ch->comm_type = COMM_UPDATE;
- ch->comm_flags= COMM_UPDATE_NOTICE;
- ch->comm_payload_size = doc_size;
-
- handle = cman_admin_init(NULL);
-
- cluster_fd = cman_get_fd(handle);
-
- /* Should we test the cman handle of file descriptor to determine connectivity? */
-
- if (cluster_fd < 0) {
- fprintf(stderr, "Unable to connect to cluster infrastructure.\n");
- return cluster_fd;
- }
-
- if (!cman_is_quorate(handle)) {
- fprintf(stderr, "Unable to honor update request. Cluster is not quorate.\n");
- return -EPERM;
- }
-
- members = get_member_list(handle);
-
- swab_header(ch);
-
- for (i = 0; i < members->count; i++) {
- if (members->nodes[i].cn_nodeid == 0)
- continue;
- if (members->nodes[i].cn_member == 0)
- continue;
-
- fd = ccs_open(members->nodes[i], cluster_base_port, 5);
-
- if (fd < 0) {
- fprintf(stderr, "Unable to open connection to %s: %s\n",
- members->nodes[i].cn_name, strerror(errno));
- free(buffer);
- free_member_list(members);
- return -errno;
- }
-
- error = write(fd, buffer, sizeof(comm_header_t) + doc_size);
-
- if (error < 0) {
- fprintf(stderr, "Unable to send msg to %s: %s\n",
- members->nodes[i].cn_name, strerror(errno));
- close(fd);
- free(buffer);
- free_member_list(members);
- return -errno;
- }
-
- tv.tv_sec = 5;
- tv.tv_usec = 0;
-
- error = read_retry(fd, &rch, sizeof(comm_header_t), &tv);
-
- swab_header(&rch);
-
- if (error < 0) {
- fprintf(stderr, "Failed to receive COMM_UPDATE_NOTICE_ACK from %s.\n",
- members->nodes[i].cn_name);
- fprintf(stderr, "Hint: Check the log on %s for reason.\n",
- members->nodes[i].cn_name);
- close(fd);
- free(buffer);
- free_member_list(members);
- return -errno;
- }
-
- close(fd);
- }
-
- swab_header(ch);
-
- ch->comm_flags = COMM_UPDATE_COMMIT;
-
- swab_header(ch);
-
- for (i=0; i < members->count; i++) {
- if (members->nodes[i].cn_nodeid == 0)
- continue;
- if (members->nodes[i].cn_member == 0)
- continue;
-
- fd = ccs_open(members->nodes[i], cluster_base_port, 5);
- if(fd < 0){
- fprintf(stderr, "Unable to open connection to %s: %s\n",
- members->nodes[i].cn_name, strerror(errno));
- free(buffer);
- free_member_list(members);
- return -errno;
- }
-
- error = write(fd, buffer, sizeof(comm_header_t));
-
- if (error < 0) {
- fprintf(stderr, "Unable to send msg to %s: %s\n",
- members->nodes[i].cn_name, strerror(errno));
- close(fd);
- free(buffer);
- free_member_list(members);
- return -errno;
- }
-
- tv.tv_sec = 5;
- tv.tv_usec = 0;
-
- error = read_retry(fd, &rch, sizeof(comm_header_t), &tv);
-
- swab_header(&rch);
-
- if (error < 0) {
- fprintf(stderr, "Failed to receive COMM_UPDATE_COMMIT_ACK from %s.\n",
- members->nodes[i].cn_name);
- fprintf(stderr, "Hint: Check the log on %s for reason.\n",
- members->nodes[i].cn_name);
- close(fd);
- free(buffer);
- free_member_list(members);
- return -errno;
- }
-
- close(fd);
- error = 0;
- }
-
- free(buffer);
- free_member_list(members);
-
- /* If we can't connect here, it doesn't mean the update failed **
- ** It means that we simply can't report the change in version */
- desc = ccs_connect();
-
- if (desc < 0) {
- fprintf(stderr, "Unable to connect to the CCS daemon: %s\n", strerror(-desc));
- return 0;
- }
-
- if ((error = ccs_get(desc, "/cluster/@config_version", &v3_str))) {
- ccs_disconnect(desc);
- return 0;
- }
-
- v3 = atoi(v3_str);
- free(v3_str);
-
- ccs_disconnect(desc);
-
- if (v2 == v3) {
- cman_version_t cman_ver;
- printf("Config file updated from version %d to %d\n", v1, v2);
- cman_get_version(handle, &cman_ver);
- cman_ver.cv_config = v2;
- if (cman_set_version(handle, &cman_ver)) {
- perror("Failed to tell cman of new version number");
- }
- } else {
- fprintf(stderr, "Warning:: Simultaneous update requests detected.\n"
- " You have lost the race.\n"
- " Old config version :: %d\n"
- " Proposed config version :: %d\n"
- " Winning config version :: %d\n\n"
- "Check " DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE " to ensure it contains the desired contents.\n", v1, v2, v3);
- return -EAGAIN;
- }
-
- return 0;
-}
-
-
-static member_list_t *get_member_list(cman_handle_t handle)
-{
- int count = 0;
-
- member_list_t *list = NULL;
- cman_node_t *nodes = NULL;
-
- do
- {
-
- if (nodes != NULL) {
- free(nodes);
- }
-
- count = cman_get_node_count(handle);
-
- if (count <= 0) {
- return NULL;
- }
-
- if (list == NULL) {
- list = malloc(sizeof(*list));
- }
-
- if (list == NULL) {
- return NULL;
- }
-
- nodes = malloc(sizeof(*nodes) * count);
-
- if (nodes == NULL) {
- free(list);
- return NULL;
- }
-
- memset(list, 0, sizeof(*list));
- memset(nodes, 0, sizeof(*nodes) * count);
-
- cman_get_nodes(handle, count, &list->count, nodes);
-
- } while (list->count != count);
-
- list->count = count;
- list->nodes = nodes;
-
- return list;
-}
-
-
-static void free_member_list(member_list_t *list)
-{
- if (list != NULL) {
- if (list->nodes != NULL) {
- free(list->nodes);
- }
- free(list);
- }
-}
-
-
-static int select_retry(int max_fd, fd_set *rfds, fd_set *wfds, fd_set *xfds,
- struct timeval *timeout)
-{
- int rv;
-
- while (1) {
- rv = select(max_fd, rfds, wfds, xfds, timeout);
- if ((rv == -1) && (errno == EINTR)) {
- /* return on EBADF/EINVAL/ENOMEM; continue on EINTR */
- continue;
- }
- return rv;
- }
-}
-
-
-static ssize_t read_retry(int fd, void *buf, int count, struct timeval *timeout)
-{
- int n, total = 0, remain = count, rv = 0;
- fd_set rfds, xfds;
-
- while (total < count)
- {
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
- FD_ZERO(&xfds);
- FD_SET(fd, &xfds);
-
- /*
- * Select on the socket, in case it closes while we're not
- * looking...
- */
- rv = select_retry(fd + 1, &rfds, NULL, &xfds, timeout);
- if (rv == -1) {
- return -1;
- }
- else if (rv == 0) {
- errno = ETIMEDOUT;
- return -1;
- }
-
- if (FD_ISSET(fd, &xfds)) {
- errno = EPIPE;
- return -1;
- }
-
- /*
- * Attempt to read off the socket
- */
- n = read(fd, buf + (off_t) total, remain);
-
- /*
- * When we know our socket was select()ed and we receive 0 bytes
- * when we read, the socket was closed.
- */
- if ((n == 0) && (rv == 1)) {
- errno = EPIPE;
- return -1;
- }
-
- if (n == -1) {
- if ((errno == EAGAIN) || (errno == EINTR)) {
- /*
- * Not ready? Wait for data to become available
- */
- continue;
- }
-
- /* Other errors: EPIPE, EINVAL, etc */
- return -1;
- }
-
- total += n;
- remain -= n;
- }
-
- return total;
-}
-
-
-static int ccs_open(cman_node_t node, uint16_t port, int timeout)
-{
- struct in_addr *addr;
- struct in6_addr *addr6;
- int fd, family;
- char buf[INET6_ADDRSTRLEN];
-
- if (globalverbose) {
- memset(buf, 0, sizeof(buf));
- printf("Processing node: %s\n", node.cn_name);
- }
-
- family = ((struct sockaddr *)&(node.cn_address.cna_address))->sa_family;
-
- if (family == AF_INET6) {
-
- addr6 = &(((struct sockaddr_in6 *)&(node.cn_address.cna_address))->sin6_addr);
-
- if (globalverbose) {
- inet_ntop(family, addr6, buf, sizeof(buf));
- printf(" family: ipv6\n address: %s\n", buf);
- }
-
- if ((fd = ipv6_connect(addr6, port, timeout)) < 0) {
- return -1;
- }
-
- } else {
-
- addr = &(((struct sockaddr_in *)&(node.cn_address.cna_address))->sin_addr);
-
- if (globalverbose) {
- inet_ntop(family, addr, buf, sizeof(buf));
- printf(" family: ipv4\n address: %s\n", buf);
- }
-
- if ((fd = ipv4_connect(addr, port, timeout)) < 0) {
- return -1;
- }
-
- }
-
- return fd;
-}
-
-
-static int ipv4_connect(struct in_addr *addr, uint16_t port, int timeout)
-{
- struct sockaddr_in sin;
-
- int fd;
-
- if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
- return -1;
- }
-
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
-
- memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));
-
- if (connect_nb(fd, (struct sockaddr *)&sin, sizeof(sin), timeout) < 0) {
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-
-static int ipv6_connect(struct in6_addr *addr, uint16_t port, int timeout)
-{
- struct sockaddr_in6 sin6;
-
- int fd;
-
- if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
- return -1;
- }
-
- memset(&sin6, 0, sizeof(sin6));
-
- sin6.sin6_family = AF_INET6;
- sin6.sin6_port = htons(port);
- sin6.sin6_flowinfo = 0;
-
- memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr));
-
- if (connect_nb(fd, (struct sockaddr *)&sin6, sizeof(sin6), timeout)) {
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-
-static int connect_nb(int fd, struct sockaddr *addr, socklen_t len, int timeout)
-{
- int err;
- int ret;
- int flags = 1;
- unsigned l;
- fd_set rfds, wfds;
-
- struct timeval tv;
-
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) {
- return -1;
- }
-
- flags = fcntl(fd, F_GETFL, 0);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-
- ret = connect(fd, addr, len);
-
- if ((ret < 0) && (errno != EINPROGRESS)) {
- return -1;
- }
-
- if (ret != 0) {
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
- FD_ZERO(&wfds);
- FD_SET(fd, &wfds);
-
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
-
- if (select_retry((fd + 1), &rfds, &wfds, NULL, &tv) == 0) {
- errno = ETIMEDOUT;
- return -1;
- }
-
- if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) {
- l = sizeof(err);
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &l) < 0) {
- close(fd);
- return -1;
- }
-
- if (err != 0) {
- close(fd);
- errno = err;
- return -1;
- }
-
- fcntl(fd, F_SETFL, flags);
- return 0;
- }
- }
-
- errno = EIO;
- return -1;
-}
diff --git a/ccs/ccs_tool/update.h b/ccs/ccs_tool/update.h
deleted file mode 100644
index 2f41aa4..0000000
--- a/ccs/ccs_tool/update.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __UPDATE_DOT_H__
-#define __UPDATE_DOT_H__
-
-int update(char *location);
-
-#endif /* __UPDATE_DOT_H__ */
diff --git a/ccs/man/Makefile b/ccs/man/Makefile
index 43133a6..abedd52 100644
--- a/ccs/man/Makefile
+++ b/ccs/man/Makefile
@@ -1,12 +1,6 @@
TARGET= cluster.conf.5 \
ccs.7 \
- ccsd.8 \
- ccs_tool.8
+ ccsd.8
include ../../make/defines.mk
-
-ifdef legacy_code
-TARGET += ccs_test.8
-endif
-
include $(OBJDIR)/make/man.mk
diff --git a/ccs/man/ccs_test.8 b/ccs/man/ccs_test.8
deleted file mode 100644
index 9dce13c..0000000
--- a/ccs/man/ccs_test.8
+++ /dev/null
@@ -1,132 +0,0 @@
-.TH ccs_test 8
-
-.SH NAME
-ccs_test - CCS daemon (ccsd) diagnostic tool
-
-.SH SYNOPSIS
-.B ccs_test
-[\fBoptions\fP]
-<\fBcommand\fP>
-
-.SH DESCRIPTION
-\fBccs_test\fP is part of the Cluster Configuration System (CCS). It is a
-diagnostic tool that reads cluster.conf information to test ccsd.
-
-.SH OPTIONS
-.TP
-\fB-h\fP
-Help. Print out the usage syntax and exit.
-.TP
-\fB-V\fP
-Print the version information and exit.
-
-.SH COMMANDS
-.TP
-\fBconnect\fP \fI[force]\fP \fI[block]\fP \fI[cluster name]\fP
-This command creates a connection to ccsd. It returns a descriptor, which
-is used as an parameter to other commands.
-
-The 'force' key-word is used to establish a connection to ccsd in the
-absence of a quorate cluster manager.
-
-The 'block' key-word is used (with the 'force' key-word) to tell ccsd to
-keep broadcasting for a valid configuration file until one is found.
-
-The 'cluster name' is used (with the 'force' key-word) to specify that
-only configuration files containing the given cluster name are valid
-possibilities.
-
-.TP
-\fBget\fP \fI<desc>\fP \fI<request>\fP
-Get the results of a given request. The 'desc' is the number returned
-from the \fBconnect\fP command. The 'request' is a valid Xpath request.
-
-If 'request' results in multiple matches, the first will be returned.
-Subsequent calls with the same 'request' will result in the subsequent
-matches. Once all the matches have been returned, a subsequent call
-will begin again with the first result.
-
-.TP
-\fBget_list\fP \fI<desc>\fP \fI<request>\fP
-Similar to the \fBget\fP command. However, issuing subsequent calls
-with the same 'request' will result in all matches being returned (one
-at a time), then null, then starting over with the first result.
-
-.TP
-\fBset\fP \fI<desc>\fP \fI<path>\fP \fI<value>\fP
-Sets a particular 'path' to the given 'value'. Not yet implemented.
-
-.TP
-\fBget_state\fP \fI<desc>\fP
-Get the state associated with a given connection.
-
-.TP
-\fBset_state\fP \fI<desc>\fP \fI<ncwp>\fP
-Set the current working path (cwp) to 'ncwp' for a given connection.
-
-.SH EXAMPLES
-.SS To connect to ccsd:
-
-> ccs_test connect
-
-Connect successful.
- Connection descriptor = 0
-
-Or, if the cluster is not yet quorate and the name of the cluster is 'mycluster':
-
-> ccs_test connect force block mycluster
-
-Connect successful.
- Connection descriptor = 0
-
-.SS To get the cluster name from ccsd:
-
-> ccs_test get 0 /cluster/@name
-
-Get successful.
- Value = <mycluster>
-
-.SS To get the connection state:
-
-> ccs_test get_state 0
-
-Get state successful.
- Current working path:
- Previous query : /cluster/@name
-
-
-.SS To set the connection state:
-
-> ccs_test set_state 0 /cluster
-
-Set state successful.
-
-
-.SS After setting the connection state, note the change:
-
-> ccs_test get_state 0
-
-Get state successful.
- Current working path: /cluster
- Previous query : /cluster/@name
-
-.SS After setting the connection state, you can now query with an absolute or relative path:
-
-> ccs_test get 0 @name
-
-Get successful.
- Value = <brassow>
-
-> ccs_test get 0 /cluster/@name
-
-Get successful.
- Value = <brassow>
-
-.SS To disconnect:
-
-> ccs_test disconnect 0
-
-Disconnect successful.
-
-.SH SEE ALSO
-ccs(7), ccsd(8), cluster.conf(5)
diff --git a/ccs/man/ccs_tool.8 b/ccs/man/ccs_tool.8
deleted file mode 100644
index ef13406..0000000
--- a/ccs/man/ccs_tool.8
+++ /dev/null
@@ -1,185 +0,0 @@
-.TH "ccs_tool" "8" "" "" ""
-.SH "NAME"
-ccs_tool \- The tool used to make online updates of CCS config files.
-
-.SH "SYNOPSIS"
-.B ccs_tool
-[\fIOPTION\fR].. <\fBcommand\fP>
-
-.SH "DESCRIPTION"
-
-\fBccs_tool\fP is part of the Cluster Configuration System (CCS). It is
-used to make online updates to cluster.conf. It can also be used to
-upgrade old style (GFS <= 6.0) CCS archives to the new xml cluster.conf
-format.
-
-.SH "OPTIONS"
-.TP
-\fB\-h\fP
-Help. Print out the usage.
-.TP
-\fB\-V\fP
-Print the version information.
-
-sub\-commands have their own options, see below for more detail
-.SH "COMMANDS"
-.TP
-\fBupdate\fP \fI<xml file>\fP
-This command is used to update the config file that ccsd is working with
-while the cman cluster is operational (i.e. online). Run this on a single
-machine to update cluster.conf on all current cluster members. This also
-notifies cman of the new config version.
-
-.TP
-\fBupgrade\fP \fI<location>\fP
-This command is used to upgrade an old CCS format archive to the new
-xml format. \fI<location>\fP is the location of the old archive,
-which can be either a block device archive or a file archive. The
-converted configuration will be printed to stdout.
-
-.TP
-\fBaddnode\fP [options] \fI<node> [<fenceoption=value>]...\fP
-Adds a new node to the cluster configuration file. Fencing device options
-are specified as key=value pairs (as many as required) and are entered into the
-configuration file as is. See the documentation for your fencing agent for more
-details (eg a powerswitch fence device may need to know which port the node is
-connected to).
-.br
-\fIOptions:\fP
-.br
-\-v <votes> Number of votes for this node (mandatory)
-.br
-\-n <nodeid> Node id for this node (optional)
-.br
-\-i <interface> Network interface to use for this node. Mandatory if the cluster
-is using multicast as transport. Forbidden if not.
-.br
-\-m <multicast> Multicast address for cluster. Only allowed on the first node to
-be added to the file. Subsequent nodes will use either multicast or broadcast
-depending on the properties of the first node.
-.br
-\-f <fencedevice> Name of fence device to use for this node. The fence device
-section must already have been added to the file, probably using the addfence command.
-.br
-\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
-.br
-\-o <file> Output file. Defaults to the same as -c
-.br
-\-C Don't run "ccs_tool update" after changing file. This will
-happen by default if the input file is the same as the output file.
-.br
-\-F Force a "ccs_tool update" even if the input and output files
-are different.
-
-
-
-.TP
-\fBdelnode\fP [options] \fI<node>\fP
-Delete a node from the cluster configuration file. Note: there is no
-"edit" command so to change the properties of a node you must delete it
-and add it back in with the new properties.
-.br
-\fIOptions:\fP
-.br
-\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
-.br
-\-o <file> Output file. Defaults to the same as -c
-.br
-\-C Don't run "ccs_tool update" after changing file. This will
-happen by default if the input file is the same as the output file.
-.br
-\-F Force a "ccs_tool update" even if the input and output files
-are different.
-
-
-
-.TP
-\fBaddfence\fP [options] \fI<name> <agent> [<option>=<value>]...\fP
-Adds a new fence device section to the cluster configuration file. <agent> is the
-name of the fence agent that controls the device. the options following are entered
-as key-value pairs. See the fence agent documentation for details about these. eg:
-you may need to enter the IP address and username/password for a powerswitch fencing
-device.
-.br
-\fIOptions:\fP
-.br
-\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
-.br
-\-o <file> Output file. Defaults to the same as -c
-.br
-\-C Don't run "ccs_tool update" after changing file. This will
-happen by default if the input file is the same as the output file.
-.br
-\-F Force a "ccs_tool update" even if the input and output files
-are different.
-
-.TP
-\fBdelfence\fP [options] \fI<node>\fP
-Deletes a fencing device from the cluster configuration file.
-delfence will allow you to remove a fence device that is in use by nodes.
-This is to allow changes to be made, but be aware that it may produce an
-invalid configuration file if you don't add it back in again.
-.br
-\fIOptions:\fP
-.br
-\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
-.br
-\-o <file> Output file. Defaults to the same as -c
-.br
-\-C Don't run "ccs_tool update" after changing file. This will
-happen by default if the input file is the same as the output file.
-.br
-\-F Force a "ccs_tool update" even if the input and output files
-are different.
-
-
-.TP
-\fBlsnode [options] \fP
-List the nodes in the configuration file. This is (hopefully obviously) not
-necessarily the same as the nodes currently in the cluster, but it should
-be a superset.
-.br
-\fIOptions:\fP
-.br
-\-v Verbose. Lists all the properties of the node, and the
-node-specific properties of the fence device too.
-.br
-\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
-
-
-.TP
-\fBlsfence [options] \fP
-List all the fence devices in the cluster configuration file.
-.br
-\fIOptions:\fP
-.br
-\-v Verbose. Lists all the properties of the fence device rather
-than just the names and agents.
-.br
-\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
-
-
-.TP
-\fBcreate [options] \fP \fI<clustername>\fP
-Create a new, skeleton, configuration file. Note that "create" on its own will
-not create a valid configuration file. Fence agents and nodes will need to be
-added to it before handing it over to ccsd. The new configuration file will
-have a version number of 1. Subsequent addnode/delnode/addfence/delfence operations
-will increment the version number by 1 each time.
-.br
-\fIOptions:\fP
-.br
-.br
-\-c <file> Config file to create. Defaults to /etc/cluster/cluster.conf
-
-.TP
-\fBaddnodeids\fP
-Adds node ID numbers to all the nodes in cluster.conf. In RHEL4, node IDs were optional
-and assigned by cman when a node joined the cluster. In RHEL5 they must be pre-assigned
-in cluster.conf. This command will not change any node IDs that are already set in
-cluster.conf, it will simply add unique node ID numbers to nodes that do not already
-have them.
-
-
-.SH "SEE ALSO"
-ccs(7), ccsd(8), cluster.conf(5)
diff --git a/config/tools/Makefile b/config/tools/Makefile
index 8b2d2fc..04cc742 100644
--- a/config/tools/Makefile
+++ b/config/tools/Makefile
@@ -1,4 +1,4 @@
include ../../make/defines.mk
include $(OBJDIR)/make/passthrough.mk
-SUBDIRS=ldap man
+SUBDIRS=ccs_tool ldap man
diff --git a/config/tools/ccs_tool/Makefile b/config/tools/ccs_tool/Makefile
new file mode 100644
index 0000000..428b9fa
--- /dev/null
+++ b/config/tools/ccs_tool/Makefile
@@ -0,0 +1,56 @@
+TARGET1 = ccs_tool
+TARGET2 = ccs_test
+
+SBINDIRT = $(TARGET1)
+SBINSYMT = $(TARGET2)
+
+include ../../../make/defines.mk
+
+ifdef legacy_code
+all: depends ${TARGET1} ${TARGET2}
+else
+all: ${TARGET1} ${TARGET2}
+endif
+
+include $(OBJDIR)/make/cobj.mk
+include $(OBJDIR)/make/clean.mk
+include $(OBJDIR)/make/install.mk
+include $(OBJDIR)/make/uninstall.mk
+
+OBJS = ccs_tool.o \
+ editconf.o
+
+ifdef legacy_code
+OBJS += update.o
+endif
+
+CFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
+CFLAGS += -I${cmanincdir} `xml2-config --cflags`
+CFLAGS += -I${ccsincdir} -I$(SRCDIR)/ccs/include
+CFLAGS += -I$(SRCDIR)/ccs/libccscompat
+CFLAGS += -I${incdir}
+
+LDFLAGS += -L${cmanlibdir} -lcman
+ifdef legacy_code
+LDFLAGS += -L$(OBJDIR)/ccs/libccscompat -lccscompat
+LDDEPS += $(OBJDIR)/ccs/libccscompat/libccscompat.a
+else
+LDFLAGS += -L${ccslibdir} -lccs
+endif
+LDFLAGS += `xml2-config --libs`
+LDFLAGS += -L${libdir}
+
+${TARGET1}: ${OBJS} ${LDDEPS}
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+${TARGET2}: ${TARGET1}
+ ln -sf ${TARGET1} ${TARGET2}
+
+ifdef legacy_code
+depends:
+ $(MAKE) -C $(OBJDIR)/ccs/libccscompat all
+endif
+
+clean: generalclean
+
+-include $(OBJS:.o=.d)
diff --git a/config/tools/ccs_tool/ccs_tool.c b/config/tools/ccs_tool/ccs_tool.c
new file mode 100644
index 0000000..c05a07e
--- /dev/null
+++ b/config/tools/ccs_tool/ccs_tool.c
@@ -0,0 +1,353 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+
+#include "copyright.cf"
+#include "editconf.h"
+#ifdef LEGACY_CODE
+#include "update.h"
+#include "libccscompat.h"
+#else
+#include "ccs.h"
+#endif
+
+
+/*
+ * Old libccs retruned -error (mostly!) but didn't set errno (sigh)
+ * New libccs sets errno correctly
+ */
+static char *errstring(int retcode)
+{
+#ifdef LEGACY_CODE
+ return strerror(retcode);
+#else
+ return strerror(errno);
+#endif
+}
+
+static void tool_print_usage(FILE *stream);
+
+int globalverbose=0;
+
+static void test_print_usage(FILE *stream);
+
+static int test_main(int argc, char *argv[], int old_format){
+ int desc=0;
+ int i=0;
+ int error = 0;
+ int force = 0, blocking = 0;
+ char *str=NULL;
+ char *cluster_name = NULL;
+
+ if(argc <= 1){
+ test_print_usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ for(i=1; i < argc; i++){
+ if(!strcmp(argv[i], "-h")){
+ test_print_usage(stdout);
+ exit(EXIT_SUCCESS);
+ }
+ if(!strcmp(argv[i], "-V")){
+ printf("%s %s (built %s %s)\n", argv[0], RELEASE_VERSION, __DATE__, __TIME__);
+ printf("%s\n", REDHAT_COPYRIGHT);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ if(!strcmp(argv[1], "connect")){
+ for(i=2; i < argc; i++){
+ if(!strcmp(argv[i], "force")){
+ printf("Force is set.\n");
+ force = 1;
+ } else if(!strcmp(argv[i], "block")){
+ printf("Blocking is set.\n");
+ blocking = 1;
+ } else {
+ cluster_name = argv[i];
+ printf("Setting cluster name to %s\n", cluster_name);
+ }
+ }
+ if(blocking && !force){
+ fprintf(stderr, "Blocking can only be used with \"force\".\n");
+ exit(EXIT_FAILURE);
+ }
+ if(force){
+ desc = ccs_force_connect(cluster_name, blocking);
+ } else {
+ if(cluster_name){
+ fprintf(stderr, "A cluster name can only be specified when using 'force'.\n");
+ exit(EXIT_FAILURE);
+ }
+ desc = ccs_connect();
+ }
+ if(desc < 0){
+ fprintf(stderr, "ccs_connect failed: %s\n", errstring(-desc));
+ exit(EXIT_FAILURE);
+ } else {
+ printf("Connect successful.\n");
+ printf(" Connection descriptor = %d\n", desc);
+#ifndef LEGACY_CODE
+ ccs_disconnect(desc);
+#endif
+ }
+ }
+ else if(!strcmp(argv[1], "disconnect")){
+ if(argc < 3){
+ fprintf(stderr, "Wrong number of arguments.\n");
+ exit(EXIT_FAILURE);
+ }
+#ifdef LEGACY_CODE
+ desc = atoi(argv[2]);
+#else
+ desc = ccs_connect();
+#endif
+ if((error = ccs_disconnect(desc))){
+ fprintf(stderr, "ccs_disconnect failed: %s\n", errstring(-error));
+ exit(EXIT_FAILURE);
+ } else {
+ printf("Disconnect successful.\n");
+ }
+ }
+ else if(!strcmp(argv[1], "get")){
+ if(argc < 4){
+ fprintf(stderr, "Wrong number of arguments.\n");
+ exit(EXIT_FAILURE);
+ }
+#ifdef LEGACY_CODE
+ desc = atoi(argv[2]);
+#else
+ desc = ccs_connect();
+#endif
+ if((desc < 0) || (error = ccs_get(desc, argv[3], &str))){
+ fprintf(stderr, "ccs_get failed: %s\n", errstring(-error));
+ exit(EXIT_FAILURE);
+ } else {
+ if (old_format) {
+ printf("Get successful.\n");
+ printf(" Value = <%s>\n", str);
+ }
+ else {
+ printf("%s\n", str);
+ }
+ if(str)free(str);
+#ifndef LEGACY_CODE
+ ccs_disconnect(desc);
+#endif
+ }
+ }
+ else {
+ fprintf(stderr, "Unknown command: %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void test_print_usage(FILE *stream)
+{
+ fprintf(stream,
+ "Usage:\n"
+ "\n"
+ "ccs_test [Options] <Command>\n"
+ "\n"
+ "Options:\n"
+ " -h Print usage.\n"
+ " -V Print version information.\n"
+ "\n"
+ "Commands:\n"
+ " connect <force> <block> Connect to CCS and return connection descriptor.\n"
+ " disconnect <desc> Disconnect from CCS.\n"
+ " get <desc> <request> Get a value from CCS.\n"
+ );
+}
+
+#ifndef LEGACY_CODE
+static int xpath_query(int argc, char **argv)
+{
+ int handle;
+ char *ret;
+ int i;
+
+ if (argc < 2) {
+ fprintf(stderr,
+ "Usage:\n"
+ "\n"
+ "ccs_tool query <xpath query>\n");
+ return 1;
+ }
+
+ /* Tell the library we want full XPath parsing */
+ fullxpath = 1;
+
+ handle = ccs_connect();
+
+ /* Process all the queries on the command-line */
+ for (i=1; i<argc; i++) {
+ if (!ccs_get(handle, argv[1], &ret)) {
+ printf("%s\n", ret);
+ free(ret);
+ }
+ else {
+ fprintf(stderr, "Query failed: %s\n", strerror(errno));
+ ccs_disconnect(handle);
+ return -1;
+ }
+ }
+ ccs_disconnect(handle);
+ return 0;
+}
+#endif
+
+static int tool_main(int argc, char *argv[])
+{
+ optind = 1;
+
+ if (argc < 2 || !strcmp(argv[optind], "-h")) {
+ tool_print_usage(stdout);
+ exit(EXIT_SUCCESS);
+ }
+ if (!strcmp(argv[optind], "-V")) {
+ printf("%s %s (built %s %s)\n", argv[0], RELEASE_VERSION,
+ __DATE__, __TIME__);
+ printf("%s\n", REDHAT_COPYRIGHT);
+ exit(EXIT_SUCCESS);
+ }
+
+ if(optind < argc){
+ if(!strcmp(argv[optind], "-verbose")){
+ optind++;
+ globalverbose=1;
+ }
+ if(!strcmp(argv[optind], "help")){
+ tool_print_usage(stdout);
+ exit(EXIT_SUCCESS);
+ }
+#ifdef LEGACY_CODE
+ /* Update is meaningless now */
+ else if(!strcmp(argv[optind], "update")){
+ if(optind+1 >= argc){
+ fprintf(stderr, "Too few arguments.\n"
+ "Try 'ccs_tool help' for help.\n");
+ exit(EXIT_FAILURE);
+ }
+ if(update(argv[optind+1])){
+ fprintf(stderr, "\nFailed to update config file.\n");
+ exit(EXIT_FAILURE);
+ }
+ printf("\nUpdate complete.\n");
+ }
+ /* Do old ccs queries */
+ else if(!strcmp(argv[optind], "query")){
+ char *new_argv[argc+2];
+ int i;
+
+ new_argv[0] = "ccs_test";
+ new_argv[1] = "get";
+ new_argv[2] = "0"; /* Dummy connection ID */
+ for (i=2; i<argc; i++)
+ new_argv[1+i] = argv[i];
+
+ return test_main(argc+1, new_argv, 0);
+ }
+#else
+ else if(!strcmp(argv[optind], "query")){
+ return xpath_query(argc-1, argv+1);
+ }
+#endif
+ else if(!strcmp(argv[optind], "addnode")){
+ add_node(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+ else if(!strcmp(argv[optind], "delnode")){
+ del_node(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+ else if(!strcmp(argv[optind], "addfence")){
+ add_fence(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+ else if(!strcmp(argv[optind], "delfence")){
+ del_fence(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+ else if(!strcmp(argv[optind], "lsnode")){
+ list_nodes(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+ else if(!strcmp(argv[optind], "lsfence")){
+ list_fences(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+ else if(!strcmp(argv[optind], "create")){
+ create_skeleton(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+ else if(!strcmp(argv[optind], "addnodeids")){
+ add_nodeids(argc-1, argv+1);
+ exit(EXIT_SUCCESS);
+ }
+
+ else {
+ fprintf(stderr, "Unknown command, %s.\n"
+ "Try 'ccs_tool help' for help.\n", argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ fprintf(stderr, "Too few arguments.\n"
+ "Try 'ccs_tool help' for help.\n");
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+}
+
+static void tool_print_usage(FILE *stream){
+ fprintf(stream,
+ "Usage:\n"
+ " ccs_tool [options] <command>\n"
+ "\n"
+ "Options:\n"
+ " -verbose Make some operations print more details.\n"
+ " -h Print this usage and exit.\n"
+ " -V Print version information and exit.\n"
+ "\n"
+ "Commands:\n"
+ " help Print this usage and exit.\n"
+#ifdef LEGACY_CODE
+ " update <xml file> Tells ccsd to upgrade to new config file version.\n"
+ " query <ccs query> Query the cluster configuration.\n"
+#else
+ " query <xpath query> Query the cluster configuration.\n"
+#endif
+ " addnode <node> Add a node\n"
+ " delnode <node> Delete a node\n"
+ " lsnode List nodes\n"
+ " lsfence List fence devices\n"
+ " addfence <fencedev> Add a new fence device\n"
+ " delfence <fencedev> Delete a fence device\n"
+ " create Create a skeleton config file\n"
+ " addnodeids Assign node ID numbers to all nodes\n"
+ "\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ char *name = strdup(argv[0]);
+
+ /*
+ * Don't be anal about the binary name.
+ * We expect either 'ccs_tool' or 'ccs_test',
+ * but interpret anything other than 'ccs_test'
+ * as 'ccs_tool'.
+ * That's not a bug, it's a feature.
+ */
+
+ if (strcmp(basename(name), "ccs_test") == 0)
+ return test_main(argc, argv, 1);
+ else
+ return tool_main(argc, argv);
+}
diff --git a/config/tools/ccs_tool/editconf.c b/config/tools/ccs_tool/editconf.c
new file mode 100644
index 0000000..53b0ed6
--- /dev/null
+++ b/config/tools/ccs_tool/editconf.c
@@ -0,0 +1,1261 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <libxml/tree.h>
+
+#ifdef LEGACY_CODE
+#include "update.h"
+#endif
+
+#define MAX_NODES 256
+
+char *prog_name = "ccs_tool";
+
+#define die(fmt, args...) \
+do { \
+ fprintf(stderr, "%s: ", prog_name); \
+ fprintf(stderr, fmt "\n", ##args); \
+ exit(EXIT_FAILURE); \
+} while (0)
+
+
+struct option_info
+{
+ char *name;
+ char *altname;
+ char *votes;
+ char *nodeid;
+ char *mcast_addr;
+ char *fence_type;
+ char *configfile;
+ char *outputfile;
+ int do_delete;
+ int tell_ccsd;
+ int force_ccsd;
+};
+
+static void config_usage(int rw)
+{
+ fprintf(stderr, " -c --configfile Name of configuration file (" DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE ")\n");
+ if (rw)
+ {
+ fprintf(stderr, " -o --outputfile Name of output file (defaults to same as --configfile)\n");
+ fprintf(stderr, " -C --no_ccs Don't tell CCSD about this change\n");
+ fprintf(stderr, " default: run \"ccs_tool update\" if file is updated in place)\n");
+ fprintf(stderr, " -F --force_ccs Force \"ccs_tool upgrade\" even if input & output files differ\n");
+ }
+}
+
+static void help_usage(void)
+{
+ fprintf(stderr, " -h --help Display this help text\n");
+}
+
+static void list_usage(const char *name)
+{
+ fprintf(stderr, "Usage: %s %s [options]\n", prog_name, name);
+ fprintf(stderr, " -v --verbose Print all properties of the item\n");
+ config_usage(0);
+ help_usage();
+
+ exit(0);
+}
+
+static void create_usage(const char *name)
+{
+ fprintf(stderr, "Usage: %s %s [-2] <clustername>\n", prog_name, name);
+ fprintf(stderr, " -2 Create a 2-node cman cluster config file\n");
+ config_usage(0);
+ help_usage();
+ fprintf(stderr, "\n"
+ "Note that \"create\" on its own will not create a valid configuration file.\n"
+ "Fence agents and nodes will need to be added to it before handing it over\n"
+ "to ccsd.\n"
+ "\n"
+ "eg:\n"
+ " ccs_tool create MyCluster\n"
+ " ccs_tool addfence apc fence_apc ipaddr=apc.domain.net user=apc password=apc\n"
+ " ccs_tool addnode node1 -n 1 -f apc port=1\n"
+ " ccs_tool addnode node2 -n 2 -f apc port=2\n"
+ " ccs_tool addnode node3 -n 3 -f apc port=3\n"
+ " ccs_tool addnode node4 -n 4 -f apc port=4\n"
+ "\n");
+
+ exit(0);
+}
+
+static void addfence_usage(const char *name)
+{
+ fprintf(stderr, "Usage: %s %s [options] <name> <agent> [param=value]\n", prog_name, name);
+ config_usage(1);
+ help_usage();
+
+ exit(0);
+}
+
+static void delfence_usage(const char *name)
+{
+ fprintf(stderr, "Usage: %s %s [options] <name>\n", prog_name, name);
+ config_usage(1);
+ help_usage();
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%s will allow you to remove a fence device that is in use by nodes.\n", name);
+ fprintf(stderr, "This is to allow changes to be made, but be aware that it may produce an\n");
+ fprintf(stderr, "invalid configuration file if you don't add it back in again.\n");
+
+ exit(0);
+}
+
+static void delnode_usage(const char *name)
+{
+ fprintf(stderr, "Usage: %s %s [options] <name>\n", prog_name, name);
+ config_usage(1);
+ help_usage();
+
+ exit(0);
+}
+
+static void addnodeid_usage(const char *name)
+{
+ fprintf(stderr, "Add node IDs to all nodes in the config file that don't have them.\n");
+ fprintf(stderr, "Nodes with IDs will not be afftected, so you can run this as many times\n");
+ fprintf(stderr, "as you like without doing any harm.\n");
+ fprintf(stderr, "It will optionally add a multicast address to the cluster config too.\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Usage: %s %s [options] <name>\n", prog_name, name);
+ fprintf(stderr, " -n --nodeid Nodeid to start with (default 1)\n");
+ fprintf(stderr, " -m --multicast Set or change the multicast address\n");
+ fprintf(stderr, " -v --verbose Print nodeids that are assigned\n");
+ config_usage(1);
+ help_usage();
+
+ exit(0);
+}
+
+static void addnode_usage(const char *name)
+{
+ fprintf(stderr, "Usage: %s %s [options] <nodename> [<fencearg>=<value>]...\n", prog_name, name);
+ fprintf(stderr, " -n --nodeid Nodeid (required)\n");
+ fprintf(stderr, " -v --votes Number of votes for this node (default 1)\n");
+ fprintf(stderr, " -a --altname Alternative name/interface for multihomed hosts\n");
+ fprintf(stderr, " -f --fence_type Type of fencing to use\n");
+ config_usage(1);
+ help_usage();
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Examples:\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Add a new node to default configuration file:\n");
+ fprintf(stderr, " %s %s -n 1 -f manual ipaddr=newnode\n", prog_name, name);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Add a new node and dump config file to stdout rather than save it\n");
+ fprintf(stderr, " %s %s -n 2 -f apc -o- newnode.temp.net port=1\n", prog_name, name);
+
+ exit(0);
+}
+
+/* Is it really ?
+ * Actually, we don't check that this is a valid multicast address(!),
+ * merely that it is a valid IP[46] address.
+ */
+static int valid_mcast_addr(char *mcast)
+{
+ struct addrinfo *ainfo;
+ struct addrinfo ahints;
+ int ret;
+
+ memset(&ahints, 0, sizeof(ahints));
+
+ ret = getaddrinfo(mcast, NULL, &ahints, &ainfo);
+ if (ret) {
+ freeaddrinfo(ainfo);
+ return 0;
+ }
+ return 1;
+}
+
+static void save_file(xmlDoc *doc, struct option_info *ninfo)
+{
+ char tmpfile[strlen(ninfo->outputfile)+5];
+ char oldfile[strlen(ninfo->outputfile)+5];
+ int using_stdout = 0;
+ mode_t old_mode;
+ int ret;
+
+ old_mode = umask(026);
+
+ if (strcmp(ninfo->outputfile, "-") == 0)
+ using_stdout = 1;
+
+ /*
+ * Save it to a temp file before moving the old one out of the way
+ */
+ if (!using_stdout)
+ {
+ snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", ninfo->outputfile);
+ snprintf(oldfile, sizeof(oldfile), "%s.old", ninfo->outputfile);
+ }
+ else
+ {
+ strcpy(tmpfile, ninfo->outputfile);
+ }
+
+ xmlKeepBlanksDefault(0);
+ ret = xmlSaveFormatFile(tmpfile, doc, 1);
+ if (ret == -1)
+ die("Error writing new config file %s", ninfo->outputfile);
+
+ if (!using_stdout)
+ {
+ if (rename(ninfo->outputfile, oldfile) == -1 && errno != ENOENT)
+ die("Can't move old config file out of the way\n");
+
+ if (rename(tmpfile, ninfo->outputfile))
+ {
+ perror("Error renaming new file to its real filename");
+
+ /* Drat, that failed, try to put the old one back */
+ if (rename(oldfile, ninfo->outputfile))
+ die("Can't move old config fileback in place - clean up after me please\n");
+ }
+ }
+
+#ifdef LEGACY_CODE
+ /* Try to tell ccsd if needed */
+ if ((strcmp(ninfo->configfile, ninfo->outputfile) == 0 && ninfo->tell_ccsd) ||
+ ninfo->force_ccsd)
+ {
+ printf("running ccs_tool update...\n");
+ update(ninfo->outputfile);
+ }
+#endif
+
+ /* free the document */
+ xmlFreeDoc(doc);
+
+ umask(old_mode);
+}
+
+static void validate_int_arg(char argopt, char *arg)
+{
+ char *tmp;
+ int val;
+
+ val = strtol(arg, &tmp, 10);
+ if (tmp == arg || tmp != arg + strlen(arg))
+ die("argument to %c (%s) is not an integer", argopt, arg);
+
+ if (val < 0)
+ die("argument to %c cannot be negative", argopt);
+}
+
+/* Get the config_version string from the file */
+static xmlChar *find_version(xmlNode *root)
+{
+ if (xmlHasProp(root, BAD_CAST "config_version"))
+ {
+ xmlChar *ver;
+
+ ver = xmlGetProp(root, BAD_CAST "config_version");
+ return ver;
+ }
+ return NULL;
+}
+
+/* Get the cluster name string from the file */
+static xmlChar *cluster_name(xmlNode *root)
+{
+ if (xmlHasProp(root, BAD_CAST "name"))
+ {
+ xmlChar *ver;
+
+ ver = xmlGetProp(root, BAD_CAST "name");
+ return ver;
+ }
+ return NULL;
+}
+
+static void increment_version(xmlNode *root_element)
+{
+ int ver;
+ unsigned char *version_string;
+ char newver[32];
+
+ /* Increment version */
+ version_string = find_version(root_element);
+ if (!version_string)
+ die("Can't find \"config_version\" in config file\n");
+
+ ver = atoi((char *)version_string);
+ snprintf(newver, sizeof(newver), "%d", ++ver);
+ xmlSetProp(root_element, BAD_CAST "config_version", BAD_CAST newver);
+}
+
+static xmlNode *findnode(xmlNode *root, char *name)
+{
+ xmlNode *cur_node;
+
+ for (cur_node = root->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, name)==0)
+ {
+ return cur_node;
+ }
+ }
+ return NULL;
+}
+
+/* Return the fence type name (& node) for a cluster node */
+static xmlChar *get_fence_type(xmlNode *clusternode, xmlNode **fencenode)
+{
+ xmlNode *f;
+
+ f = findnode(clusternode, "fence");
+ if (f)
+ {
+ f = findnode(f, "method");
+ if (f)
+ {
+ f = findnode(f, "device");
+ *fencenode = f;
+ return xmlGetProp(f, BAD_CAST "name");
+ }
+ }
+ return NULL;
+}
+
+/* Check the fence type exists under <fencedevices> */
+static xmlNode *valid_fence_type(xmlNode *root, char *fencetype)
+{
+ xmlNode *devs;
+ xmlNode *cur_node;
+
+ devs = findnode(root, "fencedevices");
+ if (!devs)
+ return NULL;
+
+ for (cur_node = devs->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "fencedevice") == 0)
+ {
+ xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
+ if (strcmp((char *)name, fencetype) == 0)
+ return cur_node;
+ }
+ }
+ return NULL;
+}
+
+/* Check the nodeid is not already in use by another node */
+static xmlNode *get_by_nodeid(xmlNode *root, int nodeid)
+{
+ xmlNode *cnodes;
+ xmlNode *cur_node;
+
+ cnodes = findnode(root, "clusternodes");
+ if (!cnodes)
+ return NULL;
+
+ for (cur_node = cnodes->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
+ {
+ xmlChar *idstring = xmlGetProp(cur_node, BAD_CAST "nodeid");
+ if (idstring && atoi((char *)idstring) == nodeid)
+ return cur_node;
+ }
+ }
+ return NULL;
+}
+
+
+/* Get the multicast address node.
+ */
+static xmlNode *find_multicast_addr(xmlNode *clusternodes)
+{
+ xmlNode *clnode = findnode(clusternodes, "cman");
+ if (clnode)
+ {
+ xmlNode *mcast = findnode(clnode, "multicast");
+ return mcast;
+ }
+ return NULL;
+}
+
+static xmlNode *find_node(xmlNode *clusternodes, char *nodename)
+{
+ xmlNode *cur_node;
+
+ for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
+ {
+ xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
+ if (strcmp((char *)name, nodename) == 0)
+ return cur_node;
+ }
+ }
+ return NULL;
+}
+
+/* Print name=value pairs for a (n XML) node.
+ * "ignore" is a string to ignore if present as a property (probably already printed on the main line)
+ */
+static int print_properties(xmlNode *node, char *prefix, char *ignore, char *ignore2)
+{
+ xmlAttr *attr;
+ int done_prefix = 0;
+
+ for (attr = node->properties; attr; attr = attr->next)
+ {
+ /* Don't print "name=" */
+ if (strcmp((char *)attr->name, "name") &&
+ strcmp((char *)attr->name, ignore) &&
+ strcmp((char *)attr->name, ignore2)
+ )
+ {
+ if (!done_prefix)
+ {
+ done_prefix = 1;
+ printf("%s", prefix);
+ }
+ printf(" %s=%s", attr->name, xmlGetProp(node, attr->name));
+ }
+ }
+ if (done_prefix)
+ printf("\n");
+ return done_prefix;
+}
+
+/* Add name=value pairs from the commandline as properties to a node */
+static void add_fence_args(xmlNode *fencenode, int argc, char **argv, int optind)
+{
+ int i;
+
+ for (i = optind; i<argc; i++)
+ {
+ char *prop;
+ char *value;
+ char *equals;
+
+ prop = strdup(argv[i]);
+ equals = strchr(prop, '=');
+ if (!equals)
+ die("option '%s' is not opt=value pair\n", prop);
+
+ value = equals+1;
+ *equals = '\0';
+
+ /* "name" is used for the fence type itself, so this is just
+ * to protect the user from their own stupidity
+ */
+ if (strcmp(prop, "name") == 0)
+ die("Can't use \"name\" as a fence argument name\n");
+
+ xmlSetProp(fencenode, BAD_CAST prop, BAD_CAST value);
+ free(prop);
+ }
+}
+
+static void add_clusternode(xmlNode *root_element, struct option_info *ninfo,
+ int argc, char **argv, int optind)
+{
+ xmlNode *clusternodes;
+ xmlNode *newnode;
+
+ xmlNode *newfence;
+ xmlNode *newfencemethod;
+ xmlNode *newfencedevice;
+
+ clusternodes = findnode(root_element, "clusternodes");
+ if (!clusternodes)
+ die("Can't find \"clusternodes\" in %s\n", ninfo->configfile);
+
+ /* Don't allow duplicate node names */
+ if (find_node(clusternodes, ninfo->name))
+ die("node %s already exists in %s\n", ninfo->name, ninfo->configfile);
+
+ /* Check for duplicate node ID */
+ if (!ninfo->nodeid)
+ die("nodeid not specified\n");
+
+ if (get_by_nodeid(root_element, atoi((char *)ninfo->nodeid)))
+ die("nodeid %s already in use\n", ninfo->nodeid);
+
+ /* Don't allow random fence types */
+ if (!valid_fence_type(root_element, ninfo->fence_type))
+ die("fence type '%s' not known\n", ninfo->fence_type);
+
+ /* Add the new node */
+ newnode = xmlNewNode(NULL, BAD_CAST "clusternode");
+ xmlSetProp(newnode, BAD_CAST "name", BAD_CAST ninfo->name);
+ xmlSetProp(newnode, BAD_CAST "votes", BAD_CAST ninfo->votes);
+ xmlSetProp(newnode, BAD_CAST "nodeid", BAD_CAST ninfo->nodeid);
+ xmlAddChild(clusternodes, newnode);
+
+ if (ninfo->altname)
+ {
+ xmlNode *altnode;
+
+ altnode = xmlNewNode(NULL, BAD_CAST "altname");
+ xmlSetProp(altnode, BAD_CAST "name", BAD_CAST ninfo->altname);
+ xmlAddChild(newnode, altnode);
+ }
+
+ /* Add the fence attributes */
+ newfence = xmlNewNode(NULL, BAD_CAST "fence");
+ newfencemethod = xmlNewNode(NULL, BAD_CAST "method");
+ xmlSetProp(newfencemethod, BAD_CAST "name", BAD_CAST "single");
+
+ newfencedevice = xmlNewNode(NULL, BAD_CAST "device");
+ xmlSetProp(newfencedevice, BAD_CAST "name", BAD_CAST ninfo->fence_type);
+
+ /* Add name=value options */
+ add_fence_args(newfencedevice, argc, argv, optind+1);
+
+ xmlAddChild(newnode, newfence);
+ xmlAddChild(newfence, newfencemethod);
+ xmlAddChild(newfencemethod, newfencedevice);
+}
+
+static xmlDoc *open_configfile(struct option_info *ninfo)
+{
+ xmlDoc *doc;
+
+ /* Init libxml */
+ xmlInitParser();
+ LIBXML_TEST_VERSION;
+
+ if (!ninfo->configfile)
+ ninfo->configfile = DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE;
+ if (!ninfo->outputfile)
+ ninfo->outputfile = ninfo->configfile;
+
+ /* Load XML document */
+ doc = xmlParseFile(ninfo->configfile);
+ if (doc == NULL)
+ die("Error: unable to parse requested configuration file\n");
+
+ return doc;
+
+}
+
+static void del_clusternode(xmlNode *root_element, struct option_info *ninfo)
+{
+ xmlNode *clusternodes;
+ xmlNode *oldnode;
+
+ clusternodes = findnode(root_element, "clusternodes");
+ if (!clusternodes)
+ {
+ fprintf(stderr, "Can't find \"clusternodes\" in %s\n", ninfo->configfile);
+ exit(1);
+ }
+
+ oldnode = find_node(clusternodes, ninfo->name);
+ if (!oldnode)
+ {
+ fprintf(stderr, "node %s does not exist in %s\n", ninfo->name, ninfo->configfile);
+ exit(1);
+ }
+
+ xmlUnlinkNode(oldnode);
+}
+
+struct option addnode_options[] =
+{
+ { "votes", required_argument, NULL, 'v'},
+ { "nodeid", required_argument, NULL, 'n'},
+ { "altname", required_argument, NULL, 'a'},
+ { "fence_type", required_argument, NULL, 'f'},
+ { "outputfile", required_argument, NULL, 'o'},
+ { "configfile", required_argument, NULL, 'c'},
+ { "no_ccs", no_argument, NULL, 'C'},
+ { "force_ccs", no_argument, NULL, 'F'},
+ { NULL, 0, NULL, 0 },
+};
+
+struct option delnode_options[] =
+{
+ { "outputfile", required_argument, NULL, 'o'},
+ { "configfile", required_argument, NULL, 'c'},
+ { "no_ccs", no_argument, NULL, 'C'},
+ { "force_ccs", no_argument, NULL, 'F'},
+ { NULL, 0, NULL, 0 },
+};
+
+struct option addfence_options[] =
+{
+ { "outputfile", required_argument, NULL, 'o'},
+ { "configfile", required_argument, NULL, 'c'},
+ { "no_ccs", no_argument, NULL, 'C'},
+ { "force_ccs", no_argument, NULL, 'F'},
+ { NULL, 0, NULL, 0 },
+};
+
+struct option addnodeid_options[] =
+{
+ { "outputfile", required_argument, NULL, 'o'},
+ { "configfile", required_argument, NULL, 'c'},
+ { "multicast", required_argument, NULL, 'm'},
+ { "nodeid", no_argument, NULL, 'n'},
+ { "verbose", no_argument, NULL, 'v'},
+ { NULL, 0, NULL, 0 },
+};
+
+struct option list_options[] =
+{
+ { "configfile", required_argument, NULL, 'c'},
+ { "verbose", no_argument, NULL, 'v'},
+ { NULL, 0, NULL, 0 },
+};
+
+
+static int next_nodeid(int startid, int *nodeids, int nodecount)
+{
+ int i;
+ int nextid = startid;
+
+retry:
+ for (i=0; i<nodecount; i++)
+ {
+ if (nodeids[i] == nextid)
+ {
+ nextid++;
+ goto retry;
+ }
+ }
+
+ return nextid;
+}
+
+void add_nodeids(int argc, char **argv)
+{
+ struct option_info ninfo;
+ unsigned char *nodenames[MAX_NODES];
+ xmlDoc *doc;
+ xmlNode *root_element;
+ xmlNode *clusternodes;
+ xmlNode *cur_node;
+ xmlNode *mcast;
+ int verbose = 0;
+ int opt;
+ int i;
+ int nodenumbers[MAX_NODES];
+ int nodeidx;
+ int totalnodes;
+ int nextid;
+
+ memset(nodenames, 0, sizeof(nodenames));
+ memset(nodenumbers, 0, sizeof(nodenumbers));
+ memset(&ninfo, 0, sizeof(ninfo));
+ ninfo.nodeid = "1";
+
+ while ( (opt = getopt_long(argc, argv, "n:o:c:m:vh?", addnodeid_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'n':
+ validate_int_arg(opt, optarg);
+ ninfo.nodeid = strdup(optarg);
+ break;
+
+ case 'c':
+ ninfo.configfile = strdup(optarg);
+ break;
+
+ case 'o':
+ ninfo.outputfile = strdup(optarg);
+ break;
+
+ case 'm':
+ if (!valid_mcast_addr(optarg)) {
+ fprintf(stderr, "%s is not a valid multicast address\n", optarg);
+ return;
+ }
+ ninfo.mcast_addr = strdup(optarg);
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case '?':
+ default:
+ addnodeid_usage(argv[0]);
+ }
+ }
+
+ doc = open_configfile(&ninfo);
+
+ root_element = xmlDocGetRootElement(doc);
+
+ increment_version(root_element);
+
+ /* Warn if the cluster doesn't have a multicast address */
+ mcast = find_multicast_addr(root_element);
+ if (!mcast & !ninfo.mcast_addr) {
+ fprintf(stderr, "\nWARNING: The cluster does not have a multicast address.\n");
+ fprintf(stderr, "A default will be assigned a run-time which might not suit your installation\n\n");
+ }
+
+ if (ninfo.mcast_addr) {
+ if (!mcast) {
+ xmlNode *cman = xmlNewNode(NULL, BAD_CAST "cman");
+ mcast = xmlNewNode(NULL, BAD_CAST "multicast");
+
+ xmlAddChild(cman, mcast);
+ xmlAddChild(root_element, cman);
+ }
+ xmlSetProp(mcast, BAD_CAST "addr", BAD_CAST ninfo.mcast_addr);
+ }
+
+ /* Get a list of nodes that /do/ have nodeids so we don't generate
+ any duplicates */
+ nodeidx=0;
+ clusternodes = findnode(root_element, "clusternodes");
+ if (!clusternodes)
+ die("Can't find \"clusternodes\" in %s\n", ninfo.configfile);
+
+
+ for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
+ {
+ xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
+ xmlChar *nodeid = xmlGetProp(cur_node, BAD_CAST "nodeid");
+ nodenames[nodeidx] = name;
+ if (nodeid)
+ nodenumbers[nodeidx] = atoi((char*)nodeid);
+ nodeidx++;
+ }
+ }
+ totalnodes = nodeidx;
+
+ /* Loop round nodes adding nodeIDs where they don't exist. */
+ nextid = next_nodeid(atoi(ninfo.nodeid), nodenumbers, totalnodes);
+ for (i=0; i<totalnodes; i++)
+ {
+ if (nodenumbers[i] == 0)
+ {
+ nodenumbers[i] = nextid;
+ nextid = next_nodeid(nextid, nodenumbers, totalnodes);
+ if (verbose)
+ fprintf(stderr, "Node %s now has id %d\n", nodenames[i], nodenumbers[i]);
+ }
+ }
+
+ /* Now write them into the tree */
+ nodeidx = 0;
+ for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
+ {
+ char tmp[80];
+ xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
+
+ assert(strcmp((char*)nodenames[nodeidx], (char*)name) == 0);
+
+ sprintf(tmp, "%d", nodenumbers[nodeidx]);
+ xmlSetProp(cur_node, BAD_CAST "nodeid", BAD_CAST tmp);
+ nodeidx++;
+ }
+ }
+
+
+ /* Write it out */
+ save_file(doc, &ninfo);
+
+ /* Shutdown libxml */
+ xmlCleanupParser();
+}
+
+void add_node(int argc, char **argv)
+{
+ struct option_info ninfo;
+ int opt;
+ xmlDoc *doc;
+ xmlNode *root_element;
+
+ memset(&ninfo, 0, sizeof(ninfo));
+ ninfo.tell_ccsd = 1;
+ ninfo.votes = "1";
+
+ while ( (opt = getopt_long(argc, argv, "v:n:a:f:o:c:CFh?", addnode_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'v':
+ validate_int_arg(opt, optarg);
+ ninfo.votes = optarg;
+ break;
+
+ case 'n':
+ validate_int_arg(opt, optarg);
+ ninfo.nodeid = optarg;
+ break;
+
+ case 'a':
+ ninfo.altname = strdup(optarg);
+ break;
+
+ case 'f':
+ ninfo.fence_type = strdup(optarg);
+ break;
+
+ case 'c':
+ ninfo.configfile = strdup(optarg);
+ break;
+
+ case 'o':
+ ninfo.outputfile = strdup(optarg);
+ break;
+
+ case 'C':
+ ninfo.tell_ccsd = 0;
+ break;
+
+ case 'F':
+ ninfo.force_ccsd = 1;
+ break;
+
+ case '?':
+ default:
+ addnode_usage(argv[0]);
+ }
+ }
+
+ /* Get node name parameter */
+ if (optind < argc)
+ ninfo.name = strdup(argv[optind]);
+ else
+ addnode_usage(argv[0]);
+
+ if (!ninfo.fence_type)
+ addnode_usage(argv[0]);
+
+
+ doc = open_configfile(&ninfo);
+
+ root_element = xmlDocGetRootElement(doc);
+
+ increment_version(root_element);
+
+ add_clusternode(root_element, &ninfo, argc, argv, optind);
+
+ /* Write it out */
+ save_file(doc, &ninfo);
+ /* Shutdown libxml */
+ xmlCleanupParser();
+
+}
+
+void del_node(int argc, char **argv)
+{
+ struct option_info ninfo;
+ int opt;
+ xmlDoc *doc;
+ xmlNode *root_element;
+
+ memset(&ninfo, 0, sizeof(ninfo));
+ ninfo.tell_ccsd = 1;
+
+ while ( (opt = getopt_long(argc, argv, "o:c:CFh?", delnode_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'c':
+ ninfo.configfile = strdup(optarg);
+ break;
+
+ case 'o':
+ ninfo.outputfile = strdup(optarg);
+ break;
+
+ case 'C':
+ ninfo.tell_ccsd = 0;
+ break;
+
+ case 'F':
+ ninfo.force_ccsd = 1;
+ break;
+
+ case '?':
+ default:
+ delnode_usage(argv[0]);
+ }
+ }
+
+ /* Get node name parameter */
+ if (optind < argc)
+ ninfo.name = strdup(argv[optind]);
+ else
+ delnode_usage(argv[0]);
+
+ doc = open_configfile(&ninfo);
+
+ root_element = xmlDocGetRootElement(doc);
+
+ increment_version(root_element);
+
+ del_clusternode(root_element, &ninfo);
+
+ /* Write it out */
+ save_file(doc, &ninfo);
+}
+
+void list_nodes(int argc, char **argv)
+{
+ xmlNode *cur_node;
+ xmlNode *root_element;
+ xmlNode *clusternodes;
+ xmlNode *fencenode = NULL;
+ xmlDocPtr doc;
+ xmlNode *mcast;
+ struct option_info ninfo;
+ int opt;
+ int verbose = 0;
+
+ memset(&ninfo, 0, sizeof(ninfo));
+
+ while ( (opt = getopt_long(argc, argv, "c:vh?", list_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'c':
+ ninfo.configfile = strdup(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ list_usage(argv[0]);
+ }
+ }
+ doc = open_configfile(&ninfo);
+
+ root_element = xmlDocGetRootElement(doc);
+
+
+ printf("\nCluster name: %s, config_version: %s\n\n",
+ (char *)cluster_name(root_element),
+ (char *)find_version(root_element));
+
+ clusternodes = findnode(root_element, "clusternodes");
+ if (!clusternodes)
+ die("Can't find \"clusternodes\" in %s\n", ninfo.configfile);
+
+ mcast = find_multicast_addr(root_element);
+ if (mcast)
+ printf("Multicast address for cluster: %s\n\n", xmlGetProp(mcast, BAD_CAST "addr"));
+
+ printf("Nodename Votes Nodeid Fencetype\n");
+ for (cur_node = clusternodes->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "clusternode") == 0)
+ {
+ xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
+ xmlChar *votes = xmlGetProp(cur_node, BAD_CAST "votes");
+ xmlChar *nodeid = xmlGetProp(cur_node, BAD_CAST "nodeid");
+ xmlChar *ftype = get_fence_type(cur_node, &fencenode);
+
+ if (!nodeid)
+ nodeid=(unsigned char *)"0";
+ if (!votes)
+ votes = (unsigned char *)"1";
+
+ printf("%-32s %3d %3d %s\n", name, atoi((char *)votes),
+ atoi((char *)nodeid),
+ ftype?ftype:(xmlChar *)"");
+ if (verbose)
+ {
+ xmlNode *a = findnode(cur_node, "altname");
+ if (a)
+ {
+ printf(" altname %s=%s", "name", xmlGetProp(a, BAD_CAST "name"));
+ if (!print_properties(a, "","",""))
+ printf("\n");
+ }
+ print_properties(cur_node, " Node properties: ", "votes", "nodeid");
+ print_properties(fencenode, " Fence properties: ", "agent", "");
+ }
+
+ }
+ }
+}
+
+void create_skeleton(int argc, char **argv)
+{
+ xmlNode *root_element;
+ xmlNode *fencedevices;
+ xmlNode *clusternodes;
+ xmlNode *rm;
+ xmlNode *rm1;
+ xmlNode *rm2;
+ xmlDocPtr doc;
+ char *clustername;
+ struct option_info ninfo;
+ struct stat st;
+ int twonode = 0;
+ int opt;
+
+ memset(&ninfo, 0, sizeof(ninfo));
+
+ while ( (opt = getopt_long(argc, argv, "c:2h?", list_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'c':
+ ninfo.outputfile = strdup(optarg);
+ break;
+
+ case '2':
+ twonode = 1;
+ break;
+
+ case '?':
+ default:
+ create_usage(argv[0]);
+ }
+ }
+ if (!ninfo.outputfile)
+ ninfo.outputfile = DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE;
+ ninfo.configfile = "-";
+
+ if (argc - optind < 1)
+ create_usage(argv[0]);
+
+ clustername = argv[optind];
+
+ if (stat(ninfo.outputfile, &st) == 0)
+ die("%s already exists", ninfo.outputfile);
+
+ /* Init libxml */
+ xmlInitParser();
+ LIBXML_TEST_VERSION;
+
+ doc = xmlNewDoc(BAD_CAST "1.0");
+ root_element = xmlNewNode(NULL, BAD_CAST "cluster");
+ xmlDocSetRootElement(doc, root_element);
+
+ xmlSetProp(root_element, BAD_CAST "name", BAD_CAST clustername);
+ xmlSetProp(root_element, BAD_CAST "config_version", BAD_CAST "1");
+
+ /* Generate extra bits for a 2node cman cluster */
+ if (twonode) {
+
+ xmlNode *cman = xmlNewNode(NULL, BAD_CAST "cman");
+ xmlSetProp(cman, BAD_CAST "two_node", BAD_CAST "1");
+ xmlSetProp(cman, BAD_CAST "expected_votes", BAD_CAST "1");
+ xmlAddChild(root_element, cman);
+ }
+
+ clusternodes = xmlNewNode(NULL, BAD_CAST "clusternodes");
+ fencedevices = xmlNewNode(NULL, BAD_CAST "fencedevices");
+ rm = xmlNewNode(NULL, BAD_CAST "rm");
+ rm1 = xmlNewNode(NULL, BAD_CAST "failoverdomains");
+
+ xmlAddChild(root_element, clusternodes);
+ xmlAddChild(root_element, fencedevices);
+ xmlAddChild(root_element, rm);
+
+ /* Create empty resource manager sections to keep GUI happy */
+ rm2 = xmlNewNode(NULL, BAD_CAST "resources");
+ xmlAddChild(rm, rm1);
+ xmlAddChild(rm, rm2);
+
+ save_file(doc, &ninfo);
+
+}
+
+void add_fence(int argc, char **argv)
+{
+ xmlNode *root_element;
+ xmlNode *fencedevices;
+ xmlNode *fencenode = NULL;
+ xmlDocPtr doc;
+ char *fencename;
+ char *agentname;
+ struct option_info ninfo;
+ int opt;
+
+ memset(&ninfo, 0, sizeof(ninfo));
+ ninfo.tell_ccsd = 1;
+
+ while ( (opt = getopt_long(argc, argv, "c:o:CFh?", list_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'c':
+ ninfo.configfile = strdup(optarg);
+ break;
+ case 'o':
+ ninfo.outputfile = strdup(optarg);
+ break;
+
+ case 'C':
+ ninfo.tell_ccsd = 0;
+ break;
+
+ case 'F':
+ ninfo.force_ccsd = 1;
+ break;
+
+ case '?':
+ default:
+ addfence_usage(argv[0]);
+ }
+ }
+
+ if (argc - optind < 2)
+ addfence_usage(argv[0]);
+
+ doc = open_configfile(&ninfo);
+ root_element = xmlDocGetRootElement(doc);
+
+ increment_version(root_element);
+
+ fencedevices = findnode(root_element, "fencedevices");
+ if (!fencedevices)
+ die("Can't find \"fencedevices\" %s\n", ninfo.configfile);
+
+ /* First param is the fence name - check it doesn't already exist */
+ fencename = argv[optind++];
+
+ if (valid_fence_type(root_element, fencename))
+ die("fence type %s already exists\n", fencename);
+
+ agentname = argv[optind++];
+
+ /* Add it */
+ fencenode = xmlNewNode(NULL, BAD_CAST "fencedevice");
+ xmlSetProp(fencenode, BAD_CAST "name", BAD_CAST fencename);
+ xmlSetProp(fencenode, BAD_CAST "agent", BAD_CAST agentname);
+
+ /* Add name=value options */
+ add_fence_args(fencenode, argc, argv, optind);
+
+ xmlAddChild(fencedevices, fencenode);
+
+ save_file(doc, &ninfo);
+}
+
+void del_fence(int argc, char **argv)
+{
+ xmlNode *root_element;
+ xmlNode *fencedevices;
+ xmlNode *fencenode;
+ xmlDocPtr doc;
+ char *fencename;
+ struct option_info ninfo;
+ int opt;
+
+ memset(&ninfo, 0, sizeof(ninfo));
+ ninfo.tell_ccsd = 1;
+
+ while ( (opt = getopt_long(argc, argv, "c:o:CFhv?", list_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'c':
+ ninfo.configfile = strdup(optarg);
+ break;
+ case 'o':
+ ninfo.outputfile = strdup(optarg);
+ break;
+
+ case 'C':
+ ninfo.tell_ccsd = 0;
+ break;
+
+ case 'F':
+ ninfo.force_ccsd = 1;
+ break;
+
+ case '?':
+ default:
+ delfence_usage(argv[0]);
+ }
+ }
+
+ if (argc - optind < 1)
+ delfence_usage(argv[0]);
+
+ fencename = argv[optind];
+
+ doc = open_configfile(&ninfo);
+ root_element = xmlDocGetRootElement(doc);
+ increment_version(root_element);
+
+ fencedevices = findnode(root_element, "fencedevices");
+ if (!fencedevices)
+ die("Can't find \"fencedevices\" in %s\n", ninfo.configfile);
+
+ fencenode = valid_fence_type(root_element, fencename);
+ if (!fencenode)
+ die("fence type %s does not exist\n", fencename);
+
+ xmlUnlinkNode(fencenode);
+
+ save_file(doc, &ninfo);
+}
+
+void list_fences(int argc, char **argv)
+{
+ xmlNode *cur_node;
+ xmlNode *root_element;
+ xmlNode *fencedevices;
+ xmlDocPtr doc;
+ struct option_info ninfo;
+ int opt;
+ int verbose=0;
+
+ memset(&ninfo, 0, sizeof(ninfo));
+
+ while ( (opt = getopt_long(argc, argv, "c:hv?", list_options, NULL)) != EOF)
+ {
+ switch(opt)
+ {
+ case 'c':
+ ninfo.configfile = strdup(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ list_usage(argv[0]);
+ }
+ }
+ doc = open_configfile(&ninfo);
+ root_element = xmlDocGetRootElement(doc);
+
+ fencedevices = findnode(root_element, "fencedevices");
+ if (!fencedevices)
+ die("Can't find \"fencedevices\" in %s\n", ninfo.configfile);
+
+
+ printf("Name Agent\n");
+ for (cur_node = fencedevices->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE && strcmp((char *)cur_node->name, "fencedevice") == 0)
+ {
+ xmlChar *name = xmlGetProp(cur_node, BAD_CAST "name");
+ xmlChar *agent = xmlGetProp(cur_node, BAD_CAST "agent");
+
+ printf("%-16s %s\n", name, agent);
+ if (verbose)
+ print_properties(cur_node, " Properties: ", "agent", "");
+ }
+ }
+}
+
diff --git a/config/tools/ccs_tool/editconf.h b/config/tools/ccs_tool/editconf.h
new file mode 100644
index 0000000..1847e2c
--- /dev/null
+++ b/config/tools/ccs_tool/editconf.h
@@ -0,0 +1,8 @@
+void add_node(int argc, char **argv);
+void add_nodeids(int argc, char **argv);
+void add_fence(int argc, char **argv);
+void del_node(int argc, char **argv);
+void del_fence(int argc, char **argv);
+void list_nodes(int argc, char **argv);
+void list_fences(int argc, char **argv);
+void create_skeleton(int argc, char **argv);
diff --git a/config/tools/ccs_tool/update.c b/config/tools/ccs_tool/update.c
new file mode 100644
index 0000000..d8c1308
--- /dev/null
+++ b/config/tools/ccs_tool/update.c
@@ -0,0 +1,673 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "comm_headers.h"
+#include "libccscompat.h"
+#include "libcman.h"
+
+typedef struct member_list {
+ int count;
+ int pad;
+ cman_node_t *nodes;
+} member_list_t;
+
+static member_list_t *get_member_list(cman_handle_t handle);
+static void free_member_list(member_list_t *list);
+
+static int select_retry(int max_fd, fd_set *rfds, fd_set *wfds, fd_set *xfds,
+ struct timeval *timeout);
+
+static ssize_t read_retry(int fd, void *buf, int count, struct timeval *timeout);
+
+static int ccs_open(cman_node_t node, uint16_t baseport, int timeout);
+static int ipv4_connect(struct in_addr *addr, uint16_t port, int timeout);
+static int ipv6_connect(struct in6_addr *addr, uint16_t port, int timeout);
+static int connect_nb(int fd, struct sockaddr *addr, socklen_t len, int timeout);
+
+extern int globalverbose;
+
+int cluster_base_port = 50008;
+
+static int get_doc_version(xmlDocPtr ldoc)
+{
+ int i;
+ int error = 0;
+
+ xmlXPathObjectPtr obj = NULL;
+ xmlXPathContextPtr ctx = NULL;
+ xmlNodePtr node = NULL;
+
+ ctx = xmlXPathNewContext(ldoc);
+
+ if (!ctx) {
+ fprintf(stderr, "Unable to create new XPath context.\n");
+ error = -EIO; /* ATTENTION -- what should this be? */
+ goto fail;
+ }
+
+ obj = xmlXPathEvalExpression((xmlChar *)"/cluster/@config_version", ctx);
+
+ if (!obj || !obj->nodesetval || (obj->nodesetval->nodeNr != 1)) {
+ fprintf(stderr, "Error while retrieving config_version.\n");
+ error = -ENODATA;
+ goto fail;
+ }
+
+ node = obj->nodesetval->nodeTab[0];
+
+ if (node->type != XML_ATTRIBUTE_NODE) {
+ fprintf(stderr, "Object returned is not of attribute type.\n");
+ error = -ENODATA;
+ goto fail;
+ }
+
+ if (!node->children->content || !strlen((char *)node->children->content)) {
+ error = -ENODATA;
+ goto fail;
+ }
+
+ for (i = 0; i < strlen((char *)node->children->content); i++) {
+ if (!isdigit(node->children->content[i])) {
+ fprintf(stderr, "config_version is not a valid integer.\n");
+ error = -EINVAL;
+ goto fail;
+ }
+ }
+
+ error = atoi((char *)node->children->content);
+
+fail:
+
+ if (ctx) {
+ xmlXPathFreeContext(ctx);
+ }
+
+ if (obj) {
+ xmlXPathFreeObject(obj);
+ }
+
+ return error;
+}
+
+
+int update(char *location)
+{
+ int error = 0;
+ int i, fd;
+ int cluster_fd = -1;
+ char true_location[256];
+ xmlDocPtr doc = NULL;
+ xmlChar *mem_doc;
+ int doc_size = 0;
+ char *buffer = NULL;
+ comm_header_t *ch = NULL, rch;
+ member_list_t *members = NULL;
+ cman_handle_t handle = NULL;
+ int desc;
+ char *v1_str,*v3_str;
+ int v1, v2, v3;
+
+ struct timeval tv;
+
+ if (location[0] != '/') {
+ memset(true_location, 0, 256);
+ if (!getcwd(true_location, 256)) {
+ fprintf(stderr, "Unable to get the current working directory.\n");
+ return -errno;
+ }
+ true_location[strlen(true_location)] = '/';
+ strncpy(true_location+strlen(true_location), location, 256-strlen(true_location));
+ } else {
+ strncpy(true_location, location, 256);
+ }
+
+ desc = ccs_connect();
+
+ if (desc < 0) {
+ fprintf(stderr, "Unable to connect to the CCS daemon: %s\n", strerror(-desc));
+ return desc;
+ }
+
+ if ((error = ccs_get(desc, "/cluster/@config_version", &v1_str))) {
+ fprintf(stderr, "Unable to get current config_version: %s\n", strerror(-error));
+ ccs_disconnect(desc);
+ return error;
+ }
+
+ ccs_disconnect(desc);
+
+ for (i = 0; i < strlen(v1_str); i++) {
+ if (!isdigit(v1_str[i])) {
+ fprintf(stderr, "config_version is not a valid integer.\n");
+ free(v1_str);
+ return -EINVAL;
+ }
+ }
+
+ v1 = atoi(v1_str);
+ free(v1_str);
+
+ doc = xmlParseFile(true_location);
+
+ if (!doc) {
+ fprintf(stderr, "Unable to parse %s\n", true_location);
+ return -EINVAL;
+ }
+
+ v2 = get_doc_version(doc);
+
+ if (v2 < 0) {
+ fprintf(stderr, "Unable to get the config_version from %s\n", location);
+ xmlFreeDoc(doc);
+ return -EINVAL;
+ }
+
+ if (v2 <= v1) {
+ fprintf(stderr,
+ "Proposed updated config file does not have greater version number.\n"
+ " Current config_version :: %d\n"
+ " Proposed config_version:: %d\n", v1, v2);
+ xmlFreeDoc(doc);
+ return -EINVAL;
+ }
+
+ xmlDocDumpFormatMemory(doc, &mem_doc, &doc_size, 0);
+
+ if (!mem_doc) {
+ fprintf(stderr, "Unable to allocate memory for update document.\n");
+ xmlFreeDoc(doc);
+ return -ENOMEM;
+ }
+
+ xmlFreeDoc(doc);
+
+ buffer = malloc(doc_size + sizeof(comm_header_t));
+
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate memory for transfer buffer.\n");
+ free(mem_doc);
+ return -ENOMEM;
+ }
+
+ memset(buffer, 0, (doc_size + sizeof(comm_header_t)));
+ ch = (comm_header_t *)buffer;
+
+ memcpy(buffer+sizeof(comm_header_t), mem_doc, doc_size);
+ free(mem_doc);
+
+ ch->comm_type = COMM_UPDATE;
+ ch->comm_flags= COMM_UPDATE_NOTICE;
+ ch->comm_payload_size = doc_size;
+
+ handle = cman_admin_init(NULL);
+
+ cluster_fd = cman_get_fd(handle);
+
+ /* Should we test the cman handle of file descriptor to determine connectivity? */
+
+ if (cluster_fd < 0) {
+ fprintf(stderr, "Unable to connect to cluster infrastructure.\n");
+ return cluster_fd;
+ }
+
+ if (!cman_is_quorate(handle)) {
+ fprintf(stderr, "Unable to honor update request. Cluster is not quorate.\n");
+ return -EPERM;
+ }
+
+ members = get_member_list(handle);
+
+ swab_header(ch);
+
+ for (i = 0; i < members->count; i++) {
+ if (members->nodes[i].cn_nodeid == 0)
+ continue;
+ if (members->nodes[i].cn_member == 0)
+ continue;
+
+ fd = ccs_open(members->nodes[i], cluster_base_port, 5);
+
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open connection to %s: %s\n",
+ members->nodes[i].cn_name, strerror(errno));
+ free(buffer);
+ free_member_list(members);
+ return -errno;
+ }
+
+ error = write(fd, buffer, sizeof(comm_header_t) + doc_size);
+
+ if (error < 0) {
+ fprintf(stderr, "Unable to send msg to %s: %s\n",
+ members->nodes[i].cn_name, strerror(errno));
+ close(fd);
+ free(buffer);
+ free_member_list(members);
+ return -errno;
+ }
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ error = read_retry(fd, &rch, sizeof(comm_header_t), &tv);
+
+ swab_header(&rch);
+
+ if (error < 0) {
+ fprintf(stderr, "Failed to receive COMM_UPDATE_NOTICE_ACK from %s.\n",
+ members->nodes[i].cn_name);
+ fprintf(stderr, "Hint: Check the log on %s for reason.\n",
+ members->nodes[i].cn_name);
+ close(fd);
+ free(buffer);
+ free_member_list(members);
+ return -errno;
+ }
+
+ close(fd);
+ }
+
+ swab_header(ch);
+
+ ch->comm_flags = COMM_UPDATE_COMMIT;
+
+ swab_header(ch);
+
+ for (i=0; i < members->count; i++) {
+ if (members->nodes[i].cn_nodeid == 0)
+ continue;
+ if (members->nodes[i].cn_member == 0)
+ continue;
+
+ fd = ccs_open(members->nodes[i], cluster_base_port, 5);
+ if(fd < 0){
+ fprintf(stderr, "Unable to open connection to %s: %s\n",
+ members->nodes[i].cn_name, strerror(errno));
+ free(buffer);
+ free_member_list(members);
+ return -errno;
+ }
+
+ error = write(fd, buffer, sizeof(comm_header_t));
+
+ if (error < 0) {
+ fprintf(stderr, "Unable to send msg to %s: %s\n",
+ members->nodes[i].cn_name, strerror(errno));
+ close(fd);
+ free(buffer);
+ free_member_list(members);
+ return -errno;
+ }
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ error = read_retry(fd, &rch, sizeof(comm_header_t), &tv);
+
+ swab_header(&rch);
+
+ if (error < 0) {
+ fprintf(stderr, "Failed to receive COMM_UPDATE_COMMIT_ACK from %s.\n",
+ members->nodes[i].cn_name);
+ fprintf(stderr, "Hint: Check the log on %s for reason.\n",
+ members->nodes[i].cn_name);
+ close(fd);
+ free(buffer);
+ free_member_list(members);
+ return -errno;
+ }
+
+ close(fd);
+ error = 0;
+ }
+
+ free(buffer);
+ free_member_list(members);
+
+ /* If we can't connect here, it doesn't mean the update failed **
+ ** It means that we simply can't report the change in version */
+ desc = ccs_connect();
+
+ if (desc < 0) {
+ fprintf(stderr, "Unable to connect to the CCS daemon: %s\n", strerror(-desc));
+ return 0;
+ }
+
+ if ((error = ccs_get(desc, "/cluster/@config_version", &v3_str))) {
+ ccs_disconnect(desc);
+ return 0;
+ }
+
+ v3 = atoi(v3_str);
+ free(v3_str);
+
+ ccs_disconnect(desc);
+
+ if (v2 == v3) {
+ cman_version_t cman_ver;
+ printf("Config file updated from version %d to %d\n", v1, v2);
+ cman_get_version(handle, &cman_ver);
+ cman_ver.cv_config = v2;
+ if (cman_set_version(handle, &cman_ver)) {
+ perror("Failed to tell cman of new version number");
+ }
+ } else {
+ fprintf(stderr, "Warning:: Simultaneous update requests detected.\n"
+ " You have lost the race.\n"
+ " Old config version :: %d\n"
+ " Proposed config version :: %d\n"
+ " Winning config version :: %d\n\n"
+ "Check " DEFAULT_CONFIG_DIR "/" DEFAULT_CONFIG_FILE " to ensure it contains the desired contents.\n", v1, v2, v3);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+
+static member_list_t *get_member_list(cman_handle_t handle)
+{
+ int count = 0;
+
+ member_list_t *list = NULL;
+ cman_node_t *nodes = NULL;
+
+ do
+ {
+
+ if (nodes != NULL) {
+ free(nodes);
+ }
+
+ count = cman_get_node_count(handle);
+
+ if (count <= 0) {
+ return NULL;
+ }
+
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ }
+
+ if (list == NULL) {
+ return NULL;
+ }
+
+ nodes = malloc(sizeof(*nodes) * count);
+
+ if (nodes == NULL) {
+ free(list);
+ return NULL;
+ }
+
+ memset(list, 0, sizeof(*list));
+ memset(nodes, 0, sizeof(*nodes) * count);
+
+ cman_get_nodes(handle, count, &list->count, nodes);
+
+ } while (list->count != count);
+
+ list->count = count;
+ list->nodes = nodes;
+
+ return list;
+}
+
+
+static void free_member_list(member_list_t *list)
+{
+ if (list != NULL) {
+ if (list->nodes != NULL) {
+ free(list->nodes);
+ }
+ free(list);
+ }
+}
+
+
+static int select_retry(int max_fd, fd_set *rfds, fd_set *wfds, fd_set *xfds,
+ struct timeval *timeout)
+{
+ int rv;
+
+ while (1) {
+ rv = select(max_fd, rfds, wfds, xfds, timeout);
+ if ((rv == -1) && (errno == EINTR)) {
+ /* return on EBADF/EINVAL/ENOMEM; continue on EINTR */
+ continue;
+ }
+ return rv;
+ }
+}
+
+
+static ssize_t read_retry(int fd, void *buf, int count, struct timeval *timeout)
+{
+ int n, total = 0, remain = count, rv = 0;
+ fd_set rfds, xfds;
+
+ while (total < count)
+ {
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ FD_ZERO(&xfds);
+ FD_SET(fd, &xfds);
+
+ /*
+ * Select on the socket, in case it closes while we're not
+ * looking...
+ */
+ rv = select_retry(fd + 1, &rfds, NULL, &xfds, timeout);
+ if (rv == -1) {
+ return -1;
+ }
+ else if (rv == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (FD_ISSET(fd, &xfds)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ /*
+ * Attempt to read off the socket
+ */
+ n = read(fd, buf + (off_t) total, remain);
+
+ /*
+ * When we know our socket was select()ed and we receive 0 bytes
+ * when we read, the socket was closed.
+ */
+ if ((n == 0) && (rv == 1)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ if (n == -1) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ /*
+ * Not ready? Wait for data to become available
+ */
+ continue;
+ }
+
+ /* Other errors: EPIPE, EINVAL, etc */
+ return -1;
+ }
+
+ total += n;
+ remain -= n;
+ }
+
+ return total;
+}
+
+
+static int ccs_open(cman_node_t node, uint16_t port, int timeout)
+{
+ struct in_addr *addr;
+ struct in6_addr *addr6;
+ int fd, family;
+ char buf[INET6_ADDRSTRLEN];
+
+ if (globalverbose) {
+ memset(buf, 0, sizeof(buf));
+ printf("Processing node: %s\n", node.cn_name);
+ }
+
+ family = ((struct sockaddr *)&(node.cn_address.cna_address))->sa_family;
+
+ if (family == AF_INET6) {
+
+ addr6 = &(((struct sockaddr_in6 *)&(node.cn_address.cna_address))->sin6_addr);
+
+ if (globalverbose) {
+ inet_ntop(family, addr6, buf, sizeof(buf));
+ printf(" family: ipv6\n address: %s\n", buf);
+ }
+
+ if ((fd = ipv6_connect(addr6, port, timeout)) < 0) {
+ return -1;
+ }
+
+ } else {
+
+ addr = &(((struct sockaddr_in *)&(node.cn_address.cna_address))->sin_addr);
+
+ if (globalverbose) {
+ inet_ntop(family, addr, buf, sizeof(buf));
+ printf(" family: ipv4\n address: %s\n", buf);
+ }
+
+ if ((fd = ipv4_connect(addr, port, timeout)) < 0) {
+ return -1;
+ }
+
+ }
+
+ return fd;
+}
+
+
+static int ipv4_connect(struct in_addr *addr, uint16_t port, int timeout)
+{
+ struct sockaddr_in sin;
+
+ int fd;
+
+ if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+
+ memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));
+
+ if (connect_nb(fd, (struct sockaddr *)&sin, sizeof(sin), timeout) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+
+static int ipv6_connect(struct in6_addr *addr, uint16_t port, int timeout)
+{
+ struct sockaddr_in6 sin6;
+
+ int fd;
+
+ if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ memset(&sin6, 0, sizeof(sin6));
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(port);
+ sin6.sin6_flowinfo = 0;
+
+ memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr));
+
+ if (connect_nb(fd, (struct sockaddr *)&sin6, sizeof(sin6), timeout)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+
+static int connect_nb(int fd, struct sockaddr *addr, socklen_t len, int timeout)
+{
+ int err;
+ int ret;
+ int flags = 1;
+ unsigned l;
+ fd_set rfds, wfds;
+
+ struct timeval tv;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) {
+ return -1;
+ }
+
+ flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ ret = connect(fd, addr, len);
+
+ if ((ret < 0) && (errno != EINPROGRESS)) {
+ return -1;
+ }
+
+ if (ret != 0) {
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ if (select_retry((fd + 1), &rfds, &wfds, NULL, &tv) == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) {
+ l = sizeof(err);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &l) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (err != 0) {
+ close(fd);
+ errno = err;
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, flags);
+ return 0;
+ }
+ }
+
+ errno = EIO;
+ return -1;
+}
diff --git a/config/tools/ccs_tool/update.h b/config/tools/ccs_tool/update.h
new file mode 100644
index 0000000..2f41aa4
--- /dev/null
+++ b/config/tools/ccs_tool/update.h
@@ -0,0 +1,6 @@
+#ifndef __UPDATE_DOT_H__
+#define __UPDATE_DOT_H__
+
+int update(char *location);
+
+#endif /* __UPDATE_DOT_H__ */
diff --git a/config/tools/man/Makefile b/config/tools/man/Makefile
index ee4d874..c40d94b 100644
--- a/config/tools/man/Makefile
+++ b/config/tools/man/Makefile
@@ -1,4 +1,10 @@
-TARGET= confdb2ldif.8
+TARGET= ccs_tool.8 \
+ confdb2ldif.8
include ../../../make/defines.mk
+
+ifdef legacy_code
+TARGET += ccs_test.8
+endif
+
include $(OBJDIR)/make/man.mk
diff --git a/config/tools/man/ccs_test.8 b/config/tools/man/ccs_test.8
new file mode 100644
index 0000000..9dce13c
--- /dev/null
+++ b/config/tools/man/ccs_test.8
@@ -0,0 +1,132 @@
+.TH ccs_test 8
+
+.SH NAME
+ccs_test - CCS daemon (ccsd) diagnostic tool
+
+.SH SYNOPSIS
+.B ccs_test
+[\fBoptions\fP]
+<\fBcommand\fP>
+
+.SH DESCRIPTION
+\fBccs_test\fP is part of the Cluster Configuration System (CCS). It is a
+diagnostic tool that reads cluster.conf information to test ccsd.
+
+.SH OPTIONS
+.TP
+\fB-h\fP
+Help. Print out the usage syntax and exit.
+.TP
+\fB-V\fP
+Print the version information and exit.
+
+.SH COMMANDS
+.TP
+\fBconnect\fP \fI[force]\fP \fI[block]\fP \fI[cluster name]\fP
+This command creates a connection to ccsd. It returns a descriptor, which
+is used as an parameter to other commands.
+
+The 'force' key-word is used to establish a connection to ccsd in the
+absence of a quorate cluster manager.
+
+The 'block' key-word is used (with the 'force' key-word) to tell ccsd to
+keep broadcasting for a valid configuration file until one is found.
+
+The 'cluster name' is used (with the 'force' key-word) to specify that
+only configuration files containing the given cluster name are valid
+possibilities.
+
+.TP
+\fBget\fP \fI<desc>\fP \fI<request>\fP
+Get the results of a given request. The 'desc' is the number returned
+from the \fBconnect\fP command. The 'request' is a valid Xpath request.
+
+If 'request' results in multiple matches, the first will be returned.
+Subsequent calls with the same 'request' will result in the subsequent
+matches. Once all the matches have been returned, a subsequent call
+will begin again with the first result.
+
+.TP
+\fBget_list\fP \fI<desc>\fP \fI<request>\fP
+Similar to the \fBget\fP command. However, issuing subsequent calls
+with the same 'request' will result in all matches being returned (one
+at a time), then null, then starting over with the first result.
+
+.TP
+\fBset\fP \fI<desc>\fP \fI<path>\fP \fI<value>\fP
+Sets a particular 'path' to the given 'value'. Not yet implemented.
+
+.TP
+\fBget_state\fP \fI<desc>\fP
+Get the state associated with a given connection.
+
+.TP
+\fBset_state\fP \fI<desc>\fP \fI<ncwp>\fP
+Set the current working path (cwp) to 'ncwp' for a given connection.
+
+.SH EXAMPLES
+.SS To connect to ccsd:
+
+> ccs_test connect
+
+Connect successful.
+ Connection descriptor = 0
+
+Or, if the cluster is not yet quorate and the name of the cluster is 'mycluster':
+
+> ccs_test connect force block mycluster
+
+Connect successful.
+ Connection descriptor = 0
+
+.SS To get the cluster name from ccsd:
+
+> ccs_test get 0 /cluster/@name
+
+Get successful.
+ Value = <mycluster>
+
+.SS To get the connection state:
+
+> ccs_test get_state 0
+
+Get state successful.
+ Current working path:
+ Previous query : /cluster/@name
+
+
+.SS To set the connection state:
+
+> ccs_test set_state 0 /cluster
+
+Set state successful.
+
+
+.SS After setting the connection state, note the change:
+
+> ccs_test get_state 0
+
+Get state successful.
+ Current working path: /cluster
+ Previous query : /cluster/@name
+
+.SS After setting the connection state, you can now query with an absolute or relative path:
+
+> ccs_test get 0 @name
+
+Get successful.
+ Value = <brassow>
+
+> ccs_test get 0 /cluster/@name
+
+Get successful.
+ Value = <brassow>
+
+.SS To disconnect:
+
+> ccs_test disconnect 0
+
+Disconnect successful.
+
+.SH SEE ALSO
+ccs(7), ccsd(8), cluster.conf(5)
diff --git a/config/tools/man/ccs_tool.8 b/config/tools/man/ccs_tool.8
new file mode 100644
index 0000000..ef13406
--- /dev/null
+++ b/config/tools/man/ccs_tool.8
@@ -0,0 +1,185 @@
+.TH "ccs_tool" "8" "" "" ""
+.SH "NAME"
+ccs_tool \- The tool used to make online updates of CCS config files.
+
+.SH "SYNOPSIS"
+.B ccs_tool
+[\fIOPTION\fR].. <\fBcommand\fP>
+
+.SH "DESCRIPTION"
+
+\fBccs_tool\fP is part of the Cluster Configuration System (CCS). It is
+used to make online updates to cluster.conf. It can also be used to
+upgrade old style (GFS <= 6.0) CCS archives to the new xml cluster.conf
+format.
+
+.SH "OPTIONS"
+.TP
+\fB\-h\fP
+Help. Print out the usage.
+.TP
+\fB\-V\fP
+Print the version information.
+
+sub\-commands have their own options, see below for more detail
+.SH "COMMANDS"
+.TP
+\fBupdate\fP \fI<xml file>\fP
+This command is used to update the config file that ccsd is working with
+while the cman cluster is operational (i.e. online). Run this on a single
+machine to update cluster.conf on all current cluster members. This also
+notifies cman of the new config version.
+
+.TP
+\fBupgrade\fP \fI<location>\fP
+This command is used to upgrade an old CCS format archive to the new
+xml format. \fI<location>\fP is the location of the old archive,
+which can be either a block device archive or a file archive. The
+converted configuration will be printed to stdout.
+
+.TP
+\fBaddnode\fP [options] \fI<node> [<fenceoption=value>]...\fP
+Adds a new node to the cluster configuration file. Fencing device options
+are specified as key=value pairs (as many as required) and are entered into the
+configuration file as is. See the documentation for your fencing agent for more
+details (eg a powerswitch fence device may need to know which port the node is
+connected to).
+.br
+\fIOptions:\fP
+.br
+\-v <votes> Number of votes for this node (mandatory)
+.br
+\-n <nodeid> Node id for this node (optional)
+.br
+\-i <interface> Network interface to use for this node. Mandatory if the cluster
+is using multicast as transport. Forbidden if not.
+.br
+\-m <multicast> Multicast address for cluster. Only allowed on the first node to
+be added to the file. Subsequent nodes will use either multicast or broadcast
+depending on the properties of the first node.
+.br
+\-f <fencedevice> Name of fence device to use for this node. The fence device
+section must already have been added to the file, probably using the addfence command.
+.br
+\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
+.br
+\-o <file> Output file. Defaults to the same as -c
+.br
+\-C Don't run "ccs_tool update" after changing file. This will
+happen by default if the input file is the same as the output file.
+.br
+\-F Force a "ccs_tool update" even if the input and output files
+are different.
+
+
+
+.TP
+\fBdelnode\fP [options] \fI<node>\fP
+Delete a node from the cluster configuration file. Note: there is no
+"edit" command so to change the properties of a node you must delete it
+and add it back in with the new properties.
+.br
+\fIOptions:\fP
+.br
+\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
+.br
+\-o <file> Output file. Defaults to the same as -c
+.br
+\-C Don't run "ccs_tool update" after changing file. This will
+happen by default if the input file is the same as the output file.
+.br
+\-F Force a "ccs_tool update" even if the input and output files
+are different.
+
+
+
+.TP
+\fBaddfence\fP [options] \fI<name> <agent> [<option>=<value>]...\fP
+Adds a new fence device section to the cluster configuration file. <agent> is the
+name of the fence agent that controls the device. the options following are entered
+as key-value pairs. See the fence agent documentation for details about these. eg:
+you may need to enter the IP address and username/password for a powerswitch fencing
+device.
+.br
+\fIOptions:\fP
+.br
+\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
+.br
+\-o <file> Output file. Defaults to the same as -c
+.br
+\-C Don't run "ccs_tool update" after changing file. This will
+happen by default if the input file is the same as the output file.
+.br
+\-F Force a "ccs_tool update" even if the input and output files
+are different.
+
+.TP
+\fBdelfence\fP [options] \fI<node>\fP
+Deletes a fencing device from the cluster configuration file.
+delfence will allow you to remove a fence device that is in use by nodes.
+This is to allow changes to be made, but be aware that it may produce an
+invalid configuration file if you don't add it back in again.
+.br
+\fIOptions:\fP
+.br
+\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
+.br
+\-o <file> Output file. Defaults to the same as -c
+.br
+\-C Don't run "ccs_tool update" after changing file. This will
+happen by default if the input file is the same as the output file.
+.br
+\-F Force a "ccs_tool update" even if the input and output files
+are different.
+
+
+.TP
+\fBlsnode [options] \fP
+List the nodes in the configuration file. This is (hopefully obviously) not
+necessarily the same as the nodes currently in the cluster, but it should
+be a superset.
+.br
+\fIOptions:\fP
+.br
+\-v Verbose. Lists all the properties of the node, and the
+node-specific properties of the fence device too.
+.br
+\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
+
+
+.TP
+\fBlsfence [options] \fP
+List all the fence devices in the cluster configuration file.
+.br
+\fIOptions:\fP
+.br
+\-v Verbose. Lists all the properties of the fence device rather
+than just the names and agents.
+.br
+\-c <file> Config file to use. Defaults to /etc/cluster/cluster.conf
+
+
+.TP
+\fBcreate [options] \fP \fI<clustername>\fP
+Create a new, skeleton, configuration file. Note that "create" on its own will
+not create a valid configuration file. Fence agents and nodes will need to be
+added to it before handing it over to ccsd. The new configuration file will
+have a version number of 1. Subsequent addnode/delnode/addfence/delfence operations
+will increment the version number by 1 each time.
+.br
+\fIOptions:\fP
+.br
+.br
+\-c <file> Config file to create. Defaults to /etc/cluster/cluster.conf
+
+.TP
+\fBaddnodeids\fP
+Adds node ID numbers to all the nodes in cluster.conf. In RHEL4, node IDs were optional
+and assigned by cman when a node joined the cluster. In RHEL5 they must be pre-assigned
+in cluster.conf. This command will not change any node IDs that are already set in
+cluster.conf, it will simply add unique node ID numbers to nodes that do not already
+have them.
+
+
+.SH "SEE ALSO"
+ccs(7), ccsd(8), cluster.conf(5)
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2008-08-14 15:54 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-14 16:00 master - config: move ccs/ccs_tool to config/tools/ccs_tool Fabio M. Di Nitto
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).