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