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