1    	/*
2    	 * Copyright 2012-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 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 <errno.h>                          // ENOMEM
13   	#include <stddef.h>                         // NULL, size_t
14   	#include <stdint.h>                         // int32_t, uint32_t
15   	#include <sys/types.h>                      // gid_t, uid_t
16   	
17   	#include <glib.h>                           // g_byte_array_free, TRUE
18   	#include <libxml/tree.h>                    // xmlNode
19   	#include <qb/qbipcs.h>                      // qb_ipcs_connection_t
20   	
21   	#include <crm/common/internal.h>            // pcmk__client_t, pcmk__find_client
22   	#include <crm/common/ipc.h>                 // crm_ipc_client_response
23   	#include <crm/common/logging.h>             // CRM_CHECK
24   	#include <crm/common/results.h>             // pcmk_rc_*, pcmk_rc_str
25   	
26   	#include "pacemaker-execd.h"                // client_disconnect_cleanup
27   	
28   	static qb_ipcs_service_t *ipcs = NULL;
29   	
30   	/*!
31   	 * \internal
32   	 * \brief Accept a new client IPC connection
33   	 *
34   	 * \param[in,out] c    New connection
35   	 * \param[in]     uid  Client user id
36   	 * \param[in]     gid  Client group id
37   	 *
38   	 * \return 0 on success, -errno otherwise
39   	 */
40   	static int32_t
41   	execd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
42   	{
43   	    pcmk__trace("New client connection %p", c);
44   	    if (pcmk__new_client(c, uid, gid) == NULL) {
45   	        return -ENOMEM;
46   	    }
47   	    return 0;
48   	}
49   	
50   	/*!
51   	 * \internal
52   	 * \brief Handle a newly created connection
53   	 *
54   	 * \param[in,out] c  New connection
55   	 */
56   	static void
57   	execd_ipc_created(qb_ipcs_connection_t *c)
58   	{
59   	    pcmk__client_t *new_client = pcmk__find_client(c);
60   	
61   	    pcmk__trace("New client connection %p", c);
62   	    pcmk__assert(new_client != NULL);
63   	    /* Now that the connection is offically established, alert
64   	     * the other clients a new connection exists. */
65   	
66   	    notify_of_new_client(new_client);
67   	}
68   	
69   	/*!
70   	 * \internal
71   	 * \brief Destroy a client IPC connection
72   	 *
73   	 * \param[in] c  Connection to destroy
74   	 *
75   	 * \return 0 (i.e. do not re-run this callback)
76   	 */
77   	static int32_t
78   	execd_ipc_closed(qb_ipcs_connection_t *c)
79   	{
80   	    pcmk__client_t *client = pcmk__find_client(c);
81   	
82   	    if (client == NULL) {
83   	        pcmk__trace("Ignoring request to clean up unknown connection %p", c);
84   	    } else {
85   	        pcmk__trace("Cleaning up closed client connection %p", c);
86   	        client_disconnect_cleanup(client->id);
87   	#ifdef PCMK__COMPILE_REMOTE
88   	        ipc_proxy_remove_provider(client);
89   	#endif
90   	        lrmd_client_destroy(client);
91   	    }
92   	
93   	    return 0;
94   	}
95   	
96   	/*!
97   	 * \internal
98   	 * \brief Destroy a client IPC connection
99   	 *
100  	 * \param[in] c  Connection to destroy
101  	 *
102  	 * \note We handle a destroyed connection the same as a closed one,
103  	 *       but we need a separate handler because the return type is different.
104  	 */
105  	static void
106  	execd_ipc_destroy(qb_ipcs_connection_t *c)
107  	{
108  	    pcmk__trace("Destroying client connection %p", c);
109  	    execd_ipc_closed(c);
110  	}
111  	
112  	/*!
113  	 * \internal
114  	 * \brief Handle a message from an IPC connection
115  	 *
116  	 * \param[in,out] c     Established IPC connection
117  	 * \param[in]     data  The message data read from the connection - this can be
118  	 *                      a complete IPC message or just a part of one if it's
119  	 *                      very large
120  	 * \param[size]   size  Unused
121  	 *
122  	 * \return 0 in all cases
123  	 */
124  	static int32_t
125  	execd_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
126  	{
127  	    int rc = pcmk_rc_ok;
128  	    uint32_t id = 0;
129  	    uint32_t flags = 0;
130  	    pcmk__client_t *client = pcmk__find_client(c);
131  	    xmlNode *msg = NULL;
132  	
133  	    // Sanity-check, and parse XML from IPC data
134  	    CRM_CHECK(client != NULL, return 0);
135  	
136  	    if (data == NULL) {
137  	        pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c));
138  	        return 0;
139  	    }
140  	
141  	    rc = pcmk__ipc_msg_append(&client->buffer, data);
142  	
143  	    if (rc == pcmk_rc_ipc_more) {
144  	        /* We haven't read the complete message yet, so just return. */
145  	        return 0;
146  	
147  	    } else if (rc == pcmk_rc_ok) {
148  	        /* We've read the complete message and there's already a header on
149  	         * the front.  Pass it off for processing.
150  	         */
151  	        msg = pcmk__client_data2xml(client, &id, &flags);
152  	        g_byte_array_free(client->buffer, TRUE);
153  	        client->buffer = NULL;
154  	
155  	    } else {
156  	        /* Some sort of error occurred reassembling the message.  All we can
157  	         * do is clean up, log an error and return.
158  	         */
159  	        pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc));
160  	
161  	        if (client->buffer != NULL) {
162  	            g_byte_array_free(client->buffer, TRUE);
163  	            client->buffer = NULL;
164  	        }
165  	
166  	        return 0;
167  	    }
168  	
169  	    CRM_CHECK(pcmk__is_set(flags, crm_ipc_client_response), goto done);
170  	
171  	    if ((msg == NULL) || execd_invalid_msg(msg)) {
172  	        pcmk__debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c));
173  	        pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_PROTOCOL);
174  	    } else {
175  	        pcmk__request_t request = {
176  	            .ipc_client     = client,
177  	            .ipc_id         = id,
178  	            .ipc_flags      = flags,
179  	            .peer           = NULL,
180  	            .xml            = msg,
181  	            .call_options   = 0,
182  	            .result         = PCMK__UNKNOWN_RESULT,
183  	        };
184  	
185  	        request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_LRMD_OP);
186  	        CRM_CHECK(request.op != NULL, goto done);
187  	
188  	        execd_handle_request(&request);
189  	    }
190  	
191  	done:
192  	    pcmk__xml_free(msg);
193  	    return 0;
194  	}
195  	
196  	static struct qb_ipcs_service_handlers ipc_callbacks = {
197  	    .connection_accept = execd_ipc_accept,
198  	    .connection_created = execd_ipc_created,
199  	    .msg_process = execd_ipc_dispatch,
200  	    .connection_closed = execd_ipc_closed,
201  	    .connection_destroyed = execd_ipc_destroy
202  	};
203  	
204  	/*!
205  	 * \internal
206  	 * \brief Clean up executor IPC communication
207  	 */
208  	void
209  	execd_ipc_cleanup(void)
210  	{
(1) Event path: Condition "ipcs != NULL", taking true branch.
211  	    if (ipcs != NULL) {
212  	        pcmk__drop_all_clients(ipcs);
CID (unavailable; MK=c35123b316c38badcce98519ac576e07) (#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".
213  	        g_clear_pointer(&ipcs, qb_ipcs_destroy);
214  	    }
215  	
216  	    pcmk__client_cleanup();
217  	}
218  	
219  	/*!
220  	 * \internal
221  	 * \brief Set up executor IPC communication
222  	 */
223  	void
224  	execd_ipc_init(void)
225  	{
226  	    pcmk__serve_execd_ipc(&ipcs, &ipc_callbacks);
227  	}
228