diff --git a/cman/cman_tool/Makefile b/cman/cman_tool/Makefile index 0dee578..149b758 100644 --- a/cman/cman_tool/Makefile +++ b/cman/cman_tool/Makefile @@ -13,12 +13,14 @@ include $(OBJDIR)/make/uninstall.mk OBJS= main.o \ join.o -CFLAGS += -DCOROSYNCBIN=\"${corosyncbin}\" +CFLAGS += -DCOROSYNCBIN=\"${corosyncbin}\" -DSBINDIR=\"${sbindir}\" CFLAGS += -I${cmanincdir} CFLAGS += -I${incdir} +CFLAGS += -I${ccsincdir} LDFLAGS += -L${cmanlibdir} -lcman LDFLAGS += -L${libdir} +LDFLAGS += -L${ccslibdir} -lconfdb -lccs ${TARGET}: ${OBJS} $(CC) -o $@ $^ $(LDFLAGS) diff --git a/cman/cman_tool/cman_tool.h b/cman/cman_tool/cman_tool.h index d6374d5..a766a12 100644 --- a/cman/cman_tool/cman_tool.h +++ b/cman/cman_tool/cman_tool.h @@ -82,6 +82,7 @@ struct commandline unsigned int config_version; int config_version_opt; + int config_novalidate_opt; int votes_opt; int expected_votes_opt; int port_opt; diff --git a/cman/cman_tool/join.c b/cman/cman_tool/join.c index 02ef73a..697e944 100644 --- a/cman/cman_tool/join.c +++ b/cman/cman_tool/join.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "libcman.h" #include "cman_tool.h" @@ -111,10 +112,19 @@ int join(commandline_t *comline, char *main_envp[]) int envptr = 0; int argvptr = 0; char scratch[1024]; + char config_modules[1024]; cman_handle_t h = NULL; int status; + hdb_handle_t object_handle; + confdb_handle_t confdb_handle; + int res; pid_t corosync_pid; int p[2]; + confdb_callbacks_t callbacks = { + .confdb_key_change_notify_fn = NULL, + .confdb_object_create_change_notify_fn = NULL, + .confdb_object_delete_change_notify_fn = NULL + }; /* * If we can talk to cman then we're already joined (or joining); @@ -166,15 +176,15 @@ int join(commandline_t *comline, char *main_envp[]) } if (comline->noconfig_opt) { envp[envptr++] = strdup("CMAN_NOCONFIG=true"); - snprintf(scratch, sizeof(scratch), "COROSYNC_DEFAULT_CONFIG_IFACE=cmanpreconfig%s", + snprintf(config_modules, sizeof(config_modules), "cmanpreconfig%s", comline->noopenais_opt?"":":openaisserviceenablestable"); - envp[envptr++] = strdup(scratch); } else { - snprintf(scratch, sizeof(scratch), "COROSYNC_DEFAULT_CONFIG_IFACE=%s:cmanpreconfig%s", comline->config_lcrso, + snprintf(config_modules, sizeof(config_modules), "%s:cmanpreconfig%s", comline->config_lcrso, comline->noopenais_opt?"":":openaisserviceenablestable"); - envp[envptr++] = strdup(scratch); } + snprintf(scratch, sizeof(scratch), "COROSYNC_DEFAULT_CONFIG_IFACE=%s", config_modules); + envp[envptr++] = strdup(scratch); /* Copy any COROSYNC_* env variables to the new daemon */ i=0; @@ -324,5 +334,19 @@ int join(commandline_t *comline, char *main_envp[]) fprintf(stderr, "corosync started, but not joined the cluster yet.\n"); cman_finish(h); + + /* Save the configuration information in corosync's objdb so we know where we came from */ + res = confdb_initialize (&confdb_handle, &callbacks); + if (res != CS_OK) + goto join_exit; + + res = confdb_object_create(confdb_handle, OBJECT_PARENT_HANDLE, "cman_private", strlen("cman_private"), &object_handle); + if (res == CS_OK) { + res = confdb_key_create(confdb_handle, object_handle, "config_modules", strlen("config_modules"), config_modules, strlen(config_modules)); + + } + confdb_finalize (confdb_handle); + +join_exit: return 0; } diff --git a/cman/cman_tool/main.c b/cman/cman_tool/main.c index 2d28bc2..d60a1c3 100644 --- a/cman/cman_tool/main.c +++ b/cman/cman_tool/main.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "copyright.cf" #include "libcman.h" @@ -9,7 +10,7 @@ #define DEFAULT_CONFIG_MODULE "xmlconfig" -#define OPTION_STRING ("m:n:v:e:2p:c:r:i:N:t:o:k:F:C:VAPwfqah?Xd::") +#define OPTION_STRING ("m:n:v:e:2p:c:r:i:N:t:o:k:F:C:VAPwfqah?XDd::") #define OP_JOIN 1 #define OP_LEAVE 2 #define OP_EXPECTED 3 @@ -116,6 +117,7 @@ static void print_usage(int subcmd) if (!subcmd || subcmd == OP_VERSION) { printf("version\n"); printf(" -r A new config version to set on all members\n"); + printf(" -D Don't check configuration for validity before loading\n"); printf("\n"); } } @@ -642,11 +644,73 @@ static void set_votes(commandline_t *comline) cman_finish(h); } +static int validate_config(commandline_t *comline, int *setversion) +{ + struct stat st; + int ccs_handle; + int ccs_res; + int cmd_res; + char *config_value; + char validator[PATH_MAX]; + char command[PATH_MAX]; + + *setversion = 0; + + /* Look for ccs_config_validate */ + snprintf(validator, sizeof(validator), "%s/ccs_config_validate", SBINDIR); + if (stat(validator, &st) != 0 || !(st.st_mode & S_IXUSR)) { + fprintf(stderr, "Cannot find ccs_config_validate, configuration was not checked but assumed to be OK.\n"); + return 0; + } + + /* Get the config modules that corosync was loaded with */ + ccs_handle = ccs_connect(); + if (!ccs_handle) { + fprintf(stderr, "Cannot contact ccs to get configuration information\n"); + return 1; + } + + ccs_res = ccs_get(ccs_handle, "/cman_private/@config_modules", &config_value); + ccs_disconnect(ccs_handle); + if (ccs_res) { + fprintf(stderr, "Cannot work out where corosync got it's configuration information from so I\n"); + fprintf(stderr, "can't validate it. Use the -D switch to bypass this and load the\n"); + fprintf(stderr, "configuration unchecked.\n"); + return 1; + } + + snprintf(command, sizeof(command), "%s -C %s", validator, config_value); + + if (comline->verbose > 1) + printf("calling '%s'\n", command); + + cmd_res = system(command); + + /* If it's OK and we are using xmlconfig then call ccs_sync to distribute it */ + if (cmd_res == 0 && strstr(config_value, "xmlconfig")) { + if (comline->verbose > 1) + printf("calling ccs_sync\n"); + + cmd_res = system("/usr/bin/ccs_sync"); + /* + * if this succeeds then it will tell cman the new version, + * so we don't have to. + */ + } + else { + /* Tell the caller to inform cman of the new version */ + *setversion = 1; + } + + return cmd_res; +} + static void version(commandline_t *comline) { struct cman_version ver; cman_handle_t h; int result; + int need_setversion=1; h = open_cman_handle(1); @@ -659,10 +723,23 @@ static void version(commandline_t *comline) goto out; } - ver.cv_config = comline->config_version; + /* By default we validate the configuration first */ + if (!comline->config_novalidate_opt) { + if (comline->verbose) + printf("Validating configuration\n"); + if (validate_config(comline, &need_setversion)) + die("Not reloading, configuration is not valid\n"); + } - if ((result = cman_set_version(h, &ver))) - die("can't set version: %s", cman_error(errno)); + if (need_setversion) { + if (comline->verbose) + printf("Telling cman the new version number\n"); + + ver.cv_config = comline->config_version; + + if ((result = cman_set_version(h, &ver))) + die("can't set version: %s", cman_error(errno)); + } out: cman_finish(h); } @@ -767,6 +844,10 @@ static void decode_arguments(int argc, char *argv[], commandline_t *comline) comline->addresses_opt = 1; break; + case 'D': + comline->config_novalidate_opt = 1; + break; + case 'n': i = comline->num_nodenames; if (i >= MAX_INTERFACES) diff --git a/cman/man/cman_tool.8 b/cman/man/cman_tool.8 index 8d45048..a257cc3 100644 --- a/cman/man/cman_tool.8 +++ b/cman/man/cman_tool.8 @@ -69,12 +69,11 @@ with -r to tell cluster members to update. .br The argument to -r is the version number that cman should look for. If that version is not currently available then cman will poll for it. If -a version of 0 is specified then cman will simply re-read the configuration -and use the version number it finds there. +a version of 0 is specified then cman will read the configuration file, +validate it, distribute it around the cluster (if necessary) and +activate it. .br -NOTE: this happens cluster-wide and the highest version number in the -cluster will be the one everyone looks for. version -r0 is not a way -to have different config versions across nodes in the cluster! +The -D flag will disable the validation stage. This is NOT recommended. .TP .I wait