diff --git a/crm/admin/Makefile.am b/crm/admin/Makefile.am index 9defc318a4..a20a83e033 100644 --- a/crm/admin/Makefile.am +++ b/crm/admin/Makefile.am @@ -1,101 +1,104 @@ # # Copyright (C) 2004 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir) -I$(top_srcdir) \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(AISPREFIX)/include/openais halibdir = $(libdir)/@HB_PKG@ COMMONLIBS = \ $(top_builddir)/lib/crm/common/libcrmcommon.la \ $(top_builddir)/lib/crm/cib/libcib.la \ -lplumb \ $(GLIBLIB) \ $(CURSESLIBS) \ $(CLUSTERLIBS) \ $(LIBRT) $(GNUTLSLIBS) halib_PYTHON = crm_primitive.py crm_utils.py crm_commands.py sbin_PROGRAMS = crmadmin cibadmin ccm_tool crm_diff crm_mon iso8601 \ crm_master crm_standby crm_failcount crm_attribute \ - crm_resource crm_verify crm_uuid + crm_resource crm_verify crm_uuid cib_shadow sbin_SCRIPTS = crm_sh ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = crmadmin_SOURCES = crmadmin.c crmadmin_LDADD = $(COMMONLIBS) $(CLUSTERLIBS) \ $(top_builddir)/lib/crm/pengine/libpe_status.la crm_uuid_SOURCES = crm_uuid.c crm_uuid_LDADD = $(GLIBLIB) cibadmin_SOURCES = cibadmin.c cibadmin_LDADD = $(COMMONLIBS) +cib_shadow_SOURCES = cib_shadow.c +cib_shadow_LDADD = $(COMMONLIBS) + ccm_tool_SOURCES = ccm_epoche.c ccm_tool_LDADD = $(COMMONLIBS) $(CLUSTERLIBS) \ $(top_builddir)/lib/crm/common/libcrmcluster.la crm_diff_SOURCES = xml_diff.c crm_diff_LDADD = $(COMMONLIBS) crm_mon_SOURCES = crm_mon.c crm_mon_LDADD = $(COMMONLIBS) -llrm \ $(top_builddir)/lib/crm/pengine/libpe_status.la # Arguments could be made that this should live in crm/pengine crm_verify_SOURCES = crm_verify.c crm_verify_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/crm/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la crm_master_SOURCES = crm_attribute.c crm_master_LDADD = $(COMMONLIBS) crm_standby_SOURCES = crm_attribute.c crm_standby_LDADD = $(COMMONLIBS) crm_attribute_SOURCES = crm_attribute.c crm_attribute_LDADD = $(COMMONLIBS) crm_failcount_SOURCES = crm_attribute.c crm_failcount_LDADD = $(COMMONLIBS) crm_resource_SOURCES = crm_resource.c crm_resource_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/crm/pengine/libpe_rules.la \ $(top_builddir)/lib/crm/pengine/libpe_status.la iso8601_SOURCES = test.iso8601.c iso8601_LDADD = $(COMMONLIBS) clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: diff --git a/crm/admin/cib_shadow.c b/crm/admin/cib_shadow.c new file mode 100644 index 0000000000..6d363a1dff --- /dev/null +++ b/crm/admin/cib_shadow.c @@ -0,0 +1,353 @@ + +/* + * Copyright (C) 2004 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_GETOPT_H +# include +#endif + +int exit_code = cib_ok; +GMainLoop *mainloop = NULL; +IPC_Channel *crmd_channel = NULL; + +const char *host = NULL; +void usage(const char *cmd, int exit_status); + +int command_options = cib_sync_call; +const char *cib_action = NULL; + +cib_t *real_cib = NULL; + +static int force_flag = 0; +#define OPTARGS "V?wc:d:r:C:D:" + + +int +main(int argc, char **argv) +{ + int rc = 0; + int flag; + int argerr = 0; + static int command = '?'; + char *shadow = NULL; + char *shadow_file = NULL; + char *admin_input_xml = NULL; + char *admin_input_file = NULL; + gboolean dangerous_cmd = FALSE; + gboolean admin_input_stdin = FALSE; + xmlNode *input = NULL; + struct stat buf; + +#ifdef HAVE_GETOPT_H + int option_index = 0; + static struct option long_options[] = { + /* Top-level Options */ + {"create", required_argument, NULL, 'c'}, + {"display", required_argument, NULL, 'd'}, + {"commit", required_argument, NULL, 'C'}, + {"delete", required_argument, NULL, 'D'}, + {"reset", required_argument, NULL, 'r'}, + {"which", no_argument, NULL, 'w'}, + + {"force", no_argument, &force_flag, 1}, + {"xml-text", required_argument, NULL, 'X'}, + {"xml-file", required_argument, NULL, 'x'}, + {"xml-pipe", no_argument, NULL, 'p'}, + {"verbose", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, '?'}, + + {0, 0, 0, 0} + }; +#endif + + crm_log_init("cib_shadow", LOG_CRIT, FALSE, FALSE, argc, argv); + + if(argc < 2) { + usage(crm_system_name, LSB_EXIT_EINVAL); + } + + while (1) { +#ifdef HAVE_GETOPT_H + flag = getopt_long(argc, argv, OPTARGS, + long_options, &option_index); +#else + flag = getopt(argc, argv, OPTARGS); +#endif + if (flag == -1 || flag == 0) + break; + + switch(flag) { + case 'c': + case 'd': + case 'r': + case 'w': + command = flag; + shadow = crm_strdup(optarg); + break; + case 'C': + case 'D': + command = flag; + dangerous_cmd = TRUE; + shadow = crm_strdup(optarg); + break; + case 'V': + command_options = command_options | cib_verbose; + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case '?': + usage(crm_system_name, LSB_EXIT_OK); + break; + case 'X': + crm_debug_2("Option %c => %s", flag, optarg); + admin_input_xml = crm_strdup(optarg); + break; + case 'x': + crm_debug_2("Option %c => %s", flag, optarg); + admin_input_file = crm_strdup(optarg); + break; + case 'p': + admin_input_stdin = TRUE; + break; + case 'f': + command_options |= cib_quorum_override; + break; + default: + printf("Argument code 0%o (%c)" + " is not (?yet?) supported\n", + flag, flag); + ++argerr; + break; + } + } + + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(crm_system_name, LSB_EXIT_EINVAL); + } + + if (optind > argc) { + ++argerr; + } + + if (argerr) { + usage(crm_system_name, LSB_EXIT_GENERIC); + } + + if(command == 'w') { + /* which shadow instance is active? */ + const char *local = getenv("CIB_shadow"); + if(local == NULL) { + fprintf(stderr, "No shadow instance provided\n"); + return cib_NOTEXISTS; + } + fprintf(stdout, "%s\n", local); + return 0; + } + + if(shadow == NULL) { + fprintf(stderr, "No shadow instance provided\n"); + fflush(stderr); + return CIBRES_MISSING_FIELD; + + } else { + const char *local = getenv("CIB_shadow"); + if(local != NULL && safe_str_neq(local, shadow) && force_flag == FALSE) { + fprintf(stderr, "The supplied shadow instance (%s) is not the same as the active one (%s).\n" + " To prevent accidental destruction of the cluster," + " the --force flag is required in order to proceed.\n", shadow, local); + fflush(stderr); + usage(crm_system_name, LSB_EXIT_GENERIC); + } + } + + if(dangerous_cmd && force_flag == FALSE) { + fprintf(stderr, "The supplied command is considered dangerous." + " To prevent accidental destruction of the cluster," + " the --force flag is required in order to proceed.\n"); + fflush(stderr); + usage(crm_system_name, LSB_EXIT_GENERIC); + } + + if(admin_input_file != NULL) { + FILE *xml_strm = fopen(admin_input_file, "r"); + input = file2xml(xml_strm, FALSE); + if(input == NULL) { + fprintf(stderr, "Couldn't parse input file: %s\n", admin_input_file); + return 1; + } + fclose(xml_strm); + + } else if(admin_input_xml != NULL) { + input = string2xml(admin_input_xml); + if(input == NULL) { + fprintf(stderr, "Couldn't parse input string: %s\n", admin_input_xml); + return 1; + } + + } else if(admin_input_stdin) { + input = stdin2xml(); + if(input == NULL) { + fprintf(stderr, "Couldn't parse input from STDIN.\n"); + return 1; + } + } + + if(input != NULL) { + crm_log_xml_debug(input, "[admin input]"); + } + + shadow_file = get_shadow_file(shadow); + if(command == 'D') { + /* delete the file */ + rc = stat(shadow_file, &buf); + if(rc == 0) { + rc = unlink(shadow_file); + if(rc != 0) { + fprintf(stderr, "Could not remove shadow instance '%s': %s\n", shadow, strerror(errno)); + return rc; + } + } + printf("Please remember to unset the CIB_shadow variable by pasting the following into your shell:\n"); + printf(" unset CIB_shadow\n"); + return rc; + } + + if(command == 'r' || command == 'c' || command == 'C') { + real_cib = cib_new_no_shadow(); + rc = real_cib->cmds->signon(real_cib, crm_system_name, cib_command); + if(rc != cib_ok) { + fprintf(stderr, "Signon to CIB failed: %s\n", cib_error2string(rc)); + return rc; + } + } + + rc = stat(shadow_file, &buf); + if(rc != 0 && command != 'c') { + fprintf(stderr, "Could not access shadow instance '%s': %s\n", shadow, strerror(errno)); + return cib_NOTEXISTS; + } + + if(command == 'c') { + xmlNode *output = NULL; + /* create a shadow instance based on the current cluster config */ + if (rc == 0 && force_flag == FALSE) { + fprintf(stderr, "A shadow instance '%s' already exists.\n" + " To prevent accidental destruction of the cluster," + " the --force flag is required in order to proceed.\n", shadow); + return cib_EXISTS; + } + + rc = real_cib->cmds->query(real_cib, NULL, &output, command_options); + if(rc != cib_ok) { + fprintf(stderr, "Could not connect to the CIB: %s\n", cib_error2string(rc)); + return rc; + } + + rc = write_xml_file(output, shadow_file, FALSE); + if(rc < 0) { + fprintf(stderr, "Could not create the shadow instance '%s': %s\n", + shadow, strerror(errno)); + return rc; + } + + printf("A new shadow instance was created. To begin using it paste the following into your shell:\n"); + printf(" CIB_shadow=%s ; export CIB_shadow\n", shadow); + + } else if(command == 'd') { + char *output_s = NULL; + FILE *shadow_FILE = fopen(shadow_file, "r"); + xmlNode *output = file2xml(shadow_FILE, FALSE); + + output_s = dump_xml_formatted(output); + printf("%s", output_s); + + crm_free(output_s); + free_xml(output); + + } else if(command == 'C') { + /* commit to the cluster */ + FILE *shadow_FILE = fopen(shadow_file, "r"); + xmlNode *input = file2xml(shadow_FILE, FALSE); + rc = real_cib->cmds->replace(real_cib, NULL, input, NULL, command_options); + if(rc != cib_ok) { + fprintf(stderr, "Could not commit shadow instance '%s' to the CIB: %s\n", + shadow, cib_error2string(rc)); + return rc; + } + printf("Please remember to unset the CIB_shadow variable by pasting the following into your shell:\n"); + printf(" unset CIB_shadow\n"); + } + + return rc; +} + +void +usage(const char *cmd, int exit_status) +{ + FILE *stream; + + stream = exit_status != 0 ? stderr : stdout; + + fprintf(stream, "usage: %s -[%s]\n", cmd, OPTARGS); + + fprintf(stream, "Options\n"); + fprintf(stream, "\t--%s (-%c)\tturn on debug info." + " additional instance increase verbosity\n", "verbose", 'V'); + fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help ", '?'); + fprintf(stream, "\nCommands\n"); + fprintf(stream, "\t--%s (-%c)\tIndicate the active shadow copy\n", "which ", 'w'); + fprintf(stream, "\t--%s (-%c) name\tCreate the named shadow copy of the active cluster configuration\n", "create ", 'c'); + fprintf(stream, "\t--%s (-%c) name\tDisplay the contents of the named shadow copy \n", "display", 'd'); + fprintf(stream, "\t--%s (-%c) name\tRecreate the named shadow copy from the active cluster configuration\n", "reset ", 'r'); + fprintf(stream, "\t--%s (-%c) name\tUpload the contents of the named shadow copy to the cluster\n", "commit ", 'C'); + fprintf(stream, "\t--%s (-%c) name\tDelete the contents of the named shadow copy\n", "delete ", 'D'); + fprintf(stream, "\nAdvanced Options\n"); + fprintf(stream, "\t--%s (-%c)\tForce the action to be performed\n", "force ", 'f'); + + fflush(stream); + + exit(exit_status); +} diff --git a/include/crm/cib.h b/include/crm/cib.h index 2fc84dd891..e604e3d694 100644 --- a/include/crm/cib.h +++ b/include/crm/cib.h @@ -1,313 +1,317 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB__H #define CIB__H #include #include #include #include #define CIB_FEATURE_SET "2.0" #define USE_PESKY_FRAGMENTS 1 /* use compare_version() for doing comparisons */ enum cib_variant { cib_undefined, cib_native, cib_file, cib_remote, cib_database, cib_edir }; enum cib_state { cib_connected_command, cib_connected_query, cib_disconnected }; enum cib_conn_type { cib_command, cib_query, cib_query_synchronous, cib_command_synchronous, cib_no_connection }; enum cib_call_options { cib_none = 0x00000000, cib_verbose = 0x00000001, cib_discard_reply = 0x00000010, cib_scope_local = 0x00000100, cib_sync_call = 0x00001000, cib_inhibit_notify = 0x00010000, cib_quorum_override = 0x00100000, cib_inhibit_bcast = 0x01000000, cib_force_diff = 0x10000000 }; #define cib_default_options = cib_none enum cib_errors { cib_ok = 0, cib_operation = -1, cib_create_msg = -2, cib_not_connected = -3, cib_not_authorized = -4, cib_send_failed = -5, cib_reply_failed = -6, cib_return_code = -7, cib_output_ptr = -8, cib_output_data = -9, cib_connection = -10, cib_authentication = -11, cib_missing = -12, cib_variant = -28, CIBRES_MISSING_ID = -13, CIBRES_MISSING_TYPE = -14, CIBRES_MISSING_FIELD = -15, CIBRES_OBJTYPE_MISMATCH = -16, CIBRES_CORRUPT = -17, CIBRES_OTHER = -18, cib_unknown = -19, cib_STALE = -20, cib_EXISTS = -21, cib_NOTEXISTS = -22, cib_ACTIVATION = -23, cib_NOSECTION = -24, cib_NOOBJECT = -25, cib_NOPARENT = -26, cib_NODECOPY = -27, cib_NOTSUPPORTED = -29, cib_registration_msg = -30, cib_callback_token = -31, cib_callback_register = -32, cib_msg_field_add = -33, cib_client_gone = -34, cib_not_master = -35, cib_client_corrupt = -36, cib_master_timeout = -37, cib_revision_unsupported= -38, cib_revision_unknown = -39, cib_missing_data = -40, cib_remote_timeout = -41, cib_no_quorum = -42, cib_diff_failed = -43, cib_diff_resync = -44, cib_old_data = -45, cib_id_check = -46, cib_dtd_validation = -47, cib_bad_section = -48, cib_bad_digest = -49, cib_bad_permissions = -50, cib_bad_config = -51, cib_invalid_argument = -52 }; enum cib_update_op { CIB_UPDATE_OP_NONE = 0, CIB_UPDATE_OP_ADD, CIB_UPDATE_OP_MODIFY, CIB_UPDATE_OP_DELETE, CIB_UPDATE_OP_MAX }; enum cib_section { cib_section_none, cib_section_all, cib_section_nodes, cib_section_constraints, cib_section_resources, cib_section_crmconfig, cib_section_status }; #define CIB_OP_SLAVE "cib_slave" #define CIB_OP_SLAVEALL "cib_slave_all" #define CIB_OP_MASTER "cib_master" #define CIB_OP_SYNC "cib_sync" #define CIB_OP_SYNC_ONE "cib_sync_one" #define CIB_OP_ISMASTER "cib_ismaster" #define CIB_OP_BUMP "cib_bump" #define CIB_OP_QUERY "cib_query" #define CIB_OP_CREATE "cib_create" #define CIB_OP_UPDATE "cib_update" #define CIB_OP_MODIFY "cib_modify" #define CIB_OP_DELETE "cib_delete" #define CIB_OP_DELETE_ALT "cib_delete_alt" #define CIB_OP_ERASE "cib_erase" #define CIB_OP_REPLACE "cib_replace" #define CIB_OP_NOTIFY "cib_notify" #define CIB_OP_APPLY_DIFF "cib_apply_diff" #define F_CIB_CLIENTID "cib_clientid" #define F_CIB_CALLOPTS "cib_callopt" #define F_CIB_CALLID "cib_callid" #define F_CIB_CALLDATA "cib_calldata" #define F_CIB_OPERATION "cib_op" #define F_CIB_ISREPLY "cib_isreplyto" #define F_CIB_SECTION "cib_section" #define F_CIB_HOST "cib_host" #define F_CIB_RC "cib_rc" #define F_CIB_DELEGATED "cib_delegated_from" #define F_CIB_OBJID "cib_object" #define F_CIB_OBJTYPE "cib_object_type" #define F_CIB_EXISTING "cib_existing_object" #define F_CIB_SEENCOUNT "cib_seen" #define F_CIB_TIMEOUT "cib_timeout" #define F_CIB_UPDATE "cib_update" #define F_CIB_CALLBACK_TOKEN "cib_async_id" #define F_CIB_GLOBAL_UPDATE "cib_update" #define F_CIB_UPDATE_RESULT "cib_update_result" #define F_CIB_CLIENTNAME "cib_clientname" #define F_CIB_NOTIFY_TYPE "cib_notify_type" #define F_CIB_NOTIFY_ACTIVATE "cib_notify_activate" #define F_CIB_UPDATE_DIFF "cib_update_diff" #define T_CIB "cib" #define T_CIB_NOTIFY "cib_notify" /* notify sub-types */ #define T_CIB_PRE_NOTIFY "cib_pre_notify" #define T_CIB_POST_NOTIFY "cib_post_notify" #define T_CIB_UPDATE_CONFIRM "cib_update_confirmation" #define T_CIB_DIFF_NOTIFY "cib_diff_notify" #define T_CIB_REPLACE_NOTIFY "cib_refresh_notify" #define cib_channel_ro "cib_ro" #define cib_channel_rw "cib_rw" #define cib_channel_callback "cib_callback" #define cib_channel_ro_synchronous "cib_ro_syncronous" #define cib_channel_rw_synchronous "cib_rw_syncronous" typedef struct cib_s cib_t; typedef struct cib_api_operations_s { int (*variant_op)( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*signon) ( cib_t *cib, const char *name, enum cib_conn_type type); int (*signoff)(cib_t *cib); int (*free) (cib_t *cib); int (*set_op_callback)( cib_t *cib, void (*callback)( const xmlNode *msg, int callid , int rc, xmlNode *output)); int (*add_notify_callback)( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); int (*del_notify_callback)( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); int (*set_connection_dnotify)( cib_t *cib, void (*dnotify)(gpointer user_data)); int (*inputfd)(cib_t* cib); int (*noop)(cib_t *cib, int call_options); int (*ping)( cib_t *cib, xmlNode **output_data, int call_options); int (*query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options); int (*query_from)( cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options); int (*is_master) (cib_t *cib); int (*set_master)(cib_t *cib, int call_options); int (*set_slave) (cib_t *cib, int call_options); int (*set_slave_all)(cib_t *cib, int call_options); int (*sync)(cib_t *cib, const char *section, int call_options); int (*sync_from)( cib_t *cib, const char *host, const char *section, int call_options); int (*bump_epoch)(cib_t *cib, int call_options); int (*create)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*modify)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*update)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*replace)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*delete)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*delete_absolute)( cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*erase)( cib_t *cib, xmlNode **output_data, int call_options); int (*quit)(cib_t *cib, int call_options); int (*register_callback)( cib_t* cib, const char *callback, int enabled); } cib_api_operations_t; struct cib_s { enum cib_state state; enum cib_conn_type type; enum cib_variant variant; int call_id; int call_timeout; void *variant_opaque; GList *notify_list; void (*op_callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output); cib_api_operations_t *cmds; }; /* Core functions */ extern cib_t *cib_new(void); extern cib_t *cib_native_new(void); extern cib_t *cib_file_new(const char *filename); extern cib_t *cib_remote_new(const char *server, const char *user, const char *passwd, int port); +extern cib_t *cib_new_no_shadow(void); +extern char *get_shadow_file(const char *name); +extern cib_t *cib_shadow_new(const char *name); + extern void cib_delete(cib_t *cib); extern int num_cib_op_callbacks(void); extern void remove_cib_op_callback(int call_id, gboolean all_callbacks); extern gboolean add_cib_op_callback_timeout( int call_id, int timeout, gboolean only_success, void *user_data, void (*callback)(xmlNode*, int, int, xmlNode*,void*)); #define add_cib_op_callback(id, flag, data, fn) add_cib_op_callback_timeout(id, 0, flag, data, fn) #include #include #endif diff --git a/lib/crm/cib/cib_client.c b/lib/crm/cib/cib_client.c index 713d734169..072004a193 100644 --- a/lib/crm/cib/cib_client.c +++ b/lib/crm/cib/cib_client.c @@ -1,519 +1,551 @@ /* * Copyright (c) 2004 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include GHashTable *cib_op_callback_table = NULL; int cib_client_set_op_callback( cib_t *cib, void (*callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output)); int cib_client_add_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); int cib_client_del_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b); #define op_common(cib) \ if(cib == NULL) { \ return cib_missing; \ } else if(cib->cmds->variant_op == NULL) { \ return cib_variant; \ } static int cib_client_noop(cib_t *cib, int call_options) { op_common(cib) return cib->cmds->variant_op( cib, CRM_OP_NOOP, NULL, NULL, NULL, NULL, call_options); } static int cib_client_ping(cib_t *cib, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op( cib, CRM_OP_PING, NULL,NULL,NULL, output_data, call_options); } static int cib_client_query(cib_t *cib, const char *section, xmlNode **output_data, int call_options) { return cib->cmds->query_from( cib, NULL, section, output_data, call_options); } static int cib_client_query_from(cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_QUERY, host, section, NULL, output_data, call_options); } static int cib_client_is_master(cib_t *cib) { op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_ISMASTER, NULL, NULL,NULL,NULL, cib_scope_local|cib_sync_call); } static int cib_client_set_slave(cib_t *cib, int call_options) { op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_SLAVE, NULL,NULL,NULL,NULL, call_options); } static int cib_client_set_slave_all(cib_t *cib, int call_options) { op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_SLAVEALL, NULL,NULL,NULL,NULL, call_options); } static int cib_client_set_master(cib_t *cib, int call_options) { op_common(cib) crm_debug_3("Adding cib_scope_local to options"); return cib->cmds->variant_op( cib, CIB_OP_MASTER, NULL,NULL,NULL,NULL, call_options|cib_scope_local); } static int cib_client_bump_epoch(cib_t *cib, int call_options) { op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_BUMP, NULL, NULL, NULL, NULL, call_options); } static int cib_client_sync(cib_t *cib, const char *section, int call_options) { return cib->cmds->sync_from(cib, NULL, section, call_options); } static int cib_client_sync_from( cib_t *cib, const char *host, const char *section, int call_options) { op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_SYNC, host, section, NULL, NULL, call_options); } static int cib_client_create(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_CREATE, NULL, section, data, output_data, call_options); } static int cib_client_modify(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_MODIFY, NULL, section, data, output_data, call_options); } static int cib_client_update(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_UPDATE, NULL, section, data, output_data, call_options); } static int cib_client_replace(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_REPLACE, NULL, section, data, output_data, call_options); } static int cib_client_delete(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_DELETE, NULL, section, data, output_data, call_options); } static int cib_client_delete_absolute( cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_DELETE_ALT, NULL, section, data, output_data, call_options); } static int cib_client_erase( cib_t *cib, xmlNode **output_data, int call_options) { op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_ERASE, NULL, NULL, NULL, output_data, call_options); } static int cib_client_quit(cib_t *cib, int call_options) { op_common(cib) return cib->cmds->variant_op( cib, CRM_OP_QUIT, NULL, NULL, NULL, NULL, call_options); } static void cib_destroy_op_callback(gpointer data) { cib_callback_client_t *blob = data; if(blob->timer && blob->timer->ref > 0) { g_source_remove(blob->timer->ref); } crm_free(blob); } +char *get_shadow_file(const char *name) +{ + return crm_concat(WORKING_DIR"/shadow", name, '.'); +} + + +cib_t* +cib_shadow_new(const char *shadow) +{ + cib_t *new_cib = NULL; + char *shadow_file = NULL; + CRM_CHECK(shadow != NULL, return NULL); + + shadow_file = get_shadow_file(shadow); + new_cib = cib_file_new(shadow_file); + crm_free(shadow_file); + + return new_cib; +} + +cib_t *cib_new_no_shadow(void) +{ + unsetenv("CIB_shadow"); + return cib_new(); +} + + cib_t* cib_new(void) { - const char *file = getenv("CIB_file"); - if(file) { - return cib_file_new(file); + const char *value = getenv("CIB_shadow"); + if(value) { + return cib_shadow_new(value); + } + + value = getenv("CIB_file"); + if(value) { + return cib_file_new(value); } - if(getenv("CIB_port")) { + value = getenv("CIB_port"); + if(value) { + int port = crm_parse_int(value, NULL); const char *server = getenv("CIB_server"); const char *user = getenv("CIB_user"); const char *pass = getenv("CIB_passwd"); - const char *port_s = getenv("CIB_port"); - int port = crm_parse_int(port_s, NULL); if(user == NULL) { user = getenv("USER"); crm_info("Defaulting to current user: %s", user); } if(server == NULL) { - server = "localhost";; + server = "localhost"; crm_info("Defaulting to localhost"); } return cib_remote_new(server, user, pass, port); } return cib_native_new(); } /* this is backwards... cib_*_new should call this not the other way around */ cib_t* cib_new_variant(void) { cib_t* new_cib = NULL; crm_malloc0(new_cib, sizeof(cib_t)); if(cib_op_callback_table != NULL) { g_hash_table_destroy(cib_op_callback_table); cib_op_callback_table = NULL; } if(cib_op_callback_table == NULL) { cib_op_callback_table = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, cib_destroy_op_callback); } new_cib->call_id = 1; new_cib->variant = cib_undefined; new_cib->type = cib_none; new_cib->state = cib_disconnected; new_cib->op_callback = NULL; new_cib->variant_opaque = NULL; new_cib->notify_list = NULL; /* the rest will get filled in by the variant constructor */ crm_malloc0(new_cib->cmds, sizeof(cib_api_operations_t)); new_cib->cmds->set_op_callback = cib_client_set_op_callback; new_cib->cmds->add_notify_callback = cib_client_add_notify_callback; new_cib->cmds->del_notify_callback = cib_client_del_notify_callback; new_cib->cmds->noop = cib_client_noop; new_cib->cmds->ping = cib_client_ping; new_cib->cmds->query = cib_client_query; new_cib->cmds->sync = cib_client_sync; new_cib->cmds->query_from = cib_client_query_from; new_cib->cmds->sync_from = cib_client_sync_from; new_cib->cmds->is_master = cib_client_is_master; new_cib->cmds->set_master = cib_client_set_master; new_cib->cmds->set_slave = cib_client_set_slave; new_cib->cmds->set_slave_all = cib_client_set_slave_all; new_cib->cmds->bump_epoch = cib_client_bump_epoch; new_cib->cmds->create = cib_client_create; new_cib->cmds->modify = cib_client_modify; new_cib->cmds->update = cib_client_update; new_cib->cmds->replace = cib_client_replace; new_cib->cmds->delete = cib_client_delete; new_cib->cmds->erase = cib_client_erase; new_cib->cmds->quit = cib_client_quit; new_cib->cmds->delete_absolute = cib_client_delete_absolute; return new_cib; } void cib_delete(cib_t *cib) { GList *list = cib->notify_list; while(list != NULL) { cib_notify_client_t *client = g_list_nth_data(list, 0); list = g_list_remove(list, client); crm_free(client); } g_hash_table_destroy(cib_op_callback_table); cib->cmds->free(cib); cib = NULL; } int cib_client_set_op_callback( cib_t *cib, void (*callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output)) { if(callback == NULL) { crm_info("Un-Setting operation callback"); } else { crm_debug_3("Setting operation callback"); } cib->op_callback = callback; return cib_ok; } int cib_client_add_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)) { GList *list_item = NULL; cib_notify_client_t *new_client = NULL; if(cib->variant != cib_native) { crm_err("Not supported"); return cib_NOTSUPPORTED; } crm_debug_2("Adding callback for %s events (%d)", event, g_list_length(cib->notify_list)); crm_malloc0(new_client, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; list_item = g_list_find_custom( cib->notify_list, new_client, ciblib_GCompareFunc); if(list_item != NULL) { crm_warn("Callback already present"); crm_free(new_client); } else { cib->notify_list = g_list_append( cib->notify_list, new_client); cib->cmds->register_callback(cib, event, 1); crm_debug_3("Callback added (%d)", g_list_length(cib->notify_list)); } return cib_ok; } int cib_client_del_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)) { GList *list_item = NULL; cib_notify_client_t *new_client = NULL; if(cib->variant != cib_native) { crm_err("Not supported"); return cib_NOTSUPPORTED; } crm_debug("Removing callback for %s events", event); crm_malloc0(new_client, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; list_item = g_list_find_custom( cib->notify_list, new_client, ciblib_GCompareFunc); cib->cmds->register_callback(cib, event, 0); if(list_item != NULL) { cib_notify_client_t *list_client = list_item->data; cib->notify_list = g_list_remove(cib->notify_list, list_client); crm_free(list_client); crm_debug_3("Removed callback"); } else { crm_debug_3("Callback not present"); } crm_free(new_client); return cib_ok; } gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b) { const cib_notify_client_t *a_client = a; const cib_notify_client_t *b_client = b; if(a_client->callback == b_client->callback && safe_str_neq(a_client->event, b_client->event)) { return 0; } else if(((long)a_client->callback) < ((long)b_client->callback)) { return -1; } return 1; } static gboolean cib_async_timeout_handler(gpointer data) { struct timer_rec_s *timer = data; int call_id = timer->call_id; cib_callback_client_t *blob = NULL; crm_err("Async call %d timed out after %ds", call_id, timer->timeout); /* Send an async reply with rc=cib_remote_timeout */ blob = g_hash_table_lookup(cib_op_callback_table, GINT_TO_POINTER(call_id)); if(blob != NULL && blob->callback != NULL) { crm_debug_3("Callback found for call %d", call_id); blob->callback(NULL, call_id, cib_remote_timeout, NULL, blob->user_data); } remove_cib_op_callback(timer->call_id, FALSE); /* Always return TRUE, never remove the handler * We do that in remove_cib_op_callback() */ return TRUE; } gboolean add_cib_op_callback_timeout( int call_id, int timeout, gboolean only_success, void *user_data, void (*callback)(xmlNode*, int, int, xmlNode*,void*)) { cib_callback_client_t *blob = NULL; if(call_id < 0) { crm_warn("CIB call failed: %s", cib_error2string(call_id)); if(only_success == FALSE) { callback(NULL, call_id, call_id, NULL, user_data); } return FALSE; } crm_malloc0(blob, sizeof(cib_callback_client_t)); blob->only_success = only_success; blob->user_data = user_data; blob->callback = callback; if(timeout > 0) { struct timer_rec_s *async_timer = NULL; crm_malloc0(async_timer, sizeof(struct timer_rec_s)); blob->timer = async_timer; async_timer->call_id = call_id; async_timer->timeout = timeout*1000; async_timer->ref = Gmain_timeout_add( async_timer->timeout, cib_async_timeout_handler, async_timer); } g_hash_table_insert(cib_op_callback_table, GINT_TO_POINTER(call_id), blob); return TRUE; } void remove_cib_op_callback(int call_id, gboolean all_callbacks) { if(all_callbacks) { if(cib_op_callback_table != NULL) { g_hash_table_destroy(cib_op_callback_table); } cib_op_callback_table = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, cib_destroy_op_callback); } else { g_hash_table_remove( cib_op_callback_table, GINT_TO_POINTER(call_id)); } } int num_cib_op_callbacks(void) { if(cib_op_callback_table == NULL) { return 0; } return g_hash_table_size(cib_op_callback_table); }