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)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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