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