diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 664210f180c..293d7b16764 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -10,7 +10,6 @@ #include #include // EACCES, ECONNREFUSED -#include // PRIu64 #include #include // NULL, size_t #include // uint32_t, uint64_t @@ -21,7 +20,6 @@ #include // gboolean, gpointer, g_*, etc. #include // xmlNode -#include // QB_FALSE #include // qb_ipcs_connection_t #include // LOG_TRACE @@ -41,7 +39,7 @@ #define EXIT_ESCALATION_MS 10000 -static uint64_t ping_seq = 0; +static long long ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; @@ -49,12 +47,10 @@ static bool ping_modified_since = false; * \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 enum cib_call_options flags - * \param[in] rc Request return code - * \param[in] call_data Request output data + * \param[in] request CIB request + * \param[in] rc Request return code (standard Pacemaker return code) + * \param[in] call_data Request output data (may be entire live CIB or result + * CIB in case of error) * * \return Reply XML (guaranteed not to be \c NULL) * @@ -62,24 +58,30 @@ static bool ping_modified_since = false; * \p pcmk__xml_free(). */ static xmlNode * -create_cib_reply(const char *op, const char *call_id, const char *client_id, - uint32_t call_options, int rc, xmlNode *call_data) +create_cib_reply(const xmlNode *request, int rc, xmlNode *call_data) { xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_CIB_REPLY); pcmk__xe_set(reply, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(reply, PCMK__XA_CIB_OP, op); - pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set_int(reply, PCMK__XA_CIB_CALLOPT, call_options); - pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, rc); - if (call_data != NULL) { - xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CIB_CALLDATA); + /* We could simplify by copying all attributes from request. We would just + * have to ensure that there are never "private" attributes that we want to + * hide from external clients with notify callbacks. + */ + pcmk__xe_set(reply, PCMK__XA_CIB_OP, + pcmk__xe_get(request, PCMK__XA_CIB_OP)); - pcmk__trace("Attaching reply output"); - pcmk__xml_copy(wrapper, call_data); - } + pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, + pcmk__xe_get(request, PCMK__XA_CIB_CALLID)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CALLOPT, + pcmk__xe_get(request, PCMK__XA_CIB_CALLOPT)); + + pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); + cib__set_calldata(reply, call_data); crm_log_xml_explicit(reply, "cib:reply"); return reply; @@ -154,128 +156,137 @@ do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, * \internal * \brief Request CIB digests from all peer nodes * - * This is used as a callback that runs 5 seconds after we modify the CIB. It - * sends a ping request to all cluster nodes. They will respond by sending their - * current digests and version info, which we will validate in - * process_ping_reply(). This serves as a check of consistency across the - * cluster after a CIB update. + * This is used as a callback that runs 5 seconds after we modify the CIB on the + * DC. It sends a ping request to all cluster nodes. They will respond by + * sending their current digests and version info, which we will validate in + * process_ping_reply(). If their digest doesn't match, we'll sync our own CIB + * to them. This helps ensure consistency across the cluster after a CIB update. * * \param[in] data Ignored * * \return \c G_SOURCE_REMOVE (to destroy the timeout) + * + * \note It's not clear why we wait 5 seconds rather than sending the ping + * request immediately after a performing a modifying op. Perhaps it's to + * avoid overwhelming other nodes with ping requests when there are a lot + * of modifying requests in a short period. The timer restarts after + * every successful modifying op, so we send ping requests **at most** + * every 5 seconds. Or perhaps it's a remnant of legacy mode (pre-1.1.12). + * In any case, the other nodes shouldn't need time to process the + * modifying op before responding to the ping request. The ping request is + * sent after the op is sent, so it should also be received after the op + * is received. */ static gboolean digest_timer_cb(gpointer data) { - char *buf = NULL; xmlNode *ping = NULL; if (!based_is_primary) { + // Only the DC sends a ping return G_SOURCE_REMOVE; } - ping_seq++; + if (++ping_seq < 0) { + ping_seq = 0; + } + g_clear_pointer(&ping_digest, free); ping_modified_since = false; - buf = pcmk__assert_asprintf("%" PRIu64, ping_seq); - ping = pcmk__xe_create(NULL, PCMK__XE_PING); pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); - pcmk__xe_set(ping, PCMK__XA_CIB_PING_ID, buf); + pcmk__xe_set_ll(ping, PCMK__XA_CIB_PING_ID, ping_seq); pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); - pcmk__trace("Requesting peer digests (%s)", buf); + pcmk__trace("Requesting peer digests (%lld)", ping_seq); pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); - free(buf); pcmk__xml_free(ping); return G_SOURCE_REMOVE; } +/*! + * \internal + * \brief Process a reply to a \c CRM_OP_PING request + * + * See \c digest_timer_cb() for details on how the ping process works, and see + * \c based_process_ping() for the construction of the ping reply. + * + * We ignore the reply if we are no longer the DC, if the reply is malformed or + * received out of sequence, or if we may have modified the CIB since the last + * time we sent a ping request. + * + * Otherwise, we compare the CIB digest received in the reply against the digest + * of the local CIB. If the digests don't match, we sync our CIB to the node + * that sent the reply. This helps to ensure that all other nodes' views of the + * CIB eventually match the DC's view of the CIB. + * + * \param[in] reply Ping reply + */ static void -process_ping_reply(xmlNode *reply) +process_ping_reply(const xmlNode *reply) { - uint64_t seq = 0; const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); - xmlNode *wrapper = pcmk__xe_first_child(reply, PCMK__XE_CIB_CALLDATA, NULL, - NULL); - xmlNode *pong = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - - const char *seq_s = pcmk__xe_get(pong, PCMK__XA_CIB_PING_ID); + xmlNode *pong = cib__get_calldata(reply); + long long seq = 0; const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); - if (seq_s == NULL) { - pcmk__debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID); + xmlNode *remote_versions = cib__get_calldata(pong); + + int rc = pcmk__xe_get_ll(pong, PCMK__XA_CIB_PING_ID, &seq); + + if (rc != pcmk_rc_ok) { + pcmk__debug("Ignoring ping reply with unset or invalid " + PCMK__XA_CIB_PING_ID ": %s", pcmk_rc_str(rc)); return; + } - } else { - long long seq_ll; - int rc = pcmk__scan_ll(seq_s, &seq_ll, 0LL); - - if (rc != pcmk_rc_ok) { - pcmk__debug("Ignoring ping reply with invalid " PCMK__XA_CIB_PING_ID - " '%s': %s", - seq_s, pcmk_rc_str(rc)); - return; - } - seq = (uint64_t) seq_ll; + if (!based_is_primary) { + pcmk__trace("Ignoring ping reply %lld from %s because we are no longer " + "DC", seq, host); + return; } - if(digest == NULL) { - pcmk__trace("Ignoring ping reply %s from %s with no digest", seq_s, + if (digest == NULL) { + pcmk__trace("Ignoring ping reply %lld from %s with no digest", seq, host); + return; + } - } else if(seq != ping_seq) { - pcmk__trace("Ignoring out of sequence ping reply %s from %s", seq_s, + if (seq != ping_seq) { + pcmk__trace("Ignoring out-of-sequence ping reply %lld from %s", seq, host); + return; + } - } else if(ping_modified_since) { - pcmk__trace("Ignoring ping reply %s from %s: cib updated since", seq_s, + if (ping_modified_since) { + pcmk__trace("Ignoring ping reply %lld from %s: CIB updated since", seq, host); + return; + } - } else { - if(ping_digest == NULL) { - pcmk__trace("Calculating new digest"); - ping_digest = pcmk__digest_xml(the_cib, true); - } + if (ping_digest == NULL) { + ping_digest = pcmk__digest_xml(the_cib, true); + } - pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, - digest); - if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { - xmlNode *wrapper = pcmk__xe_first_child(pong, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *remote_versions = pcmk__xe_first_child(wrapper, NULL, NULL, - NULL); - - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; - - if (remote_versions != NULL) { - admin_epoch_s = pcmk__xe_get(remote_versions, - PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_versions, - PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_versions, - PCMK_XA_NUM_UPDATES); - } + pcmk__trace("Processing ping reply %lld from %s (%s)", seq, host, digest); - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", - pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - ping_digest, host, - pcmk__s(admin_epoch_s, "_"), - pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), digest); - - pcmk__xml_free(remote_versions); - sync_our_cib(reply, false); - } + if (pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { + return; } + + pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", + pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), ping_digest, host, + pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(remote_versions, PCMK_XA_EPOCH), + pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES), digest); + + sync_our_cib(reply, false); } static void @@ -513,83 +524,58 @@ forward_request(xmlNode *request) static int cib_process_command(xmlNode *request, const cib__operation_t *operation, - cib__op_fn_t op_function, xmlNode **reply, bool privileged) + cib__op_fn_t op_function, xmlNode **reply) { + static mainloop_timer_t *digest_timer = NULL; + xmlNode *cib_diff = NULL; xmlNode *output = NULL; xmlNode *result_cib = NULL; - uint32_t call_options = cib_none; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); - const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); - const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - - int rc = pcmk_rc_ok; + uint32_t call_options = cib_none; bool config_changed = false; - - static mainloop_timer_t *digest_timer = NULL; - - pcmk__assert(cib_status == pcmk_rc_ok); + int rc = pcmk_rc_ok; if (digest_timer == NULL) { digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, digest_timer_cb, NULL); } - *reply = NULL; - /* Start processing the request... */ - rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, - cib_none); - if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); - } - - if (!privileged - && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { - - rc = EACCES; - goto done; - } + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_query(op_function, request, &the_cib, &output); + rc = cib__perform_op_ro(op_function, request, &the_cib, &output); goto done; } - ping_modified_since = true; - - /* result_cib must not be modified after cib_perform_op() returns. + /* result_cib must not be modified after cib__perform_op_rw() returns. * * It's not important whether the client variant is cib_native or * cib_remote. */ result_cib = the_cib; - rc = cib_perform_op(cib_undefined, op_function, request, &config_changed, - &result_cib, &cib_diff, &output); + rc = cib__perform_op_rw(cib_undefined, op_function, request, + &config_changed, &result_cib, &cib_diff, &output); - /* Always write to disk for successful ops with the flag set. This also - * negates the need to detect ordering changes. - */ if ((rc == pcmk_rc_ok) - && pcmk__is_set(operation->flags, cib__op_attr_writes_through)) { + && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - config_changed = true; - } + /* Always write to disk for successful ops with the writes-through flag + * set. This also avoids the need to detect ordering changes. + */ + const bool to_disk = config_changed + || pcmk__is_set(operation->flags, + cib__op_attr_writes_through); - if ((rc == pcmk_rc_ok) - && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { + const char *feature_set = pcmk__xe_get(the_cib, + PCMK_XA_CRM_FEATURE_SET); if (result_cib != the_cib) { - if (pcmk__is_set(operation->flags, cib__op_attr_writes_through)) { - config_changed = true; - } - - rc = based_activate_cib(result_cib, config_changed, op); + rc = based_activate_cib(result_cib, to_disk, op); } /* @COMPAT Nodes older than feature set 3.19.0 don't support @@ -603,13 +589,15 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, */ if ((operation->type == cib__op_commit_transact) && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) - && (pcmk__compare_versions(pcmk__xe_get(the_cib, - PCMK_XA_CRM_FEATURE_SET), - "3.19.0") < 0)) { + && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { sync_our_cib(request, true); } + if (cib_diff != NULL) { + ping_modified_since = true; + } + mainloop_timer_start(digest_timer); } else if (rc == pcmk_rc_schema_validation) { @@ -626,25 +614,22 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xml_free(result_cib); } - if (!pcmk__any_flags_set(call_options, - cib_dryrun|cib_inhibit_notify|cib_transaction)) { - pcmk__trace("Sending notifications %d", - pcmk__is_set(call_options, cib_dryrun)); - based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, - client_name, originator, cib_diff); + if (pcmk__any_flags_set(call_options, + cib_dryrun|cib_inhibit_notify|cib_transaction)) { + goto done; } - done: + based_diff_notify(request, rc, cib_diff); + +done: if (!pcmk__is_set(call_options, cib_discard_reply)) { - *reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), output); + *reply = create_cib_reply(request, rc, output); } if (output != the_cib) { pcmk__xml_free(output); } - pcmk__trace("done"); pcmk__xml_free(cib_diff); return rc; } @@ -821,10 +806,6 @@ based_process_request(xmlNode *request, bool privileged, } 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 = false; local_notify = false; pcmk__trace("Client is not interested in the reply"); @@ -839,21 +820,27 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), the_cib); + + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, the_cib); + } } else if (process) { time_t start_time = time(NULL); - rc = cib_process_command(request, operation, op_function, &reply, - privileged); - log_op_result(request, operation, rc, difftime(time(NULL), start_time)); + if (!privileged + && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { + + rc = EACCES; + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, NULL); + } - if ((reply == NULL) && (needs_reply || local_notify)) { - pcmk__err("Unexpected NULL reply to message"); - pcmk__log_xml_err(request, "null reply"); - goto done; + } else { + rc = cib_process_command(request, operation, op_function, &reply); } + + log_op_result(request, operation, rc, difftime(time(NULL), start_time)); } if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { @@ -861,17 +848,18 @@ based_process_request(xmlNode *request, bool privileged, pcmk__s(originator, "local"), client_name, call_id, (local_notify? " with local notification" : "")); - } else if (needs_reply && !stand_alone && (client == NULL) - && !pcmk__is_set(call_options, cib_discard_reply)) { + } else if (needs_reply && !stand_alone && (client == NULL)) { send_peer_reply(reply, originator); } - if (local_notify && (client_id != NULL)) { - do_local_notify((process? reply : request), client_id, - pcmk__is_set(call_options, cib_sync_call), - (client == NULL)); + if (!local_notify || (client_id == NULL)) { + goto done; } + do_local_notify((process? reply : request), client_id, + pcmk__is_set(call_options, cib_sync_call), + (client == NULL)); + done: pcmk__xml_free(reply); return rc; @@ -912,23 +900,38 @@ cib_force_exit(gpointer data) return FALSE; } -static void -disconnect_remote_client(gpointer key, gpointer value, gpointer user_data) +void +based_shutdown(int nsig) { - pcmk__client_t *a_client = value; + int active = 0; + xmlNode *notification = NULL; - pcmk__err("Can't disconnect client %s: Not implemented", - pcmk__client_name(a_client)); -} + if (cib_shutdown_flag) { + // Already shutting down + return; + } -static void -initiate_exit(void) -{ - int active = 0; - xmlNode *leaving = NULL; + cib_shutdown_flag = true; + + if (ipcs_ro != NULL) { + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); + } + + if (ipcs_rw != NULL) { + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); + } + + if (ipcs_shm != NULL) { + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); + } + + based_drop_remote_clients(); active = pcmk__cluster_num_active_nodes(); - if (active < 2) { // This is the last active node + if (active < 2) { pcmk__info("Exiting without sending shutdown request (no active " "peers)"); based_terminate(CRM_EX_OK); @@ -937,83 +940,16 @@ initiate_exit(void) pcmk__info("Sending shutdown request to %d peers", active); - leaving = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); - pcmk__xe_set(leaving, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(leaving, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); + notification = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); + pcmk__xe_set(notification, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(notification, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); - pcmk__cluster_send_message(NULL, pcmk_ipc_based, leaving); - pcmk__xml_free(leaving); + pcmk__cluster_send_message(NULL, pcmk_ipc_based, notification); + pcmk__xml_free(notification); pcmk__create_timer(EXIT_ESCALATION_MS, cib_force_exit, NULL); } -void -based_shutdown(int nsig) -{ - struct qb_ipcs_stats srv_stats; - - if (!cib_shutdown_flag) { - int disconnects = 0; - qb_ipcs_connection_t *c = NULL; - - cib_shutdown_flag = true; - - c = qb_ipcs_connection_first_get(ipcs_rw); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_rw, last); - - pcmk__debug("Disconnecting r/w client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; - } - - c = qb_ipcs_connection_first_get(ipcs_ro); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_ro, last); - - pcmk__debug("Disconnecting r/o client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; - } - - c = qb_ipcs_connection_first_get(ipcs_shm); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_shm, last); - - pcmk__debug("Disconnecting non-blocking r/w client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; - } - - disconnects += pcmk__ipc_client_count(); - - pcmk__debug("Disconnecting %d remote clients", - pcmk__ipc_client_count()); - pcmk__foreach_ipc_client(disconnect_remote_client, NULL); - pcmk__info("Disconnected %d clients", disconnects); - } - - qb_ipcs_stats_get(ipcs_rw, &srv_stats, QB_FALSE); - - if (pcmk__ipc_client_count() == 0) { - pcmk__info("All clients disconnected (%d)", srv_stats.active_connections); - initiate_exit(); - - } else { - pcmk__info("Waiting on %d clients to disconnect (%d)", - pcmk__ipc_client_count(), srv_stats.active_connections); - } -} - /*! * \internal * \brief Close remote sockets, free the global CIB and quit @@ -1033,6 +969,7 @@ based_terminate(int exit_status) remote_tls_fd = 0; } + g_clear_pointer(&ping_digest, free); g_clear_pointer(&the_cib, pcmk__xml_free); // Exit immediately on error diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 7d59c025f9b..ff3f5fa316a 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -280,15 +280,6 @@ based_ipc_destroy(qb_ipcs_connection_t *c) { pcmk__trace("Destroying client connection %p", c); based_ipc_closed(c); - - /* Shut down if this was the last client to leave. - * - * @TODO Is it correct to do this for destroy but not for closed? Other - * daemons handle closed and destroyed connections in the same way. - */ - if (cib_shutdown_flag) { - based_shutdown(0); - } } struct qb_ipcs_service_handlers ipc_ro_callbacks = { diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 7cd608aa0ef..cbf2ee4ac6c 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -38,7 +38,6 @@ xmlNode *the_cib = NULL; * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE * * \param[in] req Ignored - * \param[in] input Ignored * \param[in] cib Ignored * \param[in] answer Ignored * @@ -47,8 +46,7 @@ xmlNode *the_cib = NULL; * \note This is unimplemented and simply returns an error. */ int -based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* @COMPAT Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed. Note that * external clients with Pacemaker versions < 3.0.0 can send it. @@ -57,14 +55,14 @@ based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* On success, our caller will activate *cib locally, trigger a replace * notification if appropriate, and sync *cib to all nodes. On failure, our * caller will free *cib. */ int rc = pcmk_rc_ok; + xmlNode *input = cib__get_calldata(req); const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); pcmk__client_t *client = pcmk__find_client_by_id(client_id); @@ -82,8 +80,7 @@ based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); @@ -91,16 +88,13 @@ based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int -based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer) { - *answer = NULL; return pcmk_rc_ok; } int -based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* existing_cib and *cib should be identical. In the absence of ACL * filtering, they should also match the_cib. However, they may be copies @@ -112,8 +106,7 @@ based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); char *digest = pcmk__digest_xml(the_cib, true); - - xmlNode *wrapper = NULL; + xmlNode *shallow = NULL; *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE); @@ -121,15 +114,11 @@ based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, pcmk__xe_set(*answer, PCMK_XA_DIGEST, digest); pcmk__xe_set(*answer, PCMK__XA_CIB_PING_ID, seq); - wrapper = pcmk__xe_create(*answer, PCMK__XE_CIB_CALLDATA); - - if (*cib != NULL) { - // Use *cib so that ACL filtering is applied to the answer - xmlNode *shallow = pcmk__xe_create(wrapper, - (const char *) (*cib)->name); - - pcmk__xe_copy_attrs(shallow, *cib, pcmk__xaf_none); - } + // Use *cib so that ACL filtering is applied to the answer + shallow = pcmk__xe_create(NULL, (const char *) (*cib)->name); + pcmk__xe_copy_attrs(shallow, *cib, pcmk__xaf_none); + cib__set_calldata(*answer, shallow); + pcmk__xml_free(shallow); pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", host, digest, @@ -143,8 +132,7 @@ based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); @@ -158,10 +146,8 @@ based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) { - xmlNode *wrapper = NULL; xmlNode *data = NULL; const char *after_ver = NULL; @@ -170,8 +156,7 @@ based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); - wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL); - data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + data = cib__get_calldata(req); if (data == NULL) { pcmk__warn("No data specified in request"); return EPROTO; @@ -203,8 +188,7 @@ based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer) { if (based_is_primary) { pcmk__info("We are now in R/O mode"); @@ -218,13 +202,10 @@ based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - *answer = NULL; - if (pcmk__xe_get(req, PCMK__XA_CIB_ISREPLYTO) == NULL) { pcmk__info("Peer %s is requesting to shut down", host); return pcmk_rc_ok; @@ -241,19 +222,24 @@ based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer) { return sync_our_cib(req, true); } int -based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; - *answer = NULL; + xmlNode *scratch = NULL; + const char *host = pcmk__xe_get(req, PCMK__XA_SRC); + const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); + const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); + const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); + const char *original_schema = NULL; + const char *new_schema = NULL; + pcmk__node_status_t *origin = NULL; if (pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) { /* The originator of an upgrade request sends it to the DC, without @@ -261,86 +247,77 @@ based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib__process_upgrade(req, input, cib, answer); + return cib__process_upgrade(req, cib, answer); + } - } else { - xmlNode *scratch = pcmk__xml_copy(NULL, *cib); - const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - const char *original_schema = NULL; - const char *new_schema = NULL; - const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); - const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); - const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); - - original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); - if (original_schema == NULL) { - pcmk__info("Rejecting upgrade request from %s: No " - PCMK_XA_VALIDATE_WITH, - host); - return pcmk_rc_cib_corrupt; - } + scratch = pcmk__xml_copy(NULL, *cib); - rc = pcmk__update_schema(&scratch, NULL, true, true); - new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); + original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); + if (original_schema == NULL) { + pcmk__info("Rejecting upgrade request from %s: No " + PCMK_XA_VALIDATE_WITH, host); + return pcmk_rc_cib_corrupt; + } - if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { - xmlNode *up = pcmk__xe_create(NULL, __func__); + rc = pcmk__update_schema(&scratch, NULL, true, true); + new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); - rc = pcmk_rc_ok; - pcmk__notice("Upgrade request from %s verified", host); + if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { + xmlNode *up = pcmk__xe_create(NULL, __func__); - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); + rc = pcmk_rc_ok; + pcmk__notice("Upgrade request from %s verified", host); - pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); + pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); + pcmk__xe_set(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema); + pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); + pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); + pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - pcmk__xml_free(up); + pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); - } else if (rc == pcmk_rc_ok) { - rc = pcmk_rc_schema_unchanged; - } + pcmk__xml_free(up); + goto done; + } + + if (rc == pcmk_rc_ok) { + rc = pcmk_rc_schema_unchanged; + } - if (rc != pcmk_rc_ok) { - // Notify originating peer so it can notify its local clients - pcmk__node_status_t *origin = NULL; - - origin = pcmk__search_node_caches(0, host, NULL, - pcmk__node_search_cluster_member); - - pcmk__info("Rejecting upgrade request from %s: %s " - QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, - ((origin != NULL)? origin->name : "lost")); - - if (origin) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, - pcmk_rc2legacy(rc)); - if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { - pcmk__warn("Could not send CIB upgrade result to %s", host); - } - pcmk__xml_free(up); - } + // Notify originating peer so it can notify its local clients + origin = pcmk__search_node_caches(0, host, NULL, + pcmk__node_search_cluster_member); + + pcmk__info("Rejecting upgrade request from %s: %s " QB_XS " rc=%d peer=%s", + host, pcmk_rc_str(rc), rc, + ((origin != NULL)? origin->name : "lost")); + + if (origin != NULL) { + xmlNode *up = pcmk__xe_create(NULL, __func__); + + pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); + pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); + pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); + pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); + pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); + pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); + if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { + pcmk__warn("Could not send CIB upgrade result to %s", host); } - pcmk__xml_free(scratch); + pcmk__xml_free(up); } + +done: + pcmk__xml_free(scratch); return rc; } static xmlNode * -cib_msg_copy(xmlNode *msg) +cib_msg_copy(const xmlNode *msg) { static const char *field_list[] = { PCMK__XA_T, @@ -373,7 +350,7 @@ cib_msg_copy(xmlNode *msg) } int -sync_our_cib(xmlNode *request, bool all) +sync_our_cib(const xmlNode *request, bool all) { int rc = pcmk_rc_ok; char *digest = NULL; @@ -381,9 +358,7 @@ sync_our_cib(xmlNode *request, bool all) const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); pcmk__node_status_t *peer = NULL; xmlNode *replace_request = NULL; - xmlNode *wrapper = NULL; - CRM_CHECK(the_cib != NULL, return EINVAL); CRM_CHECK(all || (host != NULL), return EINVAL); pcmk__debug("Syncing CIB to %s", (all? "all peers" : host)); @@ -408,8 +383,7 @@ sync_our_cib(xmlNode *request, bool all) digest = pcmk__digest_xml(the_cib, true); pcmk__xe_set(replace_request, PCMK_XA_DIGEST, digest); - wrapper = pcmk__xe_create(replace_request, PCMK__XE_CIB_CALLDATA); - pcmk__xml_copy(wrapper, the_cib); + cib__set_calldata(replace_request, the_cib); if (!all) { peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index fddb943e086..30b934a8600 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,42 +17,20 @@ extern bool based_is_primary; extern xmlNode *the_cib; -int based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, +int based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer); - -int based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int sync_our_cib(xmlNode *request, bool all); +int based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); + +int sync_our_cib(const xmlNode *request, bool all); #endif // BASED_MESSAGES__H diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 2e671b9c35e..21458325b79 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -199,9 +199,7 @@ cib_notify_send(const xmlNode *xml) } void -based_diff_notify(const char *op, int result, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *diff) +based_diff_notify(const xmlNode *request, int rc, xmlNode *diff) { xmlNode *update_msg = NULL; xmlNode *wrapper = NULL; @@ -212,14 +210,27 @@ based_diff_notify(const char *op, int result, const char *call_id, update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY); + /* We could simplify by copying all attributes from request. We would just + * have to ensure that there are never "private" attributes that we want to + * hide from external clients with notify callbacks. + */ pcmk__xe_set(update_msg, PCMK__XA_T, PCMK__VALUE_CIB_NOTIFY); pcmk__xe_set(update_msg, PCMK__XA_SUBT, PCMK__VALUE_CIB_DIFF_NOTIFY); - pcmk__xe_set(update_msg, PCMK__XA_CIB_OP, op); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, client_name); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set(update_msg, PCMK__XA_SRC, origin); - pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, result); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_OP, + pcmk__xe_get(request, PCMK__XA_CIB_OP)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTID, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, + pcmk__xe_get(request, PCMK__XA_CIB_CALLID)); + + pcmk__xe_set(update_msg, PCMK__XA_SRC, pcmk__xe_get(request, PCMK__XA_SRC)); + pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); wrapper = pcmk__xe_create(update_msg, PCMK__XE_CIB_UPDATE_RESULT); pcmk__xml_copy(wrapper, diff); diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index 79886f79aa1..aec79e7b0b7 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -16,8 +16,6 @@ int based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client); -void based_diff_notify(const char *op, int result, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *diff); +void based_diff_notify(const xmlNode *request, int rc, xmlNode *diff); #endif // BASED_NOTIFY__H diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index edfb6796501..3746d53d887 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -544,10 +544,6 @@ based_remote_client_destroy(gpointer user_data) pcmk__free_client(client); pcmk__trace("Freed the cib client"); - - if (cib_shutdown_flag) { - based_shutdown(0); - } } static int @@ -597,9 +593,13 @@ cib_remote_listen(gpointer data) /* create gnutls session for the server socket */ new_client->remote->tls_session = pcmk__new_tls_session(tls, csock); if (new_client->remote->tls_session == NULL) { + pcmk__err("Dropping remote connection from %s because we failed to " + "create a TLS session for it", ipstr); + pcmk__free_client(new_client); close(csock); return 0; } + } else { pcmk__set_client_flags(new_client, pcmk__client_tcp); new_client->remote->tcp_socket = csock; @@ -760,3 +760,38 @@ based_remote_init(void) remote_fd = init_remote_listener(port); } } + +/*! + * \internal + * \brief Disconnect and free a CIB manager client if it is a remote client + * + * If \p value is a remote client, drop it by removing its source from the + * mainloop. It will be freed by \c based_remote_client_destroy() via + * \c remote_client_fd_callbacks. + * + * \param[in] key Ignored + * \param[in,out] value CIB manager client (pcmk__client_t *) + * \param[in] user_data Ignored + */ +static void +drop_client_if_remote(gpointer key, gpointer value, gpointer user_data) +{ + pcmk__client_t *client = value; + + if (client->remote == NULL) { + return; + } + + pcmk__notice("Disconnecting remote client %s", pcmk__client_name(client)); + mainloop_del_fd(client->remote->source); +} + +/*! + * \internal + * \brief Disconnect and free all remote CIB manager clients + */ +void +based_drop_remote_clients(void) +{ + pcmk__foreach_ipc_client(drop_client_if_remote, NULL); +} diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h index d2ab8f00802..1763dc04f55 100644 --- a/daemons/based/based_remote.h +++ b/daemons/based/based_remote.h @@ -14,5 +14,6 @@ extern int remote_fd; extern int remote_tls_fd; void based_remote_init(void); +void based_drop_remote_clients(void); #endif // BASED_REMOTE__H diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 48bbb9ac43b..cc4754daae6 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -118,7 +118,7 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, * \note This function is expected to be called only by * \p based_process_commit_transact(). * \note \p result_cib is expected to be a copy of the current CIB as created by - * \p cib_perform_op(). + * \p cib__perform_op_rw(). * \note The caller is responsible for activating and syncing \p result_cib on * success, and for freeing it on failure. */ @@ -130,7 +130,7 @@ based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, int rc = pcmk_rc_ok; char *source = NULL; - // *result_cib should be a copy of the_cib (created by cib_perform_op()) + // *result_cib should be a copy of the_cib (created by cib__perform_op_rw()) pcmk__assert((result_cib != NULL) && (*result_cib != NULL) && (*result_cib != the_cib)); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 63b5eda8a6e..1e0b0370357 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -48,7 +48,7 @@ enum cib__op_attr { //! No special attributes cib__op_attr_none = 0, - //! Modifies CIB + //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), //! Requires privileges @@ -103,8 +103,7 @@ enum cib__op_type { * replace *cib, but the replacement must become the root of the original * document. */ -typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode *input, xmlNode **cib, - xmlNode **output); +typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode **cib, xmlNode **output); typedef struct { const char *name; @@ -187,13 +186,15 @@ cib__client_triggers_refresh(const char *name) } int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); +xmlNode *cib__get_calldata(const xmlNode *request); +void cib__set_calldata(xmlNode *request, xmlNode *data); -int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, +int cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); -int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **cib, xmlNode **diff, - xmlNode **output); +int cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, @@ -207,32 +208,15 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); -int cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_bump(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index f409b3e61d4..773c0a5af48 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -151,8 +151,8 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) file_opaque_t *private = cib->variant_opaque; - // We error checked these in callers - cib__get_operation(op, &operation); + // We error checked these in callers, but make Coverity happy + pcmk__assert(cib__get_operation(op, &operation) == pcmk_rc_ok); op_function = get_op_function(operation); rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, @@ -164,12 +164,12 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies); if (read_only) { - rc = cib__perform_query(op_function, request, &private->cib_xml, + rc = cib__perform_op_ro(op_function, request, &private->cib_xml, output); } else { result_cib = private->cib_xml; - rc = cib_perform_op(cib_file, op_function, request, &changed, - &result_cib, &cib_diff, output); + rc = cib__perform_op_rw(cib_file, op_function, request, &changed, + &result_cib, &cib_diff, output); } if (pcmk__is_set(call_options, cib_transaction)) { @@ -191,8 +191,16 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) set_file_flags(private, file_flag_dirty); } + if (*output == NULL) { + goto done; + } + + if ((*output)->doc == private->cib_xml->doc) { + *output = pcmk__xml_copy(NULL, *output); + } + done: - if ((result_cib != private->cib_xml) && (result_cib != *output)) { + if (result_cib != private->cib_xml) { pcmk__xml_free(result_cib); } pcmk__xml_free(cib_diff); @@ -226,6 +234,8 @@ process_transaction_requests(cib_t *cib, xmlNode *transaction) int rc = process_request(cib, request, &output); + pcmk__xml_free(output); + if (rc != pcmk_rc_ok) { pcmk__err("Aborting transaction for CIB file client (%s) on file " "'%s' due to failed %s request: %s", @@ -265,7 +275,7 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) xmlNode *saved_cib = private->cib_xml; /* *result_cib should be a copy of private->cib_xml (created by - * cib_perform_op()) + * cib__perform_op_rw()) */ pcmk__assert((result_cib != NULL) && (*result_cib != NULL) && (*result_cib != private->cib_xml)); @@ -301,10 +311,10 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) } static int -process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib_xml, - xmlNode **answer) +process_commit_transact(xmlNode *req, xmlNode **cib_xml, xmlNode **answer) { int rc = pcmk_rc_ok; + xmlNode *input = cib__get_calldata(req); const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); cib_t *cib = NULL; @@ -403,38 +413,35 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, const cib__operation_t *operation = NULL; - pcmk__info("Handling %s operation for %s as %s", - pcmk__s(op, "invalid"), pcmk__s(section, "entire CIB"), + pcmk__info("Handling %s operation for %s as %s", pcmk__s(op, "invalid"), + pcmk__s(section, "entire CIB"), pcmk__s(user_name, "default user")); - if (output_data != NULL) { - *output_data = NULL; - } - if (cib->state == cib_disconnected) { - return -ENOTCONN; + rc = ENOTCONN; + goto done; } rc = cib__get_operation(op, &operation); - rc = pcmk_rc2legacy(rc); - if (rc != pcmk_ok) { + if (rc != pcmk_rc_ok) { // @COMPAT: At compatibility break, use rc directly - return -EPROTONOSUPPORT; + rc = EPROTONOSUPPORT; + goto done; } if (get_op_function(operation) == NULL) { // @COMPAT: At compatibility break, use EOPNOTSUPP pcmk__err("Operation %s is not supported by CIB file clients", op); - return -EPROTONOSUPPORT; + rc = EPROTONOSUPPORT; + goto done; } cib__set_call_options(call_options, "file operation", cib_no_mtime); rc = cib__create_op(cib, op, host, section, data, call_options, user_name, NULL, &request); - rc = pcmk_rc2legacy(rc); - if (rc != pcmk_ok) { - return rc; + if (rc != pcmk_rc_ok) { + goto done; } pcmk__xe_set(request, PCMK__XA_ACL_TARGET, user_name); @@ -442,30 +449,22 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, if (pcmk__is_set(call_options, cib_transaction)) { rc = cib__extend_transaction(cib, request); - rc = pcmk_rc2legacy(rc); goto done; } rc = process_request(cib, request, &output); - rc = pcmk_rc2legacy(rc); - - if ((output_data != NULL) && (output != NULL)) { - if (output->doc == private->cib_xml->doc) { - *output_data = pcmk__xml_copy(NULL, output); - } else { - *output_data = output; - } - } done: - if ((output != NULL) - && (output->doc != private->cib_xml->doc) - && ((output_data == NULL) || (output != *output_data))) { + pcmk__xml_free(request); + if (output_data != NULL) { + *output_data = output; + + } else { pcmk__xml_free(output); } - pcmk__xml_free(request); - return rc; + + return pcmk_rc2legacy(rc); } /*! diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 57c8c87545e..8c19cdda340 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -126,9 +126,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, rc = pcmk_ok; pcmk__xe_get_int(op_reply, PCMK__XA_CIB_CALLID, &reply_id); if (reply_id == cib->call_id) { - xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + xmlNode *tmp = cib__get_calldata(op_reply); pcmk__trace("Synchronous reply %d received", reply_id); if (pcmk__xe_get_int(op_reply, PCMK__XA_CIB_RC, &rc) != pcmk_rc_ok) { diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index d7a4b6d001c..0da03c0263c 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -164,9 +164,9 @@ cib__get_operation(const char *op, const cib__operation_t **operation) } int -cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer) { + const xmlNode *input = cib__get_calldata(req); int rc = xml_apply_patchset(*cib, input, true); return pcmk_legacy2rc(rc); @@ -190,7 +190,7 @@ update_counter(xmlNode *xml, const char *field, bool reset) } int -cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_bump(xmlNode *req, xmlNode **cib, xmlNode **answer) { update_counter(*cib, PCMK_XA_EPOCH, false); return pcmk_rc_ok; @@ -290,11 +290,11 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, } int -cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); xmlNode *failed = NULL; int rc = pcmk_rc_ok; xmlNode *update_section = NULL; @@ -311,7 +311,7 @@ cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, if (pcmk__strcase_any_of(section, PCMK__XE_ALL, PCMK_XE_CIB, NULL) || pcmk__xe_is(input, PCMK_XE_CIB)) { - return cib__process_modify(req, input, cib, answer); + return cib__process_modify(req, cib, answer); } // @COMPAT Deprecated since 2.1.8 @@ -354,14 +354,19 @@ cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, } static int -process_delete_xpath(const char *op, int options, const char *xpath, - xmlNode *cib) +process_delete_xpath(const xmlNode *request, xmlNode *cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s was already removed", xpath); @@ -434,8 +439,10 @@ delete_child(xmlNode *child, void *userdata) } static int -process_delete_section(const char *section, xmlNode *input, xmlNode *cib) +process_delete_section(const xmlNode *request, xmlNode *cib) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); xmlNode *obj_root = NULL; if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { @@ -460,26 +467,21 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) } int -cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_delete_xpath(op, options, section, *cib); + return process_delete_xpath(req, *cib); } - return process_delete_section(section, input, *cib); + return process_delete_section(req, *cib); } int -cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer) { xmlNode *empty = createEmptyCib(0); xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, @@ -507,14 +509,22 @@ cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, } static int -process_modify_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode *cib) +process_modify_xpath(const xmlNode *request, xmlNode *cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = NULL; - const bool score = pcmk__is_set(options, cib_score_update); - const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + uint32_t flags = pcmk__xaf_none; + + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_score_update)) { + flags = pcmk__xaf_score_update; + } if (xpath == NULL) { xpath = pcmk__cib_abs_xpath_for(PCMK_XE_CIB); @@ -556,13 +566,20 @@ process_modify_xpath(const char *op, int options, const char *xpath, } static int -process_modify_section(int options, const char *section, xmlNode *input, - xmlNode *cib) +process_modify_section(const xmlNode *request, xmlNode *cib) { - const bool score = pcmk__is_set(options, cib_score_update); - const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + + uint32_t flags = pcmk__xaf_none; xmlNode *obj_root = NULL; + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_score_update)) { + flags = pcmk__xaf_score_update; + } + if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { input = pcmk_find_cib_element(input, section); } @@ -606,31 +623,32 @@ process_modify_section(int options, const char *section, xmlNode *input, } int -cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_modify_xpath(op, options, section, input, *cib); + return process_modify_xpath(req, *cib); } - return process_modify_section(options, section, input, *cib); + return process_modify_section(req, *cib); } static int -process_query_xpath(const char *op, int options, const char *xpath, - xmlNode *cib, xmlNode **answer) +process_query_xpath(const xmlNode *request, xmlNode *cib, xmlNode **answer) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -716,10 +734,13 @@ process_query_xpath(const char *op, int options, const char *xpath, } static int -process_query_section(int options, const char *section, xmlNode *cib, - xmlNode **answer) +process_query_section(const xmlNode *request, xmlNode *cib, xmlNode **answer) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); xmlNode *obj_root = NULL; + uint32_t options = cib_none; + + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; @@ -746,28 +767,25 @@ process_query_section(int options, const char *section, xmlNode *cib, } int -cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_query_xpath(op, options, section, *cib, answer); + return process_query_xpath(req, *cib, answer); } - return process_query_section(options, section, *cib, answer); + return process_query_section(req, *cib, answer); } static bool -replace_cib_digest_matches(xmlNode *request, xmlNode *input) +replace_cib_digest_matches(const xmlNode *request) { const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); const char *expected = pcmk__xe_get(request, PCMK_XA_DIGEST); + const xmlNode *input = cib__get_calldata(request); char *calculated = NULL; bool matches = false; @@ -792,7 +810,7 @@ replace_cib_digest_matches(xmlNode *request, xmlNode *input) } static int -replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) +replace_cib(xmlNode *request, xmlNode **cib) { int updates = 0; int epoch = 0; @@ -804,12 +822,13 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) const char *reason = NULL; const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); + xmlNode *input = cib__get_calldata(request); cib_version_details(*cib, &admin_epoch, &epoch, &updates); cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); - if (!replace_cib_digest_matches(request, input)) { + if (!replace_cib_digest_matches(request)) { pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " "digest mismatch", replace_admin_epoch, replace_epoch, replace_updates, peer, admin_epoch, epoch, updates); @@ -849,13 +868,19 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) } static int -process_replace_xpath(const char *op, int options, const char *xpath, - xmlNode *request, xmlNode *input, xmlNode **cib) +process_replace_xpath(xmlNode *request, xmlNode **cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search((*cib)->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -878,7 +903,7 @@ process_replace_xpath(const char *op, int options, const char *xpath, free(path); if (match == *cib) { - rc = replace_cib(request, input, cib); + rc = replace_cib(request, cib); break; } @@ -898,9 +923,11 @@ process_replace_xpath(const char *op, int options, const char *xpath, } static int -process_replace_section(const char *section, xmlNode *request, xmlNode *input, - xmlNode **cib) +process_replace_section(xmlNode *request, xmlNode **cib) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + int rc = pcmk_rc_ok; xmlNode *obj_root = NULL; @@ -914,7 +941,7 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } if (pcmk__xe_is(input, PCMK_XE_CIB)) { - return replace_cib(request, input, cib); + return replace_cib(request, cib); } if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei) @@ -934,26 +961,21 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } int -cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_replace_xpath(op, options, section, req, input, cib); + return process_replace_xpath(req, cib); } - return process_replace_section(section, req, input, cib); + return process_replace_section(req, cib); } int -cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; uint32_t options = cib_none; diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index c2d0fc63e74..a4e240f2dbb 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -201,9 +201,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, /* do nothing more */ } else if (!(call_options & cib_discard_reply)) { - xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + xmlNode *tmp = cib__get_calldata(op_reply); if (tmp == NULL) { pcmk__trace("No output in reply to \"%s\" command %d", op, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 835f46ff7b7..d473ffb87e1 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -171,12 +171,15 @@ cib_acl_enabled(xmlNode *xml, const char *user) /*! * \internal - * \brief Get input data from a CIB request + * \brief Get call data from a CIB request * * \param[in] request CIB request XML + * + * \return Call data added by \c cib__set_calldata(), or \c NULL if none is + * found */ -static xmlNode * -get_op_input(const xmlNode *request) +xmlNode * +cib__get_calldata(const xmlNode *request) { xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, NULL, NULL); @@ -184,8 +187,30 @@ get_op_input(const xmlNode *request) return pcmk__xe_first_child(wrapper, NULL, NULL, NULL); } +/*! + * \internal + * \brief Add call data to a CIB request + * + * Add a copy of \p data to a new \c PCMK__XE_CIB_CALLDATA child of \p request. + * + * \param[in,out] request CIB request XML + * \param[in] data Call data to add a copy of (if \c NULL, do nothing) + */ +void +cib__set_calldata(xmlNode *request, xmlNode *data) +{ + xmlNode *wrapper = NULL; + + if (data == NULL) { + return; + } + + wrapper = pcmk__xe_create(request, PCMK__XE_CIB_CALLDATA); + pcmk__xml_copy(wrapper, data); +} + int -cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, +cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output) { int rc = pcmk_rc_ok; @@ -193,7 +218,6 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; - xmlNode *input = NULL; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; @@ -209,7 +233,6 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = get_op_input(req); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -231,7 +254,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, saved_cib = cib; saved_doc = cib->doc; - rc = fn(req, input, &cib, output); + rc = fn(req, &cib, output); /* Sanity check: op should be read-only (but this does not check children, * attributes, or private data) @@ -245,26 +268,34 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, && (cib == xmlDocGetRootElement(cib->doc))); } - if (*output == NULL) { - // Do nothing + if (cib_filtered == *output) { + // Let the caller have this copy + return rc; + } - } else if (cib_filtered == *output) { - // Let them have this copy - cib_filtered = NULL; + if (*output == NULL) { + goto done; + } - } else if (*output == *current_cib) { - // They already know not to free it + if (*output == *current_cib) { + // Trust the caller to check this and not free *output + goto done; + } - } else if ((cib_filtered != NULL) - && ((*output)->doc == cib_filtered->doc)) { - // We're about to free the document of which *output is a part + if ((*output)->doc == (*current_cib)->doc) { + // Give the caller a copy that it can free *output = pcmk__xml_copy(NULL, *output); + goto done; + } - } else if ((*output)->doc == (*current_cib)->doc) { - // Give them a copy they can free - *output = pcmk__xml_copy(NULL, *output); + if ((cib_filtered == NULL) || ((*output)->doc != cib_filtered->doc)) { + goto done; } + // We're about to free the document of which *output is a part + *output = pcmk__xml_copy(NULL, *output); + +done: pcmk__xml_free(cib_filtered); return rc; } @@ -353,7 +384,6 @@ check_new_feature_set(const xmlNode *new_cib) * \param[in] old_cib \c PCMK_XE_CIB element before performing operation * \param[in] new_cib \c PCMK_XE_CIB element from result of operation * \param[in] request CIB request - * \param[in] input Input data for CIB request * * \return Standard Pacemaker return code * @@ -362,8 +392,7 @@ check_new_feature_set(const xmlNode *new_cib) */ static int check_cib_version_attr(const char *attr, const xmlNode *old_cib, - const xmlNode *new_cib, const xmlNode *request, - const xmlNode *input) + const xmlNode *new_cib, const xmlNode *request) { const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); int old_version = 0; @@ -383,7 +412,6 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, pcmk__err("%s went backwards in %s request: %d -> %d", attr, op, old_version, new_version); pcmk__log_xml_warn(request, "bad-request"); - pcmk__log_xml_warn(input, "bad-input"); return pcmk_rc_old_data; } @@ -400,7 +428,6 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, * \param[in] old_cib \c PCMK_XE_CIB element before performing operation * \param[in] new_cib \c PCMK_XE_CIB element from result of operation * \param[in] request CIB request - * \param[in] input Input data for CIB request * * \return Standard Pacemaker return code * @@ -409,18 +436,17 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, */ static int check_cib_versions(const xmlNode *old_cib, const xmlNode *new_cib, - const xmlNode *request, const xmlNode *input) + const xmlNode *request) { int rc = check_cib_version_attr(PCMK_XA_ADMIN_EPOCH, old_cib, new_cib, - request, input); + request); if (rc != pcmk_rc_undetermined) { return rc; } // @TODO Why aren't we checking PCMK_XA_NUM_UPDATES if epochs are equal? - rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request, - input); + rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request); if (rc == pcmk_rc_undetermined) { rc = pcmk_rc_ok; } @@ -479,9 +505,9 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) } int -cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **cib, xmlNode **diff, - xmlNode **output) +cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output) { int rc = pcmk_rc_ok; @@ -489,7 +515,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; - xmlNode *input = NULL; bool enable_acl = false; bool manage_version = true; @@ -513,7 +538,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = get_op_input(req); enable_acl = cib_acl_enabled(*cib, user); pcmk__trace("Processing %s for section '%s', user '%s'", op, @@ -550,7 +574,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, saved_doc = (*cib)->doc; - rc = fn(req, input, cib, output); + rc = fn(req, cib, output); if (rc != pcmk_rc_ok) { goto done; } @@ -587,7 +611,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, } } - rc = check_cib_versions(old_versions, *cib, req, input); + rc = check_cib_versions(old_versions, *cib, req); pcmk__strip_xml_text(*cib); @@ -686,12 +710,7 @@ cib__create_op(cib_t *cib, const char *op, const char *host, pcmk__trace("Sending call options: %.8lx, %d", (long) call_options, call_options); pcmk__xe_set_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options); - - if (data != NULL) { - xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA); - - pcmk__xml_copy(wrapper, data); - } + cib__set_calldata(*op_msg, data); return pcmk_rc_ok; } @@ -776,12 +795,9 @@ cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc) cib_callback_client_t *blob = NULL; if (msg != NULL) { - xmlNode *wrapper = NULL; - pcmk__xe_get_int(msg, PCMK__XA_CIB_RC, &rc); pcmk__xe_get_int(msg, PCMK__XA_CIB_CALLID, &call_id); - wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL); - output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + output = cib__get_calldata(msg); } blob = cib__lookup_id(call_id); @@ -915,8 +931,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, *output = pcmk__xml_copy(NULL, input); } - rc = cib__process_apply_patch(event, diff, output, NULL); - rc = pcmk_rc2legacy(rc); + rc = xml_apply_patchset(*output, diff, true); if (rc == pcmk_ok) { return pcmk_ok; } diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 1a0e8dd4a72..3293e1b66de 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -951,43 +951,41 @@ mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks * callbacks) { mainloop_io_t *client = NULL; + const GIOCondition condition = G_IO_IN|G_IO_HUP|G_IO_NVAL|G_IO_ERR; - if (fd >= 0) { - client = calloc(1, sizeof(mainloop_io_t)); - if (client == NULL) { - return NULL; - } - client->name = strdup(name); - client->userdata = userdata; - - if (callbacks) { - client->destroy_fn = callbacks->destroy; - client->dispatch_fn_io = callbacks->dispatch; - } + if (fd < 0) { + errno = EINVAL; + return NULL; + } - client->fd = fd; - client->channel = g_io_channel_unix_new(fd); - client->source = - g_io_add_watch_full(client->channel, priority, - (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR), mainloop_gio_callback, - client, mainloop_gio_destroy); + client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); + client->name = pcmk__str_copy(name); + client->userdata = userdata; - /* Now that mainloop now holds a reference to channel, - * thanks to g_io_add_watch_full(), drop ours from g_io_channel_unix_new(). - * - * This means that channel will be free'd by: - * g_main_context_dispatch() or g_source_remove() - * -> g_source_destroy_internal() - * -> g_source_callback_unref() - * shortly after mainloop_gio_destroy() completes - */ - g_io_channel_unref(client->channel); - pcmk__trace("Added connection %d for %s[%p].%d", client->source, - client->name, client, fd); - } else { - errno = EINVAL; + if (callbacks != NULL) { + client->destroy_fn = callbacks->destroy; + client->dispatch_fn_io = callbacks->dispatch; } + client->fd = fd; + client->channel = g_io_channel_unix_new(fd); + client->source = g_io_add_watch_full(client->channel, priority, condition, + mainloop_gio_callback, client, + mainloop_gio_destroy); + + /* Now that mainloop now holds a reference to channel, thanks to + * g_io_add_watch_full(), drop ours from g_io_channel_unix_new(). + * + * This means that channel will be free'd by: + * g_main_context_dispatch() or g_source_remove() + * -> g_source_destroy_internal() + * -> g_source_callback_unref() + * shortly after mainloop_gio_destroy() completes + */ + g_io_channel_unref(client->channel); + + pcmk__trace("Added connection %d for %s[%p].%d", client->source, + client->name, client, fd); return client; } @@ -1000,7 +998,12 @@ mainloop_del_fd(mainloop_io_t *client) pcmk__trace("Removing client %s[%p]", client->name, client); - // mainloop_gio_destroy() gets called during source removal + /* g_source_remove() marks the source as destroyed, unsets the source + * callback (mainloop_gio_callback()), and destroys the callback data (the + * client) via the notify function (mainloop_gio_destroy()). We can rely on + * mainloop_gio_callback() not getting called again for this source, and on + * the client being destroyed. + */ g_source_remove(client->source); }