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