1 /* 2 * Copyright 2018-2024 the Pacemaker project contributors 3 * 4 * The version control history for this file may have further details. 5 * 6 * This source code is licensed under the GNU Lesser General Public License 7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. 8 */ 9 10 #ifndef PCMK__CRM_COMMON_MESSAGES_INTERNAL__H 11 #define PCMK__CRM_COMMON_MESSAGES_INTERNAL__H 12 13 #include <stdint.h> // uint32_t 14 #include <libxml/tree.h> // xmlNode 15 #include <crm/common/ipc_internal.h> // pcmk__client_t 16 #include <crm/common/results_internal.h> // pcmk__action_result_t 17 #include <crm/common/xml_internal.h> // pcmk__xml_copy() 18 19 #ifdef __cplusplus 20 extern "C" { 21 #endif 22 23 enum pcmk__request_flags { 24 pcmk__request_none = UINT32_C(0), 25 26 /* It would be nice if we could check for synchronous requests generically, 27 * but each daemon uses its own call options, so the daemons are responsible 28 * for setting this flag when appropriate. 29 */ 30 pcmk__request_sync = (UINT32_C(1) << 0), 31 32 /* Whether reply must use original call options (the library code does not 33 * use this, so it is for internal daemon use) 34 */ 35 pcmk__request_reuse_options = (UINT32_C(1) << 1), 36 }; 37 38 // Server request (whether from an IPC client or cluster peer) 39 typedef struct { 40 // If request is from an IPC client 41 pcmk__client_t *ipc_client; // IPC client (NULL if not via IPC) 42 uint32_t ipc_id; // IPC message ID 43 uint32_t ipc_flags; // IPC message flags 44 45 // If message is from a cluster peer 46 const char *peer; // Peer name (NULL if not via cluster) 47 48 // Common information regardless of origin 49 xmlNode *xml; // Request XML 50 int call_options; // Call options set on request 51 uint32_t flags; // Flag group of pcmk__request_flags 52 pcmk__action_result_t result; // Where to store operation result 53 54 /* It would be nice if we could pull the IPC command from the XML 55 * generically, but each daemon uses a different XML attribute for it, 56 * so the daemon is responsible for populating this field. 57 * 58 * This must be a copy of the XML field, and not just a pointer into xml, 59 * because handlers might modify the original XML. 60 * 61 * @TODO Create a per-daemon struct with IPC handlers, IPC endpoints, etc., 62 * and the name of the XML attribute for IPC commands, then replace this 63 * with a convenience function to copy the command. 64 */ 65 char *op; // IPC command name 66 } pcmk__request_t; 67 68 #define pcmk__set_request_flags(request, flags_to_set) do { \ 69 (request)->flags = pcmk__set_flags_as(__func__, __LINE__, \ 70 LOG_TRACE, "Request", "message", (request)->flags, \ 71 (flags_to_set), #flags_to_set); \ 72 } while (0) 73 74 // Type for mapping a server command to a handler 75 typedef struct { 76 const char *command; 77 xmlNode *(*handler)(pcmk__request_t *request); 78 } pcmk__server_command_t; 79 80 /*! 81 * \internal 82 * \brief Create message XML (for IPC or the cluster layer) 83 * 84 * Create standard, generic XML that can be used as a message sent via IPC or 85 * the cluster layer. Currently, not all IPC and cluster layer messaging uses 86 * this, but it should (eventually, keeping backward compatibility in mind). 87 * 88 * \param[in] server Server whose protocol defines message semantics 89 * \param[in] reply_to If NULL, create message as a request with a 90 * generated message ID, otherwise create message 91 * as a reply to this message ID 92 * \param[in] sender_system Sender's subsystem (required; this is an 93 * arbitrary string that may have meaning between 94 * the sender and recipient) 95 * \param[in] recipient_node If not NULL, add as message's recipient node 96 * (NULL typically indicates a broadcast message) 97 * \param[in] recipient_system If not NULL, add as message's recipient 98 * subsystem (this is an arbitrary string that may 99 * have meaning between the sender and recipient) 100 * \param[in] task Add as message's task (required) 101 * \param[in] data If not NULL, copy as message's data (callers 102 * should not add attributes to the returned 103 * message element, but instead pass any desired 104 * information here, though this is not always 105 * honored currently) 106 * 107 * \return Newly created message XML 108 * \note The caller is responsible for freeing the return value using 109 * \c pcmk__xml_free(). 110 */ 111 #define pcmk__new_message(server, reply_to, sender_system, \ 112 recipient_node, recipient_system, task, data) \ 113 pcmk__new_message_as(__func__, (server), (reply_to), \ 114 (sender_system), (recipient_node), \ 115 (recipient_system), (task), (data)) 116 117 /*! 118 * \internal 119 * \brief Create a Pacemaker request (for IPC or cluster layer) 120 * 121 * \param[in] server Server whose protocol defines message semantics 122 * \param[in] sender_system Sender's subsystem (required; this is an 123 * arbitrary string that may have meaning between 124 * the sender and recipient) 125 * \param[in] recipient_node If not NULL, add as message's recipient node 126 * (NULL typically indicates a broadcast message) 127 * \param[in] recipient_system If not NULL, add as message's recipient 128 * subsystem (this is an arbitrary string that may 129 * have meaning between the sender and recipient) 130 * \param[in] task Add as message's task (required) 131 * \param[in] data If not NULL, copy as message's data (callers 132 * should not add attributes to the returned 133 * message element, but instead pass any desired 134 * information here, though this is not always 135 * honored currently) 136 * 137 * \return Newly created request XML 138 * \note The caller is responsible for freeing the return value using 139 * \c pcmk__xml_free(). 140 */ 141 #define pcmk__new_request(server, sender_system, recipient_node, \ 142 recipient_system, task, data) \ 143 pcmk__new_message_as(__func__, (server), NULL, \ 144 (sender_system), (recipient_node), \ 145 (recipient_system), (task), (data)) 146 147 /*! 148 * \internal 149 * \brief Create a Pacemaker reply (for IPC or cluster layer) 150 * 151 * \param[in] original_request XML of request being replied to 152 * \param[in] data If not NULL, copy as reply's data (callers 153 * should not add attributes to the returned 154 * message element, but instead pass any desired 155 * information here, though this is not always 156 * honored currently) 157 * 158 * \return Newly created reply XML 159 * \note The caller is responsible for freeing the return value using 160 * \c pcmk__xml_free(). 161 */ 162 #define pcmk__new_reply(original_request, data) \ 163 pcmk__new_reply_as(__func__, (original_request), (data)) 164 165 xmlNode *pcmk__new_message_as(const char *origin, enum pcmk_ipc_server server, 166 const char *reply_to, const char *sender_system, 167 const char *recipient_node, 168 const char *recipient_system, const char *task, 169 xmlNode *data); 170 171 xmlNode *pcmk__new_reply_as(const char *origin, const xmlNode *original_request, 172 xmlNode *data); 173 174 GHashTable *pcmk__register_handlers(const pcmk__server_command_t handlers[]); 175 xmlNode *pcmk__process_request(pcmk__request_t *request, GHashTable *handlers); 176 void pcmk__reset_request(pcmk__request_t *request); 177 178 /*! 179 * \internal 180 * \brief Get a loggable description of a request's origin 181 * 182 * \param[in] request 183 * 184 * \return "peer" if request was via CPG, "client" if via IPC, or "originator" 185 * if unknown 186 */ 187 static inline const char * 188 pcmk__request_origin_type(const pcmk__request_t *request) /**/ 189 { 190 if ((request != NULL) && (request->ipc_client != NULL)) { 191 return "client"; 192 } else if ((request != NULL) && (request->peer != NULL)) { 193 return "peer"; 194 } else { 195 return "originator"; 196 } 197 } 198 199 /*! 200 * \internal 201 * \brief Get a loggable name for a request's origin 202 * 203 * \param[in] request 204 * 205 * \return Peer name if request was via CPG, client name if via IPC, or 206 * "(unspecified)" if unknown 207 */ 208 static inline const char * 209 pcmk__request_origin(const pcmk__request_t *request) /*
*/ 210 { 211 if ((request != NULL) && (request->ipc_client != NULL)) { 212 return pcmk__client_name(request->ipc_client); 213 } else if ((request != NULL) && (request->peer != NULL)) { 214 return request->peer; 215 } else { 216 return "(unspecified)"; 217 } 218 } 219 220 #ifdef __cplusplus 221 } 222 #endif 223 224 #endif // PCMK__CRM_COMMON_MESSAGES_INTERNAL__H