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