Line data Source code
1 : /*
2 : * Copyright 2015-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 : #include <crm_internal.h>
11 :
12 : #include <glib.h>
13 : #include <unistd.h>
14 :
15 : #include <crm/crm.h>
16 : #include <crm/common/xml.h>
17 : #include <crm/services.h>
18 : #include <crm/common/mainloop.h>
19 :
20 : #include <crm/pengine/status.h>
21 : #include <crm/cib.h>
22 : #include <crm/lrmd.h>
23 : #include <crm/lrmd_internal.h>
24 :
25 : int lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg);
26 : GHashTable *proxy_table = NULL;
27 :
28 : static void
29 0 : remote_proxy_notify_destroy(lrmd_t *lrmd, const char *session_id)
30 : {
31 : /* sending to the remote node that an ipc connection has been destroyed */
32 0 : xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
33 0 : crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY);
34 0 : crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, session_id);
35 0 : lrmd_internal_proxy_send(lrmd, msg);
36 0 : free_xml(msg);
37 0 : }
38 :
39 : /*!
40 : * \internal
41 : * \brief Acknowledge a remote proxy shutdown request
42 : *
43 : * \param[in,out] lrmd Connection to proxy
44 : */
45 : void
46 0 : remote_proxy_ack_shutdown(lrmd_t *lrmd)
47 : {
48 0 : xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
49 0 : crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_ACK);
50 0 : lrmd_internal_proxy_send(lrmd, msg);
51 0 : free_xml(msg);
52 0 : }
53 :
54 : /*!
55 : * \internal
56 : * \brief Reject a remote proxy shutdown request
57 : *
58 : * \param[in,out] lrmd Connection to proxy
59 : */
60 : void
61 0 : remote_proxy_nack_shutdown(lrmd_t *lrmd)
62 : {
63 0 : xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
64 0 : crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_NACK);
65 0 : lrmd_internal_proxy_send(lrmd, msg);
66 0 : free_xml(msg);
67 0 : }
68 :
69 : void
70 0 : remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg)
71 : {
72 : /* sending to the remote node an event msg. */
73 0 : xmlNode *event = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
74 0 : xmlNode *wrapper = NULL;
75 :
76 0 : crm_xml_add(event, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_EVENT);
77 0 : crm_xml_add(event, PCMK__XA_LRMD_IPC_SESSION, proxy->session_id);
78 :
79 0 : wrapper = pcmk__xe_create(event, PCMK__XE_LRMD_IPC_MSG);
80 0 : pcmk__xml_copy(wrapper, msg);
81 :
82 0 : crm_log_xml_explicit(event, "EventForProxy");
83 0 : lrmd_internal_proxy_send(proxy->lrm, event);
84 0 : free_xml(event);
85 0 : }
86 :
87 : void
88 0 : remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id)
89 : {
90 : /* sending to the remote node a response msg. */
91 0 : xmlNode *response = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
92 0 : xmlNode *wrapper = NULL;
93 :
94 0 : crm_xml_add(response, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_RESPONSE);
95 0 : crm_xml_add(response, PCMK__XA_LRMD_IPC_SESSION, proxy->session_id);
96 0 : crm_xml_add_int(response, PCMK__XA_LRMD_IPC_MSG_ID, msg_id);
97 :
98 0 : wrapper = pcmk__xe_create(response, PCMK__XE_LRMD_IPC_MSG);
99 0 : pcmk__xml_copy(wrapper, msg);
100 :
101 0 : lrmd_internal_proxy_send(proxy->lrm, response);
102 0 : free_xml(response);
103 0 : }
104 :
105 : static void
106 0 : remote_proxy_end_session(remote_proxy_t *proxy)
107 : {
108 0 : if (proxy == NULL) {
109 0 : return;
110 : }
111 0 : crm_trace("ending session ID %s", proxy->session_id);
112 :
113 0 : if (proxy->source) {
114 0 : mainloop_del_ipc_client(proxy->source);
115 : }
116 : }
117 :
118 : void
119 0 : remote_proxy_free(gpointer data)
120 : {
121 0 : remote_proxy_t *proxy = data;
122 :
123 0 : crm_trace("freed proxy session ID %s", proxy->session_id);
124 0 : free(proxy->node_name);
125 0 : free(proxy->session_id);
126 0 : free(proxy);
127 0 : }
128 :
129 : int
130 0 : remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata)
131 : {
132 : // Async responses from cib and friends to clients via pacemaker-remoted
133 0 : xmlNode *xml = NULL;
134 0 : uint32_t flags = 0;
135 0 : remote_proxy_t *proxy = userdata;
136 :
137 0 : xml = pcmk__xml_parse(buffer);
138 0 : if (xml == NULL) {
139 0 : crm_warn("Received a NULL msg from IPC service.");
140 0 : return 1;
141 : }
142 :
143 0 : flags = crm_ipc_buffer_flags(proxy->ipc);
144 0 : if (flags & crm_ipc_proxied_relay_response) {
145 0 : crm_trace("Passing response back to %.8s on %s: %.200s - request id: %d", proxy->session_id, proxy->node_name, buffer, proxy->last_request_id);
146 0 : remote_proxy_relay_response(proxy, xml, proxy->last_request_id);
147 0 : proxy->last_request_id = 0;
148 :
149 : } else {
150 0 : crm_trace("Passing event back to %.8s on %s: %.200s", proxy->session_id, proxy->node_name, buffer);
151 0 : remote_proxy_relay_event(proxy, xml);
152 : }
153 0 : free_xml(xml);
154 0 : return 1;
155 : }
156 :
157 :
158 : void
159 0 : remote_proxy_disconnected(gpointer userdata)
160 : {
161 0 : remote_proxy_t *proxy = userdata;
162 :
163 0 : crm_trace("destroying %p", proxy);
164 :
165 0 : proxy->source = NULL;
166 0 : proxy->ipc = NULL;
167 :
168 0 : if(proxy->lrm) {
169 0 : remote_proxy_notify_destroy(proxy->lrm, proxy->session_id);
170 0 : proxy->lrm = NULL;
171 : }
172 :
173 0 : g_hash_table_remove(proxy_table, proxy->session_id);
174 0 : }
175 :
176 : remote_proxy_t *
177 0 : remote_proxy_new(lrmd_t *lrmd, struct ipc_client_callbacks *proxy_callbacks,
178 : const char *node_name, const char *session_id, const char *channel)
179 : {
180 0 : remote_proxy_t *proxy = NULL;
181 :
182 0 : if(channel == NULL) {
183 0 : crm_err("No channel specified to proxy");
184 0 : remote_proxy_notify_destroy(lrmd, session_id);
185 0 : return NULL;
186 : }
187 :
188 0 : proxy = pcmk__assert_alloc(1, sizeof(remote_proxy_t));
189 :
190 0 : proxy->node_name = strdup(node_name);
191 0 : proxy->session_id = strdup(session_id);
192 0 : proxy->lrm = lrmd;
193 :
194 0 : if (!strcmp(pcmk__message_name(crm_system_name), CRM_SYSTEM_CRMD)
195 0 : && !strcmp(pcmk__message_name(channel), CRM_SYSTEM_CRMD)) {
196 : // The controller doesn't need to connect to itself
197 0 : proxy->is_local = TRUE;
198 :
199 : } else {
200 0 : proxy->source = mainloop_add_ipc_client(channel, G_PRIORITY_LOW, 0, proxy, proxy_callbacks);
201 0 : proxy->ipc = mainloop_get_ipc_client(proxy->source);
202 0 : if (proxy->source == NULL) {
203 0 : remote_proxy_free(proxy);
204 0 : remote_proxy_notify_destroy(lrmd, session_id);
205 0 : return NULL;
206 : }
207 : }
208 :
209 0 : crm_trace("new remote proxy client established to %s on %s, session id %s",
210 : channel, node_name, session_id);
211 0 : g_hash_table_insert(proxy_table, proxy->session_id, proxy);
212 :
213 0 : return proxy;
214 : }
215 :
216 : void
217 0 : remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg)
218 : {
219 0 : const char *op = crm_element_value(msg, PCMK__XA_LRMD_IPC_OP);
220 0 : const char *session = crm_element_value(msg, PCMK__XA_LRMD_IPC_SESSION);
221 0 : remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session);
222 0 : int msg_id = 0;
223 :
224 : /* sessions are raw ipc connections to IPC,
225 : * all we do is proxy requests/responses exactly
226 : * like they are given to us at the ipc level. */
227 :
228 0 : CRM_CHECK(op != NULL, return);
229 0 : CRM_CHECK(session != NULL, return);
230 :
231 0 : crm_element_value_int(msg, PCMK__XA_LRMD_IPC_MSG_ID, &msg_id);
232 : /* This is msg from remote ipc client going to real ipc server */
233 :
234 0 : if (pcmk__str_eq(op, LRMD_IPC_OP_DESTROY, pcmk__str_casei)) {
235 0 : remote_proxy_end_session(proxy);
236 :
237 0 : } else if (pcmk__str_eq(op, LRMD_IPC_OP_REQUEST, pcmk__str_casei)) {
238 0 : int flags = 0;
239 0 : const char *name = crm_element_value(msg, PCMK__XA_LRMD_IPC_CLIENT);
240 :
241 0 : xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_LRMD_IPC_MSG,
242 : NULL, NULL);
243 0 : xmlNode *request = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
244 :
245 0 : CRM_CHECK(request != NULL, return);
246 :
247 0 : if (proxy == NULL) {
248 : /* proxy connection no longer exists */
249 0 : remote_proxy_notify_destroy(lrmd, session);
250 0 : return;
251 : }
252 :
253 : // Controller requests MUST be handled by the controller, not us
254 0 : CRM_CHECK(proxy->is_local == FALSE,
255 : remote_proxy_end_session(proxy); return);
256 :
257 0 : if (!crm_ipc_connected(proxy->ipc)) {
258 0 : remote_proxy_end_session(proxy);
259 0 : return;
260 : }
261 0 : proxy->last_request_id = 0;
262 0 : crm_element_value_int(msg, PCMK__XA_LRMD_IPC_MSG_FLAGS, &flags);
263 0 : crm_xml_add(request, PCMK_XE_ACL_ROLE, "pacemaker-remote");
264 :
265 0 : CRM_ASSERT(node_name);
266 0 : pcmk__update_acl_user(request, PCMK__XA_LRMD_IPC_USER, node_name);
267 :
268 0 : if (pcmk_is_set(flags, crm_ipc_proxied)) {
269 0 : const char *type = crm_element_value(request, PCMK__XA_T);
270 0 : int rc = 0;
271 :
272 0 : if (pcmk__str_eq(type, PCMK__VALUE_ATTRD, pcmk__str_none)
273 0 : && (crm_element_value(request, PCMK__XA_ATTR_HOST) == NULL)
274 0 : && pcmk__str_any_of(crm_element_value(request, PCMK_XA_TASK),
275 : PCMK__ATTRD_CMD_UPDATE,
276 : PCMK__ATTRD_CMD_UPDATE_BOTH,
277 : PCMK__ATTRD_CMD_UPDATE_DELAY, NULL)) {
278 0 : pcmk__xe_add_node(request, proxy->node_name, 0);
279 : }
280 :
281 0 : rc = crm_ipc_send(proxy->ipc, request, flags, 5000, NULL);
282 :
283 0 : if(rc < 0) {
284 0 : xmlNode *op_reply = pcmk__xe_create(NULL, PCMK__XE_NACK);
285 :
286 0 : crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)",
287 : op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc);
288 :
289 : /* Send a n'ack so the caller doesn't block */
290 0 : crm_xml_add(op_reply, PCMK_XA_FUNCTION, __func__);
291 0 : crm_xml_add_int(op_reply, PCMK__XA_LINE, __LINE__);
292 0 : crm_xml_add_int(op_reply, PCMK_XA_RC, rc);
293 0 : remote_proxy_relay_response(proxy, op_reply, msg_id);
294 0 : free_xml(op_reply);
295 :
296 : } else {
297 0 : crm_trace("Relayed %s request %d from %s to %s for %s",
298 : op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name);
299 0 : proxy->last_request_id = msg_id;
300 : }
301 :
302 : } else {
303 0 : int rc = pcmk_ok;
304 0 : xmlNode *op_reply = NULL;
305 : // @COMPAT pacemaker_remoted <= 1.1.10
306 :
307 0 : crm_trace("Relaying %s request %d from %s to %s for %s",
308 : op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name);
309 :
310 0 : rc = crm_ipc_send(proxy->ipc, request, flags, 10000, &op_reply);
311 0 : if(rc < 0) {
312 0 : crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)",
313 : op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc);
314 : } else {
315 0 : crm_trace("Relayed %s request %d from %s to %s for %s",
316 : op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name);
317 : }
318 :
319 0 : if(op_reply) {
320 0 : remote_proxy_relay_response(proxy, op_reply, msg_id);
321 0 : free_xml(op_reply);
322 : }
323 : }
324 : } else {
325 0 : crm_err("Unknown proxy operation: %s", op);
326 : }
327 : }
|