diff options
Diffstat (limited to 'daemons/based')
-rw-r--r-- | daemons/based/Makefile.am | 42 | ||||
-rw-r--r-- | daemons/based/based_callbacks.c | 854 | ||||
-rw-r--r-- | daemons/based/based_common.c | 352 | ||||
-rw-r--r-- | daemons/based/based_io.c | 22 | ||||
-rw-r--r-- | daemons/based/based_messages.c | 125 | ||||
-rw-r--r-- | daemons/based/based_notify.c | 99 | ||||
-rw-r--r-- | daemons/based/based_operation.c | 59 | ||||
-rw-r--r-- | daemons/based/based_remote.c | 29 | ||||
-rw-r--r-- | daemons/based/based_transaction.c | 167 | ||||
-rw-r--r-- | daemons/based/based_transaction.h | 24 | ||||
-rw-r--r-- | daemons/based/pacemaker-based.c | 17 | ||||
-rw-r--r-- | daemons/based/pacemaker-based.h | 45 |
12 files changed, 850 insertions, 985 deletions
diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 053d93c..022fc47 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2004-2021 the Pacemaker project contributors +# Copyright 2004-2023 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -13,35 +13,37 @@ EXTRA_DIST = cib.pam halibdir = $(CRM_DAEMON_DIR) -COMMONLIBS = $(top_builddir)/lib/common/libcrmcommon.la \ - $(top_builddir)/lib/cib/libcib.la - halib_PROGRAMS = pacemaker-based -noinst_HEADERS = pacemaker-based.h +noinst_HEADERS = based_transaction.h \ + pacemaker-based.h pacemaker_based_CFLAGS = $(CFLAGS_HARDENED_EXE) pacemaker_based_LDFLAGS = $(LDFLAGS_HARDENED_EXE) -pacemaker_based_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la \ - $(COMMONLIBS) $(CLUSTERLIBS) - -pacemaker_based_SOURCES = pacemaker-based.c \ - based_callbacks.c \ - based_common.c \ - based_io.c \ - based_messages.c \ - based_notify.c \ - based_remote.c - -clean-generic: - rm -f *.log *.debug *.xml *~ - -if BUILD_LEGACY_LINKS +pacemaker_based_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la +pacemaker_based_LDADD += $(top_builddir)/lib/cib/libcib.la +pacemaker_based_LDADD += $(top_builddir)/lib/common/libcrmcommon.la +pacemaker_based_LDADD += $(CLUSTERLIBS) + +pacemaker_based_SOURCES = pacemaker-based.c \ + based_callbacks.c \ + based_io.c \ + based_messages.c \ + based_notify.c \ + based_operation.c \ + based_remote.c \ + based_transaction.c + +.PHONY: install-exec-hook install-exec-hook: +if BUILD_LEGACY_LINKS $(MKDIR_P) -- $(DESTDIR)$(CRM_DAEMON_DIR) cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f cib && $(LN_S) pacemaker-based cib +endif +.PHONY: uninstall-hook uninstall-hook: +if BUILD_LEGACY_LINKS cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f cib endif diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3726caa..4fac222 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -20,6 +20,9 @@ #include <fcntl.h> #include <inttypes.h> // PRIu64 +#include <glib.h> +#include <libxml/tree.h> + #include <crm/crm.h> #include <crm/cib.h> #include <crm/msg_xml.h> @@ -31,7 +34,6 @@ #include <pacemaker-based.h> #define EXIT_ESCALATION_MS 10000 -#define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->uname) static unsigned long cib_local_bcast_num = 0; @@ -50,11 +52,10 @@ qb_ipcs_service_t *ipcs_ro = NULL; qb_ipcs_service_t *ipcs_rw = NULL; qb_ipcs_service_t *ipcs_shm = NULL; -static void cib_process_request(xmlNode *request, gboolean privileged, - const pcmk__client_t *cib_client); - -static int cib_process_command(xmlNode *request, xmlNode **reply, - xmlNode **cib_diff, gboolean privileged); +static int cib_process_command(xmlNode *request, + const cib__operation_t *operation, + cib__op_fn_t op_function, xmlNode **reply, + xmlNode **cib_diff, bool privileged); static gboolean cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, gboolean privileged); @@ -138,11 +139,130 @@ struct qb_ipcs_service_handlers ipc_rw_callbacks = { .connection_destroyed = cib_ipc_destroy }; +/*! + * \internal + * \brief Create reply XML for a CIB request + * + * \param[in] op CIB operation type + * \param[in] call_id CIB call ID + * \param[in] client_id CIB client ID + * \param[in] call_options Group of <tt>enum cib_call_options</tt> flags + * \param[in] rc Request return code + * \param[in] call_data Request output data + * + * \return Reply XML + * + * \note The caller is responsible for freeing the return value using + * \p free_xml(). + */ +static xmlNode * +create_cib_reply(const char *op, const char *call_id, const char *client_id, + int call_options, int rc, xmlNode *call_data) +{ + xmlNode *reply = create_xml_node(NULL, "cib-reply"); + + CRM_ASSERT(reply != NULL); + + crm_xml_add(reply, F_TYPE, T_CIB); + crm_xml_add(reply, F_CIB_OPERATION, op); + crm_xml_add(reply, F_CIB_CALLID, call_id); + crm_xml_add(reply, F_CIB_CLIENTID, client_id); + crm_xml_add_int(reply, F_CIB_CALLOPTS, call_options); + crm_xml_add_int(reply, F_CIB_RC, rc); + + if (call_data != NULL) { + crm_trace("Attaching reply output"); + add_message_xml(reply, F_CIB_CALLDATA, call_data); + } + + crm_log_xml_explicit(reply, "cib:reply"); + return reply; +} + +static void +do_local_notify(const xmlNode *notify_src, const char *client_id, + bool sync_reply, bool from_peer) +{ + int rid = 0; + int call_id = 0; + pcmk__client_t *client_obj = NULL; + + CRM_ASSERT(notify_src && client_id); + + crm_element_value_int(notify_src, F_CIB_CALLID, &call_id); + + client_obj = pcmk__find_client_by_id(client_id); + if (client_obj == NULL) { + crm_debug("Could not send response %d: client %s not found", + call_id, client_id); + return; + } + + if (sync_reply) { + if (client_obj->ipcs) { + CRM_LOG_ASSERT(client_obj->request_id); + + rid = client_obj->request_id; + client_obj->request_id = 0; + + crm_trace("Sending response %d to client %s%s", + rid, pcmk__client_name(client_obj), + (from_peer? " (originator of delegated request)" : "")); + } else { + crm_trace("Sending response (call %d) to client %s%s", + call_id, pcmk__client_name(client_obj), + (from_peer? " (originator of delegated request)" : "")); + } + + } else { + crm_trace("Sending event %d to client %s%s", + call_id, pcmk__client_name(client_obj), + (from_peer? " (originator of delegated request)" : "")); + } + + switch (PCMK__CLIENT_TYPE(client_obj)) { + case pcmk__client_ipc: + { + int rc = pcmk__ipc_send_xml(client_obj, rid, notify_src, + (sync_reply? crm_ipc_flags_none + : crm_ipc_server_event)); + + if (rc != pcmk_rc_ok) { + crm_warn("%s reply to client %s failed: %s " CRM_XS " rc=%d", + (sync_reply? "Synchronous" : "Asynchronous"), + pcmk__client_name(client_obj), pcmk_rc_str(rc), + rc); + } + } + break; +#ifdef HAVE_GNUTLS_GNUTLS_H + case pcmk__client_tls: +#endif + case pcmk__client_tcp: + pcmk__remote_send_xml(client_obj->remote, notify_src); + break; + default: + crm_err("Unknown transport for client %s " + CRM_XS " flags=%#016" PRIx64, + pcmk__client_name(client_obj), client_obj->flags); + } +} + void cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request, pcmk__client_t *cib_client, gboolean privileged) { const char *op = crm_element_value(op_request, F_CIB_OPERATION); + int call_options = cib_none; + + crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options); + + /* Requests with cib_transaction set should not be sent to based directly + * (outside of a commit-transaction request) + */ + if (pcmk_is_set(call_options, cib_transaction)) { + return; + } if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { if (flags & crm_ipc_client_response) { @@ -180,9 +300,6 @@ cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request, } else if (pcmk__str_eq(type, T_CIB_DIFF_NOTIFY, pcmk__str_casei)) { bit = cib_notify_diff; - } else if (pcmk__str_eq(type, T_CIB_REPLACE_NOTIFY, pcmk__str_casei)) { - bit = cib_notify_replace; - } else { status = CRM_EX_INVALID_PARAM; } @@ -354,9 +471,7 @@ process_ping_reply(xmlNode *reply) if(remote_cib && remote_cib->children) { // Additional debug xml_calculate_changes(the_cib, remote_cib); - - pcmk__output_set_log_level(logger_out, LOG_INFO); - pcmk__xml_show_changes(logger_out, remote_cib); + pcmk__log_xml_changes(LOG_INFO, remote_cib); crm_trace("End of differences"); } @@ -367,75 +482,6 @@ process_ping_reply(xmlNode *reply) } static void -do_local_notify(xmlNode * notify_src, const char *client_id, - gboolean sync_reply, gboolean from_peer) -{ - int rid = 0; - int call_id = 0; - pcmk__client_t *client_obj = NULL; - - CRM_ASSERT(notify_src && client_id); - - crm_element_value_int(notify_src, F_CIB_CALLID, &call_id); - - client_obj = pcmk__find_client_by_id(client_id); - if (client_obj == NULL) { - crm_debug("Could not send response %d: client %s not found", - call_id, client_id); - return; - } - - if (sync_reply) { - if (client_obj->ipcs) { - CRM_LOG_ASSERT(client_obj->request_id); - - rid = client_obj->request_id; - client_obj->request_id = 0; - - crm_trace("Sending response %d to client %s%s", - rid, pcmk__client_name(client_obj), - (from_peer? " (originator of delegated request)" : "")); - } else { - crm_trace("Sending response (call %d) to client %s%s", - call_id, pcmk__client_name(client_obj), - (from_peer? " (originator of delegated request)" : "")); - } - - } else { - crm_trace("Sending event %d to client %s%s", - call_id, pcmk__client_name(client_obj), - (from_peer? " (originator of delegated request)" : "")); - } - - switch (PCMK__CLIENT_TYPE(client_obj)) { - case pcmk__client_ipc: - { - int rc = pcmk__ipc_send_xml(client_obj, rid, notify_src, - (sync_reply? crm_ipc_flags_none - : crm_ipc_server_event)); - - if (rc != pcmk_rc_ok) { - crm_warn("%s reply to client %s failed: %s " CRM_XS " rc=%d", - (sync_reply? "Synchronous" : "Asynchronous"), - pcmk__client_name(client_obj), pcmk_rc_str(rc), - rc); - } - } - break; -#ifdef HAVE_GNUTLS_GNUTLS_H - case pcmk__client_tls: -#endif - case pcmk__client_tcp: - pcmk__remote_send_xml(client_obj->remote, notify_src); - break; - default: - crm_err("Unknown transport for client %s " - CRM_XS " flags=%#016" PRIx64, - pcmk__client_name(client_obj), client_obj->flags); - } -} - -static void local_notify_destroy_callback(gpointer data) { cib_local_notify_t *notify = data; @@ -448,7 +494,7 @@ local_notify_destroy_callback(gpointer data) static void check_local_notify(int bcast_id) { - cib_local_notify_t *notify = NULL; + const cib_local_notify_t *notify = NULL; if (!local_notify_queue) { return; @@ -483,13 +529,14 @@ queue_local_notify(xmlNode * notify_src, const char *client_id, gboolean sync_re } static void -parse_local_options_v1(const pcmk__client_t *cib_client, int call_type, - int call_options, const char *host, const char *op, - gboolean *local_notify, gboolean *needs_reply, - gboolean *process, gboolean *needs_forward) +parse_local_options_v1(const pcmk__client_t *cib_client, + const cib__operation_t *operation, int call_options, + const char *host, const char *op, gboolean *local_notify, + gboolean *needs_reply, gboolean *process, + gboolean *needs_forward) { - if (cib_op_modifies(call_type) - && !(call_options & cib_inhibit_bcast)) { + if (pcmk_is_set(operation->flags, cib__op_attr_modifies) + && !pcmk_is_set(call_options, cib_inhibit_bcast)) { /* we need to send an update anyway */ *needs_reply = TRUE; } else { @@ -526,78 +573,87 @@ parse_local_options_v1(const pcmk__client_t *cib_client, int call_type, } static void -parse_local_options_v2(const pcmk__client_t *cib_client, int call_type, - int call_options, const char *host, const char *op, - gboolean *local_notify, gboolean *needs_reply, - gboolean *process, gboolean *needs_forward) +parse_local_options_v2(const pcmk__client_t *cib_client, + const cib__operation_t *operation, int call_options, + const char *host, const char *op, gboolean *local_notify, + gboolean *needs_reply, gboolean *process, + gboolean *needs_forward) { - if (cib_op_modifies(call_type)) { - if (pcmk__str_any_of(op, PCMK__CIB_REQUEST_PRIMARY, - PCMK__CIB_REQUEST_SECONDARY, NULL)) { - /* Always handle these locally */ - *process = TRUE; - *needs_reply = FALSE; - *local_notify = TRUE; - *needs_forward = FALSE; - return; - - } else { - /* Redirect all other updates via CPG */ - *needs_reply = TRUE; - *needs_forward = TRUE; - *process = FALSE; - crm_trace("%s op from %s needs to be forwarded to client %s", - op, pcmk__client_name(cib_client), - pcmk__s(host, "the primary instance")); - return; - } - } - - + // Process locally and notify local client *process = TRUE; *needs_reply = FALSE; *local_notify = TRUE; *needs_forward = FALSE; - if (stand_alone) { - crm_trace("Processing %s op from client %s (stand-alone)", + if (pcmk_is_set(operation->flags, cib__op_attr_local)) { + /* Always process locally if cib__op_attr_local is set. + * + * @COMPAT: Currently host is ignored. At a compatibility break, throw + * an error (from cib_process_request() or earlier) if host is not NULL or + * OUR_NODENAME. + */ + crm_trace("Processing always-local %s op from client %s", op, pcmk__client_name(cib_client)); - } else if (host == NULL) { - crm_trace("Processing unaddressed %s op from client %s", - op, pcmk__client_name(cib_client)); + if (!pcmk__str_eq(host, OUR_NODENAME, + pcmk__str_casei|pcmk__str_null_matches)) { - } else if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { - crm_trace("Processing locally addressed %s op from client %s", + crm_warn("Operation '%s' is always local but its target host is " + "set to '%s'", + op, host); + } + return; + } + + if (pcmk_is_set(operation->flags, cib__op_attr_modifies) + || !pcmk__str_eq(host, OUR_NODENAME, + pcmk__str_casei|pcmk__str_null_matches)) { + + // Forward modifying and non-local requests via cluster + *process = FALSE; + *needs_reply = FALSE; + *local_notify = FALSE; + *needs_forward = TRUE; + + crm_trace("%s op from %s needs to be forwarded to %s", + op, pcmk__client_name(cib_client), + pcmk__s(host, "all nodes")); + return; + } + + if (stand_alone) { + crm_trace("Processing %s op from client %s (stand-alone)", op, pcmk__client_name(cib_client)); } else { - crm_trace("%s op from %s needs to be forwarded to client %s", - op, pcmk__client_name(cib_client), host); - *needs_forward = TRUE; - *process = FALSE; + crm_trace("Processing %saddressed %s op from client %s", + ((host != NULL)? "locally " : "un"), + op, pcmk__client_name(cib_client)); } } static void -parse_local_options(const pcmk__client_t *cib_client, int call_type, - int call_options, const char *host, const char *op, - gboolean *local_notify, gboolean *needs_reply, - gboolean *process, gboolean *needs_forward) +parse_local_options(const pcmk__client_t *cib_client, + const cib__operation_t *operation, int call_options, + const char *host, const char *op, gboolean *local_notify, + gboolean *needs_reply, gboolean *process, + gboolean *needs_forward) { if(cib_legacy_mode()) { - parse_local_options_v1(cib_client, call_type, call_options, host, - op, local_notify, needs_reply, process, needs_forward); + parse_local_options_v1(cib_client, operation, call_options, host, + op, local_notify, needs_reply, process, + needs_forward); } else { - parse_local_options_v2(cib_client, call_type, call_options, host, - op, local_notify, needs_reply, process, needs_forward); + parse_local_options_v2(cib_client, operation, call_options, host, + op, local_notify, needs_reply, process, + needs_forward); } } static gboolean -parse_peer_options_v1(int call_type, xmlNode * request, - gboolean * local_notify, gboolean * needs_reply, gboolean * process, - gboolean * needs_forward) +parse_peer_options_v1(const cib__operation_t *operation, xmlNode *request, + gboolean *local_notify, gboolean *needs_reply, + gboolean *process) { const char *op = NULL; const char *host = NULL; @@ -620,7 +676,8 @@ parse_peer_options_v1(int call_type, xmlNode * request, } op = crm_element_value(request, F_CIB_OPERATION); - crm_trace("Processing %s request sent by %s", op, originator); + crm_trace("Processing legacy %s request sent by %s", op, originator); + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { /* Always process these */ *local_notify = FALSE; @@ -693,9 +750,9 @@ parse_peer_options_v1(int call_type, xmlNode * request, } static gboolean -parse_peer_options_v2(int call_type, xmlNode * request, - gboolean * local_notify, gboolean * needs_reply, gboolean * process, - gboolean * needs_forward) +parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request, + gboolean *local_notify, gboolean *needs_reply, + gboolean *process) { const char *host = NULL; const char *delegated = crm_element_value(request, F_CIB_DELEGATED); @@ -705,6 +762,10 @@ parse_peer_options_v2(int call_type, xmlNode * request, gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei); + if (originator == NULL) { // Shouldn't be possible + originator = "peer"; + } + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { /* sync_our_cib() sets F_CIB_ISREPLY */ if (reply_to) { @@ -734,10 +795,10 @@ parse_peer_options_v2(int call_type, xmlNode * request, const char *max = crm_element_value(request, F_CIB_SCHEMA_MAX); const char *upgrade_rc = crm_element_value(request, F_CIB_UPGRADE_RC); - crm_trace("Parsing %s operation%s for %s with max=%s and upgrade_rc=%s", - op, (is_reply? " reply" : ""), + crm_trace("Parsing upgrade %s for %s with max=%s and upgrade_rc=%s", + (is_reply? "reply" : "request"), (based_is_primary? "primary" : "secondary"), - (max? max : "none"), (upgrade_rc? upgrade_rc : "none")); + pcmk__s(max, "none"), pcmk__s(upgrade_rc, "none")); if (upgrade_rc != NULL) { // Our upgrade request was rejected by DC, notify clients of result @@ -752,7 +813,7 @@ parse_peer_options_v2(int call_type, xmlNode * request, goto skip_is_reply; } else { - // Ignore broadcast client requests when we're not DC + // Ignore broadcast client requests when we're not primary return FALSE; } @@ -762,22 +823,25 @@ parse_peer_options_v2(int call_type, xmlNode * request, legacy_mode = TRUE; return FALSE; - } else if (is_reply && cib_op_modifies(call_type)) { + } else if (is_reply + && pcmk_is_set(operation->flags, cib__op_attr_modifies)) { crm_trace("Ignoring legacy %s reply sent from %s to local clients", op, originator); return FALSE; } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { - /* Legacy handling */ - crm_debug("Legacy handling of %s message from %s", op, originator); *local_notify = FALSE; if (reply_to == NULL) { *process = TRUE; + } else { // Not possible? + crm_debug("Ignoring shutdown request from %s because reply_to=%s", + originator, reply_to); } return *process; } - if(is_reply) { - crm_trace("Handling %s reply sent from %s to local clients", op, originator); + if (is_reply) { + crm_trace("Will notify local clients for %s reply from %s", + op, originator); *process = FALSE; *needs_reply = FALSE; *local_notify = TRUE; @@ -797,62 +861,78 @@ parse_peer_options_v2(int call_type, xmlNode * request, return TRUE; } else if (host != NULL) { - /* this is for a specific instance and we're not it */ - crm_trace("Ignoring %s operation for instance on %s", op, host); + crm_trace("Ignoring %s request intended for CIB manager on %s", + op, host); return FALSE; } else if(is_reply == FALSE && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) { *needs_reply = TRUE; } - crm_trace("Processing %s request sent to everyone by %s/%s on %s %s", op, - crm_element_value(request, F_CIB_CLIENTNAME), - crm_element_value(request, F_CIB_CALLID), - originator, (*local_notify)?"(notify)":""); + crm_trace("Processing %s request broadcast by %s call %s on %s " + "(local clients will%s be notified)", op, + pcmk__s(crm_element_value(request, F_CIB_CLIENTNAME), "client"), + pcmk__s(crm_element_value(request, F_CIB_CALLID), "without ID"), + originator, (*local_notify? "" : "not")); return TRUE; } static gboolean -parse_peer_options(int call_type, xmlNode * request, - gboolean * local_notify, gboolean * needs_reply, gboolean * process, - gboolean * needs_forward) +parse_peer_options(const cib__operation_t *operation, xmlNode *request, + gboolean *local_notify, gboolean *needs_reply, + gboolean *process) { /* TODO: What happens when an update comes in after node A * requests the CIB from node B, but before it gets the reply (and * sends out the replace operation) */ if(cib_legacy_mode()) { - return parse_peer_options_v1( - call_type, request, local_notify, needs_reply, process, needs_forward); + return parse_peer_options_v1(operation, request, local_notify, + needs_reply, process); } else { - return parse_peer_options_v2( - call_type, request, local_notify, needs_reply, process, needs_forward); + return parse_peer_options_v2(operation, request, local_notify, + needs_reply, process); } } +/*! + * \internal + * \brief Forward a CIB request to the appropriate target host(s) + * + * \param[in] request CIB request to forward + */ static void -forward_request(xmlNode *request, int call_options) +forward_request(xmlNode *request) { const char *op = crm_element_value(request, F_CIB_OPERATION); + const char *section = crm_element_value(request, F_CIB_SECTION); const char *host = crm_element_value(request, F_CIB_HOST); + const char *originator = crm_element_value(request, F_ORIG); + const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME); + const char *call_id = crm_element_value(request, F_CIB_CALLID); - crm_xml_add(request, F_CIB_DELEGATED, OUR_NODENAME); - - if (host != NULL) { - crm_trace("Forwarding %s op to %s", op, host); - send_cluster_message(crm_get_peer(0, host), crm_msg_cib, request, FALSE); + int log_level = LOG_INFO; - } else { - crm_trace("Forwarding %s op to primary instance", op); - send_cluster_message(NULL, crm_msg_cib, request, FALSE); + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_NOOP, pcmk__str_none)) { + log_level = LOG_DEBUG; } - /* Return the request to its original state */ - xml_remove_prop(request, F_CIB_DELEGATED); + do_crm_log(log_level, + "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)", + pcmk__s(op, "invalid"), + pcmk__s(section, "all"), + pcmk__s(host, (cib_legacy_mode()? "primary" : "all")), + pcmk__s(originator, "local"), + pcmk__s(client_name, "unspecified"), + pcmk__s(call_id, "unspecified")); - if (call_options & cib_discard_reply) { - crm_trace("Client not interested in reply"); - } + crm_xml_add(request, F_CIB_DELEGATED, OUR_NODENAME); + + send_cluster_message(((host != NULL)? crm_get_peer(0, host) : NULL), + crm_msg_cib, request, FALSE); + + // Return the request to its original state + xml_remove_prop(request, F_CIB_DELEGATED); } static gboolean @@ -861,9 +941,10 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb CRM_ASSERT(msg != NULL); if (broadcast) { - /* this (successful) call modified the CIB _and_ the - * change needs to be broadcast... - * send via HA to other nodes + /* @COMPAT: Legacy code + * + * This successful call modified the CIB, and the change needs to be + * broadcast (sent via cluster to all nodes). */ int diff_add_updates = 0; int diff_add_epoch = 0; @@ -878,7 +959,7 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb CRM_LOG_ASSERT(result_diff != NULL); digest = crm_element_value(result_diff, XML_ATTR_DIGEST); - crm_element_value_int(result_diff, "format", &format); + crm_element_value_int(result_diff, PCMK_XA_FORMAT, &format); cib_diff_version_details(result_diff, &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, @@ -919,12 +1000,14 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb * \param[in] privileged Whether privileged commands may be run * (see cib_server_ops[] definition) * \param[in] cib_client IPC client that sent request (or NULL if CPG) + * + * \return Legacy Pacemaker return code */ -static void +int cib_process_request(xmlNode *request, gboolean privileged, const pcmk__client_t *cib_client) { - int call_type = 0; + // @TODO: Break into multiple smaller functions int call_options = 0; gboolean process = TRUE; // Whether to process request locally now @@ -946,12 +1029,16 @@ cib_process_request(xmlNode *request, gboolean privileged, const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME); const char *reply_to = crm_element_value(request, F_CIB_ISREPLY); + const cib__operation_t *operation = NULL; + cib__op_fn_t op_function = NULL; + crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); if ((host != NULL) && (*host == '\0')) { host = NULL; } + // @TODO: Improve trace messages. Target is accurate only for legacy mode. if (host) { target = host; @@ -970,72 +1057,68 @@ cib_process_request(xmlNode *request, gboolean privileged, crm_trace("Processing local %s operation from %s/%s intended for %s", op, client_name, call_id, target); } - rc = cib_get_operation_id(op, &call_type); + rc = cib__get_operation(op, &operation); + rc = pcmk_rc2legacy(rc); if (rc != pcmk_ok) { /* TODO: construct error reply? */ crm_err("Pre-processing of command failed: %s", pcmk_strerror(rc)); - return; + return rc; + } + + op_function = based_get_op_function(operation); + if (op_function == NULL) { + crm_err("Operation %s not supported by CIB manager", op); + return -EOPNOTSUPP; } if (cib_client != NULL) { - parse_local_options(cib_client, call_type, call_options, host, op, - &local_notify, &needs_reply, &process, &needs_forward); + parse_local_options(cib_client, operation, call_options, host, op, + &local_notify, &needs_reply, &process, + &needs_forward); - } else if (parse_peer_options(call_type, request, &local_notify, - &needs_reply, &process, &needs_forward) == FALSE) { - return; + } else if (!parse_peer_options(operation, request, &local_notify, + &needs_reply, &process)) { + return rc; + } + + if (pcmk_is_set(call_options, cib_transaction)) { + /* All requests in a transaction are processed locally against a working + * CIB copy, and we don't notify for individual requests because the + * entire transaction is atomic. + * + * We still call the option parser functions above, for the sake of log + * messages and checking whether we're the target for peer requests. + */ + process = TRUE; + needs_reply = FALSE; + local_notify = FALSE; + needs_forward = FALSE; } - is_update = cib_op_modifies(call_type); + is_update = pcmk_is_set(operation->flags, cib__op_attr_modifies); - if (call_options & cib_discard_reply) { + if (pcmk_is_set(call_options, cib_discard_reply)) { /* If the request will modify the CIB, and we are in legacy mode, we * need to build a reply so we can broadcast a diff, even if the * requester doesn't want one. */ needs_reply = is_update && cib_legacy_mode(); local_notify = FALSE; + crm_trace("Client is not interested in the reply"); } if (needs_forward) { - const char *section = crm_element_value(request, F_CIB_SECTION); - int log_level = LOG_INFO; - - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_NOOP, pcmk__str_none)) { - log_level = LOG_DEBUG; - } - - do_crm_log(log_level, - "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)", - op, - section ? section : "'all'", - pcmk__s(host, (cib_legacy_mode() ? "primary" : "all")), - originator ? originator : "local", - client_name, call_id); - - forward_request(request, call_options); - return; + forward_request(request); + return rc; } if (cib_status != pcmk_ok) { - const char *call = crm_element_value(request, F_CIB_CALLID); - rc = cib_status; crm_err("Operation ignored, cluster configuration is invalid." " Please repair and restart: %s", pcmk_strerror(cib_status)); - op_reply = create_xml_node(NULL, "cib-reply"); - crm_xml_add(op_reply, F_TYPE, T_CIB); - crm_xml_add(op_reply, F_CIB_OPERATION, op); - crm_xml_add(op_reply, F_CIB_CALLID, call); - crm_xml_add(op_reply, F_CIB_CLIENTID, client_id); - crm_xml_add_int(op_reply, F_CIB_CALLOPTS, call_options); - crm_xml_add_int(op_reply, F_CIB_RC, rc); - - crm_trace("Attaching reply output"); - add_message_xml(op_reply, F_CIB_CALLDATA, the_cib); - - crm_log_xml_explicit(op_reply, "cib:reply"); + op_reply = create_cib_reply(op, call_id, client_id, call_options, rc, + the_cib); } else if (process) { time_t finished = 0; @@ -1043,7 +1126,8 @@ cib_process_request(xmlNode *request, gboolean privileged, int level = LOG_INFO; const char *section = crm_element_value(request, F_CIB_SECTION); - rc = cib_process_command(request, &op_reply, &result_diff, privileged); + rc = cib_process_command(request, operation, op_function, &op_reply, + &result_diff, privileged); if (!is_update) { level = LOG_TRACE; @@ -1120,10 +1204,9 @@ cib_process_request(xmlNode *request, gboolean privileged, op_reply = NULL; /* the reply is queued, so don't free here */ } - } else if (call_options & cib_discard_reply) { - crm_trace("Caller isn't interested in reply"); + } else if ((cib_client == NULL) + && !pcmk_is_set(call_options, cib_discard_reply)) { - } else if (cib_client == NULL) { if (is_update == FALSE || result_diff == NULL) { crm_trace("Request not broadcast: R/O call"); @@ -1158,24 +1241,51 @@ cib_process_request(xmlNode *request, gboolean privileged, free_xml(op_reply); free_xml(result_diff); - return; + return rc; } -static char * -calculate_section_digest(const char *xpath, xmlNode * xml_obj) +/*! + * \internal + * \brief Get a CIB operation's input from the request XML + * + * \param[in] request CIB request XML + * \param[in] type CIB operation type + * \param[out] section Where to store CIB section name + * + * \return Input XML for CIB operation + * + * \note If not \c NULL, the return value is a non-const pointer to part of + * \p request. The caller should not free it directly. + */ +static xmlNode * +prepare_input(const xmlNode *request, enum cib__op_type type, + const char **section) { - xmlNode *xml_section = NULL; + xmlNode *input = NULL; + + *section = NULL; + + switch (type) { + case cib__op_apply_patch: + if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) { + input = get_message_xml(request, F_CIB_UPDATE_DIFF); + } else { + input = get_message_xml(request, F_CIB_CALLDATA); + } + break; - if (xml_obj == NULL) { - return NULL; + default: + input = get_message_xml(request, F_CIB_CALLDATA); + *section = crm_element_value(request, F_CIB_SECTION); + break; } - xml_section = get_xpath_object(xpath, xml_obj, LOG_TRACE); - if (xml_section == NULL) { - return NULL; + // Grab the specified section + if ((*section != NULL) && pcmk__xe_is(input, XML_TAG_CIB)) { + input = pcmk_find_cib_element(input, *section); } - return calculate_xml_versioned_digest(xml_section, FALSE, TRUE, CRM_FEATURE_SET); + return input; } // v1 and v2 patch formats @@ -1201,14 +1311,14 @@ contains_config_change(xmlNode *diff) } static int -cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gboolean privileged) +cib_process_command(xmlNode *request, const cib__operation_t *operation, + cib__op_fn_t op_function, xmlNode **reply, + xmlNode **cib_diff, bool privileged) { xmlNode *input = NULL; xmlNode *output = NULL; xmlNode *result_cib = NULL; - xmlNode *current_cib = NULL; - int call_type = 0; int call_options = 0; const char *op = NULL; @@ -1216,24 +1326,15 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb const char *call_id = crm_element_value(request, F_CIB_CALLID); const char *client_id = crm_element_value(request, F_CIB_CLIENTID); const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME); - const char *origin = crm_element_value(request, F_ORIG); + const char *originator = crm_element_value(request, F_ORIG); int rc = pcmk_ok; - int rc2 = pcmk_ok; - gboolean send_r_notify = FALSE; - gboolean config_changed = FALSE; - gboolean manage_counters = TRUE; + bool config_changed = false; + bool manage_counters = true; static mainloop_timer_t *digest_timer = NULL; - char *current_nodes_digest = NULL; - char *current_alerts_digest = NULL; - char *current_status_digest = NULL; - uint32_t change_section = cib_change_section_nodes - |cib_change_section_alerts - |cib_change_section_status; - CRM_ASSERT(cib_status == pcmk_ok); if(digest_timer == NULL) { @@ -1242,91 +1343,64 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb *reply = NULL; *cib_diff = NULL; - current_cib = the_cib; /* Start processing the request... */ op = crm_element_value(request, F_CIB_OPERATION); crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); - rc = cib_get_operation_id(op, &call_type); - if (rc == pcmk_ok && privileged == FALSE) { - rc = cib_op_can_run(call_type, call_options, privileged); + if (!privileged && pcmk_is_set(operation->flags, cib__op_attr_privileged)) { + rc = -EACCES; + crm_trace("Failed due to lack of privileges: %s", pcmk_strerror(rc)); + goto done; } - rc2 = cib_op_prepare(call_type, request, &input, §ion); - if (rc == pcmk_ok) { - rc = rc2; - } + input = prepare_input(request, operation->type, §ion); - if (rc != pcmk_ok) { - crm_trace("Call setup failed: %s", pcmk_strerror(rc)); - goto done; - - } else if (cib_op_modifies(call_type) == FALSE) { - rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE, - section, request, input, FALSE, &config_changed, - current_cib, &result_cib, NULL, &output); + if (!pcmk_is_set(operation->flags, cib__op_attr_modifies)) { + rc = cib_perform_op(op, call_options, op_function, true, section, + request, input, false, &config_changed, &the_cib, + &result_cib, NULL, &output); CRM_CHECK(result_cib == NULL, free_xml(result_cib)); goto done; } - /* Handle a valid write action */ + /* @COMPAT: Handle a valid write action (legacy) + * + * @TODO: Re-evaluate whether this is all truly legacy. The cib_force_diff + * portion is. However, F_CIB_GLOBAL_UPDATE may be set by a sync operation + * even in non-legacy mode, and manage_counters tells xml_create_patchset() + * whether to update version/epoch info. + */ if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) { - /* legacy code */ - manage_counters = FALSE; + manage_counters = false; cib__set_call_options(call_options, "call", cib_force_diff); crm_trace("Global update detected"); - CRM_CHECK(call_type == 3 || call_type == 4, crm_err("Call type: %d", call_type); - crm_log_xml_err(request, "bad op")); + CRM_LOG_ASSERT(pcmk__str_any_of(op, + PCMK__CIB_REQUEST_APPLY_PATCH, + PCMK__CIB_REQUEST_REPLACE, + NULL)); } ping_modified_since = TRUE; if (pcmk_is_set(call_options, cib_inhibit_bcast)) { crm_trace("Skipping update: inhibit broadcast"); - manage_counters = FALSE; - } - - if (!pcmk_is_set(call_options, cib_dryrun) - && pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) { - // Copying large CIBs accounts for a huge percentage of our CIB usage - cib__set_call_options(call_options, "call", cib_zero_copy); - } else { - cib__clear_call_options(call_options, "call", cib_zero_copy); - } - -#define XPATH_CONFIG "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION -#define XPATH_NODES XPATH_CONFIG "/" XML_CIB_TAG_NODES -#define XPATH_ALERTS XPATH_CONFIG "/" XML_CIB_TAG_ALERTS -#define XPATH_STATUS "//" XML_TAG_CIB "/" XML_CIB_TAG_STATUS - - // Calculate the hash value of the section before the change - if (pcmk__str_eq(PCMK__CIB_REQUEST_REPLACE, op, pcmk__str_none)) { - current_nodes_digest = calculate_section_digest(XPATH_NODES, - current_cib); - current_alerts_digest = calculate_section_digest(XPATH_ALERTS, - current_cib); - current_status_digest = calculate_section_digest(XPATH_STATUS, - current_cib); - crm_trace("current-digest %s:%s:%s", current_nodes_digest, - current_alerts_digest, current_status_digest); + manage_counters = false; } // result_cib must not be modified after cib_perform_op() returns - rc = cib_perform_op(op, call_options, cib_op_func(call_type), FALSE, - section, request, input, manage_counters, - &config_changed, current_cib, &result_cib, cib_diff, - &output); + rc = cib_perform_op(op, call_options, op_function, false, section, + request, input, manage_counters, &config_changed, + &the_cib, &result_cib, cib_diff, &output); + // @COMPAT: Legacy code if (!manage_counters) { int format = 1; - /* Legacy code - * If the diff is NULL at this point, it's because nothing changed - */ + // If the diff is NULL at this point, it's because nothing changed if (*cib_diff != NULL) { - crm_element_value_int(*cib_diff, "format", &format); + crm_element_value_int(*cib_diff, PCMK_XA_FORMAT, &format); } if (format == 1) { @@ -1334,92 +1408,60 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb } } - /* Always write to disk for successful replace and upgrade ops. This also + /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. */ if ((rc == pcmk_ok) - && pcmk__str_any_of(op, - PCMK__CIB_REQUEST_REPLACE, - PCMK__CIB_REQUEST_UPGRADE, - NULL)) { - config_changed = TRUE; - } - - if (rc == pcmk_ok && !pcmk_is_set(call_options, cib_dryrun)) { - crm_trace("Activating %s->%s%s%s", - crm_element_value(current_cib, XML_ATTR_NUMUPDATES), - crm_element_value(result_cib, XML_ATTR_NUMUPDATES), - (pcmk_is_set(call_options, cib_zero_copy)? " zero-copy" : ""), - (config_changed? " changed" : "")); - if (!pcmk_is_set(call_options, cib_zero_copy)) { - rc = activateCibXml(result_cib, config_changed, op); - crm_trace("Activated %s (%d)", - crm_element_value(current_cib, XML_ATTR_NUMUPDATES), rc); - } + && pcmk_is_set(operation->flags, cib__op_attr_writes_through)) { - if ((rc == pcmk_ok) && contains_config_change(*cib_diff)) { - cib_read_config(config_hash, result_cib); - } + config_changed = true; + } - if (pcmk__str_eq(PCMK__CIB_REQUEST_REPLACE, op, pcmk__str_none)) { - char *result_nodes_digest = NULL; - char *result_alerts_digest = NULL; - char *result_status_digest = NULL; - - /* Calculate the hash value of the changed section. */ - result_nodes_digest = calculate_section_digest(XPATH_NODES, - result_cib); - result_alerts_digest = calculate_section_digest(XPATH_ALERTS, - result_cib); - result_status_digest = calculate_section_digest(XPATH_STATUS, - result_cib); - crm_trace("result-digest %s:%s:%s", result_nodes_digest, - result_alerts_digest, result_status_digest); - - if (pcmk__str_eq(current_nodes_digest, result_nodes_digest, - pcmk__str_none)) { - change_section = - pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, - "CIB change section", - "change_section", change_section, - cib_change_section_nodes, "nodes"); - } + if ((rc == pcmk_ok) + && !pcmk_any_flags_set(call_options, cib_dryrun|cib_transaction)) { - if (pcmk__str_eq(current_alerts_digest, result_alerts_digest, - pcmk__str_none)) { - change_section = - pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, - "CIB change section", - "change_section", change_section, - cib_change_section_alerts, "alerts"); + if (result_cib != the_cib) { + if (pcmk_is_set(operation->flags, cib__op_attr_writes_through)) { + config_changed = true; } - if (pcmk__str_eq(current_status_digest, result_status_digest, - pcmk__str_none)) { - change_section = - pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, - "CIB change section", - "change_section", change_section, - cib_change_section_status, "status"); - } + crm_trace("Activating %s->%s%s", + crm_element_value(the_cib, XML_ATTR_NUMUPDATES), + crm_element_value(result_cib, XML_ATTR_NUMUPDATES), + (config_changed? " changed" : "")); - if (change_section != cib_change_section_none) { - send_r_notify = TRUE; + rc = activateCibXml(result_cib, config_changed, op); + if (rc != pcmk_ok) { + crm_err("Failed to activate new CIB: %s", pcmk_strerror(rc)); } - - free(result_nodes_digest); - free(result_alerts_digest); - free(result_status_digest); + } + + if ((rc == pcmk_ok) && contains_config_change(*cib_diff)) { + cib_read_config(config_hash, result_cib); + } - } else if (pcmk__str_eq(PCMK__CIB_REQUEST_ERASE, op, pcmk__str_none)) { - send_r_notify = TRUE; + /* @COMPAT Nodes older than feature set 3.19.0 don't support + * transactions. In a mixed-version cluster with nodes <3.19.0, we must + * sync the updated CIB, so that the older nodes receive the changes. + * Any node that has already applied the transaction will ignore the + * synced CIB. + * + * To ensure the updated CIB is synced from only one node, we sync it + * from the originator. + */ + if ((operation->type == cib__op_commit_transact) + && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) + && compare_version(crm_element_value(the_cib, XML_ATTR_CRM_VERSION), + "3.19.0") < 0) { + + sync_our_cib(request, TRUE); } mainloop_timer_stop(digest_timer); mainloop_timer_start(digest_timer); } else if (rc == -pcmk_err_schema_validation) { - CRM_ASSERT(!pcmk_is_set(call_options, cib_zero_copy)); + CRM_ASSERT(result_cib != the_cib); if (output != NULL) { crm_log_xml_info(output, "cib:output"); @@ -1432,61 +1474,31 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb crm_trace("Not activating %d %d %s", rc, pcmk_is_set(call_options, cib_dryrun), crm_element_value(result_cib, XML_ATTR_NUMUPDATES)); - if (!pcmk_is_set(call_options, cib_zero_copy)) { + + if (result_cib != the_cib) { free_xml(result_cib); } } - if ((call_options & (cib_inhibit_notify|cib_dryrun)) == 0) { + if (!pcmk_any_flags_set(call_options, + cib_dryrun|cib_inhibit_notify|cib_transaction)) { crm_trace("Sending notifications %d", pcmk_is_set(call_options, cib_dryrun)); - cib_diff_notify(op, rc, call_id, client_id, client_name, origin, input, - *cib_diff); + cib_diff_notify(op, rc, call_id, client_id, client_name, originator, + input, *cib_diff); } - if (send_r_notify) { - cib_replace_notify(op, rc, call_id, client_id, client_name, origin, - the_cib, *cib_diff, change_section); - } - - pcmk__output_set_log_level(logger_out, LOG_TRACE); - logger_out->message(logger_out, "xml-patchset", *cib_diff); + pcmk__log_xml_patchset(LOG_TRACE, *cib_diff); done: if (!pcmk_is_set(call_options, cib_discard_reply) || cib_legacy_mode()) { - const char *caller = crm_element_value(request, F_CIB_CLIENTID); - - *reply = create_xml_node(NULL, "cib-reply"); - crm_xml_add(*reply, F_TYPE, T_CIB); - crm_xml_add(*reply, F_CIB_OPERATION, op); - crm_xml_add(*reply, F_CIB_CALLID, call_id); - crm_xml_add(*reply, F_CIB_CLIENTID, caller); - crm_xml_add_int(*reply, F_CIB_CALLOPTS, call_options); - crm_xml_add_int(*reply, F_CIB_RC, rc); - - if (output != NULL) { - crm_trace("Attaching reply output"); - add_message_xml(*reply, F_CIB_CALLDATA, output); - } - - crm_log_xml_explicit(*reply, "cib:reply"); + *reply = create_cib_reply(op, call_id, client_id, call_options, rc, + output); } - crm_trace("cleanup"); - - if (cib_op_modifies(call_type) == FALSE && output != current_cib) { + if (output != the_cib) { free_xml(output); - output = NULL; - } - - if (call_type >= 0) { - cib_op_cleanup(call_type, call_options, &input, &output); } - - free(current_nodes_digest); - free(current_alerts_digest); - free(current_status_digest); - crm_trace("done"); return rc; } @@ -1554,12 +1566,12 @@ initiate_exit(void) xmlNode *leaving = NULL; active = crm_active_peers(); - if (active < 2) { + if (active < 2) { // This is the last active node terminate_cib(__func__, 0); return; } - crm_info("Sending disconnect notification to %d peers...", active); + crm_info("Sending shutdown request to %d peers", active); leaving = create_xml_node(NULL, "exit-notification"); crm_xml_add(leaving, F_TYPE, "cib"); @@ -1664,12 +1676,6 @@ terminate_cib(const char *caller, int fast) uninitializeCib(); - if (logger_out != NULL) { - logger_out->finish(logger_out, CRM_EX_OK, true, NULL); - pcmk__output_free(logger_out); - logger_out = NULL; - } - if (fast > 0) { /* Quit fast on error */ pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); diff --git a/daemons/based/based_common.c b/daemons/based/based_common.c deleted file mode 100644 index 7e68cf0..0000000 --- a/daemons/based/based_common.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2008-2023 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * - * This source code is licensed under the GNU General Public License version 2 - * or later (GPLv2+) WITHOUT ANY WARRANTY. - */ - -#include <crm_internal.h> - -#include <sys/param.h> -#include <stdio.h> -#include <sys/types.h> -#include <unistd.h> - -#include <stdlib.h> -#include <errno.h> -#include <fcntl.h> - -#include <crm/crm.h> -#include <crm/cib.h> -#include <crm/msg_xml.h> -#include <crm/common/ipc.h> -#include <crm/cluster.h> - -#include <crm/common/xml.h> - -#include <pacemaker-based.h> - -gboolean stand_alone = FALSE; - -extern int cib_perform_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, - gboolean privileged); - -static xmlNode * -cib_prepare_common(xmlNode * root, const char *section) -{ - xmlNode *data = NULL; - - /* extract the CIB from the fragment */ - if (root == NULL) { - return NULL; - - } else if (pcmk__strcase_any_of(crm_element_name(root), XML_TAG_FRAGMENT, - F_CRM_DATA, F_CIB_CALLDATA, NULL)) { - data = first_named_child(root, XML_TAG_CIB); - - } else { - data = root; - } - - /* grab the section specified for the command */ - if (section != NULL && data != NULL && pcmk__str_eq(crm_element_name(data), XML_TAG_CIB, pcmk__str_none)) { - data = pcmk_find_cib_element(data, section); - } - - /* crm_log_xml_trace(root, "cib:input"); */ - return data; -} - -static int -cib_prepare_none(xmlNode * request, xmlNode ** data, const char **section) -{ - *data = NULL; - *section = crm_element_value(request, F_CIB_SECTION); - return pcmk_ok; -} - -static int -cib_prepare_data(xmlNode * request, xmlNode ** data, const char **section) -{ - xmlNode *input_fragment = get_message_xml(request, F_CIB_CALLDATA); - - *section = crm_element_value(request, F_CIB_SECTION); - *data = cib_prepare_common(input_fragment, *section); - /* crm_log_xml_debug(*data, "data"); */ - return pcmk_ok; -} - -static int -cib_prepare_sync(xmlNode * request, xmlNode ** data, const char **section) -{ - *data = NULL; - *section = crm_element_value(request, F_CIB_SECTION); - return pcmk_ok; -} - -static int -cib_prepare_diff(xmlNode * request, xmlNode ** data, const char **section) -{ - xmlNode *input_fragment = NULL; - - *data = NULL; - *section = NULL; - - if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) { - input_fragment = get_message_xml(request, F_CIB_UPDATE_DIFF); - } else { - input_fragment = get_message_xml(request, F_CIB_CALLDATA); - } - - CRM_CHECK(input_fragment != NULL, crm_log_xml_warn(request, "no input")); - *data = cib_prepare_common(input_fragment, NULL); - return pcmk_ok; -} - -static int -cib_cleanup_query(int options, xmlNode ** data, xmlNode ** output) -{ - CRM_LOG_ASSERT(*data == NULL); - if ((options & cib_no_children) - || pcmk__str_eq(crm_element_name(*output), "xpath-query", pcmk__str_casei)) { - free_xml(*output); - } - return pcmk_ok; -} - -static int -cib_cleanup_data(int options, xmlNode ** data, xmlNode ** output) -{ - free_xml(*output); - *data = NULL; - return pcmk_ok; -} - -static int -cib_cleanup_output(int options, xmlNode ** data, xmlNode ** output) -{ - free_xml(*output); - return pcmk_ok; -} - -static int -cib_cleanup_none(int options, xmlNode ** data, xmlNode ** output) -{ - CRM_LOG_ASSERT(*data == NULL); - CRM_LOG_ASSERT(*output == NULL); - return pcmk_ok; -} - -static cib_operation_t cib_server_ops[] = { - // Booleans are modifies_cib, needs_privileges - { - NULL, FALSE, FALSE, - cib_prepare_none, cib_cleanup_none, cib_process_default - }, - { - PCMK__CIB_REQUEST_QUERY, FALSE, FALSE, - cib_prepare_none, cib_cleanup_query, cib_process_query - }, - { - PCMK__CIB_REQUEST_MODIFY, TRUE, TRUE, - cib_prepare_data, cib_cleanup_data, cib_process_modify - }, - { - PCMK__CIB_REQUEST_APPLY_PATCH, TRUE, TRUE, - cib_prepare_diff, cib_cleanup_data, cib_server_process_diff - }, - { - PCMK__CIB_REQUEST_REPLACE, TRUE, TRUE, - cib_prepare_data, cib_cleanup_data, cib_process_replace_svr - }, - { - PCMK__CIB_REQUEST_CREATE, TRUE, TRUE, - cib_prepare_data, cib_cleanup_data, cib_process_create - }, - { - PCMK__CIB_REQUEST_DELETE, TRUE, TRUE, - cib_prepare_data, cib_cleanup_data, cib_process_delete - }, - { - PCMK__CIB_REQUEST_SYNC_TO_ALL, FALSE, TRUE, - cib_prepare_sync, cib_cleanup_none, cib_process_sync - }, - { - PCMK__CIB_REQUEST_BUMP, TRUE, TRUE, - cib_prepare_none, cib_cleanup_output, cib_process_bump - }, - { - PCMK__CIB_REQUEST_ERASE, TRUE, TRUE, - cib_prepare_none, cib_cleanup_output, cib_process_erase - }, - { - PCMK__CIB_REQUEST_NOOP, FALSE, FALSE, - cib_prepare_none, cib_cleanup_none, cib_process_default - }, - { - PCMK__CIB_REQUEST_ABS_DELETE, TRUE, TRUE, - cib_prepare_data, cib_cleanup_data, cib_process_delete_absolute - }, - { - PCMK__CIB_REQUEST_UPGRADE, TRUE, TRUE, - cib_prepare_none, cib_cleanup_output, cib_process_upgrade_server - }, - { - PCMK__CIB_REQUEST_SECONDARY, FALSE, TRUE, - cib_prepare_none, cib_cleanup_none, cib_process_readwrite - }, - { - PCMK__CIB_REQUEST_ALL_SECONDARY, FALSE, TRUE, - cib_prepare_none, cib_cleanup_none, cib_process_readwrite - }, - { - PCMK__CIB_REQUEST_SYNC_TO_ONE, FALSE, TRUE, - cib_prepare_sync, cib_cleanup_none, cib_process_sync_one - }, - { - PCMK__CIB_REQUEST_PRIMARY, TRUE, TRUE, - cib_prepare_data, cib_cleanup_data, cib_process_readwrite - }, - { - PCMK__CIB_REQUEST_IS_PRIMARY, FALSE, TRUE, - cib_prepare_none, cib_cleanup_none, cib_process_readwrite - }, - { - PCMK__CIB_REQUEST_SHUTDOWN, FALSE, TRUE, - cib_prepare_sync, cib_cleanup_none, cib_process_shutdown_req - }, - { - CRM_OP_PING, FALSE, FALSE, - cib_prepare_none, cib_cleanup_output, cib_process_ping - }, -}; - -int -cib_get_operation_id(const char *op, int *operation) -{ - static GHashTable *operation_hash = NULL; - - if (operation_hash == NULL) { - int lpc = 0; - int max_msg_types = PCMK__NELEM(cib_server_ops); - - operation_hash = pcmk__strkey_table(NULL, free); - for (lpc = 1; lpc < max_msg_types; lpc++) { - int *value = malloc(sizeof(int)); - - if(value) { - *value = lpc; - g_hash_table_insert(operation_hash, (gpointer) cib_server_ops[lpc].operation, value); - } - } - } - - if (op != NULL) { - int *value = g_hash_table_lookup(operation_hash, op); - - if (value) { - *operation = *value; - return pcmk_ok; - } - } - crm_err("Operation %s is not valid", op); - *operation = -1; - return -EINVAL; -} - -xmlNode * -cib_msg_copy(xmlNode * msg, gboolean with_data) -{ - int lpc = 0; - const char *field = NULL; - const char *value = NULL; - xmlNode *value_struct = NULL; - - static const char *field_list[] = { - F_XML_TAGNAME, - F_TYPE, - F_CIB_CLIENTID, - F_CIB_CALLOPTS, - F_CIB_CALLID, - F_CIB_OPERATION, - F_CIB_ISREPLY, - F_CIB_SECTION, - F_CIB_HOST, - F_CIB_RC, - F_CIB_DELEGATED, - F_CIB_OBJID, - F_CIB_OBJTYPE, - F_CIB_EXISTING, - F_CIB_SEENCOUNT, - F_CIB_TIMEOUT, - F_CIB_GLOBAL_UPDATE, - F_CIB_CLIENTNAME, - F_CIB_USER, - F_CIB_NOTIFY_TYPE, - F_CIB_NOTIFY_ACTIVATE - }; - - static const char *data_list[] = { - F_CIB_CALLDATA, - F_CIB_UPDATE, - F_CIB_UPDATE_RESULT - }; - - xmlNode *copy = create_xml_node(NULL, "copy"); - - CRM_ASSERT(copy != NULL); - - for (lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) { - field = field_list[lpc]; - value = crm_element_value(msg, field); - if (value != NULL) { - crm_xml_add(copy, field, value); - } - } - for (lpc = 0; with_data && lpc < PCMK__NELEM(data_list); lpc++) { - field = data_list[lpc]; - value_struct = get_message_xml(msg, field); - if (value_struct != NULL) { - add_message_xml(copy, field, value_struct); - } - } - - return copy; -} - -cib_op_t * -cib_op_func(int call_type) -{ - return &(cib_server_ops[call_type].fn); -} - -gboolean -cib_op_modifies(int call_type) -{ - return cib_server_ops[call_type].modifies_cib; -} - -int -cib_op_can_run(int call_type, int call_options, bool privileged) -{ - if (!privileged && cib_server_ops[call_type].needs_privileges) { - return -EACCES; - } - return pcmk_ok; -} - -int -cib_op_prepare(int call_type, xmlNode * request, xmlNode ** input, const char **section) -{ - crm_trace("Prepare %d", call_type); - return cib_server_ops[call_type].prepare(request, input, section); -} - -int -cib_op_cleanup(int call_type, int options, xmlNode ** input, xmlNode ** output) -{ - crm_trace("Cleanup %d", call_type); - return cib_server_ops[call_type].cleanup(options, input, output); -} diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index fc34f39..f252ac1 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the Pacemaker project contributors + * Copyright 2004-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -22,6 +22,9 @@ #include <sys/wait.h> #include <sys/stat.h> +#include <glib.h> +#include <libxml/tree.h> + #include <crm/crm.h> #include <crm/cib.h> @@ -45,12 +48,15 @@ cib_rename(const char *old) umask(S_IWGRP | S_IWOTH | S_IROTH); new_fd = mkstemp(new); - crm_err("Archiving unusable file %s as %s", old, new); + if ((new_fd < 0) || (rename(old, new) < 0)) { - crm_perror(LOG_ERR, "Couldn't rename %s as %s", old, new); - crm_err("Disabling disk writes and continuing"); + crm_err("Couldn't archive unusable file %s (disabling disk writes and continuing)", + old); cib_writes_enabled = FALSE; + } else { + crm_err("Archived unusable file %s as %s", old, new); } + if (new_fd > 0) { close(new_fd); } @@ -107,7 +113,7 @@ static int cib_archive_filter(const struct dirent * a) if(stat(a_path, &s) != 0) { rc = errno; - crm_trace("%s - stat failed: %s (%d)", a->d_name, pcmk_strerror(rc), rc); + crm_trace("%s - stat failed: %s (%d)", a->d_name, pcmk_rc_str(rc), rc); rc = 0; } else if ((s.st_mode & S_IFREG) != S_IFREG) { @@ -189,7 +195,7 @@ readCibXmlFile(const char *dir, const char *file, gboolean discard_status) const char *name = NULL; const char *value = NULL; const char *validation = NULL; - const char *use_valgrind = getenv("PCMK_valgrind_enabled"); + const char *use_valgrind = pcmk__env_option(PCMK__ENV_VALGRIND_ENABLED); xmlNode *root = NULL; xmlNode *status = NULL; @@ -214,7 +220,7 @@ readCibXmlFile(const char *dir, const char *file, gboolean discard_status) crm_warn("Primary configuration corrupt or unusable, trying backups in %s", cib_root); lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); if (lpc < 0) { - crm_perror(LOG_NOTICE, "scandir(%s) failed", cib_root); + crm_err("scandir(%s) failed: %s", cib_root, pcmk_rc_str(errno)); } } @@ -418,7 +424,7 @@ write_cib_contents(gpointer p) pid = fork(); if (pid < 0) { - crm_perror(LOG_ERR, "Disabling disk writes after fork failure"); + crm_err("Disabling disk writes after fork failure: %s", pcmk_rc_str(errno)); cib_writes_enabled = FALSE; return FALSE; } diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index d46456c..35d639a 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -19,6 +19,9 @@ #include <sys/param.h> #include <sys/types.h> +#include <glib.h> +#include <libxml/tree.h> + #include <crm/crm.h> #include <crm/cib/internal.h> #include <crm/msg_xml.h> @@ -61,25 +64,15 @@ cib_process_shutdown_req(const char *op, int options, const char *section, xmlNo return pcmk_ok; } +// @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int -cib_process_default(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) +cib_process_noop(const char *op, int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer) { - int result = pcmk_ok; - crm_trace("Processing \"%s\" event", op); *answer = NULL; - - if (op == NULL) { - result = -EINVAL; - crm_err("No operation specified"); - - } else if (strcmp(PCMK__CIB_REQUEST_NOOP, op) != 0) { - result = -EPROTONOSUPPORT; - crm_err("Action [%s] is not supported by the CIB manager", op); - } - return result; + return pcmk_ok; } int @@ -158,10 +151,10 @@ cib_process_ping(const char *op, int options, const char *section, xmlNode * req // Append additional detail so the receiver can log the differences add_message_xml(*answer, F_CIB_CALLDATA, the_cib); }, - { + if (the_cib != NULL) { // Always include at least the version details - const char *tag = TYPE(the_cib); - xmlNode *shallow = create_xml_node(NULL, tag); + xmlNode *shallow = create_xml_node(NULL, + (const char *) the_cib->name); copy_in_properties(shallow, the_cib); add_message_xml(*answer, F_CIB_CALLDATA, shallow); @@ -250,7 +243,7 @@ cib_process_upgrade_server(const char *op, int options, const char *section, xml if (rc != pcmk_ok) { // Notify originating peer so it can notify its local clients - crm_node_t *origin = pcmk__search_cluster_node_cache(0, host); + crm_node_t *origin = pcmk__search_cluster_node_cache(0, host, NULL); crm_info("Rejecting upgrade request from %s: %s " CRM_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc, @@ -341,8 +334,7 @@ cib_server_process_diff(const char *op, int options, const char *section, xmlNod crm_warn("Requesting full CIB refresh because update failed: %s" CRM_XS " rc=%d", pcmk_strerror(rc), rc); - pcmk__output_set_log_level(logger_out, LOG_INFO); - logger_out->message(logger_out, "xml-patchset", input); + pcmk__log_xml_patchset(LOG_INFO, input); free_xml(*result_cib); *result_cib = NULL; send_sync_request(NULL); @@ -356,15 +348,16 @@ cib_process_replace_svr(const char *op, int options, const char *section, xmlNod xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) { - const char *tag = crm_element_name(input); int rc = cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer); - if (rc == pcmk_ok && pcmk__str_eq(tag, XML_TAG_CIB, pcmk__str_casei)) { + + if ((rc == pcmk_ok) && pcmk__xe_is(input, XML_TAG_CIB)) { sync_in_progress = 0; } return rc; } +// @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed int cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, @@ -373,6 +366,49 @@ cib_process_delete_absolute(const char *op, int options, const char *section, xm return -EINVAL; } +static xmlNode * +cib_msg_copy(xmlNode *msg) +{ + static const char *field_list[] = { + F_XML_TAGNAME, + F_TYPE, + F_CIB_CLIENTID, + F_CIB_CALLOPTS, + F_CIB_CALLID, + F_CIB_OPERATION, + F_CIB_ISREPLY, + F_CIB_SECTION, + F_CIB_HOST, + F_CIB_RC, + F_CIB_DELEGATED, + F_CIB_OBJID, + F_CIB_OBJTYPE, + F_CIB_EXISTING, + F_CIB_SEENCOUNT, + F_CIB_TIMEOUT, + F_CIB_GLOBAL_UPDATE, + F_CIB_CLIENTNAME, + F_CIB_USER, + F_CIB_NOTIFY_TYPE, + F_CIB_NOTIFY_ACTIVATE + }; + + xmlNode *copy = create_xml_node(NULL, "copy"); + + CRM_ASSERT(copy != NULL); + + for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) { + const char *field = field_list[lpc]; + const char *value = crm_element_value(msg, field); + + if (value != NULL) { + crm_xml_add(copy, field, value); + } + } + + return copy; +} + int sync_our_cib(xmlNode * request, gboolean all) { @@ -384,22 +420,12 @@ sync_our_cib(xmlNode * request, gboolean all) xmlNode *replace_request = NULL; CRM_CHECK(the_cib != NULL, return -EINVAL); - - replace_request = cib_msg_copy(request, FALSE); - CRM_CHECK(replace_request != NULL, return -EINVAL); + CRM_CHECK(all || (host != NULL), return -EINVAL); crm_debug("Syncing CIB to %s", all ? "all peers" : host); - if (all == FALSE && host == NULL) { - crm_log_xml_err(request, "bad sync"); - } - /* remove the "all == FALSE" condition - * - * sync_from was failing, the local client wasn't being notified - * because it didn't know it was a reply - * setting this does not prevent the other nodes from applying it - * if all == TRUE - */ + replace_request = cib_msg_copy(request); + if (host != NULL) { crm_xml_add(replace_request, F_CIB_ISREPLY, host); } @@ -425,3 +451,30 @@ sync_our_cib(xmlNode * request, gboolean all) free(digest); return result; } + +int +cib_process_commit_transaction(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer) +{ + /* On success, our caller will activate *result_cib locally, trigger a + * replace notification if appropriate, and sync *result_cib to all nodes. + * On failure, our caller will free *result_cib. + */ + int rc = pcmk_rc_ok; + const char *client_id = crm_element_value(req, F_CIB_CLIENTID); + const char *origin = crm_element_value(req, F_ORIG); + pcmk__client_t *client = pcmk__find_client_by_id(client_id); + + rc = based_commit_transaction(input, client, origin, result_cib); + + if (rc != pcmk_rc_ok) { + char *source = based_transaction_source_str(client, origin); + + crm_err("Could not commit transaction for %s: %s", + source, pcmk_rc_str(rc)); + free(source); + } + return pcmk_rc2legacy(rc); +} diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 5881f6d..00a4c54 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -21,6 +21,9 @@ #include <time.h> +#include <glib.h> +#include <libxml/tree.h> + #include <crm/crm.h> #include <crm/cib/internal.h> #include <crm/msg_xml.h> @@ -30,7 +33,7 @@ #include <pacemaker-based.h> struct cib_notification_s { - xmlNode *msg; + const xmlNode *msg; struct iovec *iov; int32_t iov_size; }; @@ -58,10 +61,6 @@ cib_notify_send_one(gpointer key, gpointer value, gpointer user_data) do_send = TRUE; - } else if (pcmk_is_set(client->flags, cib_notify_replace) - && pcmk__str_eq(type, T_CIB_REPLACE_NOTIFY, pcmk__str_casei)) { - do_send = TRUE; - } else if (pcmk_is_set(client->flags, cib_notify_confirm) && pcmk__str_eq(type, T_CIB_UPDATE_CONFIRM, pcmk__str_casei)) { do_send = TRUE; @@ -104,7 +103,7 @@ cib_notify_send_one(gpointer key, gpointer value, gpointer user_data) } static void -cib_notify_send(xmlNode * xml) +cib_notify_send(const xmlNode *xml) { struct iovec *iov; struct cib_notification_s update; @@ -198,15 +197,16 @@ cib_diff_notify(const char *op, int result, const char *call_id, crm_xml_add(update_msg, F_SUBTYPE, T_CIB_DIFF_NOTIFY); crm_xml_add(update_msg, F_CIB_OPERATION, op); crm_xml_add(update_msg, F_CIB_CLIENTID, client_id); + crm_xml_add(update_msg, F_CIB_CLIENTNAME, client_name); crm_xml_add(update_msg, F_CIB_CALLID, call_id); crm_xml_add(update_msg, F_ORIG, origin); crm_xml_add_int(update_msg, F_CIB_RC, result); if (update != NULL) { - type = crm_element_name(update); + type = (const char *) update->name; crm_trace("Setting type to update->name: %s", type); } else { - type = crm_element_name(diff); + type = (const char *) diff->name; crm_trace("Setting type to new_obj->name: %s", type); } crm_xml_add(update_msg, F_CIB_OBJID, ID(diff)); @@ -218,88 +218,7 @@ cib_diff_notify(const char *op, int result, const char *call_id, } add_message_xml(update_msg, F_CIB_UPDATE_RESULT, diff); + crm_log_xml_trace(update_msg, "diff-notify"); cib_notify_send(update_msg); free_xml(update_msg); } - -void -cib_replace_notify(const char *op, int result, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *update, xmlNode *diff, - uint32_t change_section) -{ - xmlNode *replace_msg = NULL; - - int add_updates = 0; - int add_epoch = 0; - int add_admin_epoch = 0; - - int del_updates = 0; - int del_epoch = 0; - int del_admin_epoch = 0; - - uint8_t log_level = LOG_INFO; - - if (diff == NULL) { - return; - } - - if (result != pcmk_ok) { - log_level = LOG_WARNING; - } - - cib_diff_version_details(diff, &add_admin_epoch, &add_epoch, &add_updates, - &del_admin_epoch, &del_epoch, &del_updates); - - if (del_updates < 0) { - crm_log_xml_debug(diff, "Bad replace diff"); - } - - if ((add_admin_epoch != del_admin_epoch) - || (add_epoch != del_epoch) - || (add_updates != del_updates)) { - - do_crm_log(log_level, - "Replaced CIB generation %d.%d.%d with %d.%d.%d from client " - "%s%s%s (%s) (%s)", - del_admin_epoch, del_epoch, del_updates, - add_admin_epoch, add_epoch, add_updates, - client_name, - ((call_id != NULL)? " call " : ""), pcmk__s(call_id, ""), - pcmk__s(origin, "unspecified peer"), pcmk_strerror(result)); - - } else if ((add_admin_epoch != 0) - || (add_epoch != 0) - || (add_updates != 0)) { - - do_crm_log(log_level, - "Local-only replace of CIB generation %d.%d.%d from client " - "%s%s%s (%s) (%s)", - add_admin_epoch, add_epoch, add_updates, - client_name, - ((call_id != NULL)? " call " : ""), pcmk__s(call_id, ""), - pcmk__s(origin, "unspecified peer"), pcmk_strerror(result)); - } - - replace_msg = create_xml_node(NULL, "notify-replace"); - - crm_xml_add(replace_msg, F_TYPE, T_CIB_NOTIFY); - crm_xml_add(replace_msg, F_SUBTYPE, T_CIB_REPLACE_NOTIFY); - crm_xml_add(replace_msg, F_CIB_OPERATION, op); - crm_xml_add(replace_msg, F_CIB_CLIENTID, client_id); - crm_xml_add(replace_msg, F_CIB_CALLID, call_id); - crm_xml_add(replace_msg, F_ORIG, origin); - crm_xml_add_int(replace_msg, F_CIB_RC, result); - crm_xml_add_ll(replace_msg, F_CIB_CHANGE_SECTION, - (long long) change_section); - attach_cib_generation(replace_msg, "cib-replace-generation", update); - - /* We can include update and diff if a replace callback needs them. Until - * then, avoid the overhead. - */ - - crm_log_xml_trace(replace_msg, "CIB replaced"); - - cib_notify_send(replace_msg); - free_xml(replace_msg); -} diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c new file mode 100644 index 0000000..736d425 --- /dev/null +++ b/daemons/based/based_operation.c @@ -0,0 +1,59 @@ +/* + * Copyright 2008-2023 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <glib.h> + +#include <crm/crm.h> +#include <crm/cib.h> +#include <pacemaker-based.h> + +static const cib__op_fn_t cib_op_functions[] = { + [cib__op_abs_delete] = cib_process_delete_absolute, + [cib__op_apply_patch] = cib_server_process_diff, + [cib__op_bump] = cib_process_bump, + [cib__op_commit_transact] = cib_process_commit_transaction, + [cib__op_create] = cib_process_create, + [cib__op_delete] = cib_process_delete, + [cib__op_erase] = cib_process_erase, + [cib__op_is_primary] = cib_process_readwrite, + [cib__op_modify] = cib_process_modify, + [cib__op_noop] = cib_process_noop, + [cib__op_ping] = cib_process_ping, + [cib__op_primary] = cib_process_readwrite, + [cib__op_query] = cib_process_query, + [cib__op_replace] = cib_process_replace_svr, + [cib__op_secondary] = cib_process_readwrite, + [cib__op_shutdown] = cib_process_shutdown_req, + [cib__op_sync_all] = cib_process_sync, + [cib__op_sync_one] = cib_process_sync_one, + [cib__op_upgrade] = cib_process_upgrade_server, +}; + +/*! + * \internal + * \brief Get the function that performs a given server-side CIB operation + * + * \param[in] operation Operation whose function to look up + * + * \return Function that performs \p operation within \c pacemaker-based + */ +cib__op_fn_t +based_get_op_function(const cib__operation_t *operation) +{ + enum cib__op_type type = operation->type; + + CRM_ASSERT(type >= 0); + + if (type >= PCMK__NELEM(cib_op_functions)) { + return NULL; + } + return cib_op_functions[type]; +} diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 38136d2..4aa41fa 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2021 the Pacemaker project contributors + * Copyright 2004-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -23,7 +23,9 @@ #include <stdlib.h> #include <errno.h> + #include <glib.h> +#include <libxml/tree.h> #include <crm/msg_xml.h> #include <crm/common/ipc.h> @@ -126,13 +128,13 @@ init_remote_listener(int port, gboolean encrypted) /* create server socket */ ssock = malloc(sizeof(int)); if(ssock == NULL) { - crm_perror(LOG_ERR, "Listener socket allocation failed"); + crm_err("Listener socket allocation failed: %s", pcmk_rc_str(errno)); return -1; } *ssock = socket(AF_INET, SOCK_STREAM, 0); if (*ssock == -1) { - crm_perror(LOG_ERR, "Listener socket creation failed"); + crm_err("Listener socket creation failed: %s", pcmk_rc_str(errno)); free(ssock); return -1; } @@ -141,8 +143,8 @@ init_remote_listener(int port, gboolean encrypted) optval = 1; rc = setsockopt(*ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (rc < 0) { - crm_perror(LOG_WARNING, - "Local address reuse not allowed on listener socket"); + crm_err("Local address reuse not allowed on listener socket: %s", + pcmk_rc_str(errno)); } /* bind server socket */ @@ -151,13 +153,13 @@ init_remote_listener(int port, gboolean encrypted) saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons(port); if (bind(*ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { - crm_perror(LOG_ERR, "Cannot bind to listener socket"); + crm_err("Cannot bind to listener socket: %s", pcmk_rc_str(errno)); close(*ssock); free(ssock); return -2; } if (listen(*ssock, 10) == -1) { - crm_perror(LOG_ERR, "Cannot listen on socket"); + crm_err("Cannot listen on socket: %s", pcmk_rc_str(errno)); close(*ssock); free(ssock); return -3; @@ -222,9 +224,9 @@ cib_remote_auth(xmlNode * login) return FALSE; } - tmp = crm_element_name(login); - if (!pcmk__str_eq(tmp, "cib_command", pcmk__str_casei)) { - crm_err("Wrong tag: %s", tmp); + if (!pcmk__xe_is(login, T_CIB_COMMAND)) { + crm_err("Unrecognizable message from remote client"); + crm_log_xml_info(login, "bad"); return FALSE; } @@ -296,7 +298,7 @@ cib_remote_listen(gpointer data) memset(&addr, 0, sizeof(addr)); csock = accept(ssock, (struct sockaddr *)&addr, &laddr); if (csock == -1) { - crm_perror(LOG_ERR, "Could not accept socket connection"); + crm_err("Could not accept socket connection: %s", pcmk_rc_str(errno)); return TRUE; } @@ -411,9 +413,8 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) { const char *value = NULL; - value = crm_element_name(command); - if (!pcmk__str_eq(value, "cib_command", pcmk__str_casei)) { - crm_log_xml_trace(command, "Bad command: "); + if (!pcmk__xe_is(command, T_CIB_COMMAND)) { + crm_log_xml_trace(command, "bad"); return; } diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c new file mode 100644 index 0000000..89aea2e --- /dev/null +++ b/daemons/based/based_transaction.c @@ -0,0 +1,167 @@ +/* + * Copyright 2023 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <glib.h> +#include <libxml/tree.h> + +#include "pacemaker-based.h" + +/*! + * \internal + * \brief Create a string describing the source of a commit-transaction request + * + * \param[in] client CIB client + * \param[in] origin Host where the commit request originated + * + * \return String describing the request source + * + * \note The caller is responsible for freeing the return value using \c free(). + */ +char * +based_transaction_source_str(const pcmk__client_t *client, const char *origin) +{ + char *source = NULL; + + if (client != NULL) { + source = crm_strdup_printf("client %s (%s)%s%s", + pcmk__client_name(client), + pcmk__s(client->id, "unidentified"), + ((origin != NULL)? " on " : ""), + pcmk__s(origin, "")); + + } else { + source = strdup((origin != NULL)? origin : "unknown source"); + } + + CRM_ASSERT(source != NULL); + return source; +} + +/*! + * \internal + * \brief Process requests in a transaction + * + * Stop when a request fails or when all requests have been processed. + * + * \param[in,out] transaction Transaction to process + * \param[in] client CIB client + * \param[in] source String describing the commit request source + * + * \return Standard Pacemaker return code + */ +static int +process_transaction_requests(xmlNodePtr transaction, + const pcmk__client_t *client, const char *source) +{ + for (xmlNodePtr request = first_named_child(transaction, T_CIB_COMMAND); + request != NULL; request = crm_next_same_xml(request)) { + + const char *op = crm_element_value(request, F_CIB_OPERATION); + const char *host = crm_element_value(request, F_CIB_HOST); + const cib__operation_t *operation = NULL; + int rc = cib__get_operation(op, &operation); + + if (rc == pcmk_rc_ok) { + if (!pcmk_is_set(operation->flags, cib__op_attr_transaction) + || (host != NULL)) { + + rc = EOPNOTSUPP; + } else { + /* Commit-transaction is a privileged operation. If we reached + * this point, the request came from a privileged connection. + */ + rc = cib_process_request(request, TRUE, client); + rc = pcmk_legacy2rc(rc); + } + } + + if (rc != pcmk_rc_ok) { + crm_err("Aborting CIB transaction for %s due to failed %s request: " + "%s", + source, op, pcmk_rc_str(rc)); + crm_log_xml_info(request, "Failed request"); + return rc; + } + + crm_trace("Applied %s request to transaction working CIB for %s", + op, source); + crm_log_xml_trace(request, "Successful request"); + } + + return pcmk_rc_ok; +} + +/*! + * \internal + * \brief Commit a given CIB client's transaction to a working CIB copy + * + * \param[in] transaction Transaction to commit + * \param[in] client CIB client + * \param[in] origin Host where the commit request originated + * \param[in,out] result_cib Where to store result CIB + * + * \return Standard Pacemaker return code + * + * \note This function is expected to be called only by + * \p cib_process_commit_transaction(). + * \note \p result_cib is expected to be a copy of the current CIB as created by + * \p cib_perform_op(). + * \note The caller is responsible for activating and syncing \p result_cib on + * success, and for freeing it on failure. + */ +int +based_commit_transaction(xmlNodePtr transaction, const pcmk__client_t *client, + const char *origin, xmlNodePtr *result_cib) +{ + xmlNodePtr saved_cib = the_cib; + int rc = pcmk_rc_ok; + char *source = NULL; + + CRM_ASSERT(result_cib != NULL); + + CRM_CHECK(pcmk__xe_is(transaction, T_CIB_TRANSACTION), + return pcmk_rc_no_transaction); + + /* *result_cib should be a copy of the_cib (created by cib_perform_op()). If + * not, make a copy now. Change tracking isn't strictly required here + * because: + * * Each request in the transaction will have changes tracked and ACLs + * checked if appropriate. + * * cib_perform_op() will infer changes for the commit request at the end. + */ + CRM_CHECK((*result_cib != NULL) && (*result_cib != the_cib), + *result_cib = copy_xml(the_cib)); + + source = based_transaction_source_str(client, origin); + crm_trace("Committing transaction for %s to working CIB", source); + + // Apply all changes to a working copy of the CIB + the_cib = *result_cib; + + rc = process_transaction_requests(transaction, client, origin); + + crm_trace("Transaction commit %s for %s", + ((rc == pcmk_rc_ok)? "succeeded" : "failed"), source); + + /* Some request types (for example, erase) may have freed the_cib (the + * working copy) and pointed it at a new XML object. In that case, it + * follows that *result_cib (the working copy) was freed. + * + * Point *result_cib at the updated working copy stored in the_cib. + */ + *result_cib = the_cib; + + // Point the_cib back to the unchanged original copy + the_cib = saved_cib; + + free(source); + return rc; +} diff --git a/daemons/based/based_transaction.h b/daemons/based/based_transaction.h new file mode 100644 index 0000000..9935c73 --- /dev/null +++ b/daemons/based/based_transaction.h @@ -0,0 +1,24 @@ +/* + * Copyright 2023 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_TRANSACTION__H +#define BASED_TRANSACTION__H + +#include <crm_internal.h> + +#include <libxml/tree.h> + +char *based_transaction_source_str(const pcmk__client_t *client, + const char *origin); + +int based_commit_transaction(xmlNodePtr transaction, + const pcmk__client_t *client, + const char *origin, xmlNodePtr *result_cib); + +#endif // BASED_TRANSACTION__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 129997e..5dd7938 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -16,7 +16,8 @@ #include <bzlib.h> #include <sys/types.h> -#include <libxml/parser.h> +#include <glib.h> +#include <libxml/tree.h> #include <crm/crm.h> #include <crm/cib/internal.h> @@ -42,6 +43,7 @@ gchar *cib_root = NULL; static gboolean preserve_status = FALSE; gboolean cib_writes_enabled = TRUE; +gboolean stand_alone = FALSE; int remote_fd = 0; int remote_tls_fd = 0; @@ -49,8 +51,6 @@ int remote_tls_fd = 0; GHashTable *config_hash = NULL; GHashTable *local_notify_queue = NULL; -pcmk__output_t *logger_out = NULL; - static void cib_init(void); void cib_shutdown(int nsig); static bool startCib(const char *filename); @@ -197,15 +197,6 @@ main(int argc, char **argv) goto done; } - rc = pcmk__log_output_new(&logger_out); - if (rc != pcmk_rc_ok) { - exit_code = CRM_EX_ERROR; - g_set_error(&error, PCMK__EXITC_ERROR, exit_code, - "Error creating output format log: %s", pcmk_rc_str(rc)); - goto done; - } - pcmk__output_set_log_level(logger_out, LOG_TRACE); - mainloop_add_signal(SIGTERM, cib_shutdown); mainloop_add_signal(SIGPIPE, cib_enable_writes); @@ -230,7 +221,7 @@ main(int argc, char **argv) goto done; } - if (crm_ipc_connect(old_instance)) { + if (pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok) { /* IPC end-point already up */ crm_ipc_close(old_instance); crm_ipc_destroy(old_instance); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 05e49b3..33c7642 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -18,6 +18,9 @@ #include <errno.h> #include <fcntl.h> +#include <glib.h> +#include <libxml/tree.h> + #include <crm/crm.h> #include <crm/cib.h> #include <crm/common/xml.h> @@ -26,16 +29,19 @@ #include <crm/common/mainloop.h> #include <crm/cib/internal.h> +#include "based_transaction.h" + #ifdef HAVE_GNUTLS_GNUTLS_H # include <gnutls/gnutls.h> #endif +#define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->uname) + // CIB-specific client flags enum cib_client_flags { // Notifications cib_notify_pre = (UINT64_C(1) << 0), cib_notify_post = (UINT64_C(1) << 1), - cib_notify_replace = (UINT64_C(1) << 2), cib_notify_confirm = (UINT64_C(1) << 3), cib_notify_diff = (UINT64_C(1) << 4), @@ -43,16 +49,6 @@ enum cib_client_flags { cib_is_daemon = (UINT64_C(1) << 12), }; -typedef struct cib_operation_s { - const char *operation; - gboolean modifies_cib; - gboolean needs_privileges; - int (*prepare) (xmlNode *, xmlNode **, const char **); - int (*cleanup) (int, xmlNode **, xmlNode **); - int (*fn) (const char *, int, const char *, xmlNode *, - xmlNode *, xmlNode *, xmlNode **, xmlNode **); -} cib_operation_t; - extern bool based_is_primary; extern GHashTable *config_hash; extern xmlNode *the_cib; @@ -67,7 +63,6 @@ extern gboolean stand_alone; extern gboolean cib_shutdown_flag; extern gchar *cib_root; extern int cib_status; -extern pcmk__output_t *logger_out; extern struct qb_ipcs_service_handlers ipc_ro_callbacks; extern struct qb_ipcs_service_handlers ipc_rw_callbacks; @@ -79,6 +74,8 @@ void cib_peer_callback(xmlNode *msg, void *private_data); void cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__client_t *cib_client, gboolean privileged); +int cib_process_request(xmlNode *request, gboolean privileged, + const pcmk__client_t *cib_client); void cib_shutdown(int nsig); void terminate_cib(const char *caller, int fast); gboolean cib_legacy_mode(void); @@ -92,9 +89,9 @@ int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_default(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); +int cib_process_noop(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); int cib_process_ping(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); @@ -121,25 +118,17 @@ int cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int cib_process_commit_transaction(const char *op, int options, + const char *section, xmlNode *req, + xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); void send_sync_request(const char *host); int sync_our_cib(xmlNode *request, gboolean all); -xmlNode *cib_msg_copy(xmlNode *msg, gboolean with_data); -int cib_get_operation_id(const char *op, int *operation); -cib_op_t *cib_op_func(int call_type); -gboolean cib_op_modifies(int call_type); -int cib_op_prepare(int call_type, xmlNode *request, xmlNode **input, - const char **section); -int cib_op_cleanup(int call_type, int options, xmlNode **input, - xmlNode **output); -int cib_op_can_run(int call_type, int call_options, bool privileged); +cib__op_fn_t based_get_op_function(const cib__operation_t *operation); void cib_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *update, xmlNode *diff); -void cib_replace_notify(const char *op, int result, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *update, xmlNode *diff, - uint32_t change_section); static inline const char * cib_config_lookup(const char *opt) |