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