1 /*
2 * Copyright 2010-2024 Red Hat, Inc.
3 *
4 * Author: Angus Salkeld <asalkeld@redhat.com>
5 *
6 * This file is part of libqb.
7 *
8 * libqb is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * libqb is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with libqb. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #include "os_base.h"
22 #include <poll.h>
23
24 #include "ipc_int.h"
25 #include "util_int.h"
26 #include <qb/qbdefs.h>
27 #include <qb/qbipcc.h>
28
29 qb_ipcc_connection_t *
|
(8) Event sizet: |
"max_msg_size" is a size_t parameter. |
30 qb_ipcc_connect(const char *name, size_t max_msg_size)
31 {
32 int32_t res;
33 qb_ipcc_connection_t *c = NULL;
34 struct qb_ipc_connection_response response;
35
36 c = calloc(1, sizeof(struct qb_ipcc_connection));
|
(1) Event cond_false: |
Condition "c == NULL", taking false branch. |
37 if (c == NULL) {
38 return NULL;
|
(2) Event if_end: |
End of if statement. |
39 }
40
|
(3) Event cond_true: |
Condition "max_msg_size > 12328UL /* sizeof (struct qb_ipc_connection_response) */", taking true branch. |
41 c->setup.max_msg_size = QB_MAX(max_msg_size,
42 sizeof(struct qb_ipc_connection_response));
43 (void)strlcpy(c->name, name, NAME_MAX);
44 res = qb_ipcc_us_setup_connect(c, &response);
|
(4) Event cond_true: |
Condition "res < 0", taking true branch. |
45 if (res < 0) {
|
(5) Event goto: |
Jumping to label "disconnect_and_cleanup". |
46 goto disconnect_and_cleanup;
47 }
48 qb_ipc_us_ready(&c->setup, NULL, -1, POLLIN);
49 res = qb_ipcc_connect_continue(c);
50 if (res != 0) {
51 /* qb_ipcc_connect_continue() has cleaned up for us */
52 errno = -res;
53 return NULL;
54 }
55
56 return c;
57
|
(6) Event label: |
Reached label "disconnect_and_cleanup". |
58 disconnect_and_cleanup:
|
(7) Event cond_true: |
Condition "c->setup.u.us.sock >= 0", taking true branch. |
59 if (c->setup.u.us.sock >= 0) {
60 qb_ipcc_us_sock_close(c->setup.u.us.sock);
61 }
62 free(c->receive_buf);
63 free(c);
64 errno = -res;
65 return NULL;
66 }
67
68 qb_ipcc_connection_t *
69 qb_ipcc_connect_async(const char *name, size_t max_msg_size, int *connect_fd)
70 {
71 int32_t res;
72 qb_ipcc_connection_t *c = NULL;
73 struct qb_ipc_connection_response response;
74
75 c = calloc(1, sizeof(struct qb_ipcc_connection));
76 if (c == NULL) {
77 return NULL;
78 }
79
80 c->setup.max_msg_size = QB_MAX(max_msg_size,
81 sizeof(struct qb_ipc_connection_response));
82 (void)strlcpy(c->name, name, NAME_MAX);
83 res = qb_ipcc_us_setup_connect(c, &response);
84 if (res < 0) {
85 goto disconnect_and_cleanup;
86 }
87
88 *connect_fd = c->setup.u.us.sock;
89 return c;
90
91 disconnect_and_cleanup:
92 if (c->setup.u.us.sock >= 0) {
93 qb_ipcc_us_sock_close(c->setup.u.us.sock);
94 }
95 free(c->receive_buf);
96 free(c);
97 errno = -res;
98 return NULL;
99 }
100
101 int qb_ipcc_connect_continue(struct qb_ipcc_connection * c)
102 {
103 struct qb_ipc_connection_response response;
104 int32_t res;
105
106 /* Finish up the authentication part */
107 res = qb_ipcc_setup_connect_continue(c, &response);
108 if (res != 0) {
109 goto disconnect_and_cleanup;
110 }
111
112 c->response.type = response.connection_type;
113 c->request.type = response.connection_type;
114 c->event.type = response.connection_type;
115 c->setup.type = response.connection_type;
116
117 c->response.max_msg_size = response.max_msg_size;
118 c->request.max_msg_size = response.max_msg_size;
119 c->event.max_msg_size = response.max_msg_size;
120 c->receive_buf = calloc(1, response.max_msg_size);
121 c->fc_enable_max = 1;
122 if (c->receive_buf == NULL) {
123 res = -ENOMEM;
124 goto disconnect_and_cleanup;
125 }
126
127 switch (c->request.type) {
128 case QB_IPC_SHM:
129 res = qb_ipcc_shm_connect(c, &response);
130 break;
131 case QB_IPC_SOCKET:
132 res = qb_ipcc_us_connect(c, &response);
133 break;
134 case QB_IPC_POSIX_MQ:
135 case QB_IPC_SYSV_MQ:
136 res = -ENOTSUP;
137 break;
138 default:
139 res = -EINVAL;
140 break;
141 }
142 if (res != 0) {
143 goto disconnect_and_cleanup;
144 }
145 c->is_connected = QB_TRUE;
146 return 0;
147
148 disconnect_and_cleanup:
149 if (c->setup.u.us.sock >= 0) {
150 qb_ipcc_us_sock_close(c->setup.u.us.sock);
151 }
152 free(c->receive_buf);
153 free(c);
154 errno = -res;
155 return res;
156
157 }
158
159 static int32_t
160 _check_connection_state_with(struct qb_ipcc_connection * c, int32_t res,
161 struct qb_ipc_one_way * one_way,
162 int32_t ms_timeout, int32_t events)
163 {
164 if (res >= 0) return res;
165
166 if (qb_ipc_us_sock_error_is_disconnected(res)) {
167 errno = -res;
168 qb_util_perror(LOG_DEBUG,
169 "interpreting result %d as a disconnect",
170 res);
171 c->is_connected = QB_FALSE;
172 }
173
174 if (res == -EAGAIN || res == -ETIMEDOUT) {
175 int32_t res2;
176 int32_t poll_ms = ms_timeout;
177 if (res == -ETIMEDOUT) {
178 poll_ms = 0;
179 }
180 res2 = qb_ipc_us_ready(one_way, &c->setup, poll_ms, events);
181 if (qb_ipc_us_sock_error_is_disconnected(res2)) {
182 errno = -res2;
183 qb_util_perror(LOG_DEBUG,
184 "%s %d %s",
185 "interpreting result",
186 res2,
187 "(from socket) as a disconnect");
188 c->is_connected = QB_FALSE;
189 res = res2;
190 } else if (res != -ETIMEDOUT) {
191 /* if the result we're checking against is a TIMEOUT error.
192 * don't override that result with another error that does
193 * not imply a disconnect */
194 res = res2;
195 }
196 }
197 return res;
198 }
199
200
201 static int32_t
202 _check_connection_state(struct qb_ipcc_connection * c, int32_t res)
203 {
204 if (res >= 0) return res;
205
206 if (qb_ipc_us_sock_error_is_disconnected(res)) {
207 errno = -res;
208 qb_util_perror(LOG_DEBUG,
209 "interpreting result %d as a disconnect",
210 res);
211 c->is_connected = QB_FALSE;
212 }
213 return res;
214 }
215
216 static struct qb_ipc_one_way *
217 _event_sock_one_way_get(struct qb_ipcc_connection * c)
218 {
219 if (c->needs_sock_for_poll) {
220 return &c->setup;
221 }
222 return &c->event;
223 }
224
225 static struct qb_ipc_one_way *
226 _response_sock_one_way_get(struct qb_ipcc_connection * c)
227 {
228 if (c->needs_sock_for_poll) {
229 return &c->setup;
230 }
231 return &c->response;
232 }
233
234 ssize_t
235 qb_ipcc_send(struct qb_ipcc_connection * c, const void *msg_ptr, size_t msg_len)
236 {
237 ssize_t res;
238 ssize_t res2;
239
240 if (c == NULL) {
241 return -EINVAL;
242 }
243 if (msg_len > c->request.max_msg_size) {
244 return -EMSGSIZE;
245 }
246 if (c->funcs.fc_get) {
247 res = c->funcs.fc_get(&c->request);
248 if (res < 0) {
249 return res;
250 } else if (res > 0 && res <= c->fc_enable_max) {
251 return -EAGAIN;
252 } else {
253 /*
254 * we can transmit
255 */
256 }
257 }
258
259 res = c->funcs.send(&c->request, msg_ptr, msg_len);
260 if (res == msg_len && c->needs_sock_for_poll) {
261 do {
262 res2 = qb_ipc_us_send(&c->setup, msg_ptr, 1);
263 } while (res2 == -EAGAIN);
264 if (res2 == -EPIPE) {
265 res2 = -ENOTCONN;
266 }
267 if (res2 != 1) {
268 res = res2;
269 }
270 }
271 return _check_connection_state(c, res);
272 }
273
274 int32_t
275 qb_ipcc_fc_enable_max_set(struct qb_ipcc_connection * c, uint32_t max)
276 {
277 if (c == NULL || max > 2) {
278 return -EINVAL;
279 }
280 c->fc_enable_max = max;
281 return 0;
282 }
283
284 ssize_t
285 qb_ipcc_sendv(struct qb_ipcc_connection * c, const struct iovec * iov,
286 size_t iov_len)
287 {
288 int32_t total_size = 0;
289 int32_t i;
290 int32_t res;
291 int32_t res2;
292
293 for (i = 0; i < iov_len; i++) {
294 total_size += iov[i].iov_len;
295 }
296 if (c == NULL) {
297 return -EINVAL;
298 }
299 if (total_size > c->request.max_msg_size) {
300 return -EMSGSIZE;
301 }
302
303 if (c->funcs.fc_get) {
304 res = c->funcs.fc_get(&c->request);
305 if (res < 0) {
306 return res;
307 } else if (res > 0 && res <= c->fc_enable_max) {
308 return -EAGAIN;
309 } else {
310 /*
311 * we can transmit
312 */
313 }
314 }
315
316 res = c->funcs.sendv(&c->request, iov, iov_len);
317 if (res > 0 && c->needs_sock_for_poll) {
318 do {
319 res2 = qb_ipc_us_send(&c->setup, &res, 1);
320 } while (res2 == -EAGAIN);
321 if (res2 == -EPIPE) {
322 res2 = -ENOTCONN;
323 }
324 if (res2 != 1) {
325 res = res2;
326 }
327 }
328 return _check_connection_state(c, res);
329 }
330
331 ssize_t
332 qb_ipcc_recv(struct qb_ipcc_connection * c, void *msg_ptr,
333 size_t msg_len, int32_t ms_timeout)
334 {
335 int32_t res = 0;
336 int32_t connect_res = 0;
337
338 if (c == NULL) {
339 return -EINVAL;
340 }
341
342 res = c->funcs.recv(&c->response, msg_ptr, msg_len, ms_timeout);
343 if (res >= 0) {
344 return res;
345 }
346
347 /* if we didn't get a msg, check connection state */
348 connect_res = _check_connection_state_with(c, res,
349 _response_sock_one_way_get(c),
350 ms_timeout, POLLIN);
351
352 /* only report the connection state check result if an error is returned. */
353 if (connect_res < 0) {
354 return connect_res;
355 }
356 return res;
357 }
358
359 ssize_t
360 qb_ipcc_sendv_recv(qb_ipcc_connection_t * c,
361 const struct iovec * iov, uint32_t iov_len,
362 void *res_msg, size_t res_len, int32_t ms_timeout)
363 {
364 ssize_t res = 0;
365 int32_t timeout_now;
366 int32_t timeout_rem = ms_timeout;
367
368 if (c == NULL) {
369 return -EINVAL;
370 }
371
372 if (c->funcs.fc_get) {
373 res = c->funcs.fc_get(&c->request);
374 if (res < 0) {
375 return res;
376 } else if (res > 0 && res <= c->fc_enable_max) {
377 return -EAGAIN;
378 } else {
379 /*
380 * we can transmit
381 */
382 }
383 }
384
385 res = qb_ipcc_sendv(c, iov, iov_len);
386 if (res < 0) {
387 return res;
388 }
389
390 do {
391 /* following is a liveness-driven interleaving
392 (for cases the server side failed/exited) */
393 if (timeout_rem > QB_IPC_MAX_WAIT_MS || ms_timeout == -1) {
394 timeout_now = QB_IPC_MAX_WAIT_MS;
395 } else {
396 timeout_now = timeout_rem;
397 }
398
399 res = qb_ipcc_recv(c, res_msg, res_len, timeout_now);
400 if (res == -ETIMEDOUT) {
401 if (ms_timeout < 0) {
402 res = -EAGAIN;
403 } else {
404 timeout_rem -= timeout_now;
405 if (timeout_rem > 0) {
406 res = -EAGAIN;
407 }
408 }
409 } else if (res < 0 && res != -EAGAIN) {
410 errno = -res;
411 qb_util_perror(LOG_DEBUG,
412 "qb_ipcc_recv %d timeout:(%d/%d)",
413 res, timeout_now, timeout_rem);
414 }
415 } while (res == -EAGAIN && c->is_connected);
416
417 return res;
418 }
419
420 int32_t
421 qb_ipcc_fd_get(struct qb_ipcc_connection * c, int32_t * fd)
422 {
423 if (c == NULL) {
424 return -EINVAL;
425 }
426 if (c->event.type == QB_IPC_SOCKET) {
427 *fd = c->event.u.us.sock;
428 } else {
429 *fd = c->setup.u.us.sock;
430 }
431 return 0;
432 }
433
434 int32_t
435 qb_ipcc_auth_get(struct qb_ipcc_connection * c, pid_t *pid, uid_t *uid, gid_t *gid)
436 {
437 if (c == NULL) {
438 return -EINVAL;
439 }
440 if (pid) {
441 *pid = c->server_pid;
442 }
443 if (uid) {
444 *uid = c->euid;
445 }
446 if (gid) {
447 *gid = c->egid;
448 }
449 return 0;
450 }
451
452 ssize_t
453 qb_ipcc_event_recv(struct qb_ipcc_connection * c, void *msg_pt,
454 size_t msg_len, int32_t ms_timeout)
455 {
456 char one_byte = 1;
457 int32_t res;
458 ssize_t size;
459
460 if (c == NULL) {
461 return -EINVAL;
462 }
463 res = _check_connection_state_with(c, -EAGAIN, _event_sock_one_way_get(c),
464 ms_timeout, POLLIN);
465 if (res < 0) {
466 return res;
467 }
468 size = c->funcs.recv(&c->event, msg_pt, msg_len, ms_timeout);
469 if (size > 0 && c->needs_sock_for_poll) {
470 res = qb_ipc_us_recv(&c->setup, &one_byte, 1, -1);
471 if (res != 1) {
472 size = res;
473 }
474 }
475 return _check_connection_state(c, size);
476 }
477
478 void
479 qb_ipcc_disconnect(struct qb_ipcc_connection *c)
480 {
481 struct qb_ipc_one_way *ow = NULL;
482
483 qb_util_log(LOG_TRACE, "%s(%s)", __func__, (c == NULL)? "NULL" : "");
484
485 if (c == NULL) {
486 return;
487 }
488
489 ow = _event_sock_one_way_get(c);
490 (void)_check_connection_state_with(c, -EAGAIN, ow, 0, POLLIN);
491
492 if (c->funcs.disconnect) {
493 c->funcs.disconnect(c);
494 }
495 free(c->receive_buf);
496 free(c);
497 }
498
499 void
500 qb_ipcc_context_set(struct qb_ipcc_connection *c, void *context)
501 {
502 if (c == NULL) {
503 return;
504 }
505 c->context = context;
506 }
507
508 void *qb_ipcc_context_get(struct qb_ipcc_connection *c)
509 {
510 if (c == NULL) {
511 return NULL;
512 }
513 return c->context;
514 }
515
516 int32_t
517 qb_ipcc_is_connected(qb_ipcc_connection_t *c)
518 {
519 struct qb_ipc_one_way *ow;
520
521 if (c == NULL) {
522 return QB_FALSE;
523 }
524
525 ow = _response_sock_one_way_get(c);
526 (void)_check_connection_state_with(c, -EAGAIN, ow, 0, POLLIN);
527
528 return c->is_connected;
529 }
530
531 int32_t
532 qb_ipcc_get_buffer_size(qb_ipcc_connection_t * c)
533 {
534 if (c == NULL) {
535 return -EINVAL;
536 }
537
538 return c->event.max_msg_size;
539 }
540