diff --git a/daemons/attrd/attrd_alerts.c b/daemons/attrd/attrd_alerts.c index 9bcbaf4f8f8..e58629e5dab 100644 --- a/daemons/attrd/attrd_alerts.c +++ b/daemons/attrd/attrd_alerts.c @@ -8,12 +8,12 @@ */ #include + #include #include -#include -#include #include #include + #include "pacemaker-attrd.h" static GList *attrd_alert_list = NULL; diff --git a/daemons/attrd/attrd_cib.c b/daemons/attrd/attrd_cib.c index e33d525fd41..72877d83b86 100644 --- a/daemons/attrd/attrd_cib.c +++ b/daemons/attrd/attrd_cib.c @@ -18,7 +18,6 @@ #include #include #include -#include // pcmk__get_node() #include "pacemaker-attrd.h" diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c index 20519e0911d..b6b9e2e8f23 100644 --- a/daemons/attrd/attrd_corosync.c +++ b/daemons/attrd/attrd_corosync.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/daemons/attrd/attrd_elections.c b/daemons/attrd/attrd_elections.c index c48c808fda7..11c2841a94e 100644 --- a/daemons/attrd/attrd_elections.c +++ b/daemons/attrd/attrd_elections.c @@ -12,7 +12,6 @@ #include #include -#include #include #include "pacemaker-attrd.h" diff --git a/daemons/attrd/attrd_ipc.c b/daemons/attrd/attrd_ipc.c index 3b0113ccac1..b4ae24b4811 100644 --- a/daemons/attrd/attrd_ipc.c +++ b/daemons/attrd/attrd_ipc.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/daemons/attrd/attrd_messages.c b/daemons/attrd/attrd_messages.c index ed90865fb18..21c8d108a6b 100644 --- a/daemons/attrd/attrd_messages.c +++ b/daemons/attrd/attrd_messages.c @@ -14,7 +14,6 @@ #include -#include // pcmk__get_node() #include #include "pacemaker-attrd.h" @@ -349,7 +348,7 @@ attrd_send_protocol(const pcmk__node_status_t *peer) pcmk__xml_free(attrd_op); } -gboolean +void attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, bool confirm) { const char *op = pcmk__xe_get(data, PCMK_XA_TASK); @@ -366,5 +365,5 @@ attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, bool confirm) } attrd_xml_add_writer(data); - return pcmk__cluster_send_message(node, pcmk_ipc_attrd, data); + pcmk__cluster_send_message(node, pcmk_ipc_attrd, data); } diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c index 3025f197e76..2428104f058 100644 --- a/daemons/attrd/attrd_utils.c +++ b/daemons/attrd/attrd_utils.c @@ -57,28 +57,16 @@ attrd_shutdown(int nsig) // Tell various functions not to do anthing shutting_down = true; - // Don't respond to signals while shutting down - mainloop_destroy_signal(SIGTERM); - mainloop_destroy_signal(SIGCHLD); - mainloop_destroy_signal(SIGPIPE); - mainloop_destroy_signal(SIGUSR1); - mainloop_destroy_signal(SIGUSR2); - mainloop_destroy_signal(SIGTRAP); - attrd_free_waitlist(); attrd_free_confirmations(); g_clear_pointer(&peer_protocol_vers, g_hash_table_destroy); - if ((mloop == NULL) || !g_main_loop_is_running(mloop)) { - /* If there's no main loop active, just exit. This should be possible - * only if we get SIGTERM in brief windows at start-up and shutdown. - */ - crm_exit(CRM_EX_OK); - } else { - g_main_loop_quit(mloop); - g_main_loop_unref(mloop); - } + // There should be no way to get here without the main loop running + CRM_CHECK((mloop != NULL) && g_main_loop_is_running(mloop), + crm_exit(CRM_EX_OK)); + + g_main_loop_quit(mloop); } /*! @@ -99,6 +87,7 @@ void attrd_run_mainloop(void) { g_main_loop_run(mloop); + g_clear_pointer(&mloop, g_main_loop_unref); } /* strlen("value") */ diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c index 1682ba8ba56..b04d9f88e59 100644 --- a/daemons/attrd/pacemaker-attrd.c +++ b/daemons/attrd/pacemaker-attrd.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "pacemaker-attrd.h" diff --git a/daemons/attrd/pacemaker-attrd.h b/daemons/attrd/pacemaker-attrd.h index 8d9b50ad408..a9bbfe20d9d 100644 --- a/daemons/attrd/pacemaker-attrd.h +++ b/daemons/attrd/pacemaker-attrd.h @@ -8,7 +8,7 @@ */ #ifndef PACEMAKER_ATTRD__H -# define PACEMAKER_ATTRD__H +#define PACEMAKER_ATTRD__H #include #include @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -211,8 +211,8 @@ void attrd_client_clear_failure(pcmk__request_t *request); void attrd_client_update(pcmk__request_t *request); void attrd_client_refresh(pcmk__request_t *request); xmlNode *attrd_client_query(pcmk__request_t *request); -gboolean attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, - bool confirm); +void attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, + bool confirm); xmlNode *attrd_add_value_xml(xmlNode *parent, const attribute_t *a, const attribute_value_t *v, bool force_write); @@ -266,4 +266,4 @@ void attrd_set_node_xml_id(const char *node_name, const char *node_xml_id); void attrd_forget_node_xml_id(const char *node_name); void attrd_cleanup_xml_ids(void); -#endif /* PACEMAKER_ATTRD__H */ +#endif // PACEMAKER_ATTRD__H diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 71679d43275..b9e77a5ba0e 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -23,8 +23,6 @@ #include // cib_call_options values #include // cib__* -#include // pcmk__cluster_send_message -#include // pcmk__s, pcmk__str_eq #include // crm_ipc_*, pcmk_ipc_* #include // CRM_LOG_ASSERT, CRM_CHECK #include // mainloop_* @@ -69,6 +67,10 @@ digest_timer_cb(void *data) { xmlNode *ping = NULL; + if (based_shutting_down()) { + return G_SOURCE_REMOVE; + } + if (!based_get_local_node_dc()) { // Only the DC sends a ping return G_SOURCE_REMOVE; @@ -311,49 +313,22 @@ process_ping_reply(const xmlNode *reply) } static void -log_local_options(const pcmk__client_t *client, - const cib__operation_t *operation, const char *host, - const char *op) -{ - if (pcmk__is_set(operation->flags, cib__op_attr_local)) { - pcmk__trace("Processing always-local %s op from client %s", op, - pcmk__client_name(client)); - - /* @COMPAT Currently host is ignored. At a compatibility break, throw an - * error (from based_process_request() or earlier) if host is not NULL - * or our node name. - */ - if (!pcmk__str_eq(host, based_cluster_node_name(), - pcmk__str_casei|pcmk__str_null_matches)) { - - pcmk__warn("Operation '%s' is always local but its target host is " - "set to '%s'", op, host); - } - - return; - } - - if (based_stand_alone()) { - pcmk__trace("Processing %s op from client %s (stand-alone)", op, - pcmk__client_name(client)); - - } else { - pcmk__trace("Processing %saddressed %s op from client %s", - ((host != NULL)? "locally " : "un"), op, - pcmk__client_name(client)); - } -} - -static bool -parse_peer_options(const cib__operation_t *operation, xmlNode *request, +parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, bool *local_notify, bool *needs_reply, bool *process) { const char *local_node_name = based_cluster_node_name(); - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); - const char *delegated = pcmk__xe_get(request, PCMK__XA_CIB_DELEGATED_FROM); - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - const char *reply_to = pcmk__xe_get(request, PCMK__XA_CIB_ISREPLYTO); + + /* Don't send replies for modifying ops because they already get forwarded + * to all nodes. Also don't send a reply if the client specifically told us + * not to. + */ + const bool can_reply = !operation->modifies_cib; + + const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); + const char *delegated = pcmk__xe_get(request->xml, + PCMK__XA_CIB_DELEGATED_FROM); + const char *originator = pcmk__xe_get(request->xml, PCMK__XA_SRC); + const char *reply_to = pcmk__xe_get(request->xml, PCMK__XA_CIB_ISREPLYTO); bool is_reply = pcmk__str_eq(reply_to, local_node_name, pcmk__str_casei); @@ -361,37 +336,45 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, originator = "peer"; } - if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { - process_ping_reply(request); - return false; + if (is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { + return; } - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { + if (pcmk__str_eq(request->op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { /* @COMPAT We stopped sending shutdown requests as of 3.0.2. During a * rolling upgrade, the requesting node expects a reply. We might as * well continue sending one until we no longer support rolling upgrades * from versions earlier than 3.0.2. (If the requesting node doesn't * receive a reply, it simply exits with CRM_EX_ERROR after 10 seconds.) */ - return true; + *needs_reply = true; + return; } - if (is_reply && pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); + if (is_reply + && pcmk__str_eq(request->op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { + + pcmk__trace("Will notify local clients for %s reply from %s", + request->op, originator); *process = false; - *needs_reply = false; *local_notify = true; - return true; + return; } if ((reply_to != NULL) - && pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { + && pcmk__str_eq(request->op, PCMK__CIB_REQUEST_REPLACE, + pcmk__str_none)) { // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO delegated = reply_to; - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { + /* @FIXME can_reply is always false here because cib__op_replace has + * modifies_cib=true. Should we be sending a reply here? + */ + *needs_reply = can_reply; + + } else if (pcmk__str_eq(request->op, PCMK__CIB_REQUEST_UPGRADE, + pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. * @@ -402,8 +385,9 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, * Except this time PCMK__XA_CIB_SCHEMA_MAX will be set which puts a * limit on how far newer nodes will go */ - const char *max = pcmk__xe_get(request, PCMK__XA_CIB_SCHEMA_MAX); - const char *upgrade_rc = pcmk__xe_get(request, PCMK__XA_CIB_UPGRADE_RC); + const char *max = pcmk__xe_get(request->xml, PCMK__XA_CIB_SCHEMA_MAX); + const char *upgrade_rc = pcmk__xe_get(request->xml, + PCMK__XA_CIB_UPGRADE_RC); pcmk__trace("Parsing upgrade %s for %s with max=%s and upgrade_rc=%s", (is_reply? "reply" : "request"), @@ -413,48 +397,41 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (upgrade_rc != NULL) { // Our upgrade request was rejected by DC, notify clients of result pcmk__assert(is_reply); - pcmk__xe_set(request, PCMK__XA_CIB_RC, upgrade_rc); + pcmk__xe_set(request->xml, PCMK__XA_CIB_RC, upgrade_rc); - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); + pcmk__trace("Will notify local clients for %s reply from %s", + request->op, originator); *process = false; - *needs_reply = false; *local_notify = true; - return true; + return; } if ((max == NULL) && !based_get_local_node_dc()) { // Ignore broadcast client requests when we're not the DC - return false; + *process = false; + return; } } *local_notify = pcmk__str_eq(delegated, local_node_name, pcmk__str_casei); if (pcmk__str_eq(host, local_node_name, pcmk__str_casei)) { - pcmk__trace("Processing %s request sent to us from %s", op, originator); - return true; + pcmk__trace("Processing %s request sent to us from %s", request->op, + originator); + *needs_reply = can_reply; + return; } if (host != NULL) { - pcmk__trace("Ignoring %s request intended for CIB manager on %s", op, - host); - *needs_reply = false; - return false; + pcmk__trace("Ignoring %s request intended for CIB manager on %s", + request->op, host); + *process = false; + return; } - if (is_reply || !pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { - *needs_reply = false; + if (!is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { + *needs_reply = can_reply; } - - pcmk__trace("Processing %s request broadcast by %s call %s on %s " - "(local clients will%s be notified)", op, - pcmk__s(pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME), - "client"), - pcmk__s(pcmk__xe_get(request, PCMK__XA_CIB_CALLID), - "without ID"), - originator, (*local_notify? "" : "not")); - return true; } /*! @@ -464,11 +441,13 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, * \param[in] request CIB request to forward * * \note This does nothing if running in stand-alone mode. + * \note \p request is modified within the function, but its initial state is + * restored before returning. This probably doesn't matter, however. */ static void -forward_request(xmlNode *request) +forward_request(pcmk__request_t *request) { - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); + const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); pcmk__node_status_t *peer = NULL; if (based_stand_alone()) { @@ -480,35 +459,31 @@ forward_request(xmlNode *request) } // Set PCMK__XA_CIB_DELEGATED_FROM only temporarily - pcmk__xe_set(request, PCMK__XA_CIB_DELEGATED_FROM, + pcmk__xe_set(request->xml, PCMK__XA_CIB_DELEGATED_FROM, based_cluster_node_name()); - pcmk__cluster_send_message(peer, pcmk_ipc_based, request); - pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM); + pcmk__cluster_send_message(peer, pcmk_ipc_based, request->xml); + pcmk__xe_remove_attr(request->xml, PCMK__XA_CIB_DELEGATED_FROM); } static int -based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, +based_perform_op_rw(pcmk__request_t *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { xmlNode *result_cib = based_cib; xmlNode *cib_diff = NULL; const char *feature_set = NULL; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - uint32_t call_options = cib_none; + const char *originator = pcmk__xe_get(request->xml, PCMK__XA_SRC); bool config_changed = false; int rc = pcmk_rc_ok; - pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - /* 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. */ - rc = cib__perform_op_rw(cib_undefined, op_function, request, + rc = cib__perform_op_rw(cib_undefined, op_function, request->xml, &config_changed, &result_cib, &cib_diff, output); /* On validation error, include the schema-violating result CIB in any reply @@ -521,7 +496,9 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, } // Discard result for failure or dry run - if ((rc != pcmk_rc_ok) || pcmk__any_flags_set(call_options, cib_dryrun)) { + if ((rc != pcmk_rc_ok) + || pcmk__any_flags_set(request->call_options, cib_dryrun)) { + if (result_cib != based_cib) { pcmk__xml_free(result_cib); } @@ -530,18 +507,17 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, } if (result_cib != based_cib) { - /* Always write to disk for successful ops with the writes-through flag - * set. This also avoids the need to detect ordering changes. - * - * An exception is a request within a transaction. Since a transaction + /* Write to disk on config change or successful upgrade (which may + * update PCMK_XA_VALIDATE_WITH without changing the configuration), + * unless the request is part of a transaction. Since a transaction * is atomic, intermediate results must not be written to disk. */ - const bool to_disk = !pcmk__is_set(call_options, cib_transaction) + const bool to_disk = !pcmk__is_set(request->call_options, + cib_transaction) && (config_changed - || pcmk__is_set(operation->flags, - cib__op_attr_writes_through)); + || (operation->type == cib__op_upgrade)); - rc = based_activate_cib(result_cib, to_disk, op); + rc = based_activate_cib(result_cib, to_disk, request->op); } feature_set = pcmk__xe_get(based_cib, PCMK_XA_CRM_FEATURE_SET); @@ -559,7 +535,7 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, && pcmk__str_eq(originator, based_cluster_node_name(), pcmk__str_casei) && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { - sync_our_cib(request, true); + sync_our_cib(request->xml, true); } if (cib_diff != NULL) { @@ -569,10 +545,10 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, mainloop_timer_start(digest_timer); done: - if (!pcmk__any_flags_set(call_options, + if (!pcmk__any_flags_set(request->call_options, cib_dryrun|cib_inhibit_notify|cib_transaction)) { - based_diff_notify(request, rc, cib_diff); + based_diff_notify(request->xml, rc, cib_diff); } pcmk__xml_free(cib_diff); @@ -604,7 +580,7 @@ log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, int epoch = 0; int num_updates = 0; - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + if (!operation->modifies_cib) { level = LOG_TRACE; } else if (rc != pcmk_rc_ok) { @@ -615,14 +591,9 @@ log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, originator = pcmk__s(originator, "local"); client_name = pcmk__s(client_name, "client"); - /* @FIXME based_cib should always be non-NULL, but that's currently not the - * case during shutdown - */ - if (based_cib != NULL) { - pcmk__xe_get_int(based_cib, PCMK_XA_ADMIN_EPOCH, &admin_epoch); - pcmk__xe_get_int(based_cib, PCMK_XA_EPOCH, &epoch); - pcmk__xe_get_int(based_cib, PCMK_XA_NUM_UPDATES, &num_updates); - } + pcmk__xe_get_int(based_cib, PCMK_XA_ADMIN_EPOCH, &admin_epoch); + pcmk__xe_get_int(based_cib, PCMK_XA_EPOCH, &epoch); + pcmk__xe_get_int(based_cib, PCMK_XA_NUM_UPDATES, &num_updates); do_crm_log(level, "Completed %s operation for section %s: %s (rc=%d, " @@ -657,38 +628,37 @@ send_peer_reply(xmlNode *msg, const char *originator) /*! * \internal - * \brief Handle an IPC or CPG message containing a request + * \brief Handle a request from a CIB manager client or peer * - * \param[in,out] request Request XML - * \param[in] privileged If \c true, operations with - * \c cib__op_attr_privileged can be run - * \param[in] client IPC client that sent request (\c NULL if request - * came from CPG) + * \param[in,out] request CIB manager request * * \return Standard Pacemaker return code */ int -based_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *client) +based_handle_request(pcmk__request_t *request) { // @TODO: Break into multiple smaller functions - uint32_t call_options = cib_none; - bool process = true; // Whether to process request locally now - bool needs_reply = true; // Whether to build a reply + bool needs_reply = false; // Whether to build a reply bool local_notify = false; // Whether to notify (local) requester xmlNode *reply = NULL; int rc = pcmk_rc_ok; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); - 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__s(pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME), + const char *op = pcmk__xe_get(request->xml, PCMK__XA_CIB_OP); + const char *originator = pcmk__xe_get(request->xml, PCMK__XA_SRC); + const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); + const char *call_id = pcmk__xe_get(request->xml, PCMK__XA_CIB_CALLID); + const char *reply_to = pcmk__xe_get(request->xml, PCMK__XA_CIB_ISREPLYTO); + + /* These are the client ID and name on the originator node, so we need to + * get these from the request XML. request->ipc_client is NULL if this + * request came from the cluster. + */ + const char *client_id = pcmk__xe_get(request->xml, PCMK__XA_CIB_CLIENTID); + const char *client_name = pcmk__s(pcmk__xe_get(request->xml, + PCMK__XA_CIB_CLIENTNAME), "client"); - const char *reply_to = pcmk__xe_get(request, PCMK__XA_CIB_ISREPLYTO); const cib__operation_t *operation = NULL; cib__op_fn_t op_function = NULL; @@ -696,20 +666,20 @@ based_process_request(xmlNode *request, bool privileged, xmlNode *output = NULL; time_t start_time = 0; - 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 (based_shutting_down()) { + pcmk__info("Ignoring pending CIB request during shutdown"); + pcmk__reset_request(request); + return ENOTCONN; } if (pcmk__str_empty(host)) { host = NULL; } - if (client == NULL) { + if (request->ipc_client == NULL) { pcmk__trace("Processing peer %s operation from %s/%s on %s intended " - "for %s (reply=%s)", op, client_name, call_id, originator, - pcmk__s(host, "all"), reply_to); + "for %s (reply=%s)", request->op, client_name, call_id, + originator, pcmk__s(host, "all"), reply_to); } else { const char *src = based_cluster_node_name(); @@ -717,62 +687,58 @@ based_process_request(xmlNode *request, bool privileged, src = "localhost"; } - pcmk__xe_set(request, PCMK__XA_SRC, src); + pcmk__xe_set(request->xml, PCMK__XA_SRC, src); pcmk__trace("Processing local %s operation from %s/%s intended for %s", - op, client_name, call_id, pcmk__s(host, "all")); + request->op, client_name, call_id, pcmk__s(host, "all")); } - rc = cib__get_operation(op, &operation); + rc = cib__get_operation(request->op, &operation); if (rc != pcmk_rc_ok) { - /* TODO: construct error reply? */ pcmk__err("Pre-processing of command failed: %s", pcmk_rc_str(rc)); + pcmk__reset_request(request); return rc; } op_function = based_get_op_function(operation); if (op_function == NULL) { - pcmk__err("Operation %s not supported by CIB manager", op); + pcmk__err("Operation %s not supported by CIB manager", request->op); + pcmk__reset_request(request); return EOPNOTSUPP; } - if (pcmk__is_set(call_options, cib_transaction)) { + if (pcmk__is_set(request->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. */ - needs_reply = false; - pcmk__trace("Processing %s op from %s/%s on %s locally because it's " - "part of a transaction", op, client_name, call_id, - pcmk__xe_get(request, PCMK__XA_SRC)); + "part of a transaction", request->op, client_name, call_id, + pcmk__xe_get(request->xml, PCMK__XA_SRC)); - } else if (client != NULL) { + } else if (request->ipc_client != NULL) { // Forward modifying and non-local requests via cluster if (!based_stand_alone() - && !pcmk__is_set(operation->flags, cib__op_attr_local) - && (pcmk__is_set(operation->flags, cib__op_attr_modifies) + && (operation->modifies_cib || !pcmk__str_eq(host, based_cluster_node_name(), pcmk__str_casei|pcmk__str_null_matches))) { + pcmk__trace("Forwarding %s op from %s/%s", request->op, client_name, + call_id); forward_request(request); + pcmk__reset_request(request); return pcmk_rc_ok; } // Process locally and notify local client; no peer to reply to - needs_reply = false; local_notify = true; - log_local_options(client, operation, host, op); - - } else if (!parse_peer_options(operation, request, &local_notify, - &needs_reply, &process)) { - return pcmk_rc_ok; + } else { + parse_peer_options(operation, request, &local_notify, &needs_reply, + &process); } - if (pcmk__is_set(call_options, cib_discard_reply)) { - needs_reply = false; - local_notify = false; - pcmk__trace("Client is not interested in the reply"); + if (!process) { + goto done; } if (cib_status != pcmk_rc_ok) { @@ -780,54 +746,43 @@ based_process_request(xmlNode *request, bool privileged, pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, based_cib); - } - - goto done; - } - - if (!process) { + output = based_cib; goto done; } start_time = time(NULL); - if (!privileged - && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { + if (pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none) + && pcmk__str_eq(reply_to, based_cluster_node_name(), pcmk__str_casei)) { - rc = EACCES; + process_ping_reply(request->xml); - } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &based_cib, &output); + } else if (!operation->modifies_cib) { + rc = cib__perform_op_ro(op_function, request->xml, &based_cib, &output); } else { rc = based_perform_op_rw(request, operation, op_function, &output); } - log_op_result(request, operation, rc, difftime(time(NULL), start_time)); - - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, output); - } - - if ((output != NULL) && (output->doc != based_cib->doc)) { - pcmk__xml_free(output); - } + log_op_result(request->xml, operation, rc, difftime(time(NULL), start_time)); done: - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) - && needs_reply && !based_stand_alone() && (client == NULL)) { - + if (needs_reply) { + reply = create_cib_reply(request->xml, rc, output); 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)); + do_local_notify((process? reply : request->xml), client_id, + pcmk__is_set(request->call_options, cib_sync_call), + (request->ipc_client == NULL)); + } + + if ((output != NULL) && (output->doc != based_cib->doc)) { + pcmk__xml_free(output); } pcmk__xml_free(reply); + pcmk__reset_request(request); return rc; } diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 7363e7ab2d6..29613b2c4d7 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -10,8 +10,6 @@ #ifndef BASED_CALLBACKS__H #define BASED_CALLBACKS__H -#include - #include // xmlNode #include // pcmk__client_t @@ -19,7 +17,6 @@ void based_callbacks_init(void); void based_callbacks_cleanup(void); -int based_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *client); +int based_handle_request(pcmk__request_t *request); #endif // BASED_CALLBACKS__H diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index cf8fc547d6c..b822baa63d0 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -9,7 +9,7 @@ #include -#include +#include // PRIu32 #include // NULL, size_t #include // uint32_t #include // free @@ -20,8 +20,6 @@ #include // SUPPORT_COROSYNC #include // pcmk_cluster_* -#include // pcmk__cluster_*, etc. -#include // pcmk__err, pcmk__xml_free, etc. #include // CRM_EX_DISCONNECT, pcmk_rc_ok #include "pacemaker-based.h" @@ -29,32 +27,61 @@ static pcmk_cluster_t *cluster = NULL; static void -based_peer_callback(xmlNode *msg, void *private_data) +based_peer_message(pcmk__node_status_t *peer, xmlNode *xml) { - const char *reason = NULL; - const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); - - if (pcmk__peer_cache == NULL) { - reason = "membership not established"; - goto bail; - } - - if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); - } - - based_process_request(msg, true, NULL); - return; + int rc = pcmk_rc_ok; - bail: - if (reason) { - const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); + if (based_shutting_down()) { + pcmk__info("Ignoring CPG message from %s[%" PRIu32 "] during shutdown", + peer->name, peer->cluster_layer_id); + return; - pcmk__warn("Discarding %s message from %s: %s", op, originator, reason); + } else { + pcmk__request_t request = { + .ipc_client = NULL, + .ipc_id = 0, + .ipc_flags = 0, + .peer = peer->name, + .xml = xml, + .call_options = cib_none, + .result = PCMK__UNKNOWN_RESULT, + }; + + rc = pcmk__xe_get_flags(xml, PCMK__XA_CIB_CALLOPT, + (uint32_t *) &request.call_options, cib_none); + if (rc != pcmk_rc_ok) { + pcmk__warn("Couldn't parse options from request: %s", + pcmk_rc_str(rc)); + } + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, return); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + if (pcmk__xe_get(request.xml, PCMK__XA_CIB_CLIENTNAME) == NULL) { + pcmk__xe_set(request.xml, PCMK__XA_CIB_CLIENTNAME, + pcmk__xe_get(request.xml, PCMK__XA_SRC)); + } + + based_handle_request(&request); } } #if SUPPORT_COROSYNC +/*! + * \internal + * \brief Callback for when a peer message is received + * + * \param[in] handle Cluster connection + * \param[in] group_name Group that \p nodeid is a member of + * \param[in] nodeid Peer node that sent \p msg + * \param[in] pid Process that sent \p msg + * \param[in,out] msg Received message + * \param[in] msg_len Length of \p msg + */ static void based_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *group_name, uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) @@ -69,12 +96,15 @@ based_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *group_name, xml = pcmk__xml_parse(data); if (xml == NULL) { - pcmk__err("Invalid XML: '%.120s'", data); - free(data); - return; + pcmk__err("Bad message received from %s[%" PRIu32 "]: '%.120s'", from, + nodeid, data); + + } else { + pcmk__xe_set(xml, PCMK__XA_SRC, from); + based_peer_message(pcmk__get_node(nodeid, from, NULL, + pcmk__node_search_cluster_member), + xml); } - pcmk__xe_set(xml, PCMK__XA_SRC, from); - based_peer_callback(xml, NULL); pcmk__xml_free(xml); free(data); @@ -88,8 +118,8 @@ based_cpg_destroy(void *user_data) return; } - pcmk__crit("Exiting immediately after losing connection to cluster layer"); - based_terminate(CRM_EX_DISCONNECT); + pcmk__crit("Exiting after losing connection to cluster layer"); + based_quit_main_loop(CRM_EX_DISCONNECT); } #endif diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 5e6a12153ab..0a17ea24866 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -29,7 +29,6 @@ #include // cib_file_* #include // createEmptyCib -#include // pcmk__assert_asprintf, PCMK__XE_*, etc. #include // CRM_CHECK #include // mainloop_* #include // pcmk_legacy2rc, pcmk_rc_* @@ -93,6 +92,10 @@ write_cib_async(void *user_data) pid_t pid = 0; int blackbox_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); + if (based_shutting_down()) { + pcmk__info("Skipping CIB write during shutdown"); + } + /* Disable blackbox logging before the fork to avoid two processes writing * to the same shared memory. The disable should not be done in the child, * because this would close shared memory files in the parent. diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 9e32dfdbd5f..e961c7b46ea 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -10,7 +10,6 @@ #include #include // ECONNREFUSED, ENOMEM -#include #include // NULL, size_t #include // int32_t, uint32_t #include // gid_t, uid_t @@ -20,7 +19,6 @@ #include // qb_ipcs_* #include // cib_none, cib_sync_call -#include // pcmk__client_*, pcmk__trace, etc. #include // crm_ipc_client_response #include // CRM_CHECK(), CRM_LOG_ASSERT() #include // CRM_EX_PROTOCOL, pcmk_rc_* @@ -28,8 +26,7 @@ #include "pacemaker-based.h" -static qb_ipcs_service_t *ipcs_ro = NULL; -static qb_ipcs_service_t *ipcs_rw = NULL; +static qb_ipcs_service_t *ipcs = NULL; /*! * \internal @@ -61,17 +58,16 @@ based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) * \internal * \brief Handle a message from an IPC connection * - * \param[in,out] c Established IPC connection - * \param[in] data The message data read from the connection - this - * can be a complete IPC message or just a part of - * one if it's very large - * \param[in] privileged If \c true, operations with - * \c cib__op_attr_privileged can be run + * \param[in,out] c Established IPC connection + * \param[in] data The message data read from the connection - this can be + * a complete IPC message or just a part of one if it's + * very large + * \param[in] size Unused * * \return 0 in all cases */ static int32_t -dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) +based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) { int rc = pcmk_rc_ok; uint32_t id = 0; @@ -82,20 +78,18 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) const char *op = NULL; // Sanity-check, and parse XML from IPC data - CRM_CHECK(client != NULL, return 0); + CRM_CHECK(client != NULL, goto cleanup); + if (data == NULL) { pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c)); - return 0; + goto cleanup; } - pcmk__trace("Dispatching %sprivileged request from client %s", - (privileged? "" : "un"), client->id); - rc = pcmk__ipc_msg_append(&client->buffer, data); if (rc == pcmk_rc_ipc_more) { /* We haven't read the complete message yet, so just return. */ - return 0; + goto cleanup; } else if (rc == pcmk_rc_ok) { /* We've read the complete message and there's already a header on @@ -116,30 +110,18 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) client->buffer = NULL; } - return 0; + goto cleanup; } if (msg == NULL) { pcmk__debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c)); pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_PROTOCOL); - return 0; - } - - if (client->name == NULL) { - const char *value = pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME); - - if (value == NULL) { - client->name = pcmk__itoa(client->pid); - } else { - client->name = pcmk__str_copy(value); - } + goto cleanup; } rc = pcmk__xe_get_flags(msg, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request from IPC client %s: %s", - client->name, pcmk_rc_str(rc)); - pcmk__log_xml_info(msg, "bad-call-opts"); + pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); } /* Requests with cib_transaction set should not be sent to based directly @@ -149,12 +131,11 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) pcmk__warn("Ignoring CIB request from IPC client %s with " "cib_transaction flag set outside of any transaction", client->name); - pcmk__log_xml_info(msg, "no-transaction"); goto cleanup; } if (pcmk__is_set(call_options, cib_sync_call)) { - CRM_LOG_ASSERT(flags & crm_ipc_client_response); + CRM_LOG_ASSERT(pcmk__is_set(flags, crm_ipc_client_response)); // If false, the client has two synchronous events in flight CRM_LOG_ASSERT(client->request_id == 0); @@ -163,14 +144,6 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) client->request_id = id; } - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); - - CRM_LOG_ASSERT(client->user != NULL); - pcmk__update_acl_user(msg, PCMK__XA_CIB_USER, client->user); - - pcmk__log_xml_trace(msg, "ipc-request"); - op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { @@ -184,9 +157,31 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) pcmk__xe_set(reply, PCMK__XA_CIB_OP, CRM_OP_REGISTER); pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client->id); pcmk__ipc_send_xml(client, id, reply, flags); + pcmk__xml_free(reply); + + if (client->name != NULL) { + /* client->name is set if and only if we've processed a register + * request from the client + */ + pcmk__warn("Received register request from IPC client %s that is " + "already registered", pcmk__client_name(client)); + goto cleanup; + } client->request_id = 0; - pcmk__xml_free(reply); + + client->name = pcmk__xe_get_copy(msg, PCMK__XA_CIB_CLIENTNAME); + if (client->name == NULL) { + // Fall back to PID for logging purposes + client->name = pcmk__itoa(client->pid); + } + + goto cleanup; + } + + if (client->name == NULL) { + pcmk__warn("Ignoring CIB request from unregistered client %s", + pcmk__client_name(client)); goto cleanup; } @@ -199,52 +194,39 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) } pcmk__ipc_send_ack(client, id, flags, NULL, status); - goto cleanup; - } - based_process_request(msg, privileged, client); + } else { + pcmk__request_t request = { + .ipc_client = client, + .ipc_id = id, + .ipc_flags = flags, + .peer = NULL, + .xml = msg, + .call_options = call_options, + .result = PCMK__UNKNOWN_RESULT, + }; + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, goto cleanup); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + pcmk__xe_set(request.xml, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__xe_set(request.xml, PCMK__XA_CIB_CLIENTNAME, client->name); + + CRM_LOG_ASSERT(client->user != NULL); + pcmk__update_acl_user(request.xml, PCMK__XA_CIB_USER, client->user); + + based_handle_request(&request); + } cleanup: pcmk__xml_free(msg); return 0; } -/*! - * \internal - * \brief Handle a message from a read-only IPC connection - * - * \param[in,out] c Established IPC connection - * \param[in] data The message data read from the connection - this can be - * a complete IPC message or just a part of one if it's - * very large - * \param[in] size Unused - * - * \return 0 in all cases - */ -static int32_t -based_ipc_dispatch_ro(qb_ipcs_connection_t *c, void *data, size_t size) -{ - return dispatch_common(c, data, false); -} - -/*! - * \internal - * \brief Handle a message from a read/write IPC connection - * - * \param[in,out] c Established IPC connection - * \param[in] data The message data read from the connection - this can be - * a complete IPC message or just a part of one if it's - * very large - * \param[in] size Unused - * - * \return 0 in all cases - */ -static int32_t -based_ipc_dispatch_rw(qb_ipcs_connection_t *c, void *data, size_t size) -{ - return dispatch_common(c, data, true); -} - /*! * \internal * \brief Destroy a client IPC connection @@ -281,18 +263,10 @@ based_ipc_destroy(qb_ipcs_connection_t *c) based_ipc_closed(c); } -static struct qb_ipcs_service_handlers ipc_ro_callbacks = { +static struct qb_ipcs_service_handlers ipc_callbacks = { .connection_accept = based_ipc_accept, .connection_created = NULL, - .msg_process = based_ipc_dispatch_ro, - .connection_closed = based_ipc_closed, - .connection_destroyed = based_ipc_destroy, -}; - -static struct qb_ipcs_service_handlers ipc_rw_callbacks = { - .connection_accept = based_ipc_accept, - .connection_created = NULL, - .msg_process = based_ipc_dispatch_rw, + .msg_process = based_ipc_dispatch, .connection_closed = based_ipc_closed, .connection_destroyed = based_ipc_destroy, }; @@ -304,8 +278,7 @@ static struct qb_ipcs_service_handlers ipc_rw_callbacks = { void based_ipc_init(void) { - pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipc_ro_callbacks, - &ipc_rw_callbacks); + pcmk__serve_based_ipc(&ipcs, &ipc_callbacks); } /*! @@ -315,11 +288,8 @@ based_ipc_init(void) void based_ipc_cleanup(void) { - pcmk__drop_all_clients(ipcs_ro); - g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - - pcmk__drop_all_clients(ipcs_rw); - g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); + pcmk__drop_all_clients(ipcs); + g_clear_pointer(&ipcs, qb_ipcs_destroy); /* Drop remote clients here because they're part of the IPC client table and * must be dropped before \c pcmk__client_cleanup() diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index a77344d92d1..2d4caa07b92 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -19,8 +19,6 @@ #include // QB_XS #include // PCMK__CIB_REQUEST_UPGRADE -#include // pcmk__cluster_send_message -#include // pcmk__info, pcmk__xml_free, etc. #include // pcmk_ipc_server #include // CRM_CHECK #include // pcmk_err, pcmk_ok, pcmk_rc* @@ -29,27 +27,6 @@ #include "pacemaker-based.h" -/*! - * \internal - * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE - * - * \param[in] req Ignored - * \param[in] cib Ignored - * \param[in] answer Ignored - * - * \return \c EINVAL - * - * \note This is unimplemented and simply returns an error. - */ -int -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. - */ - return EINVAL; -} - int based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer) { @@ -75,13 +52,6 @@ based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer) return rc; } -int -based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) -{ - // @COMPAT Pacemaker Remote clients <3.0.0 may send this - return (based_get_local_node_dc()? pcmk_rc_ok : EPERM); -} - // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer) @@ -130,6 +100,9 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { + // This should always be processed locally and never addressed to any host + CRM_CHECK(pcmk__xe_get(req, PCMK__XA_CIB_HOST) == NULL, return EOPNOTSUPP); + if (!based_get_local_node_dc()) { pcmk__info("We are now in R/W mode"); based_set_local_node_dc(true); @@ -150,6 +123,9 @@ based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) GList *schemas = NULL; GList *already_included = NULL; + // This should always be processed locally and never addressed to any host + CRM_CHECK(pcmk__xe_get(req, PCMK__XA_CIB_HOST) == NULL, return EOPNOTSUPP); + *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); data = cib__get_calldata(req); @@ -186,6 +162,9 @@ based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer) { + // This should always be processed locally and never addressed to any host + CRM_CHECK(pcmk__xe_get(req, PCMK__XA_CIB_HOST) == NULL, return EOPNOTSUPP); + if (based_get_local_node_dc()) { pcmk__info("We are now in R/O mode"); based_set_local_node_dc(false); @@ -220,9 +199,6 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) 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; @@ -236,8 +212,6 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) return cib__process_upgrade(req, cib, answer); } - scratch = pcmk__xml_copy(NULL, *cib); - original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); if (original_schema == NULL) { pcmk__info("Rejecting upgrade request from %s: No " @@ -245,26 +219,18 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) return pcmk_rc_cib_corrupt; } - rc = pcmk__update_schema(&scratch, NULL, true, true); + scratch = pcmk__xml_copy(NULL, *cib); + + rc = pcmk__update_schema(&scratch, NULL, true); new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - rc = pcmk_rc_ok; pcmk__notice("Upgrade request from %s verified", host); - 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__cluster_send_message(NULL, pcmk_ipc_based, up); - - pcmk__xml_free(up); + pcmk__xe_set(req, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(req, PCMK__XA_CIB_SCHEMA_MAX, new_schema); + pcmk__cluster_send_message(NULL, pcmk_ipc_based, req); goto done; } @@ -281,20 +247,10 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) ((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(up); + pcmk__xe_set(req, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(req, PCMK__XA_CIB_ISREPLYTO, host); + pcmk__xe_set_int(req, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); + pcmk__cluster_send_message(origin, pcmk_ipc_based, req); } done: @@ -305,6 +261,7 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) static xmlNode * cib_msg_copy(const xmlNode *msg) { + // @FIXME Copying CIB_CALLOPT seems problematic if it has cib_discard_reply static const char *field_list[] = { PCMK__XA_T, PCMK__XA_CIB_CLIENTID, @@ -374,9 +331,9 @@ sync_our_cib(const xmlNode *request, bool all) if (!all) { peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); } - if (!pcmk__cluster_send_message(peer, pcmk_ipc_based, replace_request)) { - rc = ENOTCONN; - } + + pcmk__cluster_send_message(peer, pcmk_ipc_based, replace_request); + pcmk__xml_free(replace_request); free(digest); return rc; diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index f06873920a5..e1167850186 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -14,11 +14,9 @@ #include // xmlNode * -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 **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); diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index d9dcab67950..47ced2c53d4 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -20,7 +20,6 @@ #include // xmlNode #include // QB_XS -#include // pcmk__client_t, etc. #include // pcmk_free_ipc_event #include // CRM_LOG_ASSERT #include // pcmk_rc_* diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 04d1c5417e8..0c7d524e49e 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -12,19 +12,16 @@ #include // NULL #include // cib__* -#include // pcmk__assert, PCMK__NELEM #include "pacemaker-based.h" static const cib__op_fn_t op_functions[] = { - [cib__op_abs_delete] = based_process_abs_delete, [cib__op_apply_patch] = cib__process_apply_patch, [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = based_process_commit_transact, [cib__op_create] = cib__process_create, [cib__op_delete] = cib__process_delete, [cib__op_erase] = cib__process_erase, - [cib__op_is_primary] = based_process_is_primary, [cib__op_modify] = cib__process_modify, [cib__op_noop] = based_process_noop, [cib__op_ping] = based_process_ping, diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 7695ea8947b..3b11ede1dcb 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -11,7 +11,6 @@ #include // htons #include // errno, EAGAIN -#include // getgrgid, getgrnam, group #include // PRIx64 #include // sockaddr_in, INADDR_ANY #include @@ -27,7 +26,6 @@ #include // QB_XS #include // CRM_DAEMON_GROUP -#include // pcmk__client_t, etc. #include // CRM_CHECK #include // mainloop_* #include // pcmk_rc_* @@ -87,57 +85,109 @@ remote_auth_timeout_cb(void *data) /*! * \internal - * \brief Check whether a given user is a member of \c CRM_DAEMON_GROUP + * \brief Read (more) TLS handshake data from a client * - * \param[in] user User name + * \param[in,out] client IPC client * - * \return \c true if \p user is a member of \c CRM_DAEMON_GROUP, or \c false - * otherwise + * \retval 0 on success or more data needed + * \retval -1 on error */ -static bool -is_daemon_group_member(const char *user) +static int +based_read_handshake_data(pcmk__client_t *client) { - int rc = pcmk_rc_ok; - gid_t gid = 0; - const struct group *group = NULL; + int rc = pcmk__read_handshake_data(client); + + if (rc == EAGAIN) { + /* No more data is available at the moment. Just return for now; we'll + * get invoked again once the client sends more. + */ + return 0; + } - /* group->gr_mem only contains those users that are listed in /etc/group. - * It won't list the user if the group is their primary (that is, it's in - * the GID field in /etc/passwd (or passwd->pw_gid as returned by getpwent). - * So, we first need to perform a primary group check. - */ - rc = pcmk__lookup_user(user, NULL, &gid); if (rc != pcmk_rc_ok) { - pcmk__notice("Rejecting remote client: could not find user '%s': %s", - user, pcmk_rc_str(rc)); + return -1; + } + + if (client->remote->auth_timeout != 0) { + g_source_remove(client->remote->auth_timeout); + client->remote->auth_timeout = 0; + } + + pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete); + pcmk__debug("Completed TLS handshake with remote client %s", + pcmk__client_name(client)); + + /* Now that the handshake is done, see if any client TLS certificate is + * close to its expiration date and log if so. If a TLS certificate is not + * in use, this function will just return so we don't need to check for the + * session type here. + */ + pcmk__tls_check_cert_expiration(client->remote->tls_session); + + // Require the client to authenticate within this time + client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, + remote_auth_timeout_cb, + client); + return 0; +} + +/*! + * \internal + * \brief Parse a remote client auth message + * + * This first validates that the message is a well-formed remote client + * authentication request and then extracts the username and password + * attributes. + * + * \param[in] msg Message from remote client + * \param[out] user Where to store username + * \param[out] password Where to store password + * \param[in] client_name Remote client name (for logging only) + * + * \return \c true if \p msg is a well-formed authentication request, or + * \c false otherwise. + * + * \note \p *user and \p *password are set to \c NULL on error. + */ +static bool +parse_auth_message(const xmlNode *msg, const char **user, const char **password, + const char *client_name) +{ + const char *op = NULL; + + if (msg == NULL) { + pcmk__warn("Rejecting remote client %s: Unrecognizable message", + client_name); return false; } - group = getgrnam(CRM_DAEMON_GROUP); - if (group == NULL) { - pcmk__err("Rejecting remote client: " CRM_DAEMON_GROUP " is not a " - "valid group"); + if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { + pcmk__warn("Rejecting remote client %s: Expected element " + "'" PCMK__XE_CIB_COMMAND "', got '%s'", client_name, + msg->name); return false; } - if (group->gr_gid == gid) { - return true; + op = pcmk__xe_get(msg, PCMK_XA_OP); + if (!pcmk__str_eq(op, "authenticate", pcmk__str_none)) { + pcmk__warn("Rejecting remote client %s: Expected " + PCMK_XA_OP "='authenticate', got " PCMK_XA_OP "='%s'", + client_name, op); + return false; } - /* If that didn't work, check if CRM_DAEMON_GROUP is a secondary group for - * the user. - */ - for (const char *const *member = (const char *const *) group->gr_mem; - *member != NULL; member++) { + *user = pcmk__xe_get(msg, PCMK_XA_USER); + *password = pcmk__xe_get(msg, PCMK__XA_PASSWORD); - if (pcmk__str_eq(user, *member, pcmk__str_none)) { - return true; - } + if ((*user == NULL) || (*password == NULL)) { + pcmk__warn("Rejecting remote client %s: No %s given", client_name, + ((*user == NULL)? "username" : "password")); + *user = NULL; + *password = NULL; + return false; } - pcmk__notice("Rejecting remote client: User %s is not a member of group %s", - user, CRM_DAEMON_GROUP); - return false; + return true; } #ifdef HAVE_PAM @@ -201,14 +251,15 @@ construct_pam_passwd(int num_msg, const struct pam_message **msg, * \internal * \brief Verify the username and password passed for a remote CIB connection * - * \param[in] user Username passed for remote CIB connection - * \param[in] passwd Password passed for remote CIB connection + * \param[in] user Username passed for remote CIB connection + * \param[in] passwd Password passed for remote CIB connection + * \param[in] client_name Remote client name (for logging only) * * \return \c true if the username and password are accepted, otherwise \c false * \note This function rejects all credentials when built without PAM support. */ static bool -authenticate_user(const char *user, const char *passwd) +authenticate_user(const char *user, const char *passwd, const char *client_name) { #ifdef HAVE_PAM int rc = 0; @@ -231,16 +282,17 @@ authenticate_user(const char *user, const char *passwd) rc = pam_start(pam_name, user, &p_conv, &pam_h); if (rc != PAM_SUCCESS) { - pcmk__warn("Rejecting remote client for user %s because PAM " - "initialization failed: %s", - user, pam_strerror(pam_h, rc)); + pcmk__warn("Rejecting remote client %s because PAM initialization " + "failed for user %s: %s", client_name, user, + pam_strerror(pam_h, rc)); goto bail; } // Check user credentials rc = pam_authenticate(pam_h, PAM_SILENT); if (rc != PAM_SUCCESS) { - pcmk__notice("Access for remote user %s denied: %s", user, + pcmk__notice("Rejecting remote client %s because PAM authentication " + "failed for user %s: %s", client_name, user, pam_strerror(pam_h, rc)); goto bail; } @@ -251,33 +303,34 @@ authenticate_user(const char *user, const char *passwd) */ rc = pam_get_item(pam_h, PAM_USER, &p_user); if (rc != PAM_SUCCESS) { - pcmk__warn("Rejecting remote client for user %s because PAM failed to " - "return final user name: %s", + pcmk__warn("Rejecting remote client %s because PAM failed to return " + "the authenticated user name for user %s: %s", client_name, user, pam_strerror(pam_h, rc)); goto bail; } + if (p_user == NULL) { - pcmk__warn("Rejecting remote client for user %s because PAM returned " - "no final user name", - user); + pcmk__warn("Rejecting remote client %s because PAM returned no " + "authenticated user name for user %s", client_name, user); goto bail; } // @TODO Why do we require these to match? if (!pcmk__str_eq(p_user, user, pcmk__str_none)) { - pcmk__warn("Rejecting remote client for user %s because PAM returned " - "different final user name %s", - user, p_user); + pcmk__warn("Rejecting remote client %s because PAM returned " + "non-matching authenticated user name %s for user %s", + client_name, user, p_user); goto bail; } // Check user account restrictions (expiration, etc.) rc = pam_acct_mgmt(pam_h, PAM_SILENT); if (rc != PAM_SUCCESS) { - pcmk__notice("Access for remote user %s denied: %s", user, - pam_strerror(pam_h, rc)); + pcmk__notice("Rejecting remote client %s because PAM denied access to " + "user %s", client_name, user, pam_strerror(pam_h, rc)); goto bail; } + pass = true; bail: @@ -285,95 +338,110 @@ authenticate_user(const char *user, const char *passwd) return pass; #else // @TODO Implement for non-PAM environments - pcmk__warn("Rejecting remote user %s because this build does not have PAM " - "support", - user); + pcmk__warn("Rejecting remote client %s (user %s) because this build does " + "not have PAM support", client_name, user); return false; #endif } +/*! + * \internal + * \brief Try to authenticate a remote client based on the message in its buffer + * + * Read the first message from the client's buffer. Validate that it's a well- + * formed remote client authentication request. Parse the username and password. + * Ensure that the user is a member of \c CRM_DAEMON_GROUP and use the + * credentials to authenticate the user via PAM (if available). Finally, on + * success, set the \c pcmk__client_authenticated flag, and send a reply + * informing the client of its success and its client ID. + * + * \param[in,out] client Remote CIB manager client + * + * \return \c true if \p client authenticated successfully, or \c false + * otherwise + */ static bool -cib_remote_auth(xmlNode * login) +based_remote_client_auth(pcmk__client_t *client) { + // @TODO If we want to debug/trace-log an auth message, strip password first const char *user = NULL; - const char *pass = NULL; - const char *tmp = NULL; + const char *password = NULL; + const char *client_name = pcmk__client_name(client); + xmlNode *msg = NULL; + xmlNode *cib_result = NULL; - if (login == NULL) { - return false; + msg = pcmk__remote_message_xml(client->remote); + if (!parse_auth_message(msg, &user, &password, client_name)) { + // Error already logged + goto done; } - if (!pcmk__xe_is(login, PCMK__XE_CIB_COMMAND)) { - pcmk__warn("Rejecting remote client: Unrecognizable message (element " - "'%s' not '" PCMK__XE_CIB_COMMAND "')", - login->name); - pcmk__log_xml_debug(login, "bad"); - return false; + if (!pcmk__is_user_in_group(user, CRM_DAEMON_GROUP)) { + pcmk__notice("Rejecting remote client %s: User %s is not a member of " + "group %s", client_name, user, CRM_DAEMON_GROUP); + goto done; } - tmp = pcmk__xe_get(login, PCMK_XA_OP); - if (!pcmk__str_eq(tmp, "authenticate", pcmk__str_casei)) { - pcmk__warn("Rejecting remote client: Unrecognizable message (operation " - "'%s' not 'authenticate')", - tmp); - pcmk__log_xml_debug(login, "bad"); - return false; + if (!authenticate_user(user, password, client_name)) { + // Error already logged + goto done; } - user = pcmk__xe_get(login, PCMK_XA_USER); - pass = pcmk__xe_get(login, PCMK__XA_PASSWORD); - if (!user || !pass) { - pcmk__warn("Rejecting remote client: No %s given", - ((user == NULL)? "username" : "password")); - pcmk__log_xml_debug(login, "bad"); - return false; + // @FIXME Should this be done regardless of whether auth succeeds? + if (client->remote->auth_timeout != 0) { + g_source_remove(client->remote->auth_timeout); + client->remote->auth_timeout = 0; } - pcmk__log_xml_debug(login, "auth"); + pcmk__set_client_flags(client, pcmk__client_authenticated); + + // @TODO What sets PCMK_XA_NAME? Added by commit 22832641. + client->name = pcmk__xe_get_copy(msg, PCMK_XA_NAME); + if (client->name == NULL) { + client->name = pcmk__str_copy(client->id); + } + + client->user = pcmk__str_copy(user); + + // Setting client->name may have changed the return value + client_name = pcmk__client_name(client); + + pcmk__notice("Remote connection accepted for authenticated user %s " + QB_XS " client %s", client->user, client_name); - return is_daemon_group_member(user) && authenticate_user(user, pass); + // Notify client of success and of its ID + cib_result = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT); + pcmk__xe_set(cib_result, PCMK__XA_CIB_OP, CRM_OP_REGISTER); + pcmk__xe_set(cib_result, PCMK__XA_CIB_CLIENTID, client->id); + + pcmk__remote_send_xml(client->remote, cib_result); + +done: + if (!pcmk__is_set(client->flags, pcmk__client_authenticated)) { + pcmk__log_xml_debug(msg, "rejected"); + } + + pcmk__xml_free(msg); + pcmk__xml_free(cib_result); + return pcmk__is_set(client->flags, pcmk__client_authenticated); } static void -cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) +based_remote_client_message(pcmk__client_t *client, xmlNode *msg) { int rc = pcmk_rc_ok; uint32_t call_options = cib_none; - const char *op = pcmk__xe_get(command, PCMK__XA_CIB_OP); + const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); - if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) { - pcmk__log_xml_trace(command, "bad"); + if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { + pcmk__debug("Unrecognizable remote data from client %s", + pcmk__client_name(client)); return; } - if (client->name == NULL) { - client->name = pcmk__str_copy(client->id); - } - - /* unset dangerous options */ - pcmk__xe_remove_attr(command, PCMK__XA_SRC); - pcmk__xe_remove_attr(command, PCMK__XA_CIB_HOST); - pcmk__xe_remove_attr(command, PCMK__XA_CIB_UPDATE); - - pcmk__xe_set(command, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(command, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__xe_set(command, PCMK__XA_CIB_CLIENTNAME, client->name); - pcmk__xe_set(command, PCMK__XA_CIB_USER, client->user); - - if (pcmk__xe_get(command, PCMK__XA_CIB_CALLID) == NULL) { - char *call_uuid = pcmk__generate_uuid(); - - /* fix the command */ - pcmk__xe_set(command, PCMK__XA_CIB_CALLID, call_uuid); - free(call_uuid); - } - - rc = pcmk__xe_get_flags(command, PCMK__XA_CIB_CALLOPT, &call_options, - cib_none); + rc = pcmk__xe_get_flags(msg, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request from remote client %s: " - "%s", client->name, pcmk_rc_str(rc)); - pcmk__log_xml_info(command, "bad-call-opts"); + pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); } /* Requests with cib_transaction set should not be sent to based directly @@ -382,26 +450,89 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) if (pcmk__is_set(call_options, cib_transaction)) { pcmk__warn("Ignoring CIB request from remote client %s with " "cib_transaction flag set outside of any transaction", - client->name); - pcmk__log_xml_info(command, "no-transaction"); + pcmk__client_name(client)); return; } - pcmk__log_xml_trace(command, "remote-request"); + /* Unset dangerous options. + * + * @TODO These were commented as "dangerous" with no explanation when this + * code was added by commit 8e08a242 (2007). We usually process whatever + * message we receive, taking a "submit a malformed request at your own + * risk" view. Our client API and CLI tools should not be able to submit a + * malformed request. A malicious user would have to send it directly, + * without our tools. If they have that level of access and are able to + * authenticate their request, then they can cause havoc regardless of + * whether we remove these "dangerous" attributes that shouldn't be present + * in a remote client's request. + * + * This seems overly paranoid, and seems like an arbitrary place to be + * paranoid. + * + * Best guesses about how they might be dangerous (or not): + * * PCMK_XA_SRC: This is mostly used for logging. Perhaps the CIB could get + * synced to the wrong host, or local client notifications could get sent + * on the wrong host? + * * PCMK__XA_CIB_HOST: It seems as if this should be allowed. Our client + * code actually sets this, apparently as a destination node. + * cib_remote_perform_op() takes a host argument and passes it to + * cib__create_op(), which sets it as PCMK__XA_CIB_HOST. + * * PCMK__XA_CIB_UPDATE: This can prevent CIB versions from being updated, + * because the update is treated as a sync. + */ + pcmk__xe_remove_attr(msg, PCMK__XA_SRC); + pcmk__xe_remove_attr(msg, PCMK__XA_CIB_HOST); + pcmk__xe_remove_attr(msg, PCMK__XA_CIB_UPDATE); - if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { - based_update_notify_flags(command, client); + // Similarly impossible via our API/tools. cib__create_op() sets this. + if (pcmk__xe_get(msg, PCMK__XA_CIB_CALLID) == NULL) { + char *call_uuid = pcmk__generate_uuid(); + + pcmk__xe_set(msg, PCMK__XA_CIB_CALLID, call_uuid); + free(call_uuid); } - based_process_request(command, true, client); + pcmk__xe_set(msg, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); + pcmk__xe_set(msg, PCMK__XA_CIB_USER, client->user); + + pcmk__log_xml_trace(msg, "remote-request"); + + if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { + based_update_notify_flags(msg, client); + + } else { + /* @TODO Should ipc_id be set to a nonzero value? client->request_id + * needs to match it if so, since pcmk__request_sync is set. + */ + pcmk__request_t request = { + .ipc_client = client, + .ipc_id = 0, + .ipc_flags = crm_ipc_flags_none, + .peer = NULL, + .xml = msg, + .call_options = call_options, + .result = PCMK__UNKNOWN_RESULT, + }; + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, return); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + based_handle_request(&request); + } } static int -cib_remote_msg(void *data) +based_remote_client_dispatch(void *data) { - xmlNode *command = NULL; + int rc = pcmk_rc_ok; + xmlNode *msg = NULL; pcmk__client_t *client = data; - int rc; const char *client_name = pcmk__client_name(client); pcmk__trace("Remote %s message received for client %s", @@ -410,36 +541,21 @@ cib_remote_msg(void *data) if ((PCMK__CLIENT_TYPE(client) == pcmk__client_tls) && !pcmk__is_set(client->flags, pcmk__client_tls_handshake_complete)) { - int rc = pcmk__read_handshake_data(client); - - if (rc == EAGAIN) { - /* No more data is available at the moment. Just return for now; - * we'll get invoked again once the client sends more. - */ - return 0; - } else if (rc != pcmk_rc_ok) { - return -1; - } + return based_read_handshake_data(client); + } - pcmk__debug("Completed TLS handshake with remote client %s", - client_name); - pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete); - if (client->remote->auth_timeout) { - g_source_remove(client->remote->auth_timeout); - } + rc = pcmk__remote_ready(client->remote, 0); + switch (rc) { + case pcmk_rc_ok: + break; - /* Now that the handshake is done, see if any client TLS certificate is - * close to its expiration date and log if so. If a TLS certificate is not - * in use, this function will just return so we don't need to check for the - * session type here. - */ - pcmk__tls_check_cert_expiration(client->remote->tls_session); + case ETIME: + // No message available to read + return 0; - // Require the client to authenticate within this time - client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, - remote_auth_timeout_cb, - client); - return 0; + default: + pcmk__trace("Error polling remote client: %s", pcmk_rc_str(rc)); + return -1; } rc = pcmk__read_available_remote_data(client->remote); @@ -448,57 +564,26 @@ cib_remote_msg(void *data) break; case EAGAIN: - /* We haven't read the whole message yet */ + // We haven't read the whole message yet return 0; default: - /* Error */ pcmk__trace("Error reading from remote client: %s", pcmk_rc_str(rc)); return -1; } - /* must pass auth before we will process anything else */ - if (!pcmk__is_set(client->flags, pcmk__client_authenticated)) { - xmlNode *reg; - const char *user = NULL; - - command = pcmk__remote_message_xml(client->remote); - if (!cib_remote_auth(command)) { - pcmk__xml_free(command); - return -1; - } - - pcmk__set_client_flags(client, pcmk__client_authenticated); - g_source_remove(client->remote->auth_timeout); - client->remote->auth_timeout = 0; - client->name = pcmk__xe_get_copy(command, PCMK_XA_NAME); + // Client must authenticate before we will process anything else + if (!pcmk__is_set(client->flags, pcmk__client_authenticated) + && !based_remote_client_auth(client)) { - user = pcmk__xe_get(command, PCMK_XA_USER); - if (user) { - client->user = pcmk__str_copy(user); - } - - pcmk__notice("Remote connection accepted for authenticated user %s " - QB_XS " client %s", - pcmk__s(user, ""), client_name); - - /* send ACK */ - reg = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT); - pcmk__xe_set(reg, PCMK__XA_CIB_OP, CRM_OP_REGISTER); - pcmk__xe_set(reg, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__remote_send_xml(client->remote, reg); - pcmk__xml_free(reg); - pcmk__xml_free(command); + return -1; } - command = pcmk__remote_message_xml(client->remote); - if (command != NULL) { - pcmk__trace("Remote message received from client %s", client_name); - cib_handle_remote_msg(client, command); - pcmk__xml_free(command); - } + msg = pcmk__remote_message_xml(client->remote); + based_remote_client_message(client, msg); + pcmk__xml_free(msg); return 0; } @@ -561,10 +646,15 @@ cib_remote_listen(void *user_data) pcmk__client_t *new_client = NULL; static struct mainloop_fd_callbacks remote_client_fd_callbacks = { - .dispatch = cib_remote_msg, + .dispatch = based_remote_client_dispatch, .destroy = based_remote_client_destroy, }; + if (based_shutting_down()) { + pcmk__info("Ignoring new remote connection during shutdown"); + return 0; + } + /* accept the connection */ laddr = sizeof(addr); memset(&addr, 0, sizeof(addr)); diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 61f84d4b331..881772a342a 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -10,14 +10,12 @@ #include #include // EOPNOTSUPP -#include #include // NULL #include // free #include // xmlNode #include // cib__* -#include // pcmk__client_t, pcmk__s, pcmk__xe_*, etc. #include // CRM_CHECK #include // pcmk_rc_* @@ -61,44 +59,70 @@ based_transaction_source_str(const pcmk__client_t *client, const char *origin) * \return Standard Pacemaker return code */ static int -process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, +process_transaction_requests(xmlNode *transaction, pcmk__client_t *client, const char *source) { - for (xmlNode *request = pcmk__xe_first_child(transaction, - PCMK__XE_CIB_COMMAND, NULL, - NULL); - request != NULL; - request = pcmk__xe_next(request, PCMK__XE_CIB_COMMAND)) { - - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); + for (xmlNode *xml = pcmk__xe_first_child(transaction, PCMK__XE_CIB_COMMAND, + NULL, NULL); + xml != NULL; xml = pcmk__xe_next(xml, PCMK__XE_CIB_COMMAND)) { + + int rc = pcmk_rc_ok; + uint32_t call_options = cib_none; + const char *op = pcmk__xe_get(xml, PCMK__XA_CIB_OP); + const char *host = pcmk__xe_get(xml, PCMK__XA_CIB_HOST); const cib__operation_t *operation = NULL; - int rc = cib__get_operation(op, &operation); + rc = pcmk__xe_get_flags(xml, 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)); + } + + rc = cib__get_operation(op, &operation); if (rc == pcmk_rc_ok) { - if (!pcmk__is_set(operation->flags, cib__op_attr_transaction) + if ((operation->type == cib__op_commit_transact) || (host != NULL)) { rc = EOPNOTSUPP; + } else { - /* Commit-transaction is a privileged operation. If we reached - * this point, the request came from a privileged connection. + /* @FIXME It would be better for this function to accept a + * pcmk__request_t argument and reuse it. In particular, the + * values below for ipc_id and ipc_flags are intended as sane + * placeholders. */ - rc = based_process_request(request, true, client); + pcmk__request_t request = { + .ipc_client = client, + .ipc_id = client->request_id, + .ipc_flags = crm_ipc_flags_none, + .peer = NULL, + .xml = xml, + .call_options = call_options, + .result = PCMK__UNKNOWN_RESULT, + }; + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, return 0); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + rc = based_handle_request(&request); } } if (rc != pcmk_rc_ok) { pcmk__err("Aborting CIB transaction for %s due to failed %s " - "request: %s", - source, op, pcmk_rc_str(rc)); - pcmk__log_xml_info(request, "Failed request"); + "request: %s", source, op, pcmk_rc_str(rc)); + pcmk__log_xml_info(xml, "failed"); return rc; } pcmk__trace("Applied %s request to transaction working CIB for %s", op, source); - pcmk__log_xml_trace(request, "Successful request"); + pcmk__log_xml_trace(xml, "successful"); } return pcmk_rc_ok; @@ -123,7 +147,7 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, * success, and for freeing it on failure. */ int -based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, +based_commit_transaction(xmlNode *transaction, pcmk__client_t *client, const char *origin, xmlNode **result_cib) { xmlNode *saved_cib = based_cib; diff --git a/daemons/based/based_transaction.h b/daemons/based/based_transaction.h index 19dc01ba529..7a062d26d05 100644 --- a/daemons/based/based_transaction.h +++ b/daemons/based/based_transaction.h @@ -1,5 +1,5 @@ /* - * Copyright 2023-2025 the Pacemaker project contributors + * Copyright 2023-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,7 @@ char *based_transaction_source_str(const pcmk__client_t *client, const char *origin); -int based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, +int based_commit_transaction(xmlNode *transaction, pcmk__client_t *client, const char *origin, xmlNode **result_cib); #endif // BASED_TRANSACTION__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 46206af7ffc..9667423b124 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -22,8 +22,6 @@ #include // xmlNode #include // CRM_CONFIG_DIR, CRM_DAEMON_USER -#include // pcmk__node_update, etc. -#include // PCMK__EXITC_ERROR, pcmk__err, etc. #include // crm_ipc_* #include // crm_log_* #include // mainloop_add_signal @@ -229,6 +227,7 @@ static void based_cleanup(void) { based_callbacks_cleanup(); + based_cluster_disconnect(); based_io_cleanup(); based_ipc_cleanup(); based_remote_cleanup(); @@ -239,48 +238,39 @@ based_cleanup(void) /*! * \internal - * \brief Clean up data structures and exit + * \brief Set an exit code and quit the main loop * - * \param[in] exit_status Exit code + * \param[in] ec Exit code */ void -based_terminate(crm_exit_t exit_status) +based_quit_main_loop(crm_exit_t ec) { - shutting_down = true; - based_cleanup(); - - if (exit_status != CRM_EX_OK) { - /* After calling g_main_loop_quit(), sources that have already been - * dispatched are still executed. On error, skip that and exit - * immediately after cleaning up data structures. - * - * @TODO Is this necessary? It would be nice to do the cleanup at the - * end of main(). If so, then one (complicated) option would be to keep - * track of all main loop sources and destroy them so that - * g_main_dispatch() ignores them. - */ - crm_exit(exit_status); + if (shutting_down) { + return; } - based_cluster_disconnect(); + shutting_down = true; + exit_code = ec; // There should be no way to get here without the main loop running CRM_CHECK((mainloop != NULL) && g_main_loop_is_running(mainloop), - crm_exit(exit_status)); + crm_exit(exit_code)); g_main_loop_quit(mainloop); } +/*! + * \internal + * \brief Quit the main loop and set the exit code to \c CRM_EX_OK + * + * \param[in] nsig Ignored + * + * \note This is a main loop signal handler function. + */ static void based_shutdown(int nsig) { - if (based_shutting_down()) { - // Already shutting down - return; - } - - shutting_down = true; - based_terminate(CRM_EX_OK); + based_quit_main_loop(CRM_EX_OK); } int @@ -338,7 +328,7 @@ main(int argc, char **argv) crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); pcmk__notice("Starting Pacemaker CIB manager"); - old_instance = crm_ipc_new(PCMK__SERVER_BASED_RO, 0); + old_instance = crm_ipc_new(PCMK__SERVER_BASED_RW, 0); if (old_instance == NULL) { /* crm_ipc_new() will have already logged an error message with * pcmk__err() @@ -380,7 +370,6 @@ main(int argc, char **argv) goto done; } - pcmk__cluster_init_node_caches(); based_callbacks_init(); based_io_init(); @@ -414,12 +403,12 @@ main(int argc, char **argv) pcmk__notice("Pacemaker CIB manager successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(processed_args); pcmk__free_arg_context(context); - based_cluster_disconnect(); based_cleanup(); pcmk__output_and_clear_error(&error, out); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index b4c53ded680..7e80a7287ef 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -36,6 +36,6 @@ void based_set_local_node_dc(bool value); bool based_shutting_down(void); bool based_stand_alone(void); -void based_terminate(crm_exit_t exit_status); +void based_quit_main_loop(crm_exit_t ec); #endif // PACEMAKER_BASED__H diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 0f916edf63b..f6e2740e3bf 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -16,8 +16,6 @@ #include #include -#include -#include #include @@ -117,9 +115,7 @@ do_shutdown_req(long long action, enum crmd_fsa_cause cause, msg = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_CRMD, NULL, CRM_SYSTEM_CRMD, CRM_OP_SHUTDOWN_REQ, NULL); - if (!pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg)) { - register_fsa_error(I_ERROR, msg_data); - } + pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg); pcmk__xml_free(msg); } @@ -241,9 +237,6 @@ crmd_exit(crm_exit_t exit_code) if (mloop != NULL) { GMainContext *ctx = g_main_loop_get_context(controld_globals.mainloop); - // Don't re-enter this block - controld_globals.mainloop = NULL; - // Try to drain the main loop before closing it for (int i = 0; (i < 10) && g_main_context_pending(ctx); i++) { g_main_context_dispatch(ctx); @@ -251,7 +244,6 @@ crmd_exit(crm_exit_t exit_code) // Exit the main loop and free it when we return from this dispatch g_main_loop_quit(mloop); - g_main_loop_unref(mloop); } throttle_fini(); diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c index e065fcc9ede..f5f9c5b0da1 100644 --- a/daemons/controld/controld_corosync.c +++ b/daemons/controld/controld_corosync.c @@ -18,7 +18,6 @@ #include #include -#include #include #include diff --git a/daemons/controld/controld_election.c b/daemons/controld/controld_election.c index 43d5f0f7e5f..12f33e41b3a 100644 --- a/daemons/controld/controld_election.c +++ b/daemons/controld/controld_election.c @@ -14,8 +14,6 @@ #include #include -#include -#include #include #include diff --git a/daemons/controld/controld_fsa.c b/daemons/controld/controld_fsa.c index b44ca99c55d..786446cc6cd 100644 --- a/daemons/controld/controld_fsa.c +++ b/daemons/controld/controld_fsa.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/daemons/controld/controld_fsa.h b/daemons/controld/controld_fsa.h index 04eeb8156cf..b1ef802f1cf 100644 --- a/daemons/controld/controld_fsa.h +++ b/daemons/controld/controld_fsa.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -16,7 +16,6 @@ # include # include # include -# include # include /*! States the controller can be in */ diff --git a/daemons/controld/controld_membership.c b/daemons/controld/controld_membership.c index fd33d0827e4..7f32d8df012 100644 --- a/daemons/controld/controld_membership.c +++ b/daemons/controld/controld_membership.c @@ -16,7 +16,6 @@ #include #include -#include #include diff --git a/daemons/controld/controld_messages.c b/daemons/controld/controld_messages.c index 319bd468d45..4690d31f4d6 100644 --- a/daemons/controld/controld_messages.c +++ b/daemons/controld/controld_messages.c @@ -18,7 +18,6 @@ #include #include -#include #include #include @@ -1130,18 +1129,14 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause) name = pcmk__xe_get(stored_msg, PCMK_XA_UNAME); if(cause == C_IPC_MESSAGE) { + pcmk__notice("Instructing peers to remove references to node %s/%d", + name, id); + msg = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_CRMD, NULL, CRM_SYSTEM_CRMD, CRM_OP_RM_NODE_CACHE, NULL); - if (!pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg)) { - pcmk__err("Could not instruct peers to remove references to " - "node %s/%u", - name, id); - } else { - pcmk__notice("Instructing peers to remove references to node " - "%s/%u", - name, id); - } + + pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg); pcmk__xml_free(msg); } else { diff --git a/daemons/controld/controld_remote_proxy.c b/daemons/controld/controld_remote_proxy.c index 261e5ac68db..5f6cf009a6b 100644 --- a/daemons/controld/controld_remote_proxy.c +++ b/daemons/controld/controld_remote_proxy.c @@ -21,7 +21,6 @@ #include // xmlNode #include // cib_* -#include // pcmk__xe_*, pcmk__xml_*, etc. #include // crm_ipc_* #include // crm_time_* #include // CRM_CHECK, crm_log_xml_explicit @@ -129,7 +128,7 @@ remote_proxy_dispatch(const char *buffer, ssize_t length, void *userdata) } flags = crm_ipc_buffer_flags(proxy->ipc); - if (flags & crm_ipc_proxied_relay_response) { + if (pcmk__is_set(flags, crm_ipc_proxied_relay_response)) { pcmk__trace("Passing response back to %.8s on %s: %.200s - request id: " "%d", proxy->session_id, proxy->node_name, buffer, proxy->last_request_id); @@ -189,6 +188,16 @@ remote_proxy_new(lrmd_t *lrmd, const char *node_name, const char *session_id, return NULL; } + /* @COMPAT Proxied clients from Pacemaker Remote nodes older than version + * 3.0.2 can connect using PCMK__SERVER_BASED_RO. Since we use + * PCMK__SERVER_BASED_RW for everything now, and since no local or same- + * versioned proxied clients can connect to PCMK__SERVER_BASED_RO, just map + * it to PCMK__SERVER_BASED_RW here. + */ + if (pcmk__str_eq(channel, PCMK__SERVER_BASED_RO, pcmk__str_none)) { + channel = PCMK__SERVER_BASED_RW; + } + proxy = pcmk__assert_alloc(1, sizeof(remote_proxy_t)); proxy->node_name = pcmk__str_copy(node_name); diff --git a/daemons/controld/controld_te_actions.c b/daemons/controld/controld_te_actions.c index befce6d4ff1..4d07fe5e57c 100644 --- a/daemons/controld/controld_te_actions.c +++ b/daemons/controld/controld_te_actions.c @@ -110,7 +110,6 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) const char *on_node = NULL; const char *router_node = NULL; - gboolean rc = TRUE; gboolean no_wait = FALSE; const pcmk__node_status_t *node = NULL; @@ -176,15 +175,9 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) node = pcmk__get_node(0, router_node, NULL, pcmk__node_search_cluster_member); - rc = pcmk__cluster_send_message(node, pcmk_ipc_controld, cmd); - free(counter); - pcmk__xml_free(cmd); - - if (rc == FALSE) { - pcmk__err("Action %d failed: send", action->id); - return ECOMM; + pcmk__cluster_send_message(node, pcmk_ipc_controld, cmd); - } else if (no_wait) { + if (no_wait) { te_action_confirmed(action, graph); } else { @@ -198,6 +191,8 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) te_start_action_timer(graph, action); } + free(counter); + pcmk__xml_free(cmd); return pcmk_rc_ok; } @@ -359,7 +354,6 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) xmlNode *cmd = NULL; xmlNode *rsc_op = NULL; - gboolean rc = TRUE; gboolean no_wait = FALSE; gboolean is_local = FALSE; @@ -430,7 +424,7 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) QB_XS " transition %s action %d", router_node, task_uuid, on_node, (no_wait? " without waiting" : ""), counter, action->id); - rc = pcmk__cluster_send_message(node, pcmk_ipc_execd, cmd); + pcmk__cluster_send_message(node, pcmk_ipc_execd, cmd); } free(counter); @@ -438,11 +432,7 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) pcmk__set_graph_action_flags(action, pcmk__graph_action_executed); - if (rc == FALSE) { - pcmk__err("Action %d failed: send", action->id); - return ECOMM; - - } else if (no_wait) { + if (no_wait) { /* Just mark confirmed. Don't bump the job count only to immediately * decrement it. */ diff --git a/daemons/controld/pacemaker-controld.c b/daemons/controld/pacemaker-controld.c index 58f17e004b1..f202981010c 100644 --- a/daemons/controld/pacemaker-controld.c +++ b/daemons/controld/pacemaker-controld.c @@ -199,6 +199,8 @@ main(int argc, char **argv) // Run mainloop controld_globals.mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(controld_globals.mainloop); + g_main_loop_unref(controld_globals.mainloop); + if (pcmk__is_set(controld_globals.fsa_input_register, R_STAYDOWN)) { pcmk__info("Inhibiting automated respawn"); exit_code = CRM_EX_FATAL; diff --git a/daemons/execd/cts-exec-helper.c b/daemons/execd/cts-exec-helper.c index 8508da6ef51..d13f045b10d 100644 --- a/daemons/execd/cts-exec-helper.c +++ b/daemons/execd/cts-exec-helper.c @@ -610,6 +610,7 @@ main(int argc, char **argv) pcmk__info("Starting"); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(processed_args); diff --git a/daemons/execd/execd_ipc.c b/daemons/execd/execd_ipc.c index c8821f51c9e..8d2f489de68 100644 --- a/daemons/execd/execd_ipc.c +++ b/daemons/execd/execd_ipc.c @@ -18,7 +18,6 @@ #include // xmlNode #include // qb_ipcs_connection_t -#include // pcmk__client_t, pcmk__find_client #include // crm_ipc_client_response #include // CRM_CHECK #include // pcmk_rc_*, pcmk_rc_str diff --git a/daemons/execd/execd_messages.c b/daemons/execd/execd_messages.c index 52384cf79be..f67d190e4b3 100644 --- a/daemons/execd/execd_messages.c +++ b/daemons/execd/execd_messages.c @@ -19,7 +19,6 @@ #include // QB_XS #include // CRM_OP_*, CRM_SYSTEM_LRMD -#include // pcmk__process_request, pcmk__xml_free #include // pcmk_exec_status, pcmk_rc_*, pcmk_rc_str #include // LRMD_OP_* diff --git a/daemons/execd/pacemaker-execd.c b/daemons/execd/pacemaker-execd.c index 26b780e8e9a..5b38e4fa934 100644 --- a/daemons/execd/pacemaker-execd.c +++ b/daemons/execd/pacemaker-execd.c @@ -439,6 +439,7 @@ main(int argc, char **argv) "accepting connections"); pcmk__notice("OCF resource agent search path is %s", PCMK__OCF_RA_PATH); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); /* should never get here */ exit_executor(); diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c index bc3c9a98838..36f71c0b844 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -19,7 +19,6 @@ #include // qb_ipcs_connection_t #include // QB_XS -#include #include // crm_ipc_flags #include // CRM_CHECK, CRM_LOG_ASSERT #include // pcmk_rc_*, pcmk_rc_str @@ -28,10 +27,8 @@ #include "pacemaker-execd.h" // lrmd_server_send_notify -static qb_ipcs_service_t *cib_ro = NULL; -static qb_ipcs_service_t *cib_rw = NULL; - static qb_ipcs_service_t *attrd_ipcs = NULL; +static qb_ipcs_service_t *based_ipcs = NULL; static qb_ipcs_service_t *controld_ipcs = NULL; static qb_ipcs_service_t *fencer_ipcs = NULL; static qb_ipcs_service_t *pacemakerd_ipcs = NULL; @@ -134,27 +131,21 @@ attrd_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) } static int32_t -fencer_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) +based_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { - return ipc_proxy_accept(c, uid, gid, "stonith-ng"); -} - -static int32_t -pacemakerd_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) -{ - return -EREMOTEIO; + return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RW); } static int32_t -cib_proxy_accept_rw(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) +fencer_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { - return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RW); + return ipc_proxy_accept(c, uid, gid, "stonith-ng"); } static int32_t -cib_proxy_accept_ro(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) +pacemakerd_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { - return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RO); + return -EREMOTEIO; } int @@ -439,6 +430,14 @@ static struct qb_ipcs_service_handlers attrd_proxy_callbacks = { .connection_destroyed = ipc_proxy_destroy }; +static struct qb_ipcs_service_handlers based_proxy_callbacks = { + .connection_accept = based_proxy_accept, + .connection_created = NULL, + .msg_process = ipc_proxy_dispatch, + .connection_closed = ipc_proxy_closed, + .connection_destroyed = ipc_proxy_destroy +}; + static struct qb_ipcs_service_handlers fencer_proxy_callbacks = { .connection_accept = fencer_proxy_accept, .connection_created = NULL, @@ -455,22 +454,6 @@ static struct qb_ipcs_service_handlers pacemakerd_proxy_callbacks = { .connection_destroyed = NULL }; -static struct qb_ipcs_service_handlers cib_proxy_callbacks_ro = { - .connection_accept = cib_proxy_accept_ro, - .connection_created = NULL, - .msg_process = ipc_proxy_dispatch, - .connection_closed = ipc_proxy_closed, - .connection_destroyed = ipc_proxy_destroy -}; - -static struct qb_ipcs_service_handlers cib_proxy_callbacks_rw = { - .connection_accept = cib_proxy_accept_rw, - .connection_created = NULL, - .msg_process = ipc_proxy_dispatch, - .connection_closed = ipc_proxy_closed, - .connection_destroyed = ipc_proxy_destroy -}; - void ipc_proxy_add_provider(pcmk__client_t *ipc_proxy) { @@ -519,9 +502,8 @@ ipc_proxy_init(void) { ipc_clients = pcmk__strkey_table(NULL, NULL); - pcmk__serve_based_ipc(&cib_ro, &cib_rw, &cib_proxy_callbacks_ro, - &cib_proxy_callbacks_rw); pcmk__serve_attrd_ipc(&attrd_ipcs, &attrd_proxy_callbacks); + pcmk__serve_based_ipc(&based_ipcs, &based_proxy_callbacks); pcmk__serve_controld_ipc(&controld_ipcs, &crmd_proxy_callbacks); if (controld_ipcs == NULL) { @@ -540,8 +522,7 @@ ipc_proxy_cleanup(void) g_clear_pointer(&ipc_clients, g_hash_table_destroy); g_clear_pointer(&attrd_ipcs, qb_ipcs_destroy); - g_clear_pointer(&cib_ro, qb_ipcs_destroy); - g_clear_pointer(&cib_rw, qb_ipcs_destroy); + g_clear_pointer(&based_ipcs, qb_ipcs_destroy); g_clear_pointer(&controld_ipcs, qb_ipcs_destroy); g_clear_pointer(&fencer_ipcs, qb_ipcs_destroy); g_clear_pointer(&pacemakerd_ipcs, qb_ipcs_destroy); diff --git a/daemons/execd/remoted_schemas.c b/daemons/execd/remoted_schemas.c index da4fece9267..191ed00b854 100644 --- a/daemons/execd/remoted_schemas.c +++ b/daemons/execd/remoted_schemas.c @@ -1,5 +1,5 @@ /* - * Copyright 2023-2025 the Pacemaker project contributors + * Copyright 2023-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -144,7 +144,7 @@ get_schema_files(void) _exit(CRM_EX_OSERR); } - rc = cib->cmds->signon(cib, crm_system_name, cib_query); + rc = cib->cmds->signon(cib, crm_system_name, cib_command); rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { pcmk__err("Could not connect to the CIB manager: %s", pcmk_rc_str(rc)); diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c index af28efc657c..d36547d76c0 100644 --- a/daemons/execd/remoted_tls.c +++ b/daemons/execd/remoted_tls.c @@ -23,7 +23,6 @@ #include // xmlNode #include // QB_XS -#include #include // CRM_CHECK #include // mainloop_* #include // pcmk_rc_str, pcmk_rc_* @@ -39,11 +38,12 @@ static int ssock = -1; /*! * \internal - * \brief Read (more) TLS handshake data from client + * \brief Read (more) TLS handshake data from a client * - * \param[in,out] client IPC client doing handshake + * \param[in,out] client IPC client * - * \return 0 on success or more data needed, -1 on error + * \retval 0 on success or more data needed + * \retval -1 on error */ static int remoted__read_handshake_data(pcmk__client_t *client) @@ -51,24 +51,27 @@ remoted__read_handshake_data(pcmk__client_t *client) int rc = pcmk__read_handshake_data(client); if (rc == EAGAIN) { - /* No more data is available at the moment. Just return for now; - * we'll get invoked again once the client sends more. + /* No more data is available at the moment. Just return for now; we'll + * get invoked again once the client sends more. */ return 0; - } else if (rc != pcmk_rc_ok) { + } + + if (rc != pcmk_rc_ok) { return -1; } - if (client->remote->auth_timeout) { + if (client->remote->auth_timeout != 0) { g_source_remove(client->remote->auth_timeout); + client->remote->auth_timeout = 0; } - client->remote->auth_timeout = 0; pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete); - pcmk__notice("Remote client connection accepted"); + pcmk__notice("Connection from remote client %s accepted", + pcmk__client_name(client)); /* Now that the handshake is done, see if any client TLS certificate is - * close to its expiration date and log if so. If a TLS certificate is not + * close to its expiration date and log if so. If a TLS certificate is not * in use, this function will just return so we don't need to check for the * session type here. */ diff --git a/daemons/fenced/cts-fence-helper.c b/daemons/fenced/cts-fence-helper.c index 2de750c3e1a..21cbb7174b8 100644 --- a/daemons/fenced/cts-fence-helper.c +++ b/daemons/fenced/cts-fence-helper.c @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -606,6 +605,7 @@ mainloop_tests(void) pcmk__info("Starting"); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); } static GOptionContext * diff --git a/daemons/fenced/fenced_cib.c b/daemons/fenced/fenced_cib.c index 803cad11e33..939518e5700 100644 --- a/daemons/fenced/fenced_cib.c +++ b/daemons/fenced/fenced_cib.c @@ -17,8 +17,6 @@ #include #include -#include - #include #include diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c index 9c8f0667b36..9be94a49231 100644 --- a/daemons/fenced/fenced_commands.c +++ b/daemons/fenced/fenced_commands.c @@ -28,7 +28,6 @@ #include #include -#include #include #include diff --git a/daemons/fenced/fenced_history.c b/daemons/fenced/fenced_history.c index 23864f7e2cf..968de2a5b21 100644 --- a/daemons/fenced/fenced_history.c +++ b/daemons/fenced/fenced_history.c @@ -18,7 +18,6 @@ #include #include -#include #include #include @@ -483,7 +482,8 @@ stonith_fence_history(xmlNode *msg, xmlNode **output, if (dev) { target = pcmk__xe_get(dev, PCMK__XA_ST_TARGET); - if (target && (options & st_opt_cs_nodeid)) { + + if ((target != NULL) && pcmk__is_set(options, st_opt_cs_nodeid)) { int nodeid; pcmk__node_status_t *node = NULL; @@ -497,14 +497,14 @@ stonith_fence_history(xmlNode *msg, xmlNode **output, } } - if (options & st_opt_cleanup) { + if (pcmk__is_set(options, st_opt_cleanup)) { const char *call_id = pcmk__xe_get(msg, PCMK__XA_ST_CALLID); pcmk__trace("Cleaning up operations on %s in %p", target, stonith_remote_op_list); stonith_fence_history_cleanup(target, (call_id != NULL)); - } else if (options & st_opt_broadcast) { + } else if (pcmk__is_set(options, st_opt_broadcast)) { /* there is no clear sign atm for when a history sync is done so send a notification for anything that smells like history-sync diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c index 9d59d054e81..496a8ede76a 100644 --- a/daemons/fenced/fenced_remote.c +++ b/daemons/fenced/fenced_remote.c @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -1246,7 +1245,7 @@ create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer) op->replies_expected, pcmk__plural_alt(op->replies_expected, "reply", "replies")); - if (op->call_options & st_opt_cs_nodeid) { + if (pcmk__is_set(op->call_options, st_opt_cs_nodeid)) { int nodeid; pcmk__node_status_t *node = NULL; @@ -1397,7 +1396,7 @@ static peer_device_info_t * find_best_peer(const char *device, remote_fencing_op_t * op, enum find_best_peer_options options) { GList *iter = NULL; - gboolean verified_devices_only = (options & FIND_PEER_VERIFIED_ONLY) ? TRUE : FALSE; + bool verified_devices_only = pcmk__is_set(options, FIND_PEER_VERIFIED_ONLY); if ((device == NULL) && pcmk__is_set(op->call_options, st_opt_topology)) { return NULL; @@ -1410,10 +1409,16 @@ find_best_peer(const char *device, remote_fencing_op_t * op, enum find_best_peer "%x", peer->host, op->target, peer->ndevices, pcmk__plural_s(peer->ndevices), peer->tried, options); - if ((options & FIND_PEER_SKIP_TARGET) && pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + + if (pcmk__is_set(options, FIND_PEER_SKIP_TARGET) + && pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + continue; } - if ((options & FIND_PEER_TARGET_ONLY) && !pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + + if (pcmk__is_set(options, FIND_PEER_TARGET_ONLY) + && !pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + continue; } @@ -1723,13 +1728,15 @@ report_timeout_period(remote_fencing_op_t * op, int op_timeout) const char *client_id = NULL; const char *call_id = NULL; - if (op->call_options & st_opt_sync_call) { + if (pcmk__is_set(op->call_options, st_opt_sync_call)) { /* There is no reason to report the timeout for a synchronous call. It * is impossible to use the reported timeout to do anything when the client * is blocking for the response. This update is only important for * async calls that require a callback to report the results in. */ return; - } else if (!op->request) { + } + + if (op->request == NULL) { return; } diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c index 531d0738c10..be249badf9e 100644 --- a/daemons/fenced/pacemaker-fenced.c +++ b/daemons/fenced/pacemaker-fenced.c @@ -446,6 +446,7 @@ main(int argc, char **argv) pcmk__notice("Pacemaker fencer successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(processed_args); diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c index 286ff374ae4..48e9b87ecbc 100644 --- a/daemons/pacemakerd/pacemakerd.c +++ b/daemons/pacemakerd/pacemakerd.c @@ -477,10 +477,11 @@ main(int argc, char **argv) pcmk__notice("Pacemaker daemon successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); + pacemakerd_ipc_cleanup(); pacemakerd_unregister_handlers(); - g_main_loop_unref(mainloop); #if SUPPORT_COROSYNC cluster_disconnect_cfg(); #endif diff --git a/daemons/schedulerd/pacemaker-schedulerd.c b/daemons/schedulerd/pacemaker-schedulerd.c index e3ce0ca7c4a..67d6bbf8702 100644 --- a/daemons/schedulerd/pacemaker-schedulerd.c +++ b/daemons/schedulerd/pacemaker-schedulerd.c @@ -164,6 +164,7 @@ main(int argc, char **argv) pcmk__notice("Pacemaker scheduler successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(options.remainder); diff --git a/include/crm/cib.h b/include/crm/cib.h index 2c180e14754..6c25b98a96f 100644 --- a/include/crm/cib.h +++ b/include/crm/cib.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,13 +8,13 @@ */ #ifndef PCMK__CRM_CIB__H -# define PCMK__CRM_CIB__H +#define PCMK__CRM_CIB__H -# include // gboolean -# include -# include -# include -# include +#include // gboolean + +// cib.h is a wrapper for the following headers +#include +#include #ifdef __cplusplus extern "C" { @@ -27,7 +27,7 @@ extern "C" { */ // Use pcmk__compare_versions() for doing comparisons -# define CIB_FEATURE_SET "2.0" +#define CIB_FEATURE_SET "2.0" /* Core functions */ @@ -46,7 +46,7 @@ void cib_free_notify(cib_t *cib); void cib_free_callbacks(cib_t *cib); // NOTE: sbd (as of at least 1.5.2) uses this -void cib_delete(cib_t * cib); +void cib_delete(cib_t *cib); void cib_dump_pending_callbacks(void); int num_cib_op_callbacks(void); @@ -62,4 +62,4 @@ void remove_cib_op_callback(int call_id, gboolean all_callbacks); #include #endif -#endif +#endif // PCMK__CRM_CIB__H diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index 41fd6094b87..2cbc9e77d50 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -16,9 +16,6 @@ #include // gboolean, GList #include // xmlNode -#include -#include - #ifdef __cplusplus extern "C" { #endif @@ -41,11 +38,18 @@ enum cib_state { cib_connected_command, // NOTE: sbd (as of at least 1.5.2) uses this value + //! \deprecated Look for \c cib_connected_command instead cib_connected_query, cib_disconnected }; +/*! + * \deprecated Do not use + * + * \note Pass \c cib_command to cib_api_operations_t:signon as long as + * that function and argument exist. + */ enum cib_conn_type { cib_command, @@ -53,8 +57,6 @@ enum cib_conn_type { cib_query, cib_no_connection, - - //! \deprecated Use \c cib_command instead cib_command_nonblocking, }; @@ -138,7 +140,17 @@ typedef struct cib_s cib_t; */ typedef struct cib_api_operations_s { // NOTE: sbd (as of at least 1.5.2) uses this - // @COMPAT At compatibility break, drop name (always use crm_system_name) + /* @COMPAT At a compatibility break, drop name (always use crm_system_name) + * and type (always use cib_command -- cib_file and cib_remote already do + * this). + */ + /*! + * \brief Sign on a client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Ignored + */ int (*signon) (cib_t *cib, const char *name, enum cib_conn_type type); // NOTE: sbd (as of at least 1.5.2) uses this @@ -169,6 +181,7 @@ typedef struct cib_api_operations_s { int (*query) (cib_t *cib, const char *section, xmlNode **output_data, int call_options); + //! \deprecated This method will be removed and should not be used int (*query_from) (cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 98a20b1e49f..d2138a667cf 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -21,65 +21,34 @@ extern "C" { #endif // Request types for CIB manager IPC/CPG -#define PCMK__CIB_REQUEST_SECONDARY "cib_slave" -#define PCMK__CIB_REQUEST_PRIMARY "cib_master" -#define PCMK__CIB_REQUEST_SYNC "cib_sync" -#define PCMK__CIB_REQUEST_IS_PRIMARY "cib_ismaster" -#define PCMK__CIB_REQUEST_BUMP "cib_bump" -#define PCMK__CIB_REQUEST_QUERY "cib_query" -#define PCMK__CIB_REQUEST_CREATE "cib_create" -#define PCMK__CIB_REQUEST_MODIFY "cib_modify" -#define PCMK__CIB_REQUEST_DELETE "cib_delete" -#define PCMK__CIB_REQUEST_ERASE "cib_erase" -#define PCMK__CIB_REQUEST_REPLACE "cib_replace" -#define PCMK__CIB_REQUEST_APPLY_PATCH "cib_apply_diff" -#define PCMK__CIB_REQUEST_UPGRADE "cib_upgrade" -#define PCMK__CIB_REQUEST_ABS_DELETE "cib_delete_alt" -#define PCMK__CIB_REQUEST_NOOP "noop" -#define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req" +#define PCMK__CIB_REQUEST_APPLY_PATCH "cib_apply_diff" +#define PCMK__CIB_REQUEST_BUMP "cib_bump" #define PCMK__CIB_REQUEST_COMMIT_TRANSACT "cib_commit_transact" -#define PCMK__CIB_REQUEST_SCHEMAS "cib_schemas" - -/*! - * \internal - * \brief Flags for CIB operation attributes - */ -enum cib__op_attr { - //! No special attributes - cib__op_attr_none = 0, - - //! May modify state (of the CIB itself or of the CIB manager) - cib__op_attr_modifies = (UINT32_C(1) << 1), - - //! Requires privileges - cib__op_attr_privileged = (UINT32_C(1) << 2), - - //! Must only be processed locally - cib__op_attr_local = (UINT32_C(1) << 3), - - //! Replaces CIB - cib__op_attr_replaces = (UINT32_C(1) << 4), - - //! Writes to disk on success - cib__op_attr_writes_through = (UINT32_C(1) << 5), - - //! Supported in a transaction - cib__op_attr_transaction = (UINT32_C(1) << 6), -}; +#define PCMK__CIB_REQUEST_CREATE "cib_create" +#define PCMK__CIB_REQUEST_DELETE "cib_delete" +#define PCMK__CIB_REQUEST_ERASE "cib_erase" +#define PCMK__CIB_REQUEST_MODIFY "cib_modify" +#define PCMK__CIB_REQUEST_NOOP "noop" +#define PCMK__CIB_REQUEST_PRIMARY "cib_master" +#define PCMK__CIB_REQUEST_QUERY "cib_query" +#define PCMK__CIB_REQUEST_REPLACE "cib_replace" +#define PCMK__CIB_REQUEST_SCHEMAS "cib_schemas" +#define PCMK__CIB_REQUEST_SECONDARY "cib_slave" +#define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req" +#define PCMK__CIB_REQUEST_SYNC "cib_sync" +#define PCMK__CIB_REQUEST_UPGRADE "cib_upgrade" /*! * \internal * \brief Types of CIB operations */ enum cib__op_type { - cib__op_abs_delete, cib__op_apply_patch, cib__op_bump, cib__op_commit_transact, cib__op_create, cib__op_delete, cib__op_erase, - cib__op_is_primary, cib__op_modify, cib__op_noop, cib__op_ping, @@ -108,7 +77,7 @@ typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode **cib, xmlNode **output); typedef struct { const char *name; enum cib__op_type type; - uint32_t flags; //!< Group of enum cib__op_attr flags + bool modifies_cib; } cib__operation_t; typedef struct { diff --git a/include/crm/cib/util.h b/include/crm/cib/util.h index 8da9b4d0074..e98f0a0d423 100644 --- a/include/crm/cib/util.h +++ b/include/crm/cib/util.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2024 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,10 +8,11 @@ */ #ifndef PCMK__CRM_CIB_UTIL__H -# define PCMK__CRM_CIB_UTIL__H +#define PCMK__CRM_CIB_UTIL__H #include // gboolean #include // xmlNode + #include // cib_t #ifdef __cplusplus @@ -58,4 +59,4 @@ int cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, } #endif -#endif +#endif // PCMK__CRM_CIB_UTIL__H diff --git a/include/crm/cib_compat.h b/include/crm/cib_compat.h index dd0d6dbfc2d..8aab578e85f 100644 --- a/include/crm/cib_compat.h +++ b/include/crm/cib_compat.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,7 +8,7 @@ */ #ifndef PCMK__CRM_CIB_COMPAT__H -# define PCMK__CRM_CIB_COMPAT__H +#define PCMK__CRM_CIB_COMPAT__H #include // cib_t diff --git a/include/crm/cluster/election_internal.h b/include/crm/cluster/election_internal.h index 4825f8e103e..93cecf943e3 100644 --- a/include/crm/cluster/election_internal.h +++ b/include/crm/cluster/election_internal.h @@ -7,6 +7,11 @@ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ +#ifndef PCMK__INCLUDED_CRM_CLUSTER_INTERNAL_H +#error "Include instead of " \ + "directly" +#endif + #ifndef PCMK__CRM_CLUSTER_ELECTION_INTERNAL__H #define PCMK__CRM_CLUSTER_ELECTION_INTERNAL__H diff --git a/include/crm/cluster/internal.h b/include/crm/cluster/internal.h index aed702eb9a1..9333f7a65bb 100644 --- a/include/crm/cluster/internal.h +++ b/include/crm/cluster/internal.h @@ -10,6 +10,8 @@ #ifndef PCMK__CRM_CLUSTER_INTERNAL__H #define PCMK__CRM_CLUSTER_INTERNAL__H +#define PCMK__INCLUDED_CRM_CLUSTER_INTERNAL_H + #include #include // uint32_t, uint64_t @@ -18,11 +20,14 @@ #include // enum crm_ipc_server #include +#include #if SUPPORT_COROSYNC #include // cpg_name, cpg_handle_t #endif +#undef PCMK__INCLUDED_CRM_CLUSTER_INTERNAL_H + #ifdef __cplusplus extern "C" { #endif @@ -243,7 +248,7 @@ void pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long, gboolean), void (*destroy)(void *)); -bool pcmk__cluster_send_message(const pcmk__node_status_t *node, +void pcmk__cluster_send_message(const pcmk__node_status_t *node, enum pcmk_ipc_server service, const xmlNode *data); diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h index b48a702993a..0b49ab6d517 100644 --- a/include/crm/common/ipc_internal.h +++ b/include/crm/common/ipc_internal.h @@ -250,10 +250,8 @@ void pcmk__serve_pacemakerd_ipc(qb_ipcs_service_t **ipcs, void pcmk__serve_schedulerd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); -void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, - qb_ipcs_service_t **ipcs_rw, - struct qb_ipcs_service_handlers *ro_cb, - struct qb_ipcs_service_handlers *rw_cb); +void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs, + struct qb_ipcs_service_handlers *cb); static inline const char * pcmk__ipc_sys_name(const char *ipc_name, const char *fallback) diff --git a/include/crm/common/schemas_internal.h b/include/crm/common/schemas_internal.h index fa4c1039099..1365bb609e5 100644 --- a/include/crm/common/schemas_internal.h +++ b/include/crm/common/schemas_internal.h @@ -40,7 +40,7 @@ bool pcmk__validate_xml(xmlNode *xml, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context); bool pcmk__configured_schema_validates(xmlNode *xml); int pcmk__update_schema(xmlNode **xml, const char *max_schema_name, - bool transform, bool to_logs); + bool to_logs); void pcmk__warn_if_schema_deprecated(const char *schema); int pcmk__update_configured_schema(xmlNode **xml, bool to_logs); diff --git a/include/crm/common/strings_internal.h b/include/crm/common/strings_internal.h index 94a93d7d118..36efdbf1044 100644 --- a/include/crm/common/strings_internal.h +++ b/include/crm/common/strings_internal.h @@ -47,8 +47,8 @@ int pcmk__uint_from_hash(GHashTable *table, const char *key, unsigned int default_val, unsigned int *result); void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator); -int pcmk__compress(const char *data, unsigned int length, unsigned int max, - char **result, unsigned int *result_len); +int pcmk__compress(const char *data, unsigned int length, char **result, + unsigned int *result_len); int pcmk__scan_ll(const char *text, long long *result, long long default_value); int pcmk__scan_min_int(const char *text, int *result, int minimum); diff --git a/include/crm/common/utils_internal.h b/include/crm/common/utils_internal.h index 82f0a5acb7a..09bb505c86a 100644 --- a/include/crm/common/utils_internal.h +++ b/include/crm/common/utils_internal.h @@ -25,9 +25,12 @@ extern "C" { #define PCMK__NELEM(a) ((int) (sizeof(a)/sizeof(a[0])) ) int pcmk__compare_versions(const char *version1, const char *version2); + int pcmk__daemon_user(uid_t *uid, gid_t *gid); -char *pcmk__generate_uuid(void); +bool pcmk__is_user_in_group(const char *user, const char *group); int pcmk__lookup_user(const char *name, uid_t *uid, gid_t *gid); + +char *pcmk__generate_uuid(void); void pcmk__panic(const char *reason); pid_t pcmk__locate_sbd(void); void pcmk__sleep_ms(unsigned int ms); diff --git a/lib/cib/cib_attrs.c b/lib/cib/cib_attrs.c index 1a2de1c5c17..a8655e6ca16 100644 --- a/lib/cib/cib_attrs.c +++ b/lib/cib/cib_attrs.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,22 +9,23 @@ #include -#include - -#include - +#include // EINVAL, ENOMSG, ENOTUNIQ, ENXIO #include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include +#include // NULL +#include // free +#include // strdup + +#include // g_*, gboolean, GString, TRUE, FALSE, etc. +#include // xmlNode + +#include // pcmk__str_*, etc. +#include // CRM_CHECK +#include // crm_create_nvpair_xml +#include // PCMK_META_*, PCMK_VALUE_* +#include // pcmk_rc_*, pcmk_ok, CRM_EX_OK, etc. +#include // PCMK_XA_*, PCMK_XE_* +#include // cib_*, *_delegate, query_node_uuid +#include // cib__*, PCMK___CIB_* static pcmk__output_t * new_output_object(const char *ty) diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 81658f69b6f..cfe7d0948fa 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.c @@ -8,22 +8,27 @@ */ #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include +#include // errno, EEXIST, EINVAL, ETIME, etc. +#include // getpwuid, struct passwd +#include +#include // NULL +#include // calloc, free, getenv, setenv, unsetenv +#include // strcmp, strerror +#include // mkdir +#include // geteuid + +#include // gboolean, g_*, G_SOURCE_CONTINUE, etc. +#include // xmlNode + +#include // cib_* +#include // cib__*, PCMK__CIB_*, etc. +#include // pcmk__str_*, pcmk__trace, etc. +#include // CRM_CHECK +#include // pcmk_rc_*, pcmk_strerror, pcmk_ok, etc. +#include // PCMK_XA_VERSION +#include // CRM_CONFIG_DIR, CRM_DAEMON_USER +#include // CRM_OP_PING static GHashTable *cib_op_callback_table = NULL; @@ -225,6 +230,7 @@ cib_client_register_callback(cib_t *cib, int call_id, int timeout, callback_name, callback, NULL); } +// @COMPAT Deprecated static int cib_client_noop(cib_t * cib, int call_options) { @@ -242,15 +248,16 @@ cib_client_ping(cib_t * cib, xmlNode ** output_data, int call_options) static int cib_client_query(cib_t * cib, const char *section, xmlNode ** output_data, int call_options) { - return cib->cmds->query_from(cib, NULL, section, output_data, call_options); + return cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, NULL, section, NULL, + output_data, call_options, cib->user); } +// @COMPAT Deprecated static int cib_client_query_from(cib_t * cib, const char *host, const char *section, xmlNode ** output_data, int call_options) { - return cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, host, section, NULL, - output_data, call_options, cib->user); + return cib->cmds->query(cib, section, output_data, call_options); } static int @@ -281,7 +288,7 @@ cib_client_upgrade(cib_t * cib, int call_options) NULL, call_options, cib->user); } -// @COMPAT cib_api_operations_t:sync is deprecated since 3.1.0 +// @COMPAT Deprecated static int cib_client_sync(cib_t * cib, const char *section, int call_options) { @@ -618,7 +625,6 @@ cib_new_variant(void) new_cib->call_id = 1; new_cib->variant = cib_undefined; - new_cib->type = cib_no_connection; new_cib->state = cib_disconnected; new_cib->variant_opaque = NULL; new_cib->notify_list = NULL; @@ -636,12 +642,8 @@ cib_new_variant(void) new_cib->cmds->register_callback = cib_client_register_callback; new_cib->cmds->register_callback_full = cib_client_register_callback_full; - new_cib->cmds->noop = cib_client_noop; // Deprecated method new_cib->cmds->ping = cib_client_ping; new_cib->cmds->query = cib_client_query; - new_cib->cmds->sync = cib_client_sync; - - new_cib->cmds->query_from = cib_client_query_from; new_cib->cmds->sync_from = cib_client_sync_from; new_cib->cmds->set_primary = set_primary; @@ -663,6 +665,11 @@ cib_new_variant(void) new_cib->cmds->fetch_schemas = cib_client_fetch_schemas; + // @COMPAT Deprecated methods + new_cib->cmds->noop = cib_client_noop; + new_cib->cmds->sync = cib_client_sync; + new_cib->cmds->query_from = cib_client_query_from; + return new_cib; } diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index da7e6c1b1b8..0c8ecc3ce8b 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -9,24 +9,29 @@ */ #include -#include -#include + +#include // errno, EINVAL, ENOENT, ENXIO, etc. #include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include +#include // NULL +#include // uint32_t, UINT32_C +#include // fprintf, rename, stderr +#include // calloc, free, getenv, mkstemp +#include // strcmp, strdup, strerror, strrchr +#include // fchmod, stat, umask, S_* +#include // gid_t, uid_t +#include // chown, close, fchown, link, unlink, etc. + +#include // gpointer, g_* +#include // xmlNode +#include // LOG_TRACE + +#include // cib_* +#include // cib__* +#include // pcmk__err, pcmk__xml_*, etc. +#include // CRM_CHECK +#include // pcmk_rc_*, pcmk_err_*, etc. +#include // PCMK_XA_*, PCMK_XE_* +#include // CRM_CONFIG_DIR, CRM_DAEMON_USER #define CIB_SERIES "cib" #define CIB_SERIES_MAX 100 @@ -145,7 +150,6 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); bool changed = false; - bool read_only = false; xmlNode *result_cib = NULL; xmlNode *cib_diff = NULL; xmlNode *local_output = NULL; @@ -166,9 +170,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); } - read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies); - - if (read_only) { + if (!operation->modifies_cib) { rc = cib__perform_op_ro(op_function, request, &private->cib_xml, &local_output); } else { @@ -188,7 +190,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) // Show validation errors to stderr pcmk__validate_xml(result_cib, NULL, NULL); - } else if ((rc == pcmk_rc_ok) && !read_only) { + } else if ((rc == pcmk_rc_ok) && operation->modifies_cib) { if (result_cib != private->cib_xml) { pcmk__xml_free(private->cib_xml); private->cib_xml = result_cib; @@ -521,12 +523,22 @@ load_file_cib(const char *filename, xmlNode **output) return pcmk_ok; } +/*! + * \internal + * \brief Sign on a native client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Ignored + */ static int file_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; file_opaque_t *private = cib->variant_opaque; + name = pcmk__s(crm_system_name, "client"); + if (private->filename == NULL) { rc = -EINVAL; } else { @@ -535,15 +547,13 @@ file_signon(cib_t *cib, const char *name, enum cib_conn_type type) if (rc == pcmk_ok) { pcmk__debug("Opened connection to local file '%s' for %s", - private->filename, pcmk__s(name, "client")); + private->filename, name); cib->state = cib_connected_command; - cib->type = cib_command; register_client(cib); } else { pcmk__info("Connection to local file '%s' for %s (client %s) failed: " - "%s", - private->filename, pcmk__s(name, "client"), private->id, + "%s", private->filename, name, private->id, pcmk_strerror(rc)); } return rc; @@ -648,7 +658,6 @@ file_signoff(cib_t *cib) pcmk__debug("Disconnecting from the CIB manager"); cib->state = cib_disconnected; - cib->type = cib_no_connection; unregister_client(cib); cib->cmds->end_transaction(cib, false, cib_none); diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 39524166492..7e47529992b 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -10,22 +10,23 @@ #include -#include -#include -#include +#include // ECOMM, EINVAL, ENOMSG, ENOTCONN, etc. #include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include +#include // NULL +#include // calloc, free +#include // ssize_t + +#include // gpointer, g_*, G_*, FALSE, TRUE +#include // xmlNode + +#include // cib_*, remove_cib_op_callback +#include // cib__*, PCMK__CIB_REQUEST_QUERY +#include // pcmk__err, pcmk__xml_*, etc. +#include // crm_ipc_* +#include // CRM_CHECK, crm_log_xml_explicit +#include // mainloop_* +#include // pcmk_rc_ok, pcmk_ok, pcmk_strerror, etc. +#include // CRM_OP_REGISTER, crm_system_name typedef struct { char *token; @@ -76,7 +77,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, return -EINVAL; } - if (call_options & cib_sync_call) { + if (pcmk__is_set(call_options, cib_sync_call)) { pcmk__set_ipc_flags(ipc_flags, "client", crm_ipc_client_response); } @@ -104,9 +105,9 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, goto done; } - /* The only reason we can receive an ACK here is if dispatch_common -> + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. + * based_ipc_dispatch does not return ACK, unlike other daemons. */ if (pcmk__xe_is(op_reply, PCMK__XE_ACK) && ack_is_failure(op_reply)) { rc = -EPROTO; @@ -115,7 +116,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, pcmk__log_xml_trace(op_reply, "Reply"); - if (!(call_options & cib_sync_call)) { + if (!pcmk__is_set(call_options, cib_sync_call)) { pcmk__trace("Async call, returning %d", cib->call_id); CRM_CHECK(cib->call_id != 0, rc = -ENOMSG; goto done); @@ -133,8 +134,11 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, rc = -EPROTO; } - if (output_data == NULL || (call_options & cib_discard_reply)) { + if ((output_data == NULL) + || pcmk__is_set(call_options, cib_discard_reply)) { + pcmk__trace("Discarding reply"); + } else { *output_data = pcmk__xml_copy(NULL, tmp); } @@ -273,115 +277,98 @@ cib_native_signoff(cib_t *cib) cib->cmds->end_transaction(cib, false, cib_none); cib->state = cib_disconnected; - cib->type = cib_no_connection; return pcmk_ok; } +/*! + * \internal + * \brief Sign on a native client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Ignored + */ static int cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; - const char *channel = NULL; cib_native_opaque_t *native = cib->variant_opaque; xmlNode *hello = NULL; + xmlNode *reply = NULL; + const char *msg_type = NULL; struct ipc_client_callbacks cib_callbacks = { .dispatch = cib_native_dispatch_internal, - .destroy = cib_native_destroy + .destroy = cib_native_destroy, }; - if (name == NULL) { - name = pcmk__s(crm_system_name, "client"); - } + name = pcmk__s(crm_system_name, "client"); cib->call_timeout = PCMK__IPC_TIMEOUT; - switch (type) { - case cib_command: - case cib_command_nonblocking: - // @COMPAT cib_command_nonblocking is deprecated since 3.0.2 - cib->state = cib_connected_command; - channel = PCMK__SERVER_BASED_RW; - break; - - case cib_query: - cib->state = cib_connected_query; - channel = PCMK__SERVER_BASED_RO; - break; - - default: - return -ENOTCONN; - } - - pcmk__trace("Connecting %s channel", channel); - - native->source = mainloop_add_ipc_client(channel, G_PRIORITY_HIGH, 0, cib, + native->source = mainloop_add_ipc_client(PCMK__SERVER_BASED_RW, + G_PRIORITY_HIGH, 0, cib, &cib_callbacks); native->ipc = mainloop_get_ipc_client(native->source); - if (rc != pcmk_ok || native->ipc == NULL || !crm_ipc_connected(native->ipc)) { + if ((native->ipc == NULL) || !crm_ipc_connected(native->ipc)) { pcmk__info("Could not connect to CIB manager for %s", name); rc = -ENOTCONN; + goto done; } - if (rc == pcmk_ok) { - rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL, - cib_sync_call, NULL, name, &hello); - rc = pcmk_rc2legacy(rc); + rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL, cib_sync_call, + NULL, name, &hello); + rc = pcmk_rc2legacy(rc); + if (rc != pcmk_ok) { + goto done; } - if (rc == pcmk_ok) { - xmlNode *reply = NULL; - const char *msg_type = NULL; - - if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, - &reply) <= 0) { - rc = -ECOMM; - goto done; - } - - /* The only reason we can receive an ACK here is if dispatch_common -> - * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. - */ - if (pcmk__xe_is(reply, PCMK__XE_ACK) && ack_is_failure(reply)) { - rc = -EPROTO; - pcmk__xml_free(reply); - goto done; - } - - msg_type = pcmk__xe_get(reply, PCMK__XA_CIB_OP); + if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, + &reply) <= 0) { + rc = -ECOMM; + goto done; + } - pcmk__log_xml_trace(reply, "reg-reply"); + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> + * pcmk__client_data2xml processed something that's not valid XML. + * based_ipc_dispatch does not return ACK, unlike other daemons. + */ + if (pcmk__xe_is(reply, PCMK__XE_ACK) && ack_is_failure(reply)) { + rc = -EPROTO; + goto done; + } - if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { - pcmk__info("Reply to CIB registration message has unknown type " - "'%s'", - msg_type); - rc = -EPROTO; + pcmk__log_xml_trace(reply, "reg-reply"); + msg_type = pcmk__xe_get(reply, PCMK__XA_CIB_OP); - } else { - native->token = pcmk__xe_get_copy(reply, PCMK__XA_CIB_CLIENTID); - if (native->token == NULL) { - rc = -EPROTO; - } - } + if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_none)) { + pcmk__info("Reply to CIB registration message has unknown type '%s'", + msg_type); + rc = -EPROTO; + goto done; + } - pcmk__xml_free(reply); + native->token = pcmk__xe_get_copy(reply, PCMK__XA_CIB_CLIENTID); + if (native->token == NULL) { + rc = -EPROTO; + goto done; } + pcmk__info("Successfully connected to CIB manager for %s", name); + cib->state = cib_connected_command; + done: pcmk__xml_free(hello); + pcmk__xml_free(reply); - if (rc == pcmk_ok) { - pcmk__info("Successfully connected to CIB manager for %s", name); - return pcmk_ok; + if (rc != pcmk_ok) { + pcmk__info("Connection to CIB manager for %s failed: %s", name, + pcmk_strerror(rc)); + cib_native_signoff(cib); } - pcmk__info("Connection to CIB manager for %s failed: %s", name, - pcmk_strerror(rc)); - cib_native_signoff(cib); return rc; } diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 41ead4081b2..fd00d86e965 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -9,124 +9,80 @@ #include +#include // EEXIST, EINVAL, ENXIO #include -#include // uint32_t -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include // xmlXPathObject, etc. - -#include -#include - -#include +#include // NULL +#include // uint32_t +#include // free + +#include // g_*, GHashTable, gpointer +#include // xmlGetNodePath, xmlNode, XML_ELEMENT_NODE +#include // xmlChar +#include // xmlXPathObject, xmlXPathFreeObject + +#include // cib_*, createEmptyCib +#include // cib__*, PCMK__CIB_* +#include // pcmk_cib_*, pcmk_find_cib_element +#include // pcmk__err, pcmk__xml_*, etc. +#include // CRM_CHECK +#include // pcmk_rc_*, pcmk_legacy2rc +#include // xml_apply_patchset, PCMK_XA_, PCMK_XE_* +#include // CRM_OP_PING // @TODO: Free this via crm_exit() when libcib gets merged with libcrmcommon static GHashTable *operation_table = NULL; static const cib__operation_t cib_ops[] = { { - PCMK__CIB_REQUEST_ABS_DELETE, cib__op_abs_delete, - cib__op_attr_modifies|cib__op_attr_privileged - }, - { - PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction - }, - { - PCMK__CIB_REQUEST_BUMP, cib__op_bump, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch, true }, { - PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_replaces - |cib__op_attr_writes_through + PCMK__CIB_REQUEST_BUMP, cib__op_bump, true }, { - PCMK__CIB_REQUEST_CREATE, cib__op_create, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, true }, { - PCMK__CIB_REQUEST_DELETE, cib__op_delete, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + PCMK__CIB_REQUEST_CREATE, cib__op_create, true }, { - PCMK__CIB_REQUEST_ERASE, cib__op_erase, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_replaces - |cib__op_attr_transaction + PCMK__CIB_REQUEST_DELETE, cib__op_delete, true }, { - PCMK__CIB_REQUEST_IS_PRIMARY, cib__op_is_primary, - cib__op_attr_privileged + PCMK__CIB_REQUEST_ERASE, cib__op_erase, true }, { - PCMK__CIB_REQUEST_MODIFY, cib__op_modify, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + PCMK__CIB_REQUEST_MODIFY, cib__op_modify, true }, { - PCMK__CIB_REQUEST_NOOP, cib__op_noop, cib__op_attr_none + PCMK__CIB_REQUEST_NOOP, cib__op_noop, false }, { - CRM_OP_PING, cib__op_ping, cib__op_attr_none + CRM_OP_PING, cib__op_ping, false }, { - // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support - PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, - cib__op_attr_modifies|cib__op_attr_privileged|cib__op_attr_local + PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, false }, { - PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none + PCMK__CIB_REQUEST_QUERY, cib__op_query, false }, { - PCMK__CIB_REQUEST_REPLACE, cib__op_replace, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_replaces - |cib__op_attr_writes_through - |cib__op_attr_transaction + PCMK__CIB_REQUEST_REPLACE, cib__op_replace, true }, { - PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_local + PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, false }, { - PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, - cib__op_attr_privileged|cib__op_attr_local + PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, false }, { - PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_privileged + PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, false }, { - PCMK__CIB_REQUEST_SYNC, cib__op_sync, cib__op_attr_privileged + PCMK__CIB_REQUEST_SYNC, cib__op_sync, false }, { - PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_writes_through - |cib__op_attr_transaction + PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, true }, }; @@ -1010,7 +966,7 @@ cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) // pcmk__update_schema() may free the original validate-with string original_schema = pcmk__xe_get_copy(*cib, PCMK_XA_VALIDATE_WITH); - rc = pcmk__update_schema(&updated, max_schema, true, + rc = pcmk__update_schema(&updated, max_schema, !pcmk__is_set(options, cib_verbose)); *cib = pcmk__xml_replace_with_copy(*cib, updated); pcmk__xml_free(updated); diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index eab7e5841bc..6f8c9c55a43 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -9,9 +9,10 @@ #include -#include // ENOTCONN, EPROTO, EAGAIN -#include // false, bool, true -#include // NULL, free, calloc +#include // ENOTCONN, EPROTO, EAGAIN, etc. +#include // bool, true, false +#include // NULL +#include // calloc, free, getenv #include // strdup #include // shutdown, SHUT_RDWR #include // time, time_t @@ -22,12 +23,10 @@ #include // xmlNode #include // QB_XS -#include // cib_t, cib_remote_new -#include // cib__create_op, cib__extend_transaction -#include -#include // mainloop_fd_callbacks -#include // pcmk_rc_str, pcmk_rc_* -#include // PCMK_XA_*, +#include // cib_* +#include // cib__* +#include // mainloop_* +#include // pcmk_rc_*, pcmk_ok, etc. #include // CRM_OP_REGISTER, crm_system_name // GnuTLS handshake timeout in seconds @@ -35,8 +34,6 @@ static pcmk__tls_t *tls = NULL; -#include - typedef struct { int port; char *server; @@ -106,18 +103,19 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, } pcmk__trace("Sending %s message to the CIB manager", op); - if (!(call_options & cib_sync_call)) { + if (!pcmk__is_set(call_options, cib_sync_call)) { pcmk__remote_send_xml(&private->callback, op_msg); } else { pcmk__remote_send_xml(&private->command, op_msg); } pcmk__xml_free(op_msg); - if ((call_options & cib_discard_reply)) { + if (pcmk__is_set(call_options, cib_discard_reply)) { pcmk__trace("Discarding reply"); return pcmk_ok; + } - } else if (!(call_options & cib_sync_call)) { + if (!pcmk__is_set(call_options, cib_sync_call)) { return cib->call_id; } @@ -173,9 +171,9 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, return -ENOMSG; } - /* The only reason we can receive an ACK here is if dispatch_common -> + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. + * based_ipc_dispatch does not return ACK, unlike other daemons. */ if (pcmk__xe_is(op_reply, PCMK__XE_ACK) && ack_is_failure(op_reply)) { pcmk__xml_free(op_reply); @@ -197,10 +195,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, pcmk__log_xml_warn(op_reply, "failed"); } - if (output_data == NULL) { - /* do nothing more */ - - } else if (!(call_options & cib_discard_reply)) { + if (output_data != NULL) { xmlNode *tmp = cib__get_calldata(op_reply); if (tmp == NULL) { @@ -527,9 +522,9 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) goto done; } - /* The only reason we can receive an ACK here is if dispatch_common -> + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. + * based_ipc_dispatch does not return ACK, unlike other daemons. */ if (pcmk__xe_is(answer, PCMK__XE_ACK) && ack_is_failure(answer)) { rc = -EPROTO; @@ -542,7 +537,7 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) msg_type = pcmk__xe_get(answer, PCMK__XA_CIB_OP); tmp_ticket = pcmk__xe_get(answer, PCMK__XA_CIB_CLIENTID); - if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { + if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_none)) { pcmk__err("Invalid registration message: %s", msg_type); rc = -EPROTO; @@ -569,15 +564,21 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) return rc; } +/*! + * \internal + * \brief Sign on a native client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Ignored + */ static int cib_remote_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; cib_remote_opaque_t *private = cib->variant_opaque; - if (name == NULL) { - name = pcmk__s(crm_system_name, "client"); - } + name = pcmk__s(crm_system_name, "client"); if (private->passwd == NULL) { if (private->out == NULL) { @@ -607,7 +608,6 @@ cib_remote_signon(cib_t *cib, const char *name, enum cib_conn_type type) pcmk__info("Opened connection to %s:%d for %s", private->server, private->port, name); cib->state = cib_connected_command; - cib->type = cib_command; } else { pcmk__info("Connection to %s:%d for %s failed: %s\n", private->server, @@ -627,7 +627,6 @@ cib_remote_signoff(cib_t *cib) cib->cmds->end_transaction(cib, false, cib_none); cib->state = cib_disconnected; - cib->type = cib_no_connection; return rc; } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 4416e454773..4894fd614fb 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -7,20 +7,33 @@ * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include -#include -#include +#include // errno, EACCES, EAGAIN, EALREADY, etc. +#include +#include // NULL +#include // uint32_t +#include // free, getenv +#include // LOG_CRIT, LOG_INFO + +#include // gboolean, GHashTable, g_*, etc. +#include // xmlNode +#include // QB_XS + +#include // cib_*, createEmptyCib, etc. +#include // cib__*, PCMK__CIB_* +#include // pcmk_acl_*, xml_acl_* +#include // pcmk_find_cib_element +#include // pcmk__err, pcmk__xml_*, etc. +#include // crm_time_* +#include // CRM_CHECK +#include // pcmk_unpack_nvpair_blocks +#include // PCMK_OPT_*, PCMK_VALUE_* +#include // pcmk_rc_*, pcmk_err_*, pcmk_ok, etc. +#include // pcmk_rule_input_t +#include // xml_*_patchset, PCMK_XA_*, PCMK_XE_* +#include // CRM_FEATURE_SET, crm_system_name gboolean cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates) @@ -713,24 +726,25 @@ validate_transaction_request(const xmlNode *request) const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); const cib__operation_t *operation = NULL; - int rc = cib__get_operation(op, &operation); + int rc = pcmk_rc_ok; + rc = cib__get_operation(op, &operation); if (rc != pcmk_rc_ok) { // cib__get_operation() logs error return rc; } - if (!pcmk__is_set(operation->flags, cib__op_attr_transaction)) { + if (operation->type == cib__op_commit_transact) { pcmk__err("Operation %s is not supported in CIB transactions", op); return EOPNOTSUPP; } if (host != NULL) { pcmk__err("Operation targeting a specific node (%s) is not supported " - "in a CIB transaction", - host); + "in a CIB transaction", host); return EOPNOTSUPP; } + return pcmk_rc_ok; } diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index ec2d54a1ae2..ef1a71c0bcb 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -27,7 +27,6 @@ #include #include -#include #include "crmcluster_private.h" /*! @@ -204,24 +203,21 @@ pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(void *)) * \param[in] node Cluster node to send message to * \param[in] service Message type to use in message host info * \param[in] data XML message to send - * - * \return \c true on success, or \c false otherwise */ -bool +void pcmk__cluster_send_message(const pcmk__node_status_t *node, enum pcmk_ipc_server service, const xmlNode *data) { - // @TODO Return standard Pacemaker return code switch (pcmk_get_cluster_layer()) { #if SUPPORT_COROSYNC case pcmk_cluster_layer_corosync: - return pcmk__cpg_send_xml(data, node, service); + pcmk__cpg_send_xml(data, node, service); + return; #endif // SUPPORT_COROSYNC default: - break; + return; } - return false; } /*! diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index 5b1c9136745..612ff5d3fe3 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -28,8 +28,6 @@ #include // QB_XS #include // pcmk_cluster_*, etc. -#include // pcmk__cluster_private_t members -#include // pcmk__corosync2rc, pcmk__err, etc. #include // crm_ipc_is_authentic_process #include // CRM_LOG_ASSERT #include // mainloop_* @@ -461,6 +459,9 @@ pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long, * \param[in,out] cluster Initialized cluster object to connect * * \return Standard Pacemaker return code + * + * \note This initializes the node caches on success by calling + * \c pcmk__get_node(). */ int pcmk__corosync_connect(pcmk_cluster_t *cluster) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index c121a2f9d07..f18773902b5 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -16,7 +16,7 @@ #include #include // uint32_t #include -#include // size_t +#include // pid_t, size_t #include #include @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -52,7 +51,7 @@ static int cs_message_timer = 0; struct pcmk__cpg_host_s { uint32_t id; - uint32_t pid; + uint32_t pid; // For logging only gboolean local; // Unused but needed for compatibility enum pcmk_ipc_server type; // For logging only uint32_t size; @@ -80,7 +79,7 @@ typedef struct pcmk__cpg_msg_s pcmk__cpg_msg_t; static void crm_cs_flush(void *data); -#define msg_data_len(msg) (msg->is_compressed?msg->compressed_size:msg->size) +#define msg_data_len(msg) (msg->is_compressed? msg->compressed_size : msg->size) #define cs_repeat(rc, counter, max, code) do { \ rc = code; \ @@ -903,55 +902,48 @@ pcmk__cpg_disconnect(pcmk_cluster_t *cluster) /*! * \internal - * \brief Send string data via Corosync CPG - * - * \param[in] data Data to send - * \param[in] node Cluster node to send message to - * \param[in] dest Type of message to send + * \brief Send an XML message via Corosync CPG * - * \return \c true on success, or \c false otherwise + * \param[in] msg XML message to send + * \param[in] node Cluster node to send message to + * \param[in] dest Type of message to send */ -static bool -send_cpg_text(const char *data, const pcmk__node_status_t *node, - enum pcmk_ipc_server dest) +void +pcmk__cpg_send_xml(const xmlNode *xml, const pcmk__node_status_t *node, + enum pcmk_ipc_server dest) { - static int msg_id = 0; - static int local_pid = 0; - static int local_name_len = 0; static const char *local_name = NULL; + static size_t local_name_len = 0; + static pid_t local_pid = 0; + static int msg_id = 0; char *target = NULL; struct iovec *iov; pcmk__cpg_msg_t *msg = NULL; + GString *data = NULL; + // @TODO Refactor to take a cluster object and use its node name? if (local_name == NULL) { local_name = pcmk__cluster_local_node_name(); - } - if ((local_name_len == 0) && (local_name != NULL)) { - local_name_len = strlen(local_name); - } - if (data == NULL) { - data = ""; - } + if (local_name != NULL) { + local_name_len = strlen(local_name); + pcmk__assert(local_name_len <= UINT32_MAX); + } - if (local_pid == 0) { local_pid = getpid(); + CRM_LOG_ASSERT((local_pid > 0) && (local_pid <= UINT32_MAX)); } msg = pcmk__assert_alloc(1, sizeof(pcmk__cpg_msg_t)); - - msg_id++; - msg->id = msg_id; + msg->id = ++msg_id; msg->header.error = CS_OK; - msg->host.type = dest; if (node != NULL) { if (node->name != NULL) { target = pcmk__str_copy(node->name); msg->host.size = strlen(node->name); - memset(msg->host.uname, 0, MAX_NAME); memcpy(msg->host.uname, node->name, msg->host.size); } else { @@ -965,26 +957,30 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, msg->sender.id = 0; msg->sender.type = pcmk__parse_server(crm_system_name); - msg->sender.pid = local_pid; - msg->sender.size = local_name_len; - memset(msg->sender.uname, 0, MAX_NAME); + msg->sender.pid = (uint32_t) local_pid; + msg->sender.size = (uint32_t) local_name_len; + memcpy(msg->sender.uname, local_name, local_name_len); - if ((local_name != NULL) && (msg->sender.size != 0)) { - memcpy(msg->sender.uname, local_name, msg->sender.size); - } + data = g_string_sized_new(1024); + pcmk__xml_string(xml, 0, data, 0); - msg->size = 1 + strlen(data); + msg->size = data->len + 1; msg->header.size = sizeof(pcmk__cpg_msg_t) + msg->size; + /* @FIXME This is a mess of conversions among size_t, unsigned int, and + * uint32_t. It might be worth sending multipart messages, as we do in our + * IPC library and as Netlink does. This is also the only caller of + * pcmk__compress(), so we could get rid of it if this no longer needed it. + */ if (msg->size < PCMK__BZ2_THRESHOLD) { msg = pcmk__realloc(msg, msg->header.size); - memcpy(msg->data, data, msg->size); + memcpy(msg->data, data->str, msg->size); } else { char *compressed = NULL; unsigned int new_size = 0; - if (pcmk__compress(data, (unsigned int) msg->size, 0, &compressed, + if (pcmk__compress(data->str, (unsigned int) msg->size, &compressed, &new_size) == pcmk_rc_ok) { msg->header.size = sizeof(pcmk__cpg_msg_t) + new_size; @@ -996,7 +992,7 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, } else { msg = pcmk__realloc(msg, msg->header.size); - memcpy(msg->data, data, msg->size); + memcpy(msg->data, data->str, msg->size); } free(compressed); @@ -1006,44 +1002,14 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, iov->iov_base = msg; iov->iov_len = msg->header.size; - if (msg->compressed_size > 0) { - pcmk__trace("Queueing CPG message %" PRIu32 " to %s " - "(%zu bytes, %" PRIu32 " bytes compressed payload): %.200s", - msg->id, target, iov->iov_len, msg->compressed_size, data); - } else { - pcmk__trace("Queueing CPG message %" PRIu32 " to %s " - "(%zu bytes, %" PRIu32 " bytes payload): %.200s", - msg->id, target, iov->iov_len, msg->size, data); - } - - free(target); + pcmk__trace("Queueing CPG message %" PRIu32 " to %s (%zu bytes, " + "%" PRIu32 " bytes %spayload): %.200s", + msg->id, target, iov->iov_len, msg_data_len(msg), + (msg->is_compressed? "compressed " : ""), data->str); cs_message_queue = g_list_append(cs_message_queue, iov); crm_cs_flush(&pcmk_cpg_handle); - return true; -} - -/*! - * \internal - * \brief Send an XML message via Corosync CPG - * - * \param[in] msg XML message to send - * \param[in] node Cluster node to send message to - * \param[in] dest Type of message to send - * - * \return TRUE on success, otherwise FALSE - */ -bool -pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, - enum pcmk_ipc_server dest) -{ - bool rc = true; - GString *data = g_string_sized_new(1024); - - pcmk__xml_string(msg, 0, data, 0); - - rc = send_cpg_text(data->str, node, dest); + free(target); g_string_free(data, TRUE); - return rc; } diff --git a/lib/cluster/crmcluster_private.h b/lib/cluster/crmcluster_private.h index 44a9d33798d..0db9eca79ec 100644 --- a/lib/cluster/crmcluster_private.h +++ b/lib/cluster/crmcluster_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the Pacemaker project contributors + * Copyright 2020-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -71,7 +71,7 @@ G_GNUC_INTERNAL uint32_t pcmk__cpg_local_nodeid(cpg_handle_t handle); G_GNUC_INTERNAL -bool pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, +void pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, enum pcmk_ipc_server dest); #endif // SUPPORT_COROSYNC diff --git a/lib/cluster/election.c b/lib/cluster/election.c index db44bfdfccd..27655d55982 100644 --- a/lib/cluster/election.c +++ b/lib/cluster/election.c @@ -17,8 +17,6 @@ #include #include -#include -#include #include "crmcluster_private.h" #define STORM_INTERVAL 2 /* in seconds */ diff --git a/lib/cluster/membership.c b/lib/cluster/membership.c index 14b33ec9fbe..81ce288005e 100644 --- a/lib/cluster/membership.c +++ b/lib/cluster/membership.c @@ -20,7 +20,6 @@ #include #include -#include #include #include "crmcluster_private.h" @@ -1129,7 +1128,7 @@ crm_update_peer_proc(const char *source, pcmk__node_status_t *node, changed = TRUE; } - } else if (node->processes & flag) { + } else if (pcmk__is_set(node->processes, flag)) { node->processes = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, "Peer process", node->name, node->processes, diff --git a/lib/common/crmcommon_private.h b/lib/common/crmcommon_private.h index 22ff3814a07..33088baf3af 100644 --- a/lib/common/crmcommon_private.h +++ b/lib/common/crmcommon_private.h @@ -147,9 +147,6 @@ G_GNUC_INTERNAL void pcmk__unpack_acls(xmlDoc *source, xml_doc_private_t *target, const char *user); -G_GNUC_INTERNAL -bool pcmk__is_user_in_group(const char *user, const char *group); - G_GNUC_INTERNAL void pcmk__apply_acls(xmlDoc *doc); diff --git a/lib/common/fuzzers/iso8601_fuzzer.c b/lib/common/fuzzers/iso8601_fuzzer.c index e7c0ecb5b41..51e5fcf1c94 100644 --- a/lib/common/fuzzers/iso8601_fuzzer.c +++ b/lib/common/fuzzers/iso8601_fuzzer.c @@ -7,6 +7,8 @@ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ +#include + #include #include #include @@ -15,7 +17,6 @@ #include // qb_util_timespec_from_epoch_get() #include -#include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) diff --git a/lib/common/fuzzers/scores_fuzzer.c b/lib/common/fuzzers/scores_fuzzer.c index 3e06cf5fc13..375bba5e0c7 100644 --- a/lib/common/fuzzers/scores_fuzzer.c +++ b/lib/common/fuzzers/scores_fuzzer.c @@ -7,12 +7,13 @@ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ +#include + #include #include #include #include -#include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) diff --git a/lib/common/io.c b/lib/common/io.c index 15347e93c50..b8c07970896 100644 --- a/lib/common/io.c +++ b/lib/common/io.c @@ -252,6 +252,7 @@ pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat) pcmk_rc_str(rc)); return false; } + if (target_stat->st_uid != daemon_uid) { pcmk__notice("%s is not owned by user " CRM_DAEMON_USER " " QB_XS " uid %lld != %lld", @@ -259,13 +260,15 @@ pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat) (long long) target_stat->st_uid); return false; } - if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) { + + if (!pcmk__any_flags_set(target_stat->st_mode, S_IRUSR|S_IWUSR)) { pcmk__notice("%s is not readable and writable by user %s " QB_XS " st_mode=0%lo", target_name, CRM_DAEMON_USER, (unsigned long) target_stat->st_mode); return false; } + return true; } @@ -289,13 +292,14 @@ pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat) return false; } - if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) { + if (!pcmk__any_flags_set(target_stat->st_mode, S_IRGRP|S_IWGRP)) { pcmk__notice("%s is not readable and writable by group %s " QB_XS " st_mode=0%lo", target_name, CRM_DAEMON_GROUP, (unsigned long) target_stat->st_mode); return false; } + return true; } diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index ec92392918f..453831ee75a 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1061,28 +1061,21 @@ pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c, * \internal * \brief Add an IPC server to the main loop for the CIB manager API * - * \param[out] ipcs_ro New IPC server for read-only CIB manager API - * \param[out] ipcs_rw New IPC server for read/write CIB manager API - * \param[in] ro_cb IPC callbacks for read-only API - * \param[in] rw_cb IPC callbacks for read/write and shared-memory APIs + * \param[out] ipcs Where to store newly created IPC server + * \param[in] cb IPC callbacks * * \note This function exits fatally on error. */ void -pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, - struct qb_ipcs_service_handlers *ro_cb, - struct qb_ipcs_service_handlers *rw_cb) +pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs, + struct qb_ipcs_service_handlers *cb) { - pcmk__assert((ipcs_ro != NULL) && (*ipcs_ro == NULL) && (ro_cb != NULL) - && (ipcs_rw != NULL) && (*ipcs_rw == NULL) && (rw_cb != NULL)); - - *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO, QB_IPC_SHM, - ro_cb); + pcmk__assert((ipcs != NULL) && (*ipcs == NULL) && (cb != NULL)); - *ipcs_rw = mainloop_add_ipc_server(PCMK__SERVER_BASED_RW, QB_IPC_SHM, - rw_cb); + *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_based), + QB_IPC_SHM, cb); - if ((*ipcs_ro == NULL) || (*ipcs_rw == NULL)) { + if (*ipcs == NULL) { pcmk__crit("Failed to create %s IPC server; shutting down", pcmk__server_log_name(pcmk_ipc_based)); pcmk__crit("Verify pacemaker and pacemaker_remote are not both " @@ -1159,6 +1152,8 @@ void pcmk__serve_execd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { + pcmk__assert((ipcs != NULL) && (*ipcs == NULL) && (cb != NULL)); + *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_execd), QB_IPC_SHM, cb); diff --git a/lib/common/logging.c b/lib/common/logging.c index 96e3b62bae2..c49fda6b360 100644 --- a/lib/common/logging.c +++ b/lib/common/logging.c @@ -31,7 +31,6 @@ #include // LOG_TRACE, qb_log_* #include // CRM_DAEMON_USER, CRM_*_DIR -#include // pcmk__env_*, pcmk__output_*, etc. #include // do_crm_log, CRM_CHECK, etc. #include // crm_signal_handler, mainloop_add_signal #include // PCMK_VALUE_NONE diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index b3457ff026b..7aee7ae85a7 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -687,7 +687,7 @@ mainloop_gio_callback(GIOChannel *gio, GIOCondition condition, void *data) pcmk__assert(client->fd == g_io_channel_unix_get_fd(gio)); - if (condition & G_IO_IN) { + if (pcmk__is_set(condition, G_IO_IN)) { if (client->ipc) { long read_rc = 0L; int max = 10; @@ -738,12 +738,14 @@ mainloop_gio_callback(GIOChannel *gio, GIOCondition condition, void *data) client->name, client, condition); rc = G_SOURCE_REMOVE; - } else if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR)) { + } else if (pcmk__any_flags_set(condition, + (G_IO_HUP | G_IO_NVAL | G_IO_ERR))) { + pcmk__trace("The connection %s[%p] has been closed (I/O condition=%d)", client->name, client, condition); rc = G_SOURCE_REMOVE; - } else if ((condition & G_IO_IN) == 0) { + } else if (!pcmk__is_set(condition, G_IO_IN)) { /* #define GLIB_SYSDEF_POLLIN =1 #define GLIB_SYSDEF_POLLPRI =2 diff --git a/lib/common/mock.c b/lib/common/mock.c index 69d3dc4e4a2..9f14c9fe196 100644 --- a/lib/common/mock.c +++ b/lib/common/mock.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2025 the Pacemaker project contributors + * Copyright 2021-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -223,20 +223,16 @@ __wrap_getpid(void) } -/* setgrent(), getgrent() and endgrent() +/* getgrnam() * - * If pcmk__mock_grent is set to true, getgrent() will behave as if the only + * If pcmk__mock_getgrnam is set to true, getgrnam() will behave as if the only * groups on the system are: * * - grp0 (user0, user1) * - grp1 (user1) - * - grp2 (user2, user1) */ -bool pcmk__mock_grent = false; - -// Index of group that will be returned next from getgrent() -static int group_idx = 0; +bool pcmk__mock_getgrnam = false; // Data used for testing static const char* grp0_members[] = { @@ -247,10 +243,6 @@ static const char* grp1_members[] = { "user1", NULL }; -static const char* grp2_members[] = { - "user2", "user1", NULL -}; - /* An array of "groups" (a struct from grp.h) * * The members of the groups are initalized here to some testing data, casting @@ -260,43 +252,27 @@ static const char* grp2_members[] = { * string literal = const char* (cannot be changed b/c ? ) * vs. char* (it's getting casted to this) */ -static const int NUM_GROUPS = 3; static struct group groups[] = { {(char*)"grp0", (char*)"", 0, (char**)grp0_members}, {(char*)"grp1", (char*)"", 1, (char**)grp1_members}, - {(char*)"grp2", (char*)"", 2, (char**)grp2_members}, }; -// This function resets the group_idx to 0. -void -__wrap_setgrent(void) { - if (pcmk__mock_grent) { - group_idx = 0; - } else { - __real_setgrent(); - } -} - -/* This function returns the next group entry in the list of groups, or - * NULL if there aren't any left. - * group_idx is a global variable which keeps track of where you are in the list +/* This function returns the group entry whose name matches the argument, or + * NULL if no match is found. */ struct group * -__wrap_getgrent(void) { - if (pcmk__mock_grent) { - if (group_idx >= NUM_GROUPS) { - return NULL; +__wrap_getgrnam(const char *name) { + if (pcmk__mock_getgrnam) { + for (int i = 0; i < PCMK__NELEM(groups); i++) { + if (pcmk__str_eq(groups[i].gr_name, name, pcmk__str_none)) { + return &groups[i]; + } } - return &groups[group_idx++]; - } else { - return __real_getgrent(); - } -} -void -__wrap_endgrent(void) { - if (!pcmk__mock_grent) { - __real_endgrent(); + return NULL; + + } else { + return __real_getgrnam(name); } } diff --git a/lib/common/mock_private.h b/lib/common/mock_private.h index 5fa2d918fa6..896f526ae11 100644 --- a/lib/common/mock_private.h +++ b/lib/common/mock_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2021-2025 the Pacemaker project contributors + * Copyright 2021-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -66,18 +66,14 @@ extern bool pcmk__mock_getpid; pid_t __real_getpid(void); pid_t __wrap_getpid(void); -extern bool pcmk__mock_grent; -void __real_setgrent(void); -void __wrap_setgrent(void); -struct group * __wrap_getgrent(void); -struct group * __real_getgrent(void); -void __wrap_endgrent(void); -void __real_endgrent(void); - extern bool pcmk__mock_getpwnam; struct passwd *__real_getpwnam(const char *name); struct passwd *__wrap_getpwnam(const char *name); +extern bool pcmk__mock_getgrnam; +struct group *__real_getgrnam(const char *name); +struct group *__wrap_getgrnam(const char *name); + extern bool pcmk__mock_readlink; ssize_t __real_readlink(const char *restrict path, char *restrict buf, size_t bufsize); diff --git a/lib/common/schemas.c b/lib/common/schemas.c index 5c2dc92c569..6ed78f217ac 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.c @@ -1110,16 +1110,13 @@ get_configured_schema(const xmlNode *xml) * after being transformed) * \param[in] max_schema_name If not NULL, do not update \p xml to any * schema later than this one - * \param[in] transform If false, do not update \p xml to any schema - * that requires an XSL transform * \param[in] to_logs If false, certain validation errors will be * sent to stderr rather than logged * * \return Standard Pacemaker return code */ int -pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, - bool to_logs) +pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool to_logs) { int max_stable_schemas = xml_latest_schema_index(); int max_schema_index = 0; @@ -1185,7 +1182,7 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, } // coverity[null_field] The index check ensures entry->next is not NULL - if (!transform || (current_schema->transforms == NULL) + if ((current_schema->transforms == NULL) || validate_with_silent((*xml)->doc, entry->next->data)) { /* The next schema either doesn't require a transform or validates * successfully even without the transform. Skip the transform and @@ -1212,8 +1209,9 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, if ((best_schema != NULL) && (best_schema->schema_index > original_schema->schema_index)) { - pcmk__info("%s the configuration schema to %s", - (transform? "Transformed" : "Upgraded"), best_schema->name); + + pcmk__info("Transformed the configuration schema to %s", + best_schema->name); pcmk__xe_set(*xml, PCMK_XA_VALIDATE_WITH, best_schema->name); } return rc; @@ -1259,7 +1257,7 @@ pcmk__update_configured_schema(xmlNode **xml, bool to_logs) entry = NULL; converted = pcmk__xml_copy(NULL, *xml); - if (pcmk__update_schema(&converted, NULL, true, to_logs) == pcmk_rc_ok) { + if (pcmk__update_schema(&converted, NULL, to_logs) == pcmk_rc_ok) { new_schema_name = pcmk__xe_get(converted, PCMK_XA_VALIDATE_WITH); entry = pcmk__get_schema(new_schema_name); } diff --git a/lib/common/servers.c b/lib/common/servers.c index 6a4fdc7405b..e5a6715db2f 100644 --- a/lib/common/servers.c +++ b/lib/common/servers.c @@ -29,66 +29,72 @@ * members, and libqb IPC server endpoints for both the old and new names, and * could drop the old names only after we no longer supported connections with * older nodes. + * + * @TODO It would be easy to use system_names[0] as a server's IPC name. + * Everything would automatically use the new names except for proxied + * connections from *older* Pacemaker Remote nodes. We would just have to map + * the old names to the new names in remote_proxy_new(), the same as we're + * currently mapping PCMK__SERVER_BASED_RO to PCMK__SERVER_BASED_RW there. */ static struct { const char *log_name; // Readable server name for use in logs const char *system_names[2]; // crm_system_name values (subdaemon names) - const char *ipc_names[2]; // libqb IPC names used to contact server + const char *ipc_name; // libqb IPC name used to contact server const char *message_types[3]; // IPC/cluster message types sent to server } server_info[] = { [pcmk_ipc_unknown] = { NULL, { NULL, NULL, }, - { NULL, NULL, }, + NULL, { NULL, NULL, NULL, }, }, [pcmk_ipc_attrd] = { "attribute manager", { PCMK__SERVER_ATTRD, NULL, }, - { PCMK__VALUE_ATTRD, NULL, }, + PCMK__VALUE_ATTRD, { PCMK__VALUE_ATTRD, NULL, NULL, }, }, [pcmk_ipc_based] = { "CIB manager", { PCMK__SERVER_BASED, NULL, }, - { PCMK__SERVER_BASED_RW, PCMK__SERVER_BASED_RO, }, + PCMK__SERVER_BASED_RW, { CRM_SYSTEM_CIB, NULL, NULL, }, }, [pcmk_ipc_controld] = { "controller", { PCMK__SERVER_CONTROLD, NULL, }, - { PCMK__VALUE_CRMD, NULL, }, + PCMK__VALUE_CRMD, { PCMK__VALUE_CRMD, CRM_SYSTEM_DC, CRM_SYSTEM_TENGINE, }, }, [pcmk_ipc_execd] = { "executor", { PCMK__SERVER_EXECD, PCMK__SERVER_REMOTED, }, - { PCMK__VALUE_LRMD, NULL, }, + PCMK__VALUE_LRMD, { PCMK__VALUE_LRMD, NULL, NULL, }, }, [pcmk_ipc_fenced] = { "fencer", { PCMK__SERVER_FENCED, NULL, }, - { PCMK__VALUE_STONITH_NG, NULL, }, + PCMK__VALUE_STONITH_NG, { PCMK__VALUE_STONITH_NG, NULL, NULL, }, }, [pcmk_ipc_pacemakerd] = { "launcher", { PCMK__SERVER_PACEMAKERD, NULL, }, - { CRM_SYSTEM_MCP, NULL, }, + CRM_SYSTEM_MCP, { CRM_SYSTEM_MCP, NULL, NULL, }, }, [pcmk_ipc_schedulerd] = { "scheduler", { PCMK__SERVER_SCHEDULERD, NULL, }, - { CRM_SYSTEM_PENGINE, NULL, }, + CRM_SYSTEM_PENGINE, { CRM_SYSTEM_PENGINE, NULL, NULL, }, }, }; @@ -131,7 +137,7 @@ pcmk__server_log_name(enum pcmk_ipc_server server) /*! * \internal - * \brief Return the (primary) IPC endpoint name for a server + * \brief Return the IPC endpoint name for a server * * \param[in] server Server to get IPC endpoint for * @@ -144,7 +150,7 @@ pcmk__server_ipc_name(enum pcmk_ipc_server server) { CRM_CHECK((server > 0) && (server < PCMK__NELEM(server_info)), return NULL); - return server_info[server].ipc_names[0]; + return server_info[server].ipc_name; } /*! @@ -191,13 +197,11 @@ pcmk__parse_server(const char *text) return server; } } - for (name = 0; - (name < 2) && (server_info[server].ipc_names[name] != NULL); - ++name) { - if (strcmp(text, server_info[server].ipc_names[name]) == 0) { - return server; - } + + if (pcmk__str_eq(text, server_info[server].ipc_name, pcmk__str_none)) { + return server; } + for (name = 0; (name < 3) && (server_info[server].message_types[name] != NULL); ++name) { diff --git a/lib/common/strings.c b/lib/common/strings.c index 0a6feff7b19..432269a1a37 100644 --- a/lib/common/strings.c +++ b/lib/common/strings.c @@ -585,15 +585,14 @@ pcmk__add_separated_word(GString **list, size_t init_size, const char *word, * * \param[in] data Data to compress * \param[in] length Number of characters of data to compress - * \param[in] max Maximum size of compressed data (or 0 to estimate) * \param[out] result Where to store newly allocated compressed result * \param[out] result_len Where to store actual compressed length of result * * \return Standard Pacemaker return code */ int -pcmk__compress(const char *data, unsigned int length, unsigned int max, - char **result, unsigned int *result_len) +pcmk__compress(const char *data, unsigned int length, char **result, + unsigned int *result_len) { int rc; char *compressed = NULL; @@ -603,17 +602,15 @@ pcmk__compress(const char *data, unsigned int length, unsigned int max, struct timespec before_t; #endif - if (max == 0) { - max = (length * 1.01) + 601; // Size guaranteed to hold result - } + // Size guaranteed to hold result. (@TODO Why 601? See commit 4e20d332.) + *result_len = (length * 1.01) + 601; #ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_MONOTONIC, &before_t); #endif - compressed = pcmk__assert_alloc((size_t) max, sizeof(char)); + compressed = pcmk__assert_alloc((size_t) *result_len, sizeof(char)); - *result_len = max; rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, PCMK__BZ2_BLOCKS, 0, PCMK__BZ2_WORK); rc = pcmk__bzlib2rc(rc); diff --git a/lib/common/tests/acl/Makefile.am b/lib/common/tests/acl/Makefile.am index e8887ff7851..36c59d89d81 100644 --- a/lib/common/tests/acl/Makefile.am +++ b/lib/common/tests/acl/Makefile.am @@ -13,8 +13,7 @@ include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. -check_PROGRAMS = pcmk__is_user_in_group_test -check_PROGRAMS += pcmk_acl_required_test +check_PROGRAMS = pcmk_acl_required_test check_PROGRAMS += xml_acl_denied_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/acl/pcmk__is_user_in_group_test.c b/lib/common/tests/acl/pcmk__is_user_in_group_test.c deleted file mode 100644 index b698b4cc244..00000000000 --- a/lib/common/tests/acl/pcmk__is_user_in_group_test.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020-2025 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 - -#include - -#include -#include - -#include "../../crmcommon_private.h" -#include "mock_private.h" - -static void -is_pcmk__is_user_in_group(void **state) -{ - pcmk__mock_grent = true; - - // null user - assert_false(pcmk__is_user_in_group(NULL, "grp0")); - // null group - assert_false(pcmk__is_user_in_group("user0", NULL)); - // nonexistent group - assert_false(pcmk__is_user_in_group("user0", "nonexistent_group")); - // user is in group - assert_true(pcmk__is_user_in_group("user0", "grp0")); - // user is not in group - assert_false(pcmk__is_user_in_group("user2", "grp0")); - - pcmk__mock_grent = false; -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(is_pcmk__is_user_in_group)) diff --git a/lib/common/tests/strings/pcmk__compress_test.c b/lib/common/tests/strings/pcmk__compress_test.c index 85994b7a498..40906a6b37e 100644 --- a/lib/common/tests/strings/pcmk__compress_test.c +++ b/lib/common/tests/strings/pcmk__compress_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2025 the Pacemaker project contributors + * Copyright 2022-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -25,19 +25,11 @@ simple_compress(void **state) char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; - assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len), pcmk_rc_ok); + assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, &result, &len), + pcmk_rc_ok); assert_memory_equal(result, SIMPLE_COMPRESSED, 13); } -static void -max_too_small(void **state) -{ - char *result = pcmk__assert_alloc(1024, sizeof(char)); - unsigned int len; - - assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), EFBIG); -} - static void calloc_fails(void **state) { char *result = pcmk__assert_alloc(1024, sizeof(char)); @@ -50,7 +42,7 @@ calloc_fails(void **state) { expect_uint_value(__wrap_calloc, nmemb, (size_t) ((40 * 1.01) + 601)); expect_uint_value(__wrap_calloc, size, sizeof(char)); - pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len); + pcmk__compress(SIMPLE_DATA, 40, &result, &len); pcmk__mock_calloc = false; // Use the real calloc() } ); @@ -58,5 +50,4 @@ calloc_fails(void **state) { PCMK__UNIT_TEST(NULL, NULL, cmocka_unit_test(simple_compress), - cmocka_unit_test(max_too_small), cmocka_unit_test(calloc_fails)) diff --git a/lib/common/tests/utils/Makefile.am b/lib/common/tests/utils/Makefile.am index fc99b307958..266593bfd9a 100644 --- a/lib/common/tests/utils/Makefile.am +++ b/lib/common/tests/utils/Makefile.am @@ -17,6 +17,7 @@ check_PROGRAMS += pcmk__daemon_user_test check_PROGRAMS += pcmk__fail_attr_name_test check_PROGRAMS += pcmk__failcount_name_test check_PROGRAMS += pcmk__getpid_s_test +check_PROGRAMS += pcmk__is_user_in_group_test check_PROGRAMS += pcmk__lastfailure_name_test check_PROGRAMS += pcmk__lookup_user_test check_PROGRAMS += pcmk__realloc_test diff --git a/lib/common/tests/utils/pcmk__is_user_in_group_test.c b/lib/common/tests/utils/pcmk__is_user_in_group_test.c new file mode 100644 index 00000000000..cc862ed7835 --- /dev/null +++ b/lib/common/tests/utils/pcmk__is_user_in_group_test.c @@ -0,0 +1,82 @@ +/* + * Copyright 2020-2026 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 + +#include + +#include "mock_private.h" + +#define assert_user_in_group(user, group, expected) \ + do { \ + /* Primary group: grp1 */ \ + const struct passwd entry = { .pw_gid = 1 }; \ + \ + expect_string(__wrap_getpwnam, name, user); \ + will_return(__wrap_getpwnam, 0); \ + will_return(__wrap_getpwnam, &entry); \ + \ + if (expected) { \ + assert_true(pcmk__is_user_in_group(user, group)); \ + } else { \ + assert_false(pcmk__is_user_in_group(user, group)); \ + } \ + } while (0); + +static int +setup(void **state) +{ + pcmk__mock_getgrnam = true; + pcmk__mock_getpwnam = true; + return 0; +} + +static int +teardown(void **state) +{ + pcmk__mock_getgrnam = false; + pcmk__mock_getpwnam = false; + return 0; +} + +static void +null_args(void **state) +{ + pcmk__assert_asserts(pcmk__is_user_in_group(NULL, NULL)); + pcmk__assert_asserts(pcmk__is_user_in_group(NULL, "grp0")); + pcmk__assert_asserts(pcmk__is_user_in_group("user0", NULL)); +} + +static void +user_in_group(void **state) +{ + // user0 is not in grp1's member list + assert_user_in_group("user0", "grp1", true); + + // user1 has grp1 as primary group and is also in grp1's member list + assert_user_in_group("user1", "grp1", true); + + // user1 has grp1 as primary group but is in grp0's member list + assert_user_in_group("user1", "grp0", true); +} + +static void +user_not_in_group(void **state) +{ + // Group does not exist + assert_user_in_group("user0", "nonexistent_group", false); + + // Group exists but user is not a member + assert_user_in_group("user2", "grp0", false); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(null_args), + cmocka_unit_test(user_in_group), + cmocka_unit_test(user_not_in_group)) diff --git a/lib/common/tls.c b/lib/common/tls.c index 1d834a59812..9d0d5a4474e 100644 --- a/lib/common/tls.c +++ b/lib/common/tls.c @@ -26,7 +26,6 @@ #include // gnutls_x509_* #include // QB_XS -#include #include // crm_time_* #include // CRM_CHECK #include // pcmk_rc_* @@ -643,7 +642,7 @@ pcmk__cred_file_useable(const char *location, bool *file_exists) return false; } - if ((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + if (pcmk__any_flags_set(sb.st_mode, S_IRWXG|S_IRWXO)) { pcmk__err("Refusing to use PSK credentials file %s because it has " "group and/or other permissions set", location); return false; diff --git a/lib/common/utils.c b/lib/common/utils.c index ad8e94ba67a..77413ae1218 100644 --- a/lib/common/utils.c +++ b/lib/common/utils.c @@ -63,35 +63,62 @@ pcmk_common_cleanup(void) xmlCleanupParser(); } +/*! + * \internal + * \brief Check whether a given user is a member of a given group + * + * \param[in] user User name + * \param[in] group Group name + * + * \return \c true if \p user is a member of \p group, or \c false otherwise + */ bool pcmk__is_user_in_group(const char *user, const char *group) { - struct group *grent; - char **gr_mem; + int rc = pcmk_rc_ok; + gid_t gid = 0; + const struct group *group_entry = NULL; + + pcmk__assert((user != NULL) && (group != NULL)); + + /* group->gr_mem only contains those users that are listed in /etc/group. + * It won't list the user if the group is their primary (that is, it's in + * the GID field in /etc/passwd (or passwd->pw_gid as returned by getpwent). + * So, we first need to perform a primary group check. + */ + rc = pcmk__lookup_user(user, NULL, &gid); + if (rc != pcmk_rc_ok) { + pcmk__info("Could not find user '%s': %s", user, pcmk_rc_str(rc)); + return false; + } + + errno = 0; + group_entry = getgrnam(group); + if (errno != 0) { + pcmk__info("Could not find group '%s': %s", group, strerror(errno)); + return false; + } - if (user == NULL || group == NULL) { + if (group_entry == NULL) { + pcmk__info("Could not find group '%s'", group); return false; } - - setgrent(); - while ((grent = getgrent()) != NULL) { - if (grent->gr_mem == NULL) { - continue; - } - if(strcmp(group, grent->gr_name) != 0) { - continue; - } + if (group_entry->gr_gid == gid) { + return true; + } - gr_mem = grent->gr_mem; - while (*gr_mem != NULL) { - if (!strcmp(user, *gr_mem++)) { - endgrent(); - return true; - } + /* If the primary group didn't match, check if group is a secondary group + * for the user + */ + for (const char *const *member = (const char *const *) group_entry->gr_mem; + *member != NULL; member++) { + + if (pcmk__str_eq(user, *member, pcmk__str_none)) { + return true; } } - endgrent(); + return false; } diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c index 33e41d7ff8f..3e2ff23439f 100644 --- a/lib/fencing/st_client.c +++ b/lib/fencing/st_client.c @@ -1388,7 +1388,7 @@ stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int opti private->op_callback = callback; } else if (call_id < 0) { // Call failed immediately, so call callback now - if (!(options & st_opt_report_only_success)) { + if (!pcmk__is_set(options, st_opt_report_only_success)) { pcmk__action_result_t result = PCMK__UNKNOWN_RESULT; pcmk__trace("Call failed, calling %s: %s", callback_name, @@ -1405,10 +1405,10 @@ stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int opti blob = pcmk__assert_alloc(1, sizeof(stonith_callback_client_t)); blob->id = callback_name; - blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE; + blob->only_success = pcmk__is_set(options, st_opt_report_only_success); blob->user_data = user_data; blob->callback = callback; - blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE; + blob->allow_timeout_updates = pcmk__is_set(options, st_opt_timeout_updates); if (timeout > 0) { set_callback_timeout(blob, stonith, call_id, timeout); @@ -1637,7 +1637,7 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod { enum crm_ipc_flags ipc_flags = crm_ipc_flags_none; - if (call_options & st_opt_sync_call) { + if (pcmk__is_set(call_options, st_opt_sync_call)) { pcmk__set_ipc_flags(ipc_flags, "fencing command", crm_ipc_client_response); } @@ -1660,7 +1660,7 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod pcmk__log_xml_trace(op_reply, "Reply"); - if (!(call_options & st_opt_sync_call)) { + if (!pcmk__is_set(call_options, st_opt_sync_call)) { pcmk__trace("Async call %d, returning", stonith->call_id); pcmk__xml_free(op_reply); return stonith->call_id; @@ -1677,7 +1677,9 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod rc = pcmk_rc2legacy(stonith__result2rc(&result)); pcmk__reset_result(&result); - if ((call_options & st_opt_discard_reply) || output_data == NULL) { + if (pcmk__is_set(call_options, st_opt_discard_reply) + || (output_data == NULL)) { + pcmk__trace("Discarding reply"); } else { diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c index 65b23df8edd..8af18bf14d6 100644 --- a/lib/lrmd/lrmd_client.c +++ b/lib/lrmd/lrmd_client.c @@ -26,7 +26,6 @@ #include // PCMK_DEFAULT_ACTION_TIMEOUT_MS #include // PCMK_RESOURCE_CLASS_STONITH -#include #include // crm_ipc_* #include // CRM_CHECK, CRM_LOG_ASSERT #include // mainloop_set_trigger diff --git a/lib/pacemaker/pcmk_acl.c b/lib/pacemaker/pcmk_acl.c index 5f47e76e2ee..31fa1130f0d 100644 --- a/lib/pacemaker/pcmk_acl.c +++ b/lib/pacemaker/pcmk_acl.c @@ -25,7 +25,6 @@ #include #include -#include #include diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c index b43bb2fd799..bc28e1a1ea9 100644 --- a/lib/pacemaker/pcmk_fence.c +++ b/lib/pacemaker/pcmk_fence.c @@ -222,6 +222,7 @@ pcmk__request_fencing(stonith_t *st, const char *target, const char *action, mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); free(async_fence_data.name); diff --git a/lib/services/dbus.c b/lib/services/dbus.c index a21da13e599..bd32ab141dd 100644 --- a/lib/services/dbus.c +++ b/lib/services/dbus.c @@ -82,18 +82,19 @@ dispatch_messages(void) static const char* dbus_watch_flags_to_string(int flags) { - const char *watch_type; - - if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) { - watch_type = "read/write"; - } else if (flags & DBUS_WATCH_READABLE) { - watch_type = "read"; - } else if (flags & DBUS_WATCH_WRITABLE) { - watch_type = "write"; - } else { - watch_type = "neither read nor write"; + if (pcmk__all_flags_set(flags, DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE)) { + return "read/write"; + } + + if (pcmk__is_set(flags, DBUS_WATCH_READABLE)) { + return "read"; } - return watch_type; + + if (pcmk__is_set(flags, DBUS_WATCH_WRITABLE)) { + return "write"; + } + + return "neither read nor write"; } /*! @@ -120,7 +121,10 @@ dispatch_fd_data(void *userdata) dbus_watch_get_unix_fd(watch), flags, dbus_watch_flags_to_string(flags)); - if (enabled && (flags & (DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE))) { + if (enabled + && pcmk__any_flags_set(flags, + DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE)) { + oom = !dbus_watch_handle(watch, flags); } else if (enabled) { diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c index 6ae1605ebbc..74a02d9dd2c 100644 --- a/lib/services/services_linux.c +++ b/lib/services/services_linux.c @@ -1092,16 +1092,17 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) wait_reason = NULL; if (poll_rc > 0) { - if (fds[0].revents & POLLIN) { + if (pcmk__is_set(fds[0].revents, POLLIN)) { svc_read_output(op->opaque->stdout_fd, op, FALSE); } - if (fds[1].revents & POLLIN) { + if (pcmk__is_set(fds[1].revents, POLLIN)) { svc_read_output(op->opaque->stderr_fd, op, TRUE); } - if ((fds[2].revents & POLLIN) + if (pcmk__is_set(fds[2].revents, POLLIN) && sigchld_received(fds[2].fd, op->pid, data)) { + wait_rc = waitpid(op->pid, &status, WNOHANG); if ((wait_rc > 0) || ((wait_rc < 0) && (errno == ECHILD))) { diff --git a/mk/tap.mk b/mk/tap.mk index bac6d0e7e09..194fc61d34b 100644 --- a/mk/tap.mk +++ b/mk/tap.mk @@ -1,5 +1,5 @@ # -# Copyright 2021-2025 the Pacemaker project contributors +# Copyright 2021-2026 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -19,16 +19,14 @@ CLEANFILES = *.log *.trs WRAPPED = abort \ calloc \ - endgrent \ fopen \ getenv \ getpid \ - getgrent \ + getgrnam \ getpwnam \ readlink \ realloc \ setenv \ - setgrent \ strdup \ unsetenv diff --git a/python/pacemaker/_cts/patterns.py b/python/pacemaker/_cts/patterns.py index 4b69cfe9a14..2cff9504969 100644 --- a/python/pacemaker/_cts/patterns.py +++ b/python/pacemaker/_cts/patterns.py @@ -177,7 +177,7 @@ def __init__(self): r"error.*: Operation 'reboot' .* using FencingFail returned ", r"getinfo response error: 1$", r"sbd.* error: inquisitor_child: DEBUG MODE IS ACTIVE", - r"sbd.* pcmk:\s*error:.*Connection to cib_ro.* (failed|closed)", + r"sbd.* pcmk:\s*error:.*Connection to cib_rw.* (failed|closed)", ] self._bad_news = [ diff --git a/tests/test-headers.sh b/tests/test-headers.sh index 764431dfca9..d92bf90a8b7 100644 --- a/tests/test-headers.sh +++ b/tests/test-headers.sh @@ -34,6 +34,7 @@ do cat >"$TESTFILE" < #ifndef $PROTECT diff --git a/tools/cibadmin.c b/tools/cibadmin.c index 935e087b387..9b05acc0758 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.c @@ -331,7 +331,7 @@ cibadmin_post_upgrade(pcmk__output_t *out, cib_t *cib_conn, int call_options, if (cib_conn->cmds->query(cib_conn, NULL, &obj, call_options) == pcmk_ok) { - pcmk__update_schema(&obj, NULL, true, false); + pcmk__update_schema(&obj, NULL, false); } pcmk__xml_free(obj); } diff --git a/tools/cibsecret.c b/tools/cibsecret.c index bbf1d318a52..4e520ed0707 100644 --- a/tools/cibsecret.c +++ b/tools/cibsecret.c @@ -25,7 +25,6 @@ #include // xmlChar #include // cib__clean_up_connection, cib__signon_query -#include #include #include // crm_element_value, PCMK_XA_* diff --git a/tools/crm_mon.c b/tools/crm_mon.c index c45a4dfe2a4..2c615249943 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1090,7 +1090,7 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, void *user_data) * Exit with an error, otherwise the process would persist in the * background and significantly raise the CPU usage. */ - if ((condition & G_IO_ERR) && (condition & G_IO_HUP)) { + if (pcmk__all_flags_set(condition, G_IO_ERR|G_IO_HUP)) { rc = G_SOURCE_REMOVE; clean_up(CRM_EX_IOERR); } @@ -1098,11 +1098,11 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, void *user_data) /* The connection/fd has been closed. Refresh the screen and remove this * event source hence ignore stdin. */ - if (condition & (G_IO_HUP | G_IO_NVAL)) { + if (pcmk__any_flags_set(condition, G_IO_HUP|G_IO_NVAL)) { rc = G_SOURCE_REMOVE; } - if ((condition & G_IO_IN) == 0) { + if (!pcmk__is_set(condition, G_IO_IN)) { return rc; } diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 750c3d7d2cf..4150824c24a 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -2379,9 +2379,7 @@ main(int argc, char **argv) cib__clean_up_connection(&cib_conn); pcmk_free_ipc_api(controld_api); pcmk_free_scheduler(scheduler); - if (mainloop != NULL) { - g_main_loop_unref(mainloop); - } + g_clear_pointer(&mainloop, g_main_loop_unref); pcmk__output_and_clear_error(&error, out); diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c index 217e3b203e9..6e81367a184 100644 --- a/tools/crm_resource_runtime.c +++ b/tools/crm_resource_runtime.c @@ -27,7 +27,6 @@ #include // PCMK_ACTION_MONITOR #include // pcmk_get_ra_caps, pcmk_ra_cap_* #include // pcmk_cib_xpath_for -#include #include // pcmk_ipc_api_t #include // pcmk_controld_api_* #include // crm_time_new diff --git a/tools/stonith_admin.c b/tools/stonith_admin.c index ef8face6f3d..4780ce663c6 100644 --- a/tools/stonith_admin.c +++ b/tools/stonith_admin.c @@ -26,7 +26,6 @@ #include #include -#include #include #include // stonith__register_messages()