1    	/*
2    	 * Copyright 2009-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 <errno.h>                            // ECONNREFUSED, ENOMEM
13   	#include <inttypes.h>                         // int32_t, uint32_t, PRIu32
14   	#include <stdio.h>                            // NULL, size_t
15   	#include <sys/types.h>                        // gid_t, uid_t
16   	
17   	#include <libxml/tree.h>                      // xmlNode
18   	#include <glib.h>                             // g_byte_array_free, TRUE
19   	#include <qb/qbipcs.h>                        // for qb_ipcs_connection_t
20   	
21   	#include "pacemaker-fenced.h"                 // fenced_get_local_node
22   	
23   	#include <crm/common/ipc.h>                   // crm_ipc_flags, pcmk_ipc_fenced
24   	#include <crm/common/results.h>               // pcmk_rc_*, pcmk_rc_str
25   	#include <crm/fencing/internal.h>             // STONITH_OP_*
26   	#include <crm/crm.h>                          // CRM_OP_RM_NODE_CACHE
27   	#include <crm/stonith-ng.h>                   // stonith_call_options
28   	
29   	static qb_ipcs_service_t *ipcs = NULL;
30   	
31   	static void
32   	handle_ipc_reply(pcmk__client_t *client, xmlNode *request)
33   	{
34   	    const char *op = pcmk__xe_get(request, PCMK__XA_ST_OP);
35   	
36   	    if (pcmk__str_eq(op, STONITH_OP_QUERY, pcmk__str_none)) {
37   	        process_remote_stonith_query(request);
38   	
39   	    } else if (pcmk__str_any_of(op, STONITH_OP_NOTIFY, STONITH_OP_FENCE,
40   	                                NULL)) {
41   	        fenced_process_fencing_reply(request);
42   	
43   	    } else {
44   	        pcmk__err("Ignoring unknown %s reply from client %s",
45   	                  pcmk__s(op, "untyped"), pcmk__client_name(client));
46   	        pcmk__log_xml_warn(request, "UnknownOp");
47   	        return;
48   	    }
49   	
50   	    pcmk__debug("Processed %s reply from client %s", op,
51   	                pcmk__client_name(client));
52   	}
53   	
54   	/*!
55   	 * \internal
56   	 * \brief Accept a new client IPC connection
57   	 *
58   	 * \param[in,out] c    New connection
59   	 * \param[in]     uid  Client user id
60   	 * \param[in]     gid  Client group id
61   	 *
62   	 * \return 0 on success, -errno otherwise
63   	 */
64   	static int32_t
65   	fenced_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
66   	{
67   	    pcmk__trace("New client connection %p", c);
68   	    if (stonith_shutdown_flag) {
69   	        pcmk__info("Ignoring new connection from pid %d during shutdown",
70   	                   pcmk__client_pid(c));
71   	        return -ECONNREFUSED;
72   	    }
73   	
74   	    if (pcmk__new_client(c, uid, gid) == NULL) {
75   	        return -ENOMEM;
76   	    }
77   	    return 0;
78   	}
79   	
80   	/*!
81   	 * \internal
82   	 * \brief Handle a message from an IPC connection
83   	 *
84   	 * \param[in,out] c     Established IPC connection
85   	 * \param[in]     data  The message data read from the connection - this can be
86   	 *                      a complete IPC message or just a part of one if it's
87   	 *                      very large
88   	 * \param[in]     size  Unused
89   	 *
90   	 * \return 0 in all cases
91   	 */
92   	static int32_t
93   	fenced_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
94   	{
95   	    uint32_t id = 0;
96   	    uint32_t flags = 0;
97   	    uint32_t call_options = st_opt_none;
98   	    xmlNode *msg = NULL;
99   	    pcmk__client_t *client = pcmk__find_client(c);
100  	    const char *op = NULL;
101  	    int rc = pcmk_rc_ok;
102  	
103  	    // Sanity-check, and parse XML from IPC data
104  	    CRM_CHECK(client != NULL, return 0);
105  	    if (data == NULL) {
106  	        pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c));
107  	        return 0;
108  	    }
109  	
110  	    rc = pcmk__ipc_msg_append(&client->buffer, data);
111  	
112  	    if (rc == pcmk_rc_ipc_more) {
113  	        /* We haven't read the complete message yet, so just return. */
114  	        return 0;
115  	
116  	    } else if (rc == pcmk_rc_ok) {
117  	        /* We've read the complete message and there's already a header on
118  	         * the front.  Pass it off for processing.
119  	         */
120  	        msg = pcmk__client_data2xml(client, &id, &flags);
121  	        g_byte_array_free(client->buffer, TRUE);
122  	        client->buffer = NULL;
123  	
124  	    } else {
125  	        /* Some sort of error occurred reassembling the message.  All we can
126  	         * do is clean up, log an error and return.
127  	         */
128  	        pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc));
129  	
130  	        if (client->buffer != NULL) {
131  	            g_byte_array_free(client->buffer, TRUE);
132  	            client->buffer = NULL;
133  	        }
134  	
135  	        return 0;
136  	    }
137  	
138  	    if (msg == NULL) {
139  	        pcmk__debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c));
140  	        pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_PROTOCOL);
141  	        return 0;
142  	    }
143  	
144  	    op = pcmk__xe_get(msg, PCMK__XA_CRM_TASK);
145  	    if (pcmk__str_eq(op, CRM_OP_RM_NODE_CACHE, pcmk__str_casei)) {
146  	        pcmk__xe_set(msg, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
147  	        pcmk__xe_set(msg, PCMK__XA_ST_OP, op);
148  	        pcmk__xe_set(msg, PCMK__XA_ST_CLIENTID, client->id);
149  	        pcmk__xe_set(msg, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(client));
150  	        pcmk__xe_set(msg, PCMK__XA_ST_CLIENTNODE, fenced_get_local_node());
151  	
152  	        pcmk__cluster_send_message(NULL, pcmk_ipc_fenced, msg);
153  	        goto done;
154  	    }
155  	
156  	    if (client->name == NULL) {
157  	        const char *value = pcmk__xe_get(msg, PCMK__XA_ST_CLIENTNAME);
158  	
159  	        client->name = pcmk__assert_asprintf("%s.%u", pcmk__s(value, "unknown"),
160  	                                             client->pid);
161  	    }
162  	
163  	    rc = pcmk__xe_get_flags(msg, PCMK__XA_ST_CALLOPT, &call_options,
164  	                            st_opt_none);
165  	    if (rc != pcmk_rc_ok) {
166  	        pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc));
167  	    }
168  	
169  	    pcmk__trace("Flags %#08" PRIx32 "/%#08x for command %" PRIu32
170  	                " from client %s",
171  	                flags, call_options, id, pcmk__client_name(client));
172  	
173  	    if (pcmk__is_set(call_options, st_opt_sync_call)) {
174  	        pcmk__assert(pcmk__is_set(flags, crm_ipc_client_response));
175  	        /* This means the client has two synchronous events in-flight */
176  	        CRM_LOG_ASSERT(client->request_id == 0);
177  	        /* Reply only to the last one */
178  	        client->request_id = id;
179  	    }
180  	
181  	    pcmk__xe_set(msg, PCMK__XA_ST_CLIENTID, client->id);
182  	    pcmk__xe_set(msg, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(client));
183  	    pcmk__xe_set(msg, PCMK__XA_ST_CLIENTNODE, fenced_get_local_node());
184  	
185  	    if (pcmk__xpath_find_one(msg->doc, "//" PCMK__XE_ST_REPLY,
186  	                             PCMK__LOG_NEVER) != NULL) {
187  	        handle_ipc_reply(client, msg);
188  	
189  	    } else {
190  	        pcmk__request_t request = {
191  	            .ipc_client     = client,
192  	            .ipc_id         = id,
193  	            .ipc_flags      = flags,
194  	            .peer           = NULL,
195  	            .xml            = msg,
196  	            .call_options   = call_options,
197  	            .result         = PCMK__UNKNOWN_RESULT,
198  	        };
199  	
200  	        request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_ST_OP);
201  	        CRM_CHECK(request.op != NULL, goto done);
202  	
203  	        if (pcmk__is_set(request.call_options, st_opt_sync_call)) {
204  	            pcmk__set_request_flags(&request, pcmk__request_sync);
205  	        }
206  	
207  	        fenced_handle_request(&request);
208  	    }
209  	
210  	done:
211  	    pcmk__xml_free(msg);
212  	    return 0;
213  	}
214  	
215  	/*!
216  	 * \internal
217  	 * \brief Destroy a client IPC connection
218  	 *
219  	 * \param[in] c  Connection to destroy
220  	 *
221  	 * \return 0 (i.e. do not re-run this callback)
222  	 */
223  	static int32_t
224  	fenced_ipc_closed(qb_ipcs_connection_t *c)
225  	{
226  	    pcmk__client_t *client = pcmk__find_client(c);
227  	
228  	    if (client == NULL) {
229  	        pcmk__trace("Ignoring request to clean up unknown connection %p", c);
230  	    } else {
231  	        pcmk__trace("Cleaning up closed client connection %p", c);
232  	        pcmk__free_client(client);
233  	    }
234  	
235  	    return 0;
236  	}
237  	
238  	/*!
239  	 * \internal
240  	 * \brief Destroy a client IPC connection
241  	 *
242  	 * \param[in] c  Connection to destroy
243  	 *
244  	 * \note We handle a destroyed connection the same as a closed one,
245  	 *       but we need a separate handler because the return type is different.
246  	 */
247  	static void
248  	fenced_ipc_destroy(qb_ipcs_connection_t *c)
249  	{
250  	    pcmk__trace("Destroying client connection %p", c);
251  	    fenced_ipc_closed(c);
252  	}
253  	
254  	static struct qb_ipcs_service_handlers ipc_callbacks = {
255  	    .connection_accept = fenced_ipc_accept,
256  	    .connection_created = NULL,
257  	    .msg_process = fenced_ipc_dispatch,
258  	    .connection_closed = fenced_ipc_closed,
259  	    .connection_destroyed = fenced_ipc_destroy
260  	};
261  	
262  	/*!
263  	 * \internal
264  	 * \brief Clean up fenced IPC communication
265  	 */
266  	void
267  	fenced_ipc_cleanup(void)
268  	{
(1) Event path: Condition "ipcs != NULL", taking true branch.
269  	    if (ipcs != NULL) {
270  	        pcmk__drop_all_clients(ipcs);
CID (unavailable; MK=1e0e556b7a5e2d9bcebbb6f6ce62deed) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(2) Event assign_union_field: The union field "in" of "_pp" is written.
(3) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
271  	        g_clear_pointer(&ipcs, qb_ipcs_destroy);
272  	    }
273  	
274  	    pcmk__client_cleanup();
275  	}
276  	
277  	/*!
278  	 * \internal
279  	 * \brief Set up fenced IPC communication
280  	 */
281  	void
282  	fenced_ipc_init(void)
283  	{
284  	    pcmk__serve_fenced_ipc(&ipcs, &ipc_callbacks);
285  	}
286