diff --git a/include/crm/common/util.h b/include/crm/common/util.h index 04f7c3148c..b5041c1bfb 100644 --- a/include/crm/common/util.h +++ b/include/crm/common/util.h @@ -1,176 +1,179 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program 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 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 Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CRM_COMMON_UTIL__H # define CRM_COMMON_UTIL__H /** * \file * \brief Utility functions * \ingroup core */ # include # include # include # include # include # include # include # include # if SUPPORT_HEARTBEAT # include # else # define NORMALNODE "normal" # define ACTIVESTATUS "active"/* fully functional, and all links are up */ # define DEADSTATUS "dead" /* Status of non-working link or machine */ # define PINGSTATUS "ping" /* Status of a working ping node */ # define JOINSTATUS "join" /* Status when an api client joins */ # define LEAVESTATUS "leave" /* Status when an api client leaves */ # define ONLINESTATUS "online"/* Status of an online client */ # define OFFLINESTATUS "offline" /* Status of an offline client */ # endif /* public string functions (from strings.c) */ char *crm_itoa_stack(int an_int, char *buf, size_t len); char *crm_itoa(int an_int); gboolean crm_is_true(const char *s); int crm_str_to_boolean(const char *s, int *ret); int crm_parse_int(const char *text, const char *default_text); char * crm_strip_trailing_newline(char *str); gboolean crm_str_eq(const char *a, const char *b, gboolean use_case); gboolean safe_str_neq(const char *a, const char *b); guint crm_strcase_hash(gconstpointer v); guint g_str_hash_traditional(gconstpointer v); # define safe_str_eq(a, b) crm_str_eq(a, b, FALSE) # define crm_str_hash g_str_hash_traditional /* used with hash tables where case does not matter */ static inline gboolean crm_strcase_equal(gconstpointer a, gconstpointer b) { return crm_str_eq((const char *) a, (const char *) b, FALSE); } /*! * \brief Create hash table with dynamically allocated string keys/values * * \return Newly hash table * \note It is the caller's responsibility to free the result, using * g_hash_table_destroy(). */ static inline GHashTable * crm_str_table_new() { return g_hash_table_new_full(crm_str_hash, g_str_equal, free, free); } /*! * \brief Create hash table with case-insensitive dynamically allocated string keys/values * * \return Newly hash table * \note It is the caller's responsibility to free the result, using * g_hash_table_destroy(). */ static inline GHashTable * crm_strcase_table_new() { return g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, free, free); } GHashTable *crm_str_table_dup(GHashTable *old_table); # define crm_atoi(text, default_text) crm_parse_int(text, default_text) /* public I/O functions (from io.c) */ void crm_build_path(const char *path_c, mode_t mode); long long crm_get_msec(const char *input); unsigned long long crm_get_interval(const char *input); int char2score(const char *score); char *score2char(int score); char *score2char_stack(int score, char *buf, size_t len); +/* public operation functions (from operations.c) */ +gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, + int *interval); +gboolean decode_transition_key(const char *key, char **uuid, int *action, + int *transition_id, int *target_rc); +gboolean decode_transition_magic(const char *magic, char **uuid, + int *transition_id, int *action_id, + int *op_status, int *op_rc, int *target_rc); +int rsc_op_expected_rc(lrmd_event_data_t *event); +gboolean did_rsc_op_fail(lrmd_event_data_t *event, int target_rc); + int compare_version(const char *version1, const char *version2); -gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval); -gboolean decode_transition_key(const char *key, char **uuid, int *action, int *transition_id, - int *target_rc); -gboolean decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id, - int *op_status, int *op_rc, int *target_rc); /* coverity[+kill] */ void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork); static inline gboolean is_not_set(long long word, long long bit) { return ((word & bit) == 0); } static inline gboolean is_set(long long word, long long bit) { return ((word & bit) == bit); } static inline gboolean is_set_any(long long word, long long bit) { return ((word & bit) != 0); } static inline guint crm_hash_table_size(GHashTable * hashtable) { if (hashtable == NULL) { return 0; } return g_hash_table_size(hashtable); } char *crm_meta_name(const char *field); const char *crm_meta_value(GHashTable * hash, const char *field); -int rsc_op_expected_rc(lrmd_event_data_t * event); -gboolean did_rsc_op_fail(lrmd_event_data_t * event, int target_rc); - char *crm_md5sum(const char *buffer); char *crm_generate_uuid(void); bool crm_is_daemon_name(const char *name); int crm_user_lookup(const char *name, uid_t * uid, gid_t * gid); #ifdef HAVE_GNUTLS_GNUTLS_H void crm_gnutls_global_init(void); #endif int crm_exit(int rc); bool pcmk_acl_required(const char *user); char *crm_generate_ra_key(const char *class, const char *provider, const char *type); #endif diff --git a/include/crm_internal.h b/include/crm_internal.h index e40b4ba625..acfca0778d 100644 --- a/include/crm_internal.h +++ b/include/crm_internal.h @@ -1,390 +1,388 @@ /* crm_internal.h */ /* * Copyright (C) 2006 - 2008 * Andrew Beekhof * * This program 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 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CRM_INTERNAL__H # define CRM_INTERNAL__H # include # include # include # include # include # include # include # include # include /* Dynamic loading of libraries */ void *find_library_function(void **handle, const char *lib, const char *fn, int fatal); void *convert_const_pointer(const void *ptr); /* For ACLs */ char *uid2username(uid_t uid); void determine_request_user(const char *user, xmlNode * request, const char *field); const char *crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user); # if ENABLE_ACL # include static inline gboolean is_privileged(const char *user) { if (user == NULL) { return FALSE; } else if (strcmp(user, CRM_DAEMON_USER) == 0) { return TRUE; } else if (strcmp(user, "root") == 0) { return TRUE; } return FALSE; } # endif /* CLI option processing*/ # ifdef HAVE_GETOPT_H # include # else # define no_argument 0 # define required_argument 1 # endif # define pcmk_option_default 0x00000 # define pcmk_option_hidden 0x00001 # define pcmk_option_paragraph 0x00002 # define pcmk_option_example 0x00004 struct crm_option { /* Fields from 'struct option' in getopt.h */ /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; /* Custom fields */ const char *desc; long flags; }; void crm_set_options(const char *short_options, const char *usage, struct crm_option *long_options, const char *app_desc); int crm_get_option(int argc, char **argv, int *index); int crm_get_option_long(int argc, char **argv, int *index, const char **longname); int crm_help(char cmd, int exit_code); /* Cluster Option Processing */ typedef struct pe_cluster_option_s { const char *name; const char *alt_name; const char *type; const char *values; const char *default_value; gboolean(*is_valid) (const char *); const char *description_short; const char *description_long; } pe_cluster_option; const char *cluster_option(GHashTable * options, gboolean(*validate) (const char *), const char *name, const char *old_name, const char *def_value); const char *get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name); void config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option * option_list, int len); void verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len); gboolean check_time(const char *value); gboolean check_timer(const char *value); gboolean check_boolean(const char *value); gboolean check_number(const char *value); gboolean check_positive_number(const char *value); gboolean check_quorum(const char *value); gboolean check_script(const char *value); gboolean check_utilization(const char *value); long crm_get_sbd_timeout(void); gboolean check_sbd_timeout(const char *value); -/* Shared PE/crmd functionality */ -void filter_action_parameters(xmlNode * param_set, const char *version); - -/* Resource operation updates */ -xmlNode *create_operation_update(xmlNode * parent, lrmd_event_data_t * event, - const char * caller_version, int target_rc, const char * node, - const char * origin, int level); - /* char2score */ extern int node_score_red; extern int node_score_green; extern int node_score_yellow; extern int node_score_infinity; /* Assorted convenience functions */ int crm_pid_active(long pid, const char *daemon); void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile); +/* from operations.c */ char *generate_op_key(const char *rsc_id, const char *op_type, int interval); char *generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type); char *generate_transition_magic_v202(const char *transition_key, int op_status); char *generate_transition_magic(const char *transition_key, int op_status, int op_rc); char *generate_transition_key(int action, int transition_id, int target_rc, const char *node); +void filter_action_parameters(xmlNode *param_set, const char *version); +xmlNode *create_operation_update(xmlNode *parent, lrmd_event_data_t *event, + const char *caller_version, int target_rc, + const char *node, const char *origin, + int level); static inline long long crm_clear_bit(const char *function, int line, const char *target, long long word, long long bit) { long long rc = (word & ~bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s cleared by %s:%d", bit, target, function, line); } else { crm_trace("Bit 0x%.8llx cleared by %s:%d", bit, function, line); } return rc; } static inline long long crm_set_bit(const char *function, int line, const char *target, long long word, long long bit) { long long rc = (word | bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s set by %s:%d", bit, target, function, line); } else { crm_trace("Bit 0x%.8llx set by %s:%d", bit, function, line); } return rc; } # define set_bit(word, bit) word = crm_set_bit(__FUNCTION__, __LINE__, NULL, word, bit) # define clear_bit(word, bit) word = crm_clear_bit(__FUNCTION__, __LINE__, NULL, word, bit) char *generate_hash_key(const char *crm_msg_reference, const char *sys); /*! remote tcp/tls helper functions */ typedef struct crm_remote_s crm_remote_t; int crm_remote_send(crm_remote_t * remote, xmlNode * msg); int crm_remote_ready(crm_remote_t * remote, int total_timeout /*ms */ ); gboolean crm_remote_recv(crm_remote_t * remote, int total_timeout /*ms */ , int *disconnected); xmlNode *crm_remote_parse_buffer(crm_remote_t * remote); int crm_remote_tcp_connect(const char *host, int port); int crm_remote_tcp_connect_async(const char *host, int port, int timeout, /*ms */ int *timer_id, void *userdata, void (*callback) (void *userdata, int sock)); int crm_remote_accept(int ssock); void crm_sockaddr2str(void *sa, char *s); # ifdef HAVE_GNUTLS_GNUTLS_H /*! * \internal * \brief Initiate the client handshake after establishing the tcp socket. * \note This is a blocking function, it will block until the entire handshake * is complete or until the timeout period is reached. * \retval 0 success * \retval negative, failure */ int crm_initiate_client_tls_handshake(crm_remote_t * remote, int timeout_ms); /*! * \internal * \brief Create client or server session for anon DH encryption credentials * \param sock, the socket the session will use for transport * \param type, GNUTLS_SERVER or GNUTLS_CLIENT * \param credentials, gnutls_anon_server_credentials_t or gnutls_anon_client_credentials_t * * \retval gnutls_session_t * on success * \retval NULL on failure */ void *crm_create_anon_tls_session(int sock, int type, void *credentials); /*! * \internal * \brief Create client or server session for PSK credentials * \param sock, the socket the session will use for transport * \param type, GNUTLS_SERVER or GNUTLS_CLIENT * \param credentials, gnutls_psk_server_credentials_t or gnutls_osk_client_credentials_t * * \retval gnutls_session_t * on success * \retval NULL on failure */ void *create_psk_tls_session(int csock, int type, void *credentials); # endif # define REMOTE_MSG_TERMINATOR "\r\n\r\n" const char *daemon_option(const char *option); void set_daemon_option(const char *option, const char *value); gboolean daemon_option_enabled(const char *daemon, const char *option); void strip_text_nodes(xmlNode * xml); void pcmk_panic(const char *origin); void sysrq_init(void); pid_t pcmk_locate_sbd(void); long crm_pidfile_inuse(const char *filename, long mypid, const char *daemon); long crm_read_pidfile(const char *filename); # define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); } # define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); } # define attrd_channel T_ATTRD # define F_ATTRD_KEY "attr_key" # define F_ATTRD_ATTRIBUTE "attr_name" # define F_ATTRD_REGEX "attr_regex" # define F_ATTRD_TASK "task" # define F_ATTRD_VALUE "attr_value" # define F_ATTRD_SET "attr_set" # define F_ATTRD_IS_REMOTE "attr_is_remote" # define F_ATTRD_IS_PRIVATE "attr_is_private" # define F_ATTRD_SECTION "attr_section" # define F_ATTRD_DAMPEN "attr_dampening" # define F_ATTRD_IGNORE_LOCALLY "attr_ignore_locally" # define F_ATTRD_HOST "attr_host" # define F_ATTRD_HOST_ID "attr_host_id" # define F_ATTRD_USER "attr_user" # define F_ATTRD_WRITER "attr_writer" # define F_ATTRD_VERSION "attr_version" # define F_ATTRD_RESOURCE "attr_resource" # define F_ATTRD_OPERATION "attr_clear_operation" # define F_ATTRD_INTERVAL "attr_clear_interval" /* attrd operations */ # define ATTRD_OP_PEER_REMOVE "peer-remove" # define ATTRD_OP_UPDATE "update" # define ATTRD_OP_UPDATE_BOTH "update-both" # define ATTRD_OP_UPDATE_DELAY "update-delay" # define ATTRD_OP_QUERY "query" # define ATTRD_OP_REFRESH "refresh" # define ATTRD_OP_FLUSH "flush" # define ATTRD_OP_SYNC "sync" # define ATTRD_OP_SYNC_RESPONSE "sync-response" # define ATTRD_OP_CLEAR_FAILURE "clear-failure" # if SUPPORT_COROSYNC # if CS_USES_LIBQB # include # include typedef struct qb_ipc_request_header cs_ipc_header_request_t; typedef struct qb_ipc_response_header cs_ipc_header_response_t; # else # include # include # include typedef coroipc_request_header_t cs_ipc_header_request_t; typedef coroipc_response_header_t cs_ipc_header_response_t; # endif # else typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_request_t; typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); int error __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_response_t; # endif void attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); void stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); qb_ipcs_service_t * crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb); void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb); void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw, qb_ipcs_service_t *ipcs_shm); static inline void *realloc_safe(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (ret == NULL) { free(ptr); /* make coverity happy */ abort(); } return ret; } const char *crm_xml_add_last_written(xmlNode *xml_node); void crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth); void crm_buffer_add_char(char **buffer, int *offset, int *max, char c); gboolean crm_digest_verify(xmlNode *input, const char *expected); /* cross-platform compatibility functions */ char *crm_compat_realpath(const char *path); /* IPC Proxy Backend Shared Functions */ typedef struct remote_proxy_s { char *node_name; char *session_id; gboolean is_local; crm_ipc_t *ipc; mainloop_io_t *source; uint32_t last_request_id; lrmd_t *lrm; } remote_proxy_t; remote_proxy_t *remote_proxy_new( lrmd_t *lrmd, struct ipc_client_callbacks *proxy_callbacks, const char *node_name, const char *session_id, const char *channel); int remote_proxy_check(lrmd_t *lrmd, GHashTable *hash); void remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg); void remote_proxy_ack_shutdown(lrmd_t *lrmd); void remote_proxy_nack_shutdown(lrmd_t *lrmd); int remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata); void remote_proxy_disconnected(gpointer data); void remote_proxy_free(gpointer data); void remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg); void remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id); #endif /* CRM_INTERNAL__H */ diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index d8b593943a..77e50d5f94 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -1,49 +1,50 @@ # # 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. # include $(top_srcdir)/Makefile.common AM_CPPFLAGS += -I$(top_builddir)/lib/gnu -I$(top_srcdir)/lib/gnu ## libraries lib_LTLIBRARIES = libcrmcommon.la # Can't use -Wcast-qual here because glib insists on pretending things are const # when they're not and thus we need the crm_element_value_const() hack # s390 needs -fPIC # s390-suse-linux/bin/ld: .libs/ipc.o: relocation R_390_PC32DBL against `__stack_chk_fail@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC CFLAGS = $(CFLAGS_COPY:-Wcast-qual=) -fPIC libcrmcommon_la_LDFLAGS = -version-info 10:0:7 libcrmcommon_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libcrmcommon_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) libcrmcommon_la_LIBADD = @LIBADD_DL@ $(GNUTLSLIBS) libcrmcommon_la_SOURCES = compat.c digest.c ipc.c io.c procfs.c utils.c xml.c \ iso8601.c remote.c mainloop.c logging.c watchdog.c \ - schemas.c strings.c xpath.c attrd_client.c alerts.c + schemas.c strings.c xpath.c attrd_client.c alerts.c \ + operations.c if BUILD_CIBSECRETS libcrmcommon_la_SOURCES += cib_secrets.c endif libcrmcommon_la_SOURCES += $(top_builddir)/lib/gnu/md5.c clean-generic: rm -f *.log *.debug *.xml *~ diff --git a/lib/common/operations.c b/lib/common/operations.c new file mode 100644 index 0000000000..58bf9ba400 --- /dev/null +++ b/lib/common/operations.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2004-2017 Andrew Beekhof + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/*! + * \brief Generate an operation key + * + * \param[in] rsc_id ID of resource being operated on + * \param[in] op_type Operation name + * \param[in] interval Operation interval + * + * \return Newly allocated memory containing operation key as string + * + * \note It is the caller's responsibility to free() the result. + */ +char * +generate_op_key(const char *rsc_id, const char *op_type, int interval) +{ + CRM_ASSERT(rsc_id != NULL); + CRM_ASSERT(op_type != NULL); + CRM_ASSERT(interval >= 0); + return crm_strdup_printf("%s_%s_%d", rsc_id, op_type, interval); +} + +gboolean +parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval) +{ + char *notify = NULL; + char *mutable_key = NULL; + char *mutable_key_ptr = NULL; + int len = 0, offset = 0, ch = 0; + + CRM_CHECK(key != NULL, return FALSE); + + *interval = 0; + len = strlen(key); + offset = len - 1; + + crm_trace("Source: %s", key); + + while (offset > 0 && isdigit(key[offset])) { + int digits = len - offset; + + ch = key[offset] - '0'; + CRM_CHECK(ch < 10, return FALSE); + CRM_CHECK(ch >= 0, return FALSE); + while (digits > 1) { + digits--; + ch = ch * 10; + } + *interval += ch; + offset--; + } + + crm_trace(" Interval: %d", *interval); + CRM_CHECK(key[offset] == '_', return FALSE); + + mutable_key = strdup(key); + mutable_key[offset] = 0; + offset--; + + while (offset > 0 && key[offset] != '_') { + offset--; + } + + CRM_CHECK(key[offset] == '_', free(mutable_key); + return FALSE); + + mutable_key_ptr = mutable_key + offset + 1; + + crm_trace(" Action: %s", mutable_key_ptr); + + *op_type = strdup(mutable_key_ptr); + + mutable_key[offset] = 0; + offset--; + + CRM_CHECK(mutable_key != mutable_key_ptr, free(mutable_key); + return FALSE); + + notify = strstr(mutable_key, "_post_notify"); + if (notify && safe_str_eq(notify, "_post_notify")) { + notify[0] = 0; + } + + notify = strstr(mutable_key, "_pre_notify"); + if (notify && safe_str_eq(notify, "_pre_notify")) { + notify[0] = 0; + } + + crm_trace(" Resource: %s", mutable_key); + *rsc_id = mutable_key; + + return TRUE; +} + +char * +generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type) +{ + int len = 12; + char *op_id = NULL; + + CRM_CHECK(rsc_id != NULL, return NULL); + CRM_CHECK(op_type != NULL, return NULL); + CRM_CHECK(notify_type != NULL, return NULL); + + len += strlen(op_type); + len += strlen(rsc_id); + len += strlen(notify_type); + if(len > 0) { + op_id = malloc(len); + } + if (op_id != NULL) { + sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type); + } + return op_id; +} + +char * +generate_transition_magic_v202(const char *transition_key, int op_status) +{ + int len = 80; + char *fail_state = NULL; + + CRM_CHECK(transition_key != NULL, return NULL); + + len += strlen(transition_key); + + fail_state = malloc(len); + if (fail_state != NULL) { + snprintf(fail_state, len, "%d:%s", op_status, transition_key); + } + return fail_state; +} + +char * +generate_transition_magic(const char *transition_key, int op_status, int op_rc) +{ + int len = 80; + char *fail_state = NULL; + + CRM_CHECK(transition_key != NULL, return NULL); + + len += strlen(transition_key); + + fail_state = malloc(len); + if (fail_state != NULL) { + snprintf(fail_state, len, "%d:%d;%s", op_status, op_rc, transition_key); + } + return fail_state; +} + +gboolean +decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id, + int *op_status, int *op_rc, int *target_rc) +{ + int res = 0; + char *key = NULL; + gboolean result = TRUE; + + CRM_CHECK(magic != NULL, return FALSE); + CRM_CHECK(op_rc != NULL, return FALSE); + CRM_CHECK(op_status != NULL, return FALSE); + + key = calloc(1, strlen(magic) + 1); + res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key); + if (res != 3) { + crm_warn("Only found %d items in: '%s'", res, magic); + free(key); + return FALSE; + } + + CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE); + + free(key); + return result; +} + +char * +generate_transition_key(int transition_id, int action_id, int target_rc, const char *node) +{ + int len = 40; + char *fail_state = NULL; + + CRM_CHECK(node != NULL, return NULL); + + len += strlen(node); + + fail_state = malloc(len); + if (fail_state != NULL) { + snprintf(fail_state, len, "%d:%d:%d:%-*s", action_id, transition_id, target_rc, 36, node); + } + return fail_state; +} + +gboolean +decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id, + int *target_rc) +{ + int res = 0; + gboolean done = FALSE; + + CRM_CHECK(uuid != NULL, return FALSE); + CRM_CHECK(target_rc != NULL, return FALSE); + CRM_CHECK(action_id != NULL, return FALSE); + CRM_CHECK(transition_id != NULL, return FALSE); + + *uuid = calloc(1, 37); + res = sscanf(key, "%d:%d:%d:%36s", action_id, transition_id, target_rc, *uuid); + switch (res) { + case 4: + /* Post Pacemaker 0.6 */ + done = TRUE; + break; + case 3: + case 2: + /* this can be tricky - the UUID might start with an integer */ + + /* Until Pacemaker 0.6 */ + done = TRUE; + *target_rc = -1; + res = sscanf(key, "%d:%d:%36s", action_id, transition_id, *uuid); + if (res == 2) { + *action_id = -1; + res = sscanf(key, "%d:%36s", transition_id, *uuid); + CRM_CHECK(res == 2, done = FALSE); + + } else if (res != 3) { + CRM_CHECK(res == 3, done = FALSE); + } + break; + + case 1: + /* Prior to Heartbeat 2.0.8 */ + done = TRUE; + *action_id = -1; + *target_rc = -1; + res = sscanf(key, "%d:%36s", transition_id, *uuid); + CRM_CHECK(res == 2, done = FALSE); + break; + default: + crm_crit("Unhandled sscanf result (%d) for %s", res, key); + } + + if (strlen(*uuid) != 36) { + crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key); + } + + if (done == FALSE) { + crm_err("Cannot decode '%s' rc=%d", key, res); + + free(*uuid); + *uuid = NULL; + *target_rc = -1; + *action_id = -1; + *transition_id = -1; + } + + return done; +} + +void +filter_action_parameters(xmlNode * param_set, const char *version) +{ + char *key = NULL; + char *timeout = NULL; + char *interval = NULL; + + const char *attr_filter[] = { + XML_ATTR_ID, + XML_ATTR_CRM_VERSION, + XML_LRM_ATTR_OP_DIGEST, + XML_LRM_ATTR_TARGET, + XML_LRM_ATTR_TARGET_UUID, + "pcmk_external_ip" + }; + + gboolean do_delete = FALSE; + int lpc = 0; + static int meta_len = 0; + + if (meta_len == 0) { + meta_len = strlen(CRM_META); + } + + if (param_set == NULL) { + return; + } + + for (lpc = 0; lpc < DIMOF(attr_filter); lpc++) { + xml_remove_prop(param_set, attr_filter[lpc]); + } + + key = crm_meta_name(XML_LRM_ATTR_INTERVAL); + interval = crm_element_value_copy(param_set, key); + free(key); + + key = crm_meta_name(XML_ATTR_TIMEOUT); + timeout = crm_element_value_copy(param_set, key); + + if (param_set) { + xmlAttrPtr xIter = param_set->properties; + + while (xIter) { + const char *prop_name = (const char *)xIter->name; + + xIter = xIter->next; + do_delete = FALSE; + if (strncasecmp(prop_name, CRM_META, meta_len) == 0) { + do_delete = TRUE; + } + + if (do_delete) { + xml_remove_prop(param_set, prop_name); + } + } + } + + if (crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) { + /* Re-instate the operation's timeout value */ + if (timeout != NULL) { + crm_xml_add(param_set, key, timeout); + } + } + + free(interval); + free(timeout); + free(key); +} + +#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +static void +append_digest(lrmd_event_data_t * op, xmlNode * update, const char *version, const char *magic, + int level) +{ + /* this will enable us to later determine that the + * resource's parameters have changed and we should force + * a restart + */ + char *digest = NULL; + xmlNode *args_xml = NULL; + + if (op->params == NULL) { + return; + } + + args_xml = create_xml_node(NULL, XML_TAG_PARAMS); + g_hash_table_foreach(op->params, hash2field, args_xml); + filter_action_parameters(args_xml, version); + digest = calculate_operation_digest(args_xml, version); + +#if 0 + if (level < get_crm_log_level() + && op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) { + char *digest_source = dump_xml_unformatted(args_xml); + + do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n", + digest, ID(update), magic, digest_source); + free(digest_source); + } +#endif + crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest); + + free_xml(args_xml); + free(digest); +} + +int +rsc_op_expected_rc(lrmd_event_data_t * op) +{ + int rc = 0; + + if (op && op->user_data) { + int dummy = 0; + char *uuid = NULL; + + decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &rc); + free(uuid); + } + return rc; +} + +gboolean +did_rsc_op_fail(lrmd_event_data_t * op, int target_rc) +{ + switch (op->op_status) { + case PCMK_LRM_OP_CANCELLED: + case PCMK_LRM_OP_PENDING: + return FALSE; + break; + + case PCMK_LRM_OP_NOTSUPPORTED: + case PCMK_LRM_OP_TIMEOUT: + case PCMK_LRM_OP_ERROR: + return TRUE; + break; + + default: + if (target_rc != op->rc) { + return TRUE; + } + } + + return FALSE; +} + +xmlNode * +create_operation_update(xmlNode * parent, lrmd_event_data_t * op, const char * caller_version, + int target_rc, const char * node, const char * origin, int level) +{ + char *key = NULL; + char *magic = NULL; + char *op_id = NULL; + char *op_id_additional = NULL; + char *local_user_data = NULL; + const char *exit_reason = NULL; + + xmlNode *xml_op = NULL; + const char *task = NULL; + gboolean dc_munges_migrate_ops = (compare_version(caller_version, "3.0.3") < 0); + gboolean dc_needs_unique_ops = (compare_version(caller_version, "3.0.6") < 0); + + CRM_CHECK(op != NULL, return NULL); + do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%d)", + origin, op->rsc_id, op->op_type, services_lrm_status_str(op->op_status), + op->interval); + + crm_trace("DC version: %s", caller_version); + + task = op->op_type; + /* remap the task name under various scenarios + * this makes life easier for the PE when trying determine the current state + */ + if (crm_str_eq(task, "reload", TRUE)) { + if (op->op_status == PCMK_LRM_OP_DONE) { + task = CRMD_ACTION_START; + } else { + task = CRMD_ACTION_STATUS; + } + + } else if (dc_munges_migrate_ops && crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) { + /* if the migrate_from fails it will have enough info to do the right thing */ + if (op->op_status == PCMK_LRM_OP_DONE) { + task = CRMD_ACTION_STOP; + } else { + task = CRMD_ACTION_STATUS; + } + + } else if (dc_munges_migrate_ops + && op->op_status == PCMK_LRM_OP_DONE + && crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) { + task = CRMD_ACTION_START; + } + + key = generate_op_key(op->rsc_id, task, op->interval); + if (dc_needs_unique_ops && op->interval > 0) { + op_id = strdup(key); + + } else if (crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) { + const char *n_type = crm_meta_value(op->params, "notify_type"); + const char *n_task = crm_meta_value(op->params, "notify_operation"); + + CRM_LOG_ASSERT(n_type != NULL); + CRM_LOG_ASSERT(n_task != NULL); + op_id = generate_notify_key(op->rsc_id, n_type, n_task); + + /* these are not yet allowed to fail */ + op->op_status = PCMK_LRM_OP_DONE; + op->rc = 0; + + } else if (did_rsc_op_fail(op, target_rc)) { + op_id = generate_op_key(op->rsc_id, "last_failure", 0); + if (op->interval == 0) { + /* Ensure 'last' gets updated too in case recording-pending="true" */ + op_id_additional = generate_op_key(op->rsc_id, "last", 0); + } + exit_reason = op->exit_reason; + + } else if (op->interval > 0) { + op_id = strdup(key); + + } else { + op_id = generate_op_key(op->rsc_id, "last", 0); + } + + again: + xml_op = find_entity(parent, XML_LRM_TAG_RSC_OP, op_id); + if (xml_op == NULL) { + xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP); + } + + if (op->user_data == NULL) { + crm_debug("Generating fake transition key for:" + " %s_%s_%d %d from %s", + op->rsc_id, op->op_type, op->interval, op->call_id, origin); + local_user_data = generate_transition_key(-1, op->call_id, target_rc, FAKE_TE_ID); + op->user_data = local_user_data; + } + + if(magic == NULL) { + magic = generate_transition_magic(op->user_data, op->op_status, op->rc); + } + + crm_xml_add(xml_op, XML_ATTR_ID, op_id); + crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key); + crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task); + crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin); + crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version); + crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data); + crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic); + crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason); + crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */ + + crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id); + crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc); + crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status); + crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval); + + if (compare_version("2.1", caller_version) <= 0) { + if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) { + crm_trace("Timing data (%s_%s_%d): last=%u change=%u exec=%u queue=%u", + op->rsc_id, op->op_type, op->interval, + op->t_run, op->t_rcchange, op->exec_time, op->queue_time); + + if (op->interval == 0) { + /* The values are the same for non-recurring ops */ + crm_xml_add_int(xml_op, XML_RSC_OP_LAST_RUN, op->t_run); + crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run); + + } else if(op->t_rcchange) { + /* last-run is not accurate for recurring ops */ + crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_rcchange); + + } else { + /* ...but is better than nothing otherwise */ + crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run); + } + + crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time); + crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time); + } + } + + if (crm_str_eq(op->op_type, CRMD_ACTION_MIGRATE, TRUE) + || crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) { + /* + * Record migrate_source and migrate_target always for migrate ops. + */ + const char *name = XML_LRM_ATTR_MIGRATE_SOURCE; + + crm_xml_add(xml_op, name, crm_meta_value(op->params, name)); + + name = XML_LRM_ATTR_MIGRATE_TARGET; + crm_xml_add(xml_op, name, crm_meta_value(op->params, name)); + } + + append_digest(op, xml_op, caller_version, magic, LOG_DEBUG); + + if (op_id_additional) { + free(op_id); + op_id = op_id_additional; + op_id_additional = NULL; + goto again; + } + + if (local_user_data) { + free(local_user_data); + op->user_data = NULL; + } + free(magic); + free(op_id); + free(key); + return xml_op; +} diff --git a/lib/common/utils.c b/lib/common/utils.c index 3305c0929e..9bb5bab415 100644 --- a/lib/common/utils.c +++ b/lib/common/utils.c @@ -1,2030 +1,1456 @@ /* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif -#include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #ifndef MAXLINE # define MAXLINE 512 #endif #ifdef HAVE_GETOPT_H # include #endif #ifndef PW_BUFFER_LEN # define PW_BUFFER_LEN 500 #endif CRM_TRACE_INIT_DATA(common); gboolean crm_config_error = FALSE; gboolean crm_config_warning = FALSE; char *crm_system_name = NULL; int node_score_red = 0; int node_score_green = 0; int node_score_yellow = 0; int node_score_infinity = INFINITY; static struct crm_option *crm_long_options = NULL; static const char *crm_app_description = NULL; static char *crm_short_options = NULL; static const char *crm_app_usage = NULL; int crm_exit(int rc) { mainloop_cleanup(); #if HAVE_LIBXML2 crm_trace("cleaning up libxml"); crm_xml_cleanup(); #endif crm_trace("exit %d", rc); qb_log_fini(); free(crm_short_options); free(crm_system_name); exit(ABS(rc)); /* Always exit with a positive value so that it can be passed to crm_error * * Otherwise the system wraps it around and people * have to jump through hoops figuring out what the * error was */ return rc; /* Can never happen, but allows return crm_exit(rc) * where "return rc" was used previously - which * keeps compilers happy. */ } gboolean check_time(const char *value) { if (crm_get_msec(value) < 5000) { return FALSE; } return TRUE; } gboolean check_timer(const char *value) { if (crm_get_msec(value) < 0) { return FALSE; } return TRUE; } gboolean check_boolean(const char *value) { int tmp = FALSE; if (crm_str_to_boolean(value, &tmp) != 1) { return FALSE; } return TRUE; } gboolean check_number(const char *value) { errno = 0; if (value == NULL) { return FALSE; } else if (safe_str_eq(value, MINUS_INFINITY_S)) { } else if (safe_str_eq(value, INFINITY_S)) { } else { crm_int_helper(value, NULL); } if (errno != 0) { return FALSE; } return TRUE; } gboolean check_positive_number(const char* value) { if (safe_str_eq(value, INFINITY_S) || (crm_int_helper(value, NULL))) { return TRUE; } return FALSE; } gboolean check_quorum(const char *value) { if (safe_str_eq(value, "stop")) { return TRUE; } else if (safe_str_eq(value, "freeze")) { return TRUE; } else if (safe_str_eq(value, "ignore")) { return TRUE; } else if (safe_str_eq(value, "suicide")) { return TRUE; } return FALSE; } gboolean check_script(const char *value) { struct stat st; if(safe_str_eq(value, "/dev/null")) { return TRUE; } if(stat(value, &st) != 0) { crm_err("Script %s does not exist", value); return FALSE; } if(S_ISREG(st.st_mode) == 0) { crm_err("Script %s is not a regular file", value); return FALSE; } if( (st.st_mode & (S_IXUSR | S_IXGRP )) == 0) { crm_err("Script %s is not executable", value); return FALSE; } return TRUE; } gboolean check_utilization(const char *value) { char *end = NULL; long number = strtol(value, &end, 10); if(end && end[0] != '%') { return FALSE; } else if(number < 0) { return FALSE; } return TRUE; } int char2score(const char *score) { int score_f = 0; if (score == NULL) { } else if (safe_str_eq(score, MINUS_INFINITY_S)) { score_f = -node_score_infinity; } else if (safe_str_eq(score, INFINITY_S)) { score_f = node_score_infinity; } else if (safe_str_eq(score, "+" INFINITY_S)) { score_f = node_score_infinity; } else if (safe_str_eq(score, "red")) { score_f = node_score_red; } else if (safe_str_eq(score, "yellow")) { score_f = node_score_yellow; } else if (safe_str_eq(score, "green")) { score_f = node_score_green; } else { score_f = crm_parse_int(score, NULL); if (score_f > 0 && score_f > node_score_infinity) { score_f = node_score_infinity; } else if (score_f < 0 && score_f < -node_score_infinity) { score_f = -node_score_infinity; } } return score_f; } char * score2char_stack(int score, char *buf, size_t len) { if (score >= node_score_infinity) { strncpy(buf, INFINITY_S, 9); } else if (score <= -node_score_infinity) { strncpy(buf, MINUS_INFINITY_S , 10); } else { return crm_itoa_stack(score, buf, len); } return buf; } char * score2char(int score) { if (score >= node_score_infinity) { return strdup(INFINITY_S); } else if (score <= -node_score_infinity) { return strdup("-" INFINITY_S); } return crm_itoa(score); } const char * cluster_option(GHashTable * options, gboolean(*validate) (const char *), const char *name, const char *old_name, const char *def_value) { const char *value = NULL; CRM_ASSERT(name != NULL); if (options != NULL) { value = g_hash_table_lookup(options, name); } if (value == NULL && old_name && options != NULL) { value = g_hash_table_lookup(options, old_name); if (value != NULL) { crm_config_warn("Using deprecated name '%s' for" " cluster option '%s'", old_name, name); g_hash_table_insert(options, strdup(name), strdup(value)); value = g_hash_table_lookup(options, old_name); } } if (value == NULL) { crm_trace("Using default value '%s' for cluster option '%s'", def_value, name); if (options == NULL) { return def_value; } else if(def_value == NULL) { return def_value; } g_hash_table_insert(options, strdup(name), strdup(def_value)); value = g_hash_table_lookup(options, name); } if (validate && validate(value) == FALSE) { crm_config_err("Value '%s' for cluster option '%s' is invalid." " Defaulting to %s", value, name, def_value); g_hash_table_replace(options, strdup(name), strdup(def_value)); value = g_hash_table_lookup(options, name); } return value; } const char * get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name) { int lpc = 0; const char *value = NULL; gboolean found = FALSE; for (lpc = 0; lpc < len; lpc++) { if (safe_str_eq(name, option_list[lpc].name)) { found = TRUE; value = cluster_option(options, option_list[lpc].is_valid, option_list[lpc].name, option_list[lpc].alt_name, option_list[lpc].default_value); } } CRM_CHECK(found, crm_err("No option named: %s", name)); return value; } void config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option * option_list, int len) { int lpc = 0; fprintf(stdout, "" "\n" "\n" " %s\n" " %s\n" " %s\n" " \n", name, version, desc_long, desc_short); for (lpc = 0; lpc < len; lpc++) { if (option_list[lpc].description_long == NULL && option_list[lpc].description_short == NULL) { continue; } fprintf(stdout, " \n" " %s\n" " \n" " %s%s%s\n" " \n", option_list[lpc].name, option_list[lpc].description_short, option_list[lpc].type, option_list[lpc].default_value, option_list[lpc].description_long ? option_list[lpc]. description_long : option_list[lpc].description_short, option_list[lpc].values ? " Allowed values: " : "", option_list[lpc].values ? option_list[lpc].values : ""); } fprintf(stdout, " \n\n"); } void verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len) { int lpc = 0; for (lpc = 0; lpc < len; lpc++) { cluster_option(options, option_list[lpc].is_valid, option_list[lpc].name, option_list[lpc].alt_name, option_list[lpc].default_value); } } char * generate_hash_key(const char *crm_msg_reference, const char *sys) { char *hash_key = crm_concat(sys ? sys : "none", crm_msg_reference, '_'); crm_trace("created hash key: (%s)", hash_key); return hash_key; } int crm_user_lookup(const char *name, uid_t * uid, gid_t * gid) { int rc = pcmk_ok; char *buffer = NULL; struct passwd pwd; struct passwd *pwentry = NULL; buffer = calloc(1, PW_BUFFER_LEN); rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry); if (pwentry) { if (uid) { *uid = pwentry->pw_uid; } if (gid) { *gid = pwentry->pw_gid; } crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid); } else { rc = rc? -rc : -EINVAL; crm_info("User %s lookup: %s", name, pcmk_strerror(rc)); } free(buffer); return rc; } static int crm_version_helper(const char *text, char **end_text) { int atoi_result = -1; CRM_ASSERT(end_text != NULL); errno = 0; if (text != NULL && text[0] != 0) { atoi_result = (int)strtol(text, end_text, 10); if (errno == EINVAL) { crm_err("Conversion of '%s' %c failed", text, text[0]); atoi_result = -1; } } return atoi_result; } /* * version1 < version2 : -1 * version1 = version2 : 0 * version1 > version2 : 1 */ int compare_version(const char *version1, const char *version2) { int rc = 0; int lpc = 0; char *ver1_copy = NULL, *ver2_copy = NULL; char *rest1 = NULL, *rest2 = NULL; if (version1 == NULL && version2 == NULL) { return 0; } else if (version1 == NULL) { return -1; } else if (version2 == NULL) { return 1; } ver1_copy = strdup(version1); ver2_copy = strdup(version2); rest1 = ver1_copy; rest2 = ver2_copy; while (1) { int digit1 = 0; int digit2 = 0; lpc++; if (rest1 == rest2) { break; } if (rest1 != NULL) { digit1 = crm_version_helper(rest1, &rest1); } if (rest2 != NULL) { digit2 = crm_version_helper(rest2, &rest2); } if (digit1 < digit2) { rc = -1; break; } else if (digit1 > digit2) { rc = 1; break; } if (rest1 != NULL && rest1[0] == '.') { rest1++; } if (rest1 != NULL && rest1[0] == 0) { rest1 = NULL; } if (rest2 != NULL && rest2[0] == '.') { rest2++; } if (rest2 != NULL && rest2[0] == 0) { rest2 = NULL; } } free(ver1_copy); free(ver2_copy); if (rc == 0) { crm_trace("%s == %s (%d)", version1, version2, lpc); } else if (rc < 0) { crm_trace("%s < %s (%d)", version1, version2, lpc); } else if (rc > 0) { crm_trace("%s > %s (%d)", version1, version2, lpc); } return rc; } gboolean do_stderr = FALSE; #ifndef NUMCHARS # define NUMCHARS "0123456789." #endif #ifndef WHITESPACE # define WHITESPACE " \t\n\r\f" #endif unsigned long long crm_get_interval(const char *input) { unsigned long long msec = 0; if (input == NULL) { return msec; } else if (input[0] != 'P') { long long tmp = crm_get_msec(input); if(tmp > 0) { msec = tmp; } } else { crm_time_t *interval = crm_time_parse_duration(input); msec = 1000 * crm_time_get_seconds(interval); crm_time_free(interval); } return msec; } long long crm_get_msec(const char *input) { const char *cp = input; const char *units; long long multiplier = 1000; long long divisor = 1; long long msec = -1; char *end_text = NULL; /* double dret; */ if (input == NULL) { return msec; } cp += strspn(cp, WHITESPACE); units = cp + strspn(cp, NUMCHARS); units += strspn(units, WHITESPACE); if (strchr(NUMCHARS, *cp) == NULL) { return msec; } if (strncasecmp(units, "ms", 2) == 0 || strncasecmp(units, "msec", 4) == 0) { multiplier = 1; divisor = 1; } else if (strncasecmp(units, "us", 2) == 0 || strncasecmp(units, "usec", 4) == 0) { multiplier = 1; divisor = 1000; } else if (strncasecmp(units, "s", 1) == 0 || strncasecmp(units, "sec", 3) == 0) { multiplier = 1000; divisor = 1; } else if (strncasecmp(units, "m", 1) == 0 || strncasecmp(units, "min", 3) == 0) { multiplier = 60 * 1000; divisor = 1; } else if (strncasecmp(units, "h", 1) == 0 || strncasecmp(units, "hr", 2) == 0) { multiplier = 60 * 60 * 1000; divisor = 1; } else if (*units != EOS && *units != '\n' && *units != '\r') { return msec; } msec = crm_int_helper(cp, &end_text); if (msec > LLONG_MAX/multiplier) { /* arithmetics overflow while multiplier/divisor mutually exclusive */ return LLONG_MAX; } msec *= multiplier; msec /= divisor; /* dret += 0.5; */ /* msec = (long long)dret; */ return msec; } -/*! - * \brief Generate an operation key - * - * \param[in] rsc_id ID of resource being operated on - * \param[in] op_type Operation name - * \param[in] interval Operation interval - * - * \return Newly allocated memory containing operation key as string - * - * \note It is the caller's responsibility to free() the result. - */ -char * -generate_op_key(const char *rsc_id, const char *op_type, int interval) -{ - CRM_ASSERT(rsc_id != NULL); - CRM_ASSERT(op_type != NULL); - CRM_ASSERT(interval >= 0); - return crm_strdup_printf("%s_%s_%d", rsc_id, op_type, interval); -} - -gboolean -parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval) -{ - char *notify = NULL; - char *mutable_key = NULL; - char *mutable_key_ptr = NULL; - int len = 0, offset = 0, ch = 0; - - CRM_CHECK(key != NULL, return FALSE); - - *interval = 0; - len = strlen(key); - offset = len - 1; - - crm_trace("Source: %s", key); - - while (offset > 0 && isdigit(key[offset])) { - int digits = len - offset; - - ch = key[offset] - '0'; - CRM_CHECK(ch < 10, return FALSE); - CRM_CHECK(ch >= 0, return FALSE); - while (digits > 1) { - digits--; - ch = ch * 10; - } - *interval += ch; - offset--; - } - - crm_trace(" Interval: %d", *interval); - CRM_CHECK(key[offset] == '_', return FALSE); - - mutable_key = strdup(key); - mutable_key[offset] = 0; - offset--; - - while (offset > 0 && key[offset] != '_') { - offset--; - } - - CRM_CHECK(key[offset] == '_', free(mutable_key); - return FALSE); - - mutable_key_ptr = mutable_key + offset + 1; - - crm_trace(" Action: %s", mutable_key_ptr); - - *op_type = strdup(mutable_key_ptr); - - mutable_key[offset] = 0; - offset--; - - CRM_CHECK(mutable_key != mutable_key_ptr, free(mutable_key); - return FALSE); - - notify = strstr(mutable_key, "_post_notify"); - if (notify && safe_str_eq(notify, "_post_notify")) { - notify[0] = 0; - } - - notify = strstr(mutable_key, "_pre_notify"); - if (notify && safe_str_eq(notify, "_pre_notify")) { - notify[0] = 0; - } - - crm_trace(" Resource: %s", mutable_key); - *rsc_id = mutable_key; - - return TRUE; -} - -char * -generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type) -{ - int len = 12; - char *op_id = NULL; - - CRM_CHECK(rsc_id != NULL, return NULL); - CRM_CHECK(op_type != NULL, return NULL); - CRM_CHECK(notify_type != NULL, return NULL); - - len += strlen(op_type); - len += strlen(rsc_id); - len += strlen(notify_type); - if(len > 0) { - op_id = malloc(len); - } - if (op_id != NULL) { - sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type); - } - return op_id; -} - -char * -generate_transition_magic_v202(const char *transition_key, int op_status) -{ - int len = 80; - char *fail_state = NULL; - - CRM_CHECK(transition_key != NULL, return NULL); - - len += strlen(transition_key); - - fail_state = malloc(len); - if (fail_state != NULL) { - snprintf(fail_state, len, "%d:%s", op_status, transition_key); - } - return fail_state; -} - -char * -generate_transition_magic(const char *transition_key, int op_status, int op_rc) -{ - int len = 80; - char *fail_state = NULL; - - CRM_CHECK(transition_key != NULL, return NULL); - - len += strlen(transition_key); - - fail_state = malloc(len); - if (fail_state != NULL) { - snprintf(fail_state, len, "%d:%d;%s", op_status, op_rc, transition_key); - } - return fail_state; -} - -gboolean -decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id, - int *op_status, int *op_rc, int *target_rc) -{ - int res = 0; - char *key = NULL; - gboolean result = TRUE; - - CRM_CHECK(magic != NULL, return FALSE); - CRM_CHECK(op_rc != NULL, return FALSE); - CRM_CHECK(op_status != NULL, return FALSE); - - key = calloc(1, strlen(magic) + 1); - res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key); - if (res != 3) { - crm_warn("Only found %d items in: '%s'", res, magic); - free(key); - return FALSE; - } - - CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE); - - free(key); - return result; -} - -char * -generate_transition_key(int transition_id, int action_id, int target_rc, const char *node) -{ - int len = 40; - char *fail_state = NULL; - - CRM_CHECK(node != NULL, return NULL); - - len += strlen(node); - - fail_state = malloc(len); - if (fail_state != NULL) { - snprintf(fail_state, len, "%d:%d:%d:%-*s", action_id, transition_id, target_rc, 36, node); - } - return fail_state; -} - -gboolean -decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id, - int *target_rc) -{ - int res = 0; - gboolean done = FALSE; - - CRM_CHECK(uuid != NULL, return FALSE); - CRM_CHECK(target_rc != NULL, return FALSE); - CRM_CHECK(action_id != NULL, return FALSE); - CRM_CHECK(transition_id != NULL, return FALSE); - - *uuid = calloc(1, 37); - res = sscanf(key, "%d:%d:%d:%36s", action_id, transition_id, target_rc, *uuid); - switch (res) { - case 4: - /* Post Pacemaker 0.6 */ - done = TRUE; - break; - case 3: - case 2: - /* this can be tricky - the UUID might start with an integer */ - - /* Until Pacemaker 0.6 */ - done = TRUE; - *target_rc = -1; - res = sscanf(key, "%d:%d:%36s", action_id, transition_id, *uuid); - if (res == 2) { - *action_id = -1; - res = sscanf(key, "%d:%36s", transition_id, *uuid); - CRM_CHECK(res == 2, done = FALSE); - - } else if (res != 3) { - CRM_CHECK(res == 3, done = FALSE); - } - break; - - case 1: - /* Prior to Heartbeat 2.0.8 */ - done = TRUE; - *action_id = -1; - *target_rc = -1; - res = sscanf(key, "%d:%36s", transition_id, *uuid); - CRM_CHECK(res == 2, done = FALSE); - break; - default: - crm_crit("Unhandled sscanf result (%d) for %s", res, key); - } - - if (strlen(*uuid) != 36) { - crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key); - } - - if (done == FALSE) { - crm_err("Cannot decode '%s' rc=%d", key, res); - - free(*uuid); - *uuid = NULL; - *target_rc = -1; - *action_id = -1; - *transition_id = -1; - } - - return done; -} - -void -filter_action_parameters(xmlNode * param_set, const char *version) -{ - char *key = NULL; - char *timeout = NULL; - char *interval = NULL; - - const char *attr_filter[] = { - XML_ATTR_ID, - XML_ATTR_CRM_VERSION, - XML_LRM_ATTR_OP_DIGEST, - XML_LRM_ATTR_TARGET, - XML_LRM_ATTR_TARGET_UUID, - "pcmk_external_ip" - }; - - gboolean do_delete = FALSE; - int lpc = 0; - static int meta_len = 0; - - if (meta_len == 0) { - meta_len = strlen(CRM_META); - } - - if (param_set == NULL) { - return; - } - - for (lpc = 0; lpc < DIMOF(attr_filter); lpc++) { - xml_remove_prop(param_set, attr_filter[lpc]); - } - - key = crm_meta_name(XML_LRM_ATTR_INTERVAL); - interval = crm_element_value_copy(param_set, key); - free(key); - - key = crm_meta_name(XML_ATTR_TIMEOUT); - timeout = crm_element_value_copy(param_set, key); - - if (param_set) { - xmlAttrPtr xIter = param_set->properties; - - while (xIter) { - const char *prop_name = (const char *)xIter->name; - - xIter = xIter->next; - do_delete = FALSE; - if (strncasecmp(prop_name, CRM_META, meta_len) == 0) { - do_delete = TRUE; - } - - if (do_delete) { - xml_remove_prop(param_set, prop_name); - } - } - } - - if (crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) { - /* Re-instate the operation's timeout value */ - if (timeout != NULL) { - crm_xml_add(param_set, key, timeout); - } - } - - free(interval); - free(timeout); - free(key); -} - extern bool crm_is_daemon; /* coverity[+kill] */ void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork) { int rc = 0; int pid = 0; int status = 0; /* Implied by the parent's error logging below */ /* crm_write_blackbox(0); */ if(crm_is_daemon == FALSE) { /* This is a command line tool - do not fork */ /* crm_add_logfile(NULL); * Record it to a file? */ crm_enable_stderr(TRUE); /* Make sure stderr is enabled so we can tell the caller */ do_fork = FALSE; /* Just crash if needed */ } if (do_core == FALSE) { crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition); return; } else if (do_fork) { pid = fork(); } else { crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition); } if (pid == -1) { crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s", function, file, line, assert_condition); return; } else if(pid == 0) { /* Child process */ abort(); return; } /* Parent process */ crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s", function, pid, file, line, assert_condition); crm_write_blackbox(SIGTRAP, NULL); do { rc = waitpid(pid, &status, 0); if(rc == pid) { return; /* Job done */ } } while(errno == EINTR); if (errno == ECHILD) { /* crm_mon does this */ crm_trace("Cannot wait on forked child %d - SIGCHLD is probably set to SIG_IGN", pid); return; } crm_perror(LOG_ERR, "Cannot wait on forked child %d", pid); } int crm_pid_active(long pid, const char *daemon) { static int have_proc_pid = 0; if(have_proc_pid == 0) { char proc_path[PATH_MAX], exe_path[PATH_MAX]; /* check to make sure pid hasn't been reused by another process */ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", (long unsigned int)getpid()); have_proc_pid = 1; if(readlink(proc_path, exe_path, PATH_MAX - 1) < 0) { have_proc_pid = -1; } } if (pid <= 0) { return -1; } else if (kill(pid, 0) < 0 && errno == ESRCH) { return 0; } else if(daemon == NULL || have_proc_pid == -1) { return 1; } else { int rc = 0; char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX]; /* check to make sure pid hasn't been reused by another process */ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid); rc = readlink(proc_path, exe_path, PATH_MAX - 1); if (rc < 0 && errno == EACCES) { crm_perror(LOG_INFO, "Could not read from %s", proc_path); return 1; } else if (rc < 0) { crm_perror(LOG_ERR, "Could not read from %s", proc_path); return 0; } exe_path[rc] = 0; if(daemon[0] != '/') { rc = snprintf(myexe_path, sizeof(proc_path), CRM_DAEMON_DIR"/%s", daemon); myexe_path[rc] = 0; } else { rc = snprintf(myexe_path, sizeof(proc_path), "%s", daemon); myexe_path[rc] = 0; } if (strcmp(exe_path, myexe_path) == 0) { return 1; } } return 0; } #define LOCKSTRLEN 11 long crm_read_pidfile(const char *filename) { int fd; struct stat sbuf; long pid = -ENOENT; char buf[LOCKSTRLEN + 1]; if ((fd = open(filename, O_RDONLY)) < 0) { goto bail; } if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) { sleep(2); /* if someone was about to create one, * give'm a sec to do so */ } if (read(fd, buf, sizeof(buf)) < 1) { goto bail; } if (sscanf(buf, "%lu", &pid) > 0) { if (pid <= 0) { pid = -ESRCH; } else { crm_trace("Got pid %lu from %s\n", pid, filename); } } bail: if (fd >= 0) { close(fd); } return pid; } long crm_pidfile_inuse(const char *filename, long mypid, const char *daemon) { long pid = crm_read_pidfile(filename); if (pid < 2) { /* Invalid pid */ pid = -ENOENT; unlink(filename); } else if (mypid && pid == mypid) { /* In use by us */ pid = pcmk_ok; } else if (crm_pid_active(pid, daemon) == FALSE) { /* Contains a stale value */ unlink(filename); pid = -ENOENT; } else if (mypid && pid != mypid) { /* locked by existing process - give up */ pid = -EEXIST; } return pid; } static int crm_lock_pidfile(const char *filename, const char *name) { long mypid = 0; int fd = 0, rc = 0; char buf[LOCKSTRLEN + 1]; mypid = (unsigned long)getpid(); rc = crm_pidfile_inuse(filename, 0, name); if (rc == -ENOENT) { /* exists but the process is not active */ } else if (rc != pcmk_ok) { /* locked by existing process - give up */ return rc; } if ((fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) { /* Hmmh, why did we fail? Anyway, nothing we can do about it */ return -errno; } snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN - 1, mypid); rc = write(fd, buf, LOCKSTRLEN); close(fd); if (rc != LOCKSTRLEN) { crm_perror(LOG_ERR, "Incomplete write to %s", filename); return -errno; } return crm_pidfile_inuse(filename, mypid, name); } void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile) { int rc; long pid; const char *devnull = "/dev/null"; if (daemonize == FALSE) { return; } /* Check before we even try... */ rc = crm_pidfile_inuse(pidfile, 1, name); if(rc < pcmk_ok && rc != -ENOENT) { pid = crm_read_pidfile(pidfile); crm_err("%s: already running [pid %ld in %s]", name, pid, pidfile); printf("%s: already running [pid %ld in %s]\n", name, pid, pidfile); crm_exit(rc); } pid = fork(); if (pid < 0) { fprintf(stderr, "%s: could not start daemon\n", name); crm_perror(LOG_ERR, "fork"); crm_exit(EINVAL); } else if (pid > 0) { crm_exit(pcmk_ok); } rc = crm_lock_pidfile(pidfile, name); if(rc < pcmk_ok) { crm_err("Could not lock '%s' for %s: %s (%d)", pidfile, name, pcmk_strerror(rc), rc); printf("Could not lock '%s' for %s: %s (%d)\n", pidfile, name, pcmk_strerror(rc), rc); crm_exit(rc); } umask(S_IWGRP | S_IWOTH | S_IROTH); close(STDIN_FILENO); (void)open(devnull, O_RDONLY); /* Stdin: fd 0 */ close(STDOUT_FILENO); (void)open(devnull, O_WRONLY); /* Stdout: fd 1 */ close(STDERR_FILENO); (void)open(devnull, O_WRONLY); /* Stderr: fd 2 */ } char * crm_meta_name(const char *field) { int lpc = 0; int max = 0; char *crm_name = NULL; CRM_CHECK(field != NULL, return NULL); crm_name = crm_concat(CRM_META, field, '_'); /* Massage the names so they can be used as shell variables */ max = strlen(crm_name); for (; lpc < max; lpc++) { switch (crm_name[lpc]) { case '-': crm_name[lpc] = '_'; break; } } return crm_name; } const char * crm_meta_value(GHashTable * hash, const char *field) { char *key = NULL; const char *value = NULL; key = crm_meta_name(field); if (key) { value = g_hash_table_lookup(hash, key); free(key); } return value; } static struct option * crm_create_long_opts(struct crm_option *long_options) { struct option *long_opts = NULL; #ifdef HAVE_GETOPT_H int index = 0, lpc = 0; /* * A previous, possibly poor, choice of '?' as the short form of --help * means that getopt_long() returns '?' for both --help and for "unknown option" * * This dummy entry allows us to differentiate between the two in crm_get_option() * and exit with the correct error code */ long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option)); long_opts[index].name = "__dummmy__"; long_opts[index].has_arg = 0; long_opts[index].flag = 0; long_opts[index].val = '_'; index++; for (lpc = 0; long_options[lpc].name != NULL; lpc++) { if (long_options[lpc].name[0] == '-') { continue; } long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option)); /*fprintf(stderr, "Creating %d %s = %c\n", index, * long_options[lpc].name, long_options[lpc].val); */ long_opts[index].name = long_options[lpc].name; long_opts[index].has_arg = long_options[lpc].has_arg; long_opts[index].flag = long_options[lpc].flag; long_opts[index].val = long_options[lpc].val; index++; } /* Now create the list terminator */ long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option)); long_opts[index].name = NULL; long_opts[index].has_arg = 0; long_opts[index].flag = 0; long_opts[index].val = 0; #endif return long_opts; } void crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options, const char *app_desc) { if (short_options) { crm_short_options = strdup(short_options); } else if (long_options) { int lpc = 0; int opt_string_len = 0; char *local_short_options = NULL; for (lpc = 0; long_options[lpc].name != NULL; lpc++) { if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) { local_short_options = realloc_safe(local_short_options, opt_string_len + 4); local_short_options[opt_string_len++] = long_options[lpc].val; /* getopt(3) says: Two colons mean an option takes an optional arg; */ if (long_options[lpc].has_arg == optional_argument) { local_short_options[opt_string_len++] = ':'; } if (long_options[lpc].has_arg >= required_argument) { local_short_options[opt_string_len++] = ':'; } local_short_options[opt_string_len] = 0; } } crm_short_options = local_short_options; crm_trace("Generated short option string: '%s'", local_short_options); } if (long_options) { crm_long_options = long_options; } if (app_desc) { crm_app_description = app_desc; } if (app_usage) { crm_app_usage = app_usage; } } int crm_get_option(int argc, char **argv, int *index) { return crm_get_option_long(argc, argv, index, NULL); } int crm_get_option_long(int argc, char **argv, int *index, const char **longname) { #ifdef HAVE_GETOPT_H static struct option *long_opts = NULL; if (long_opts == NULL && crm_long_options) { long_opts = crm_create_long_opts(crm_long_options); } *index = 0; if (long_opts) { int flag = getopt_long(argc, argv, crm_short_options, long_opts, index); switch (flag) { case 0: if (long_opts[*index].val) { return long_opts[*index].val; } else if (longname) { *longname = long_opts[*index].name; } else { crm_notice("Unhandled option --%s", long_opts[*index].name); return flag; } case -1: /* End of option processing */ break; case ':': crm_trace("Missing argument"); crm_help('?', 1); break; case '?': crm_help('?', *index ? 0 : 1); break; } return flag; } #endif if (crm_short_options) { return getopt(argc, argv, crm_short_options); } return -1; } int crm_help(char cmd, int exit_code) { int i = 0; FILE *stream = (exit_code ? stderr : stdout); if (cmd == 'v' || cmd == '$') { fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION); fprintf(stream, "Written by Andrew Beekhof\n"); goto out; } if (cmd == '!') { fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES); goto out; } fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description); if (crm_app_usage) { fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage); } if (crm_long_options) { fprintf(stream, "Options:\n"); for (i = 0; crm_long_options[i].name != NULL; i++) { if (crm_long_options[i].flags & pcmk_option_hidden) { } else if (crm_long_options[i].flags & pcmk_option_paragraph) { fprintf(stream, "%s\n\n", crm_long_options[i].desc); } else if (crm_long_options[i].flags & pcmk_option_example) { fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc); } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) { fprintf(stream, "%s\n", crm_long_options[i].desc); } else { /* is val printable as char ? */ if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) { fprintf(stream, " -%c,", crm_long_options[i].val); } else { fputs(" ", stream); } fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name, crm_long_options[i].has_arg == optional_argument ? "[=value]" : crm_long_options[i].has_arg == required_argument ? "=value" : "", crm_long_options[i].desc ? crm_long_options[i].desc : ""); } } } else if (crm_short_options) { fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description); for (i = 0; crm_short_options[i] != 0; i++) { int has_arg = no_argument /* 0 */; if (crm_short_options[i + 1] == ':') { if (crm_short_options[i + 2] == ':') has_arg = optional_argument /* 2 */; else has_arg = required_argument /* 1 */; } fprintf(stream, " -%c %s\n", crm_short_options[i], has_arg == optional_argument ? "[value]" : has_arg == required_argument ? "{value}" : ""); i += has_arg; } } fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT); out: return crm_exit(exit_code); } void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb) { *ipcs_ro = mainloop_add_ipc_server(cib_channel_ro, QB_IPC_NATIVE, ro_cb); *ipcs_rw = mainloop_add_ipc_server(cib_channel_rw, QB_IPC_NATIVE, rw_cb); *ipcs_shm = mainloop_add_ipc_server(cib_channel_shm, QB_IPC_SHM, rw_cb); if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) { crm_err("Failed to create cib servers: exiting and inhibiting respawn."); crm_warn("Verify pacemaker and pacemaker_remote are not both enabled."); crm_exit(DAEMON_RESPAWN_STOP); } } void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw, qb_ipcs_service_t *ipcs_shm) { qb_ipcs_destroy(ipcs_ro); qb_ipcs_destroy(ipcs_rw); qb_ipcs_destroy(ipcs_shm); } qb_ipcs_service_t * crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb) { return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb); } void attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb); if (*ipcs == NULL) { crm_err("Failed to create attrd servers: exiting and inhibiting respawn."); crm_warn("Verify pacemaker and pacemaker_remote are not both enabled."); crm_exit(DAEMON_RESPAWN_STOP); } } void stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server("stonith-ng", QB_IPC_NATIVE, cb); if (*ipcs == NULL) { crm_err("Failed to create stonith-ng servers: exiting and inhibiting respawn."); crm_warn("Verify pacemaker and pacemaker_remote are not both enabled."); crm_exit(DAEMON_RESPAWN_STOP); } } -#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -static void -append_digest(lrmd_event_data_t * op, xmlNode * update, const char *version, const char *magic, - int level) -{ - /* this will enable us to later determine that the - * resource's parameters have changed and we should force - * a restart - */ - char *digest = NULL; - xmlNode *args_xml = NULL; - - if (op->params == NULL) { - return; - } - - args_xml = create_xml_node(NULL, XML_TAG_PARAMS); - g_hash_table_foreach(op->params, hash2field, args_xml); - filter_action_parameters(args_xml, version); - digest = calculate_operation_digest(args_xml, version); - -#if 0 - if (level < get_crm_log_level() - && op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) { - char *digest_source = dump_xml_unformatted(args_xml); - - do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n", - digest, ID(update), magic, digest_source); - free(digest_source); - } -#endif - crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest); - - free_xml(args_xml); - free(digest); -} - -int -rsc_op_expected_rc(lrmd_event_data_t * op) -{ - int rc = 0; - - if (op && op->user_data) { - int dummy = 0; - char *uuid = NULL; - - decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &rc); - free(uuid); - } - return rc; -} - -gboolean -did_rsc_op_fail(lrmd_event_data_t * op, int target_rc) -{ - switch (op->op_status) { - case PCMK_LRM_OP_CANCELLED: - case PCMK_LRM_OP_PENDING: - return FALSE; - break; - - case PCMK_LRM_OP_NOTSUPPORTED: - case PCMK_LRM_OP_TIMEOUT: - case PCMK_LRM_OP_ERROR: - return TRUE; - break; - - default: - if (target_rc != op->rc) { - return TRUE; - } - } - - return FALSE; -} - -xmlNode * -create_operation_update(xmlNode * parent, lrmd_event_data_t * op, const char * caller_version, - int target_rc, const char * node, const char * origin, int level) -{ - char *key = NULL; - char *magic = NULL; - char *op_id = NULL; - char *op_id_additional = NULL; - char *local_user_data = NULL; - const char *exit_reason = NULL; - - xmlNode *xml_op = NULL; - const char *task = NULL; - gboolean dc_munges_migrate_ops = (compare_version(caller_version, "3.0.3") < 0); - gboolean dc_needs_unique_ops = (compare_version(caller_version, "3.0.6") < 0); - - CRM_CHECK(op != NULL, return NULL); - do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%d)", - origin, op->rsc_id, op->op_type, services_lrm_status_str(op->op_status), - op->interval); - - crm_trace("DC version: %s", caller_version); - - task = op->op_type; - /* remap the task name under various scenarios - * this makes life easier for the PE when trying determine the current state - */ - if (crm_str_eq(task, "reload", TRUE)) { - if (op->op_status == PCMK_LRM_OP_DONE) { - task = CRMD_ACTION_START; - } else { - task = CRMD_ACTION_STATUS; - } - - } else if (dc_munges_migrate_ops && crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) { - /* if the migrate_from fails it will have enough info to do the right thing */ - if (op->op_status == PCMK_LRM_OP_DONE) { - task = CRMD_ACTION_STOP; - } else { - task = CRMD_ACTION_STATUS; - } - - } else if (dc_munges_migrate_ops - && op->op_status == PCMK_LRM_OP_DONE - && crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) { - task = CRMD_ACTION_START; - } - - key = generate_op_key(op->rsc_id, task, op->interval); - if (dc_needs_unique_ops && op->interval > 0) { - op_id = strdup(key); - - } else if (crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) { - const char *n_type = crm_meta_value(op->params, "notify_type"); - const char *n_task = crm_meta_value(op->params, "notify_operation"); - - CRM_LOG_ASSERT(n_type != NULL); - CRM_LOG_ASSERT(n_task != NULL); - op_id = generate_notify_key(op->rsc_id, n_type, n_task); - - /* these are not yet allowed to fail */ - op->op_status = PCMK_LRM_OP_DONE; - op->rc = 0; - - } else if (did_rsc_op_fail(op, target_rc)) { - op_id = generate_op_key(op->rsc_id, "last_failure", 0); - if (op->interval == 0) { - /* Ensure 'last' gets updated too in case recording-pending="true" */ - op_id_additional = generate_op_key(op->rsc_id, "last", 0); - } - exit_reason = op->exit_reason; - - } else if (op->interval > 0) { - op_id = strdup(key); - - } else { - op_id = generate_op_key(op->rsc_id, "last", 0); - } - - again: - xml_op = find_entity(parent, XML_LRM_TAG_RSC_OP, op_id); - if (xml_op == NULL) { - xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP); - } - - if (op->user_data == NULL) { - crm_debug("Generating fake transition key for:" - " %s_%s_%d %d from %s", - op->rsc_id, op->op_type, op->interval, op->call_id, origin); - local_user_data = generate_transition_key(-1, op->call_id, target_rc, FAKE_TE_ID); - op->user_data = local_user_data; - } - - if(magic == NULL) { - magic = generate_transition_magic(op->user_data, op->op_status, op->rc); - } - - crm_xml_add(xml_op, XML_ATTR_ID, op_id); - crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key); - crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task); - crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin); - crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version); - crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data); - crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic); - crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason); - crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */ - - crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id); - crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc); - crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status); - crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval); - - if (compare_version("2.1", caller_version) <= 0) { - if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) { - crm_trace("Timing data (%s_%s_%d): last=%u change=%u exec=%u queue=%u", - op->rsc_id, op->op_type, op->interval, - op->t_run, op->t_rcchange, op->exec_time, op->queue_time); - - if (op->interval == 0) { - /* The values are the same for non-recurring ops */ - crm_xml_add_int(xml_op, XML_RSC_OP_LAST_RUN, op->t_run); - crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run); - - } else if(op->t_rcchange) { - /* last-run is not accurate for recurring ops */ - crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_rcchange); - - } else { - /* ...but is better than nothing otherwise */ - crm_xml_add_int(xml_op, XML_RSC_OP_LAST_CHANGE, op->t_run); - } - - crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time); - crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time); - } - } - - if (crm_str_eq(op->op_type, CRMD_ACTION_MIGRATE, TRUE) - || crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) { - /* - * Record migrate_source and migrate_target always for migrate ops. - */ - const char *name = XML_LRM_ATTR_MIGRATE_SOURCE; - - crm_xml_add(xml_op, name, crm_meta_value(op->params, name)); - - name = XML_LRM_ATTR_MIGRATE_TARGET; - crm_xml_add(xml_op, name, crm_meta_value(op->params, name)); - } - - append_digest(op, xml_op, caller_version, magic, LOG_DEBUG); - - if (op_id_additional) { - free(op_id); - op_id = op_id_additional; - op_id_additional = NULL; - goto again; - } - - if (local_user_data) { - free(local_user_data); - op->user_data = NULL; - } - free(magic); - free(op_id); - free(key); - return xml_op; -} - bool pcmk_acl_required(const char *user) { #if ENABLE_ACL if(user == NULL || strlen(user) == 0) { crm_trace("no user set"); return FALSE; } else if (strcmp(user, CRM_DAEMON_USER) == 0) { return FALSE; } else if (strcmp(user, "root") == 0) { return FALSE; } crm_trace("acls required for %s", user); return TRUE; #else crm_trace("acls not supported"); return FALSE; #endif } #if ENABLE_ACL char * uid2username(uid_t uid) { struct passwd *pwent = getpwuid(uid); if (pwent == NULL) { crm_perror(LOG_ERR, "Cannot get password entry of uid: %d", uid); return NULL; } else { return strdup(pwent->pw_name); } } const char * crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user) { /* field is only checked for backwards compatibility */ static const char *effective_user = NULL; const char *requested_user = NULL; const char *user = NULL; if(effective_user == NULL) { effective_user = uid2username(geteuid()); } requested_user = crm_element_value(request, XML_ACL_TAG_USER); if(requested_user == NULL) { requested_user = crm_element_value(request, field); } if (is_privileged(effective_user) == FALSE) { /* We're not running as a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */ user = effective_user; } else if(peer_user == NULL && requested_user == NULL) { /* No user known or requested, use 'effective_user' and make sure one is set for the request */ user = effective_user; } else if(peer_user == NULL) { /* No user known, trusting 'requested_user' */ user = requested_user; } else if (is_privileged(peer_user) == FALSE) { /* The peer is not a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */ user = peer_user; } else if (requested_user == NULL) { /* Even if we're privileged, make sure there is always a value set */ user = peer_user; } else { /* Legal delegation to 'requested_user' */ user = requested_user; } /* Yes, pointer comparision */ if(user != crm_element_value(request, XML_ACL_TAG_USER)) { crm_xml_add(request, XML_ACL_TAG_USER, user); } if(field != NULL && user != crm_element_value(request, field)) { crm_xml_add(request, field, user); } return requested_user; } void determine_request_user(const char *user, xmlNode * request, const char *field) { /* Get our internal validation out of the way first */ CRM_CHECK(user != NULL && request != NULL && field != NULL, return); /* If our peer is a privileged user, we might be doing something on behalf of someone else */ if (is_privileged(user) == FALSE) { /* We're not a privileged user, set or overwrite any existing value for $field */ crm_xml_replace(request, field, user); } else if (crm_element_value(request, field) == NULL) { /* Even if we're privileged, make sure there is always a value set */ crm_xml_replace(request, field, user); /* } else { Legal delegation */ } crm_trace("Processing msg as user '%s'", crm_element_value(request, field)); } #endif void * find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal) { char *error; void *a_function; if (*handle == NULL) { *handle = dlopen(lib, RTLD_LAZY); } if (!(*handle)) { crm_err("%sCould not open %s: %s", fatal ? "Fatal: " : "", lib, dlerror()); if (fatal) { crm_exit(DAEMON_RESPAWN_STOP); } return NULL; } a_function = dlsym(*handle, fn); if (a_function == NULL) { error = dlerror(); crm_err("%sCould not find %s in %s: %s", fatal ? "Fatal: " : "", fn, lib, error); if (fatal) { crm_exit(DAEMON_RESPAWN_STOP); } } return a_function; } void * convert_const_pointer(const void *ptr) { /* Worst function ever */ return (void *)ptr; } #ifdef HAVE_UUID_UUID_H # include #endif char * crm_generate_uuid(void) { unsigned char uuid[16]; char *buffer = malloc(37); /* Including NUL byte */ uuid_generate(uuid); uuid_unparse(uuid, buffer); return buffer; } /*! * \brief Check whether a string represents a cluster daemon name * * \param[in] name String to check * * \return TRUE if name is standard client name used by daemons, FALSE otherwise */ bool crm_is_daemon_name(const char *name) { return (name && (!strcmp(name, CRM_SYSTEM_CRMD) || !strcmp(name, CRM_SYSTEM_STONITHD) || !strcmp(name, T_ATTRD) || !strcmp(name, CRM_SYSTEM_CIB) || !strcmp(name, CRM_SYSTEM_MCP) || !strcmp(name, CRM_SYSTEM_DC) || !strcmp(name, CRM_SYSTEM_TENGINE) || !strcmp(name, CRM_SYSTEM_LRMD))); } #include char * crm_md5sum(const char *buffer) { int lpc = 0, len = 0; char *digest = NULL; unsigned char raw_digest[MD5_DIGEST_SIZE]; if (buffer == NULL) { buffer = ""; } len = strlen(buffer); crm_trace("Beginning digest of %d bytes", len); digest = malloc(2 * MD5_DIGEST_SIZE + 1); if(digest) { md5_buffer(buffer, len, raw_digest); for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) { sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]); } digest[(2 * MD5_DIGEST_SIZE)] = 0; crm_trace("Digest %s.", digest); } else { crm_err("Could not create digest"); } return digest; } #ifdef HAVE_GNUTLS_GNUTLS_H void crm_gnutls_global_init(void) { signal(SIGPIPE, SIG_IGN); gnutls_global_init(); } #endif char * crm_generate_ra_key(const char *class, const char *provider, const char *type) { if (!class && !provider && !type) { return NULL; } return crm_strdup_printf("%s%s%s:%s", (class? class : ""), (provider? ":" : ""), (provider? provider : ""), (type? type : "")); }