1    	/*
2    	 * Copyright 2022-2026 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 General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <inttypes.h>   // PRIu32
13   	#include <stdbool.h>
14   	
15   	#include <glib.h>
16   	
17   	#include <crm/cluster/internal.h>   // pcmk__get_node()
18   	#include <crm/common/xml.h>
19   	
20   	#include "pacemaker-attrd.h"
21   	
22   	int minimum_protocol_version = -1;
23   	
24   	static GHashTable *attrd_handlers = NULL;
25   	
26   	static bool
27   	is_sync_point_attr(xmlAttrPtr attr, void *data)
28   	{
29   	    return pcmk__str_eq((const char *) attr->name, PCMK__XA_ATTR_SYNC_POINT, pcmk__str_none);
30   	}
31   	
32   	static int
33   	remove_sync_point_attribute(xmlNode *xml, void *data)
34   	{
35   	    pcmk__xe_remove_matching_attrs(xml, false, is_sync_point_attr, NULL);
36   	    pcmk__xe_foreach_child(xml, PCMK_XE_OP, remove_sync_point_attribute, NULL);
37   	    return pcmk_rc_ok;
38   	}
39   	
40   	/* Sync points on a multi-update IPC message to an attrd too old to support
41   	 * multi-update messages won't work.  Strip the sync point attribute off here
42   	 * so we don't pretend to support this situation and instead ACK the client
43   	 * immediately.
44   	 */
45   	static void
46   	remove_unsupported_sync_points(pcmk__request_t *request)
47   	{
48   	    if (request->xml->children != NULL && !ATTRD_SUPPORTS_MULTI_MESSAGE(minimum_protocol_version) &&
49   	        attrd_request_has_sync_point(request->xml)) {
50   	        pcmk__warn("Ignoring sync point in request from %s because not all "
51   	                   "nodes support it",
52   	                   pcmk__request_origin(request));
53   	        remove_sync_point_attribute(request->xml, NULL);
54   	    }
55   	}
56   	
57   	static xmlNode *
58   	handle_unknown_request(pcmk__request_t *request)
59   	{
60   	    pcmk__err("Unknown %s request %s from %s %s",
61   	              (request->ipc_client != NULL) ? "IPC" : "CPG",
62   	              request->op, pcmk__request_origin_type(request),
63   	              pcmk__request_origin(request));
64   	    pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
65   	                        "Unknown request type '%s' (bug?)",
66   	                        pcmk__s(request->op, ""));
67   	    return NULL;
68   	}
69   	
70   	static xmlNode *
71   	handle_clear_failure_request(pcmk__request_t *request)
72   	{
73   	    if (request->peer != NULL) {
74   	        /* It is not currently possible to receive this as a peer command,
75   	         * but will be, if we one day enable propagating this operation.
76   	         */
77   	        attrd_peer_clear_failure(request);
78   	        pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
79   	        return NULL;
80   	    }
81   	
82   	    remove_unsupported_sync_points(request);
83   	
84   	    if (attrd_request_has_sync_point(request->xml)) {
85   	        /* If this client supplied a sync point it wants to wait for, add it to
86   	         * the wait list. Clients on this list will not receive an ACK until
87   	         * their sync point is hit which will result in the client stalled there
88   	         * until it receives a response.
89   	         *
90   	         * All other clients will receive the expected response as normal.
91   	         */
92   	        attrd_add_client_to_waitlist(request);
93   	
94   	    } else {
95   	        /* If the client doesn't want to wait for a sync point, go ahead and
96   	         * send the ACK immediately. Otherwise, we'll send the ACK when the
97   	         * appropriate sync point is reached.
98   	         */
99   	        attrd_send_ack(request->ipc_client, request->ipc_id,
100  	                       request->ipc_flags);
101  	    }
102  	
103  	    attrd_client_clear_failure(request);
104  	    return NULL;
105  	}
106  	
107  	static xmlNode *
108  	handle_confirm_request(pcmk__request_t *request)
109  	{
110  	    int callid = 0;
111  	
112  	    if (request->ipc_client != NULL) {
113  	        return handle_unknown_request(request);
114  	    }
115  	
116  	    pcmk__debug("Received confirmation from %s", request->peer);
117  	
118  	    if (pcmk__xe_get_int(request->xml, PCMK__XA_CALL_ID,
119  	                         &callid) != pcmk_rc_ok) {
120  	        pcmk__set_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
121  	                         "Could not get callid from XML");
122  	    } else {
123  	        attrd_handle_confirmation(callid, request->peer);
124  	    }
125  	
126  	    pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
127  	    return NULL;
128  	}
129  	
130  	static xmlNode *
131  	handle_query_request(pcmk__request_t *request)
132  	{
133  	    if (request->peer != NULL) {
134  	        return handle_unknown_request(request);
135  	    }
136  	
137  	    return attrd_client_query(request);
138  	}
139  	
140  	static xmlNode *
141  	handle_remove_request(pcmk__request_t *request)
142  	{
143  	    const char *host = NULL;
144  	    bool reap = false;
145  	
146  	    if (request->ipc_client != NULL) {
147  	        attrd_client_peer_remove(request);
148  	        return NULL;
149  	    }
150  	
151  	    host = pcmk__xe_get(request->xml, PCMK__XA_ATTR_HOST);
152  	
153  	    if (pcmk__xe_get_bool(request->xml, PCMK__XA_REAP,
154  	                          &reap) != pcmk_rc_ok) {
155  	        reap = true; // Default to true for backward compatibility
156  	    }
157  	
158  	    attrd_peer_remove(host, reap, request->peer);
159  	    pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
160  	    return NULL;
161  	}
162  	
163  	static xmlNode *
164  	handle_refresh_request(pcmk__request_t *request)
165  	{
166  	    if (request->peer != NULL) {
167  	        return handle_unknown_request(request);
168  	    }
169  	
170  	    attrd_client_refresh(request);
171  	    return NULL;
172  	}
173  	
174  	static xmlNode *
175  	handle_sync_response_request(pcmk__request_t *request)
176  	{
177  	    pcmk__node_status_t *peer = NULL;
178  	    bool peer_won = false;
179  	
180  	    if (request->ipc_client != NULL) {
181  	        return handle_unknown_request(request);
182  	    }
183  	
184  	    peer = pcmk__get_node(0, request->peer, NULL,
185  	                          pcmk__node_search_cluster_member);
186  	    peer_won = attrd_check_for_new_writer(peer, request->xml);
187  	
188  	    if (!pcmk__str_eq(peer->name, attrd_cluster->priv->node_name,
189  	                      pcmk__str_casei)) {
190  	        attrd_peer_sync_response(peer, peer_won, request->xml);
191  	    }
192  	
193  	    pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
194  	    return NULL;
195  	}
196  	
197  	static xmlNode *
198  	handle_update_request(pcmk__request_t *request)
199  	{
200  	    if (request->peer != NULL) {
201  	        const char *host = pcmk__xe_get(request->xml, PCMK__XA_ATTR_HOST);
202  	        pcmk__node_status_t *peer =
203  	            pcmk__get_node(0, request->peer, NULL,
204  	                           pcmk__node_search_cluster_member);
205  	
206  	        attrd_peer_update(peer, request->xml, host, false);
207  	        pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
208  	        return NULL;
209  	    }
210  	
211  	    remove_unsupported_sync_points(request);
212  	
213  	    if (attrd_request_has_sync_point(request->xml)) {
214  	        /* If this client supplied a sync point it wants to wait for, add it to
215  	         * the wait list. Clients on this list will not receive an ACK until
216  	         * their sync point is hit which will result in the client stalled there
217  	         * until it receives a response.
218  	         *
219  	         * All other clients will receive the expected response as normal.
220  	         */
221  	        attrd_add_client_to_waitlist(request);
222  	
223  	    } else {
224  	        /* If the client doesn't want to wait for a sync point, go ahead and
225  	         * send the ACK immediately. Otherwise, we'll send the ACK when the
226  	         * appropriate sync point is reached.
227  	         *
228  	         * In the normal case, attrd_client_update() can be called recursively,
229  	         * which makes where to send the ACK tricky. Doing it here ensures that
230  	         * the client only ever receives one.
231  	         */
232  	        attrd_send_ack(request->ipc_client, request->ipc_id,
233  	                       request->flags|crm_ipc_client_response);
234  	    }
235  	
236  	    attrd_client_update(request);
237  	    return NULL;
238  	}
239  	
240  	static void
241  	attrd_register_handlers(void)
242  	{
243  	    pcmk__server_command_t handlers[] = {
244  	        { PCMK__ATTRD_CMD_CLEAR_FAILURE, handle_clear_failure_request },
245  	        { PCMK__ATTRD_CMD_CONFIRM, handle_confirm_request },
246  	        { PCMK__ATTRD_CMD_PEER_REMOVE, handle_remove_request },
247  	        { PCMK__ATTRD_CMD_QUERY, handle_query_request },
248  	        { PCMK__ATTRD_CMD_REFRESH, handle_refresh_request },
249  	        { PCMK__ATTRD_CMD_SYNC_RESPONSE, handle_sync_response_request },
250  	        { PCMK__ATTRD_CMD_UPDATE, handle_update_request },
251  	        { PCMK__ATTRD_CMD_UPDATE_DELAY, handle_update_request },
252  	        { PCMK__ATTRD_CMD_UPDATE_BOTH, handle_update_request },
253  	        { NULL, handle_unknown_request },
254  	    };
255  	
256  	    attrd_handlers = pcmk__register_handlers(handlers);
257  	}
258  	
259  	void
260  	attrd_unregister_handlers(void)
261  	{
CID (unavailable; MK=950be591a1fc61aaa6f7d0695c9b3831) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(1) Event assign_union_field: The union field "in" of "_pp" is written.
(2) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
262  	    g_clear_pointer(&attrd_handlers, g_hash_table_destroy);
263  	}
264  	
265  	void
266  	attrd_handle_request(pcmk__request_t *request)
267  	{
268  	    xmlNode *reply = NULL;
269  	    char *log_msg = NULL;
270  	    const char *exec_status_s = NULL;
271  	    const char *reason = NULL;
272  	
273  	    if (attrd_handlers == NULL) {
274  	        attrd_register_handlers();
275  	    }
276  	
277  	    reply = pcmk__process_request(request, attrd_handlers);
278  	
279  	    if (reply != NULL) {
280  	        pcmk__log_xml_trace(reply, "Reply");
281  	
282  	        if (request->ipc_client != NULL) {
283  	            pcmk__ipc_send_xml(request->ipc_client, request->ipc_id, reply,
284  	                               request->ipc_flags);
285  	        } else {
286  	            pcmk__err("Not sending CPG reply to client");
287  	        }
288  	
289  	        pcmk__xml_free(reply);
290  	    }
291  	
292  	    exec_status_s = pcmk_exec_status_str(request->result.execution_status);
293  	    reason = request->result.exit_reason;
294  	    log_msg = pcmk__assert_asprintf("Processed %s request from %s %s: %s%s%s%s",
295  	                                    request->op,
296  	                                    pcmk__request_origin_type(request),
297  	                                    pcmk__request_origin(request),
298  	                                    exec_status_s,
299  	                                    (reason == NULL)? "" : " (",
300  	                                    pcmk__s(reason, ""),
301  	                                    (reason == NULL)? "" : ")");
302  	
303  	    if (!pcmk__result_ok(&request->result)) {
304  	        pcmk__warn("%s", log_msg);
305  	    } else {
306  	        pcmk__debug("%s", log_msg);
307  	    }
308  	
309  	    free(log_msg);
310  	    pcmk__reset_request(request);
311  	}
312  	
313  	/*!
314  	    \internal
315  	    \brief Send or broadcast private attribute for local node with protocol version
316  	*/
317  	void
318  	attrd_send_protocol(const pcmk__node_status_t *peer)
319  	{
320  	    xmlNode *attrd_op = pcmk__xe_create(NULL, __func__);
321  	
322  	    pcmk__xe_set(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD);
323  	    pcmk__xe_set(attrd_op, PCMK__XA_SRC, crm_system_name);
324  	    pcmk__xe_set(attrd_op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
325  	    pcmk__xe_set(attrd_op, PCMK__XA_ATTR_NAME, CRM_ATTR_PROTOCOL);
326  	    pcmk__xe_set(attrd_op, PCMK__XA_ATTR_VALUE, ATTRD_PROTOCOL_VERSION);
327  	    pcmk__xe_set_int(attrd_op, PCMK__XA_ATTR_IS_PRIVATE, 1);
328  	    pcmk__xe_set(attrd_op, PCMK__XA_ATTR_HOST, attrd_cluster->priv->node_name);
329  	    pcmk__xe_set(attrd_op, PCMK__XA_ATTR_HOST_ID,
330  	                 attrd_cluster->priv->node_xml_id);
331  	
332  	    if (peer == NULL) {
333  	        pcmk__debug("Broadcasting attrd protocol version "
334  	                    ATTRD_PROTOCOL_VERSION " for node %s[%" PRIu32 "]",
335  	                    pcmk__s(attrd_cluster->priv->node_name, "unknown"),
336  	                    attrd_cluster->priv->node_id);
337  	
338  	    } else {
339  	        pcmk__debug("Sending attrd protocol version " ATTRD_PROTOCOL_VERSION " "
340  	                    "for node %s[%" PRIu32 "] to node %s[%" PRIu32 "]",
341  	                    pcmk__s(attrd_cluster->priv->node_name, "unknown"),
342  	                    attrd_cluster->priv->node_id,
343  	                    pcmk__s(peer->name, "unknown"), peer->cluster_layer_id);
344  	    }
345  	
346  	    attrd_send_message(peer, attrd_op, false); /* ends up at attrd_peer_message() */
347  	
348  	    pcmk__xml_free(attrd_op);
349  	}
350  	
351  	gboolean
352  	attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, bool confirm)
353  	{
354  	    const char *op = pcmk__xe_get(data, PCMK_XA_TASK);
355  	
356  	    pcmk__xe_set(data, PCMK__XA_T, PCMK__VALUE_ATTRD);
357  	    pcmk__xe_set(data, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
358  	
359  	    /* Request a confirmation from the destination peer node (which could
360  	     * be all if node is NULL) that the message has been received and
361  	     * acted upon.
362  	     */
363  	    if (!pcmk__str_eq(op, PCMK__ATTRD_CMD_CONFIRM, pcmk__str_none)) {
364  	        pcmk__xe_set_bool(data, PCMK__XA_CONFIRM, confirm);
365  	    }
366  	
367  	    attrd_xml_add_writer(data);
368  	    return pcmk__cluster_send_message(node, pcmk_ipc_attrd, data);
369  	}
370