diff --git a/include/crm/common/ipc.h b/include/crm/common/ipc.h index 02eac4f106..1fc570a9a4 100644 --- a/include/crm/common/ipc.h +++ b/include/crm/common/ipc.h @@ -1,218 +1,213 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__CRM_COMMON_IPC__H #define PCMK__CRM_COMMON_IPC__H #include #include #include #ifdef __cplusplus extern "C" { #endif /** * \file * \brief IPC interface to Pacemaker daemons * * \ingroup core */ /* * The library supports two methods of creating IPC connections. The older code * allows connecting to any arbitrary IPC name. The newer code only allows * connecting to one of the Pacemaker daemons. * * As daemons are converted to use the new model, the old functions should be * considered deprecated for use with those daemons. Once all daemons are * converted, the old functions should be officially deprecated as public API * and eventually made internal API. */ /* * Pacemaker daemon IPC */ /* @COMPAT This is also used internally for cluster message types, but it's not * worth the hassle of redefining this public API just to change the name. */ //! Available IPC interfaces enum pcmk_ipc_server { pcmk_ipc_unknown, //!< Unknown or invalid pcmk_ipc_attrd, //!< Attribute manager pcmk_ipc_based, //!< CIB manager pcmk_ipc_controld, //!< Controller pcmk_ipc_execd, //!< Executor pcmk_ipc_fenced, //!< Fencer pcmk_ipc_pacemakerd, //!< Launcher pcmk_ipc_schedulerd, //!< Scheduler }; // NOTE: sbd (as of at least 1.5.2) uses this enum //! Possible event types that an IPC event callback can be called for enum pcmk_ipc_event { pcmk_ipc_event_connect, //!< Result of asynchronous connection attempt // NOTE: sbd (as of at least 1.5.2) uses this value pcmk_ipc_event_disconnect, //!< Termination of IPC connection // NOTE: sbd (as of at least 1.5.2) uses this value pcmk_ipc_event_reply, //!< Daemon's reply to client IPC request pcmk_ipc_event_notify, //!< Notification from daemon }; //! How IPC replies should be dispatched enum pcmk_ipc_dispatch { pcmk_ipc_dispatch_main, //!< Attach IPC to GMainLoop for dispatch pcmk_ipc_dispatch_poll, //!< Caller will poll and dispatch IPC pcmk_ipc_dispatch_sync, //!< Sending a command will wait for any reply }; // NOTE: sbd (as of at least 1.5.2) uses this //! Client connection to Pacemaker IPC typedef struct pcmk_ipc_api_s pcmk_ipc_api_t; /*! * \brief Callback function type for Pacemaker daemon IPC APIs * * \param[in,out] api IPC API connection * \param[in] event_type The type of event that occurred * \param[in] status Event status * \param[in,out] event_data Event-specific data * \param[in,out] user_data Caller data provided when callback was registered * * \note For connection and disconnection events, event_data may be NULL (for * local IPC) or the name of the connected node (for remote IPC, for * daemons that support that). For reply and notify events, event_data is * defined by the specific daemon API. */ typedef void (*pcmk_ipc_callback_t)(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data, void *user_data); // NOTE: sbd (as of at least 1.5.2) uses this int pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server); // NOTE: sbd (as of at least 1.5.2) uses this void pcmk_free_ipc_api(pcmk_ipc_api_t *api); // NOTE: sbd (as of at least 1.5.2) uses this int pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type); void pcmk_disconnect_ipc(pcmk_ipc_api_t *api); int pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms); void pcmk_dispatch_ipc(pcmk_ipc_api_t *api); // NOTE: sbd (as of at least 1.5.2) uses this void pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb, void *user_data); const char *pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log); bool pcmk_ipc_is_connected(pcmk_ipc_api_t *api); int pcmk_ipc_purge_node(pcmk_ipc_api_t *api, const char *node_name, uint32_t nodeid); /* * Generic IPC API (to eventually be deprecated as public API and made internal) */ /* *INDENT-OFF* */ enum crm_ipc_flags { crm_ipc_flags_none = 0x00000000, crm_ipc_compressed = 0x00000001, /* Message has been compressed */ crm_ipc_proxied = 0x00000100, /* _ALL_ replies to proxied connections need to be sent as events */ crm_ipc_client_response = 0x00000200, /* A Response is expected in reply */ // These are options for Pacemaker's internal use only (pcmk__ipc_send_*()) crm_ipc_server_event = 0x00010000, /* Send an Event instead of a Response */ crm_ipc_server_free = 0x00020000, /* Free the iovec after sending */ crm_ipc_proxied_relay_response = 0x00040000, /* all replies to proxied connections are sent as events, this flag preserves whether the event should be treated as an actual event, or a response.*/ }; /* *INDENT-ON* */ typedef struct crm_ipc_s crm_ipc_t; crm_ipc_t *crm_ipc_new(const char *name, size_t max_size); bool crm_ipc_connect(crm_ipc_t * client); void crm_ipc_close(crm_ipc_t * client); void crm_ipc_destroy(crm_ipc_t * client); void pcmk_free_ipc_event(struct iovec *event); int crm_ipc_send(crm_ipc_t *client, const xmlNode *message, enum crm_ipc_flags flags, int32_t ms_timeout, xmlNode **reply); int crm_ipc_get_fd(crm_ipc_t * client); bool crm_ipc_connected(crm_ipc_t * client); int crm_ipc_ready(crm_ipc_t * client); long crm_ipc_read(crm_ipc_t * client); const char *crm_ipc_buffer(crm_ipc_t * client); uint32_t crm_ipc_buffer_flags(crm_ipc_t * client); const char *crm_ipc_name(crm_ipc_t * client); unsigned int crm_ipc_default_buffer_size(void); /*! * \brief Check the authenticity of the IPC socket peer process (legacy) * * If everything goes well, peer's authenticity is verified by the means * of comparing against provided referential UID and GID (either satisfies), * and the result of this check can be deduced from the return value. * As an exception, detected UID of 0 ("root") satisfies arbitrary * provided referential daemon's credentials. * * \param[in] sock IPC related, connected Unix socket to check peer of * \param[in] refuid referential UID to check against * \param[in] refgid referential GID to check against * \param[out] gotpid to optionally store obtained PID of the peer * (not available on FreeBSD, special value of 1 * used instead, and the caller is required to * special case this value respectively) * \param[out] gotuid to optionally store obtained UID of the peer * \param[out] gotgid to optionally store obtained GID of the peer * * \return 0 if IPC related socket's peer is not authentic given the * referential credentials (see above), 1 if it is, * negative value on error (generally expressing -errno unless * it was zero even on nonhappy path, -pcmk_err_generic is * returned then; no message is directly emitted) * * \note While this function is tolerant on what constitutes authorized * IPC daemon process (its effective user matches UID=0 or \p refuid, * or at least its group matches \p refgid), either or both (in case * of UID=0) mismatches on the expected credentials of such peer * process \e shall be investigated at the caller when value of 1 * gets returned there, since higher-than-expected privileges in * respect to the expected/intended credentials possibly violate * the least privilege principle and may pose an additional risk * (i.e. such accidental inconsistency shall be eventually fixed). */ int crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid, pid_t *gotpid, uid_t *gotuid, gid_t *gotgid); -// @COMPAT Make internal when we can break API backward compatibility -//! \deprecated Do not use -xmlNode *create_hello_message(const char *uuid, const char *client_name, - const char *major_version, const char *minor_version); - #ifdef __cplusplus } #endif #endif diff --git a/lib/common/ipc_controld.c b/lib/common/ipc_controld.c index f08b1b8445..37e1f50aa6 100644 --- a/lib/common/ipc_controld.c +++ b/lib/common/ipc_controld.c @@ -1,672 +1,674 @@ /* * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include // PRIu32 #include #include // uint32_t #include #include #include #include #include #include #include #include "crmcommon_private.h" struct controld_api_private_s { char *client_uuid; unsigned int replies_expected; }; +static xmlNode *create_hello_message(const char *uuid, const char *client_name, + const char *major_version, + const char *minor_version); + /*! * \internal * \brief Get a string representation of a controller API reply type * * \param[in] reply Controller API reply type * * \return String representation of a controller API reply type */ const char * pcmk__controld_api_reply2str(enum pcmk_controld_api_reply reply) { switch (reply) { case pcmk_controld_reply_reprobe: return "reprobe"; case pcmk_controld_reply_info: return "info"; case pcmk_controld_reply_resource: return "resource"; case pcmk_controld_reply_ping: return "ping"; case pcmk_controld_reply_nodes: return "nodes"; default: return "unknown"; } } // \return Standard Pacemaker return code static int new_data(pcmk_ipc_api_t *api) { struct controld_api_private_s *private = NULL; api->api_data = calloc(1, sizeof(struct controld_api_private_s)); if (api->api_data == NULL) { return errno; } private = api->api_data; /* This is set to the PID because that's how it was always done, but PIDs * are not unique because clients can be remote. The value appears to be * unused other than as part of PCMK__XA_CRM_SYS_FROM in IPC requests, which * is only compared against the internal system names (CRM_SYSTEM_TENGINE, * etc.), so it shouldn't be a problem. */ private->client_uuid = pcmk__getpid_s(); /* @TODO Implement a call ID model similar to the CIB, executor, and fencer * IPC APIs, so that requests and replies can be matched, and * duplicate replies can be discarded. */ return pcmk_rc_ok; } static void free_data(void *data) { free(((struct controld_api_private_s *) data)->client_uuid); free(data); } // \return Standard Pacemaker return code static int post_connect(pcmk_ipc_api_t *api) { /* The controller currently requires clients to register via a hello * request, but does not reply back. */ struct controld_api_private_s *private = api->api_data; const char *client_name = crm_system_name? crm_system_name : "client"; xmlNode *hello; int rc; hello = create_hello_message(private->client_uuid, client_name, PCMK__CONTROLD_API_MAJOR, PCMK__CONTROLD_API_MINOR); rc = pcmk__send_ipc_request(api, hello); pcmk__xml_free(hello); if (rc != pcmk_rc_ok) { crm_info("Could not send IPC hello to %s: %s " QB_XS " rc=%s", pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc); } else { crm_debug("Sent IPC hello to %s", pcmk_ipc_name(api, true)); } return rc; } static void set_node_info_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data) { data->reply_type = pcmk_controld_reply_info; if (msg_data == NULL) { return; } data->data.node_info.have_quorum = pcmk__xe_attr_is_true(msg_data, PCMK_XA_HAVE_QUORUM); data->data.node_info.is_remote = pcmk__xe_attr_is_true(msg_data, PCMK_XA_REMOTE_NODE); /* Integer node_info.id is currently valid only for Corosync nodes. * * @TODO: Improve handling after pcmk__node_status_t is refactored to handle * layer-specific data better. */ crm_element_value_int(msg_data, PCMK_XA_ID, &(data->data.node_info.id)); data->data.node_info.uuid = crm_element_value(msg_data, PCMK_XA_ID); data->data.node_info.uname = crm_element_value(msg_data, PCMK_XA_UNAME); data->data.node_info.state = crm_element_value(msg_data, PCMK_XA_CRMD); } static void set_ping_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data) { data->reply_type = pcmk_controld_reply_ping; if (msg_data == NULL) { return; } data->data.ping.sys_from = crm_element_value(msg_data, PCMK__XA_CRM_SUBSYSTEM); data->data.ping.fsa_state = crm_element_value(msg_data, PCMK__XA_CRMD_STATE); data->data.ping.result = crm_element_value(msg_data, PCMK_XA_RESULT); } static void set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data) { pcmk_controld_api_node_t *node_info; data->reply_type = pcmk_controld_reply_nodes; for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL, NULL); node != NULL; node = pcmk__xe_next_same(node)) { long long id_ll = 0; node_info = pcmk__assert_alloc(1, sizeof(pcmk_controld_api_node_t)); crm_element_value_ll(node, PCMK_XA_ID, &id_ll); if (id_ll > 0) { node_info->id = id_ll; } node_info->uname = crm_element_value(node, PCMK_XA_UNAME); node_info->state = crm_element_value(node, PCMK__XA_IN_CCM); data->data.nodes = g_list_prepend(data->data.nodes, node_info); } } static bool reply_expected(pcmk_ipc_api_t *api, const xmlNode *request) { // We only need to handle commands that API functions can send return pcmk__str_any_of(crm_element_value(request, PCMK__XA_CRM_TASK), PCMK__CONTROLD_CMD_NODES, CRM_OP_LRM_DELETE, CRM_OP_LRM_FAIL, CRM_OP_NODE_INFO, CRM_OP_PING, CRM_OP_REPROBE, CRM_OP_RM_NODE_CACHE, NULL); } static bool dispatch(pcmk_ipc_api_t *api, xmlNode *reply) { struct controld_api_private_s *private = api->api_data; crm_exit_t status = CRM_EX_OK; xmlNode *wrapper = NULL; xmlNode *msg_data = NULL; const char *value = NULL; pcmk_controld_api_reply_t reply_data = { pcmk_controld_reply_unknown, NULL, NULL, }; if (pcmk__xe_is(reply, PCMK__XE_ACK)) { /* ACKs are trivial responses that do not count toward expected replies, * and do not have all the fields that validation requires, so skip that * processing. */ return private->replies_expected > 0; } if (private->replies_expected > 0) { private->replies_expected--; } // Do some basic validation of the reply value = crm_element_value(reply, PCMK__XA_SUBT); if (pcmk__str_eq(value, PCMK__VALUE_REQUEST, pcmk__str_none)) { /* @COMPAT Controllers <3.0.0 set PCMK__XA_SUBT to PCMK__VALUE_REQUEST * for certain replies. Once we no longer support Pacemaker Remote nodes * connecting to cluster nodes <3.0.0, or rolling upgrades from <3.0.0, * we can drop this check. */ crm_trace("Received a reply that was marked as a request " "(bug unless sent by a controller <3.0.0)"); } else if (!pcmk__str_eq(value, PCMK__VALUE_RESPONSE, pcmk__str_none)) { crm_info("Unrecognizable message from controller: " "invalid message type '%s'", pcmk__s(value, "")); status = CRM_EX_PROTOCOL; goto done; } if (pcmk__str_empty(crm_element_value(reply, PCMK_XA_REFERENCE))) { crm_info("Unrecognizable message from controller: no reference"); status = CRM_EX_PROTOCOL; goto done; } value = crm_element_value(reply, PCMK__XA_CRM_TASK); if (pcmk__str_empty(value)) { crm_info("Unrecognizable message from controller: no command name"); status = CRM_EX_PROTOCOL; goto done; } // Parse useful info from reply reply_data.feature_set = crm_element_value(reply, PCMK_XA_VERSION); reply_data.host_from = crm_element_value(reply, PCMK__XA_SRC); wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL); msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); if (!strcmp(value, CRM_OP_REPROBE)) { reply_data.reply_type = pcmk_controld_reply_reprobe; } else if (!strcmp(value, CRM_OP_NODE_INFO)) { set_node_info_data(&reply_data, msg_data); } else if (!strcmp(value, CRM_OP_INVOKE_LRM)) { reply_data.reply_type = pcmk_controld_reply_resource; reply_data.data.resource.node_state = msg_data; } else if (!strcmp(value, CRM_OP_PING)) { set_ping_data(&reply_data, msg_data); } else if (!strcmp(value, PCMK__CONTROLD_CMD_NODES)) { set_nodes_data(&reply_data, msg_data); } else { crm_info("Unrecognizable message from controller: unknown command '%s'", value); status = CRM_EX_PROTOCOL; } done: pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data); // Free any reply data that was allocated if (pcmk__str_eq(value, PCMK__CONTROLD_CMD_NODES, pcmk__str_casei)) { g_list_free_full(reply_data.data.nodes, free); } return false; // No further replies needed } pcmk__ipc_methods_t * pcmk__controld_api_methods(void) { pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t)); if (cmds != NULL) { cmds->new_data = new_data; cmds->free_data = free_data; cmds->post_connect = post_connect; cmds->reply_expected = reply_expected; cmds->dispatch = dispatch; } return cmds; } /*! * \internal * \brief Create XML for a controller IPC request * * \param[in] api Controller connection * \param[in] op Controller IPC command name * \param[in] node Node name to set as destination host * \param[in] msg_data XML to attach to request as message data * * \return Newly allocated XML for request */ static xmlNode * create_controller_request(const pcmk_ipc_api_t *api, const char *op, const char *node, xmlNode *msg_data) { struct controld_api_private_s *private = NULL; const char *sys_to = NULL; char *sender_system = NULL; xmlNode *request = NULL; if (api == NULL) { return NULL; } private = api->api_data; if ((node == NULL) && !strcmp(op, CRM_OP_PING)) { sys_to = CRM_SYSTEM_DC; } else { sys_to = CRM_SYSTEM_CRMD; } sender_system = crm_strdup_printf("%s_%s", private->client_uuid, pcmk__s(crm_system_name, "client")); request = pcmk__new_request(pcmk_ipc_controld, sender_system, node, sys_to, op, msg_data); free(sender_system); return request; } // \return Standard Pacemaker return code static int send_controller_request(pcmk_ipc_api_t *api, const xmlNode *request, bool reply_is_expected) { if (crm_element_value(request, PCMK_XA_REFERENCE) == NULL) { return EINVAL; } if (reply_is_expected) { struct controld_api_private_s *private = api->api_data; private->replies_expected++; } return pcmk__send_ipc_request(api, request); } static xmlNode * create_reprobe_message_data(const char *target_node, const char *router_node) { xmlNode *msg_data; msg_data = pcmk__xe_create(NULL, "data_for_" CRM_OP_REPROBE); crm_xml_add(msg_data, PCMK__META_ON_NODE, target_node); if ((router_node != NULL) && !pcmk__str_eq(router_node, target_node, pcmk__str_casei)) { crm_xml_add(msg_data, PCMK__XA_ROUTER_NODE, router_node); } return msg_data; } /*! * \brief Send a reprobe controller operation * * \param[in,out] api Controller connection * \param[in] target_node Name of node to reprobe * \param[in] router_node Router node for host * * \return Standard Pacemaker return code * \note Event callback will get a reply of type pcmk_controld_reply_reprobe. */ int pcmk_controld_api_reprobe(pcmk_ipc_api_t *api, const char *target_node, const char *router_node) { xmlNode *request; xmlNode *msg_data; int rc = pcmk_rc_ok; if (api == NULL) { return EINVAL; } if (router_node == NULL) { router_node = target_node; } crm_debug("Sending %s IPC request to reprobe %s via %s", pcmk_ipc_name(api, true), pcmk__s(target_node, "local node"), pcmk__s(router_node, "local node")); msg_data = create_reprobe_message_data(target_node, router_node); request = create_controller_request(api, CRM_OP_REPROBE, router_node, msg_data); rc = send_controller_request(api, request, true); pcmk__xml_free(msg_data); pcmk__xml_free(request); return rc; } /*! * \brief Send a "node info" controller operation * * \param[in,out] api Controller connection * \param[in] nodeid ID of node to get info for (or 0 for local node) * * \return Standard Pacemaker return code * \note Event callback will get a reply of type pcmk_controld_reply_info. */ int pcmk_controld_api_node_info(pcmk_ipc_api_t *api, uint32_t nodeid) { xmlNode *request; int rc = pcmk_rc_ok; request = create_controller_request(api, CRM_OP_NODE_INFO, NULL, NULL); if (request == NULL) { return EINVAL; } if (nodeid > 0) { crm_xml_add_ll(request, PCMK_XA_ID, nodeid); } rc = send_controller_request(api, request, true); pcmk__xml_free(request); return rc; } /*! * \brief Ask the controller for status * * \param[in,out] api Controller connection * \param[in] node_name Name of node whose status is desired (NULL for DC) * * \return Standard Pacemaker return code * \note Event callback will get a reply of type pcmk_controld_reply_ping. */ int pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name) { xmlNode *request; int rc = pcmk_rc_ok; request = create_controller_request(api, CRM_OP_PING, node_name, NULL); if (request == NULL) { return EINVAL; } rc = send_controller_request(api, request, true); pcmk__xml_free(request); return rc; } /*! * \brief Ask the controller for cluster information * * \param[in,out] api Controller connection * * \return Standard Pacemaker return code * \note Event callback will get a reply of type pcmk_controld_reply_nodes. */ int pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api) { xmlNode *request; int rc = EINVAL; request = create_controller_request(api, PCMK__CONTROLD_CMD_NODES, NULL, NULL); if (request != NULL) { rc = send_controller_request(api, request, true); pcmk__xml_free(request); } return rc; } // \return Standard Pacemaker return code static int controller_resource_op(pcmk_ipc_api_t *api, const char *op, const char *target_node, const char *router_node, bool cib_only, const char *rsc_id, const char *rsc_long_id, const char *standard, const char *provider, const char *type) { int rc = pcmk_rc_ok; char *key; xmlNode *request, *msg_data, *xml_rsc, *params; if (api == NULL) { return EINVAL; } if (router_node == NULL) { router_node = target_node; } msg_data = pcmk__xe_create(NULL, PCMK__XE_RSC_OP); /* The controller logs the transition key from resource op requests, so we * need to have *something* for it. * @TODO don't use "crm-resource" */ key = pcmk__transition_key(0, getpid(), 0, "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx"); crm_xml_add(msg_data, PCMK__XA_TRANSITION_KEY, key); free(key); crm_xml_add(msg_data, PCMK__META_ON_NODE, target_node); if (!pcmk__str_eq(router_node, target_node, pcmk__str_casei)) { crm_xml_add(msg_data, PCMK__XA_ROUTER_NODE, router_node); } if (cib_only) { // Indicate that only the CIB needs to be cleaned crm_xml_add(msg_data, PCMK__XA_MODE, PCMK__VALUE_CIB); } xml_rsc = pcmk__xe_create(msg_data, PCMK_XE_PRIMITIVE); crm_xml_add(xml_rsc, PCMK_XA_ID, rsc_id); crm_xml_add(xml_rsc, PCMK__XA_LONG_ID, rsc_long_id); crm_xml_add(xml_rsc, PCMK_XA_CLASS, standard); crm_xml_add(xml_rsc, PCMK_XA_PROVIDER, provider); crm_xml_add(xml_rsc, PCMK_XA_TYPE, type); params = pcmk__xe_create(msg_data, PCMK__XE_ATTRIBUTES); crm_xml_add(params, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); // The controller parses the timeout from the request key = crm_meta_name(PCMK_META_TIMEOUT); crm_xml_add(params, key, "60000"); /* 1 minute */ //@TODO pass as arg free(key); request = create_controller_request(api, op, router_node, msg_data); rc = send_controller_request(api, request, true); pcmk__xml_free(msg_data); pcmk__xml_free(request); return rc; } /*! * \brief Ask the controller to fail a resource * * \param[in,out] api Controller connection * \param[in] target_node Name of node resource is on * \param[in] router_node Router node for target * \param[in] rsc_id ID of resource to fail * \param[in] rsc_long_id Long ID of resource (if any) * \param[in] standard Standard of resource * \param[in] provider Provider of resource (if any) * \param[in] type Type of resource to fail * * \return Standard Pacemaker return code * \note Event callback will get a reply of type pcmk_controld_reply_resource. */ int pcmk_controld_api_fail(pcmk_ipc_api_t *api, const char *target_node, const char *router_node, const char *rsc_id, const char *rsc_long_id, const char *standard, const char *provider, const char *type) { crm_debug("Sending %s IPC request to fail %s (a.k.a. %s) on %s via %s", pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"), pcmk__s(rsc_long_id, "no other names"), pcmk__s(target_node, "unspecified node"), pcmk__s(router_node, "unspecified node")); return controller_resource_op(api, CRM_OP_LRM_FAIL, target_node, router_node, false, rsc_id, rsc_long_id, standard, provider, type); } /*! * \brief Ask the controller to refresh a resource * * \param[in,out] api Controller connection * \param[in] target_node Name of node resource is on * \param[in] router_node Router node for target * \param[in] rsc_id ID of resource to refresh * \param[in] rsc_long_id Long ID of resource (if any) * \param[in] standard Standard of resource * \param[in] provider Provider of resource (if any) * \param[in] type Type of resource * \param[in] cib_only If true, clean resource from CIB only * * \return Standard Pacemaker return code * \note Event callback will get a reply of type pcmk_controld_reply_resource. */ int pcmk_controld_api_refresh(pcmk_ipc_api_t *api, const char *target_node, const char *router_node, const char *rsc_id, const char *rsc_long_id, const char *standard, const char *provider, const char *type, bool cib_only) { crm_debug("Sending %s IPC request to refresh %s (a.k.a. %s) on %s via %s", pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"), pcmk__s(rsc_long_id, "no other names"), pcmk__s(target_node, "unspecified node"), pcmk__s(router_node, "unspecified node")); return controller_resource_op(api, CRM_OP_LRM_DELETE, target_node, router_node, cib_only, rsc_id, rsc_long_id, standard, provider, type); } /*! * \brief Get the number of IPC replies currently expected from the controller * * \param[in] api Controller IPC API connection * * \return Number of replies expected */ unsigned int pcmk_controld_api_replies_expected(const pcmk_ipc_api_t *api) { struct controld_api_private_s *private = api->api_data; return private->replies_expected; } /*! + * \internal * \brief Create XML for a controller IPC "hello" message - * - * \deprecated This function is deprecated as part of the public C API. */ -// \todo make this static to this file when breaking API backward compatibility -xmlNode * +static xmlNode * create_hello_message(const char *uuid, const char *client_name, const char *major_version, const char *minor_version) { xmlNode *hello_node = NULL; xmlNode *hello = NULL; char *sender_system = NULL; if (pcmk__str_empty(uuid) || pcmk__str_empty(client_name) || pcmk__str_empty(major_version) || pcmk__str_empty(minor_version)) { crm_err("Could not create IPC hello message from %s (UUID %s): " "missing information", client_name? client_name : "unknown client", uuid? uuid : "unknown"); return NULL; } hello_node = pcmk__xe_create(NULL, PCMK__XE_OPTIONS); crm_xml_add(hello_node, PCMK__XA_MAJOR_VERSION, major_version); crm_xml_add(hello_node, PCMK__XA_MINOR_VERSION, minor_version); crm_xml_add(hello_node, PCMK__XA_CLIENT_NAME, client_name); // @TODO Nothing uses this. Drop, or keep for debugging? crm_xml_add(hello_node, PCMK__XA_CLIENT_UUID, uuid); sender_system = crm_strdup_printf("%s_%s", uuid, client_name); hello = pcmk__new_request(pcmk_ipc_controld, sender_system, NULL, NULL, CRM_OP_HELLO, hello_node); free(sender_system); pcmk__xml_free(hello_node); if (hello == NULL) { crm_err("Could not create IPC hello message from %s (UUID %s): " "Request creation failed", client_name, uuid); return NULL; } crm_trace("Created hello message from %s (UUID %s)", client_name, uuid); return hello; }