1 /*
2  * Copyright (C) 2010 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 *
qb_ipcc_connect(const char * name,size_t max_msg_size)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));
37 	if (c == NULL) {
38 		return NULL;
39 	}
40 
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);
45 	if (res < 0) {
46 		goto disconnect_and_cleanup;
47 	}
48 	c->response.type = response.connection_type;
49 	c->request.type = response.connection_type;
50 	c->event.type = response.connection_type;
51 	c->setup.type = response.connection_type;
52 
53 	c->response.max_msg_size = response.max_msg_size;
54 	c->request.max_msg_size = response.max_msg_size;
55 	c->event.max_msg_size = response.max_msg_size;
56 	c->receive_buf = calloc(1, response.max_msg_size);
57 	c->fc_enable_max = 1;
58 	if (c->receive_buf == NULL) {
59 		res = -ENOMEM;
60 		goto disconnect_and_cleanup;
61 	}
62 
63 	switch (c->request.type) {
64 	case QB_IPC_SHM:
65 		res = qb_ipcc_shm_connect(c, &response);
66 		break;
67 	case QB_IPC_SOCKET:
68 		res = qb_ipcc_us_connect(c, &response);
69 		break;
70 	case QB_IPC_POSIX_MQ:
71 	case QB_IPC_SYSV_MQ:
72 		res = -ENOTSUP;
73 		break;
74 	default:
75 		res = -EINVAL;
76 		break;
77 	}
78 	if (res != 0) {
79 		goto disconnect_and_cleanup;
80 	}
81 	c->is_connected = QB_TRUE;
82 	return c;
83 
84 disconnect_and_cleanup:
85 	if (c->setup.u.us.sock >= 0) {
86 		qb_ipcc_us_sock_close(c->setup.u.us.sock);
87 	}
88 	free(c->receive_buf);
89 	free(c);
90 	errno = -res;
91 	return NULL;
92 }
93 
94 static int32_t
_check_connection_state_with(struct qb_ipcc_connection * c,int32_t res,struct qb_ipc_one_way * one_way,int32_t ms_timeout,int32_t events)95 _check_connection_state_with(struct qb_ipcc_connection * c, int32_t res,
96 			     struct qb_ipc_one_way * one_way,
97 			     int32_t ms_timeout, int32_t events)
98 {
99 	if (res >= 0) return res;
100 
101 	if (qb_ipc_us_sock_error_is_disconnected(res)) {
102 		errno = -res;
103 		qb_util_perror(LOG_DEBUG,
104 			       "interpreting result %d as a disconnect",
105 			       res);
106 		c->is_connected = QB_FALSE;
107 	}
108 
109 	if (res == -EAGAIN || res == -ETIMEDOUT) {
110 		int32_t res2;
111 		int32_t poll_ms = ms_timeout;
112 		if (res == -ETIMEDOUT) {
113 			poll_ms = 0;
114 		}
115 		res2 = qb_ipc_us_ready(one_way, &c->setup, poll_ms, events);
116 		if (qb_ipc_us_sock_error_is_disconnected(res2)) {
117 			errno = -res2;
118 			qb_util_perror(LOG_DEBUG,
119 				       "%s %d %s",
120 				       "interpreting result",
121 				       res2,
122 				       "(from socket) as a disconnect");
123 			c->is_connected = QB_FALSE;
124 			res = res2;
125 		} else if (res != -ETIMEDOUT) {
126 			/* if the result we're checking against is a TIMEOUT error.
127 			 * don't override that result with another error that does
128 			 * not imply a disconnect */
129 			res = res2;
130 		}
131 	}
132 	return res;
133 }
134 
135 
136 static int32_t
_check_connection_state(struct qb_ipcc_connection * c,int32_t res)137 _check_connection_state(struct qb_ipcc_connection * c, int32_t res)
138 {
139 	if (res >= 0) return res;
140 
141 	if (qb_ipc_us_sock_error_is_disconnected(res)) {
142 		errno = -res;
143 		qb_util_perror(LOG_DEBUG,
144 			    "interpreting result %d as a disconnect",
145 			    res);
146 		c->is_connected = QB_FALSE;
147 	}
148 	return res;
149 }
150 
151 static struct qb_ipc_one_way *
_event_sock_one_way_get(struct qb_ipcc_connection * c)152 _event_sock_one_way_get(struct qb_ipcc_connection * c)
153 {
154 	if (c->needs_sock_for_poll) {
155 		return &c->setup;
156 	}
157 	return &c->event;
158 }
159 
160 static struct qb_ipc_one_way *
_response_sock_one_way_get(struct qb_ipcc_connection * c)161 _response_sock_one_way_get(struct qb_ipcc_connection * c)
162 {
163 	if (c->needs_sock_for_poll) {
164 		return &c->setup;
165 	}
166 	return &c->response;
167 }
168 
169 ssize_t
qb_ipcc_send(struct qb_ipcc_connection * c,const void * msg_ptr,size_t msg_len)170 qb_ipcc_send(struct qb_ipcc_connection * c, const void *msg_ptr, size_t msg_len)
171 {
172 	ssize_t res;
173 	ssize_t res2;
174 
175 	if (c == NULL) {
176 		return -EINVAL;
177 	}
178 	if (msg_len > c->request.max_msg_size) {
179 		return -EMSGSIZE;
180 	}
181 	if (c->funcs.fc_get) {
182 		res = c->funcs.fc_get(&c->request);
183 		if (res < 0) {
184 			return res;
185 		} else if (res > 0 && res <= c->fc_enable_max) {
186 			return -EAGAIN;
187 		} else {
188 			/*
189 			 * we can transmit
190 			 */
191 		}
192 	}
193 
194 	res = c->funcs.send(&c->request, msg_ptr, msg_len);
195 	if (res == msg_len && c->needs_sock_for_poll) {
196 		do {
197 			res2 = qb_ipc_us_send(&c->setup, msg_ptr, 1);
198 		} while (res2 == -EAGAIN);
199 		if (res2 == -EPIPE) {
200 			res2 = -ENOTCONN;
201 		}
202 		if (res2 != 1) {
203 			res = res2;
204 		}
205 	}
206 	return _check_connection_state(c, res);
207 }
208 
209 int32_t
qb_ipcc_fc_enable_max_set(struct qb_ipcc_connection * c,uint32_t max)210 qb_ipcc_fc_enable_max_set(struct qb_ipcc_connection * c, uint32_t max)
211 {
212 	if (c == NULL || max > 2) {
213 		return -EINVAL;
214 	}
215 	c->fc_enable_max = max;
216 	return 0;
217 }
218 
219 ssize_t
qb_ipcc_sendv(struct qb_ipcc_connection * c,const struct iovec * iov,size_t iov_len)220 qb_ipcc_sendv(struct qb_ipcc_connection * c, const struct iovec * iov,
221 	      size_t iov_len)
222 {
223 	int32_t total_size = 0;
224 	int32_t i;
225 	int32_t res;
226 	int32_t res2;
227 
228 	for (i = 0; i < iov_len; i++) {
229 		total_size += iov[i].iov_len;
230 	}
231 	if (c == NULL) {
232 		return -EINVAL;
233 	}
234 	if (total_size > c->request.max_msg_size) {
235 		return -EMSGSIZE;
236 	}
237 
238 	if (c->funcs.fc_get) {
239 		res = c->funcs.fc_get(&c->request);
240 		if (res < 0) {
241 			return res;
242 		} else if (res > 0 && res <= c->fc_enable_max) {
243 			return -EAGAIN;
244 		} else {
245 			/*
246 			 * we can transmit
247 			 */
248 		}
249 	}
250 
251 	res = c->funcs.sendv(&c->request, iov, iov_len);
252 	if (res > 0 && c->needs_sock_for_poll) {
253 		do {
254 			res2 = qb_ipc_us_send(&c->setup, &res, 1);
255 		} while (res2 == -EAGAIN);
256 		if (res2 == -EPIPE) {
257 			res2 = -ENOTCONN;
258 		}
259 		if (res2 != 1) {
260 			res = res2;
261 		}
262 	}
263 	return _check_connection_state(c, res);
264 }
265 
266 ssize_t
qb_ipcc_recv(struct qb_ipcc_connection * c,void * msg_ptr,size_t msg_len,int32_t ms_timeout)267 qb_ipcc_recv(struct qb_ipcc_connection * c, void *msg_ptr,
268 	     size_t msg_len, int32_t ms_timeout)
269 {
270 	int32_t res = 0;
271 	int32_t connect_res = 0;
272 
273 	if (c == NULL) {
274 		return -EINVAL;
275 	}
276 
277 	res = c->funcs.recv(&c->response, msg_ptr, msg_len, ms_timeout);
278 	if (res >= 0) {
279 		return res;
280 	}
281 
282 	/* if we didn't get a msg, check connection state */
283 	connect_res = _check_connection_state_with(c, res,
284 					    _response_sock_one_way_get(c),
285 					    ms_timeout, POLLIN);
286 
287 	/* only report the connection state check result if an error is returned. */
288 	if (connect_res < 0) {
289 		return connect_res;
290 	}
291 	return res;
292 }
293 
294 ssize_t
qb_ipcc_sendv_recv(qb_ipcc_connection_t * c,const struct iovec * iov,uint32_t iov_len,void * res_msg,size_t res_len,int32_t ms_timeout)295 qb_ipcc_sendv_recv(qb_ipcc_connection_t * c,
296 		   const struct iovec * iov, uint32_t iov_len,
297 		   void *res_msg, size_t res_len, int32_t ms_timeout)
298 {
299 	ssize_t res = 0;
300 	int32_t timeout_now;
301 	int32_t timeout_rem = ms_timeout;
302 
303 	if (c == NULL) {
304 		return -EINVAL;
305 	}
306 
307 	if (c->funcs.fc_get) {
308 		res = c->funcs.fc_get(&c->request);
309 		if (res < 0) {
310 			return res;
311 		} else if (res > 0 && res <= c->fc_enable_max) {
312 			return -EAGAIN;
313 		} else {
314 			/*
315 			 * we can transmit
316 			 */
317 		}
318 	}
319 
320 	res = qb_ipcc_sendv(c, iov, iov_len);
321 	if (res < 0) {
322 		return res;
323 	}
324 
325 	do {
326 		/* following is a liveness-driven interleaving
327 		   (for cases the server side failed/exited) */
328 		if (timeout_rem > QB_IPC_MAX_WAIT_MS || ms_timeout == -1) {
329 			timeout_now = QB_IPC_MAX_WAIT_MS;
330 		} else {
331 			timeout_now = timeout_rem;
332 		}
333 
334 		res = qb_ipcc_recv(c, res_msg, res_len, timeout_now);
335 		if (res == -ETIMEDOUT) {
336 			if (ms_timeout < 0) {
337 				res = -EAGAIN;
338 			} else {
339 				timeout_rem -= timeout_now;
340 				if (timeout_rem > 0) {
341 					res = -EAGAIN;
342 				}
343 			}
344 		} else	if (res < 0 && res != -EAGAIN) {
345 			errno = -res;
346 			qb_util_perror(LOG_DEBUG,
347 				       "qb_ipcc_recv %d timeout:(%d/%d)",
348 				       res, timeout_now, timeout_rem);
349 		}
350 	} while (res == -EAGAIN && c->is_connected);
351 
352 	return res;
353 }
354 
355 int32_t
qb_ipcc_fd_get(struct qb_ipcc_connection * c,int32_t * fd)356 qb_ipcc_fd_get(struct qb_ipcc_connection * c, int32_t * fd)
357 {
358 	if (c == NULL) {
359 		return -EINVAL;
360 	}
361 	if (c->event.type == QB_IPC_SOCKET) {
362 		*fd = c->event.u.us.sock;
363 	} else {
364 		*fd = c->setup.u.us.sock;
365 	}
366 	return 0;
367 }
368 
369 int32_t
qb_ipcc_auth_get(struct qb_ipcc_connection * c,pid_t * pid,uid_t * uid,gid_t * gid)370 qb_ipcc_auth_get(struct qb_ipcc_connection * c, pid_t *pid, uid_t *uid, gid_t *gid)
371 {
372 	if (c == NULL) {
373 		return -EINVAL;
374 	}
375 	if (pid) {
376 		*pid = c->server_pid;
377 	}
378 	if (uid) {
379 		*uid = c->euid;
380 	}
381 	if (gid) {
382 		*gid = c->egid;
383 	}
384 	return 0;
385 }
386 
387 ssize_t
qb_ipcc_event_recv(struct qb_ipcc_connection * c,void * msg_pt,size_t msg_len,int32_t ms_timeout)388 qb_ipcc_event_recv(struct qb_ipcc_connection * c, void *msg_pt,
389 		   size_t msg_len, int32_t ms_timeout)
390 {
391 	char one_byte = 1;
392 	int32_t res;
393 	ssize_t size;
394 
395 	if (c == NULL) {
396 		return -EINVAL;
397 	}
398 	res = _check_connection_state_with(c, -EAGAIN, _event_sock_one_way_get(c),
399 					   ms_timeout, POLLIN);
400 	if (res < 0) {
401 		return res;
402 	}
403 	size = c->funcs.recv(&c->event, msg_pt, msg_len, ms_timeout);
404 	if (size > 0 && c->needs_sock_for_poll) {
405 		res = qb_ipc_us_recv(&c->setup, &one_byte, 1, -1);
406 		if (res != 1) {
407 			size = res;
408 		}
409 	}
410 	return _check_connection_state(c, size);
411 }
412 
413 void
qb_ipcc_disconnect(struct qb_ipcc_connection * c)414 qb_ipcc_disconnect(struct qb_ipcc_connection *c)
415 {
416 	struct qb_ipc_one_way *ow = NULL;
417 
418 	qb_util_log(LOG_DEBUG, "%s()", __func__);
419 
420 	if (c == NULL) {
421 		return;
422 	}
423 
424 	ow = _event_sock_one_way_get(c);
425 	(void)_check_connection_state_with(c, -EAGAIN, ow, 0, POLLIN);
426 
427 	if (c->funcs.disconnect) {
428 		c->funcs.disconnect(c);
429 	}
430 	free(c->receive_buf);
431 	free(c);
432 }
433 
434 void
qb_ipcc_context_set(struct qb_ipcc_connection * c,void * context)435 qb_ipcc_context_set(struct qb_ipcc_connection *c, void *context)
436 {
437 	if (c == NULL) {
438 		return;
439 	}
440 	c->context = context;
441 }
442 
qb_ipcc_context_get(struct qb_ipcc_connection * c)443 void *qb_ipcc_context_get(struct qb_ipcc_connection *c)
444 {
445 	if (c == NULL) {
446 		return NULL;
447 	}
448 	return c->context;
449 }
450 
451 int32_t
qb_ipcc_is_connected(qb_ipcc_connection_t * c)452 qb_ipcc_is_connected(qb_ipcc_connection_t *c)
453 {
454 	struct qb_ipc_one_way *ow;
455 
456 	if (c == NULL) {
457 		return QB_FALSE;
458 	}
459 
460 	ow = _response_sock_one_way_get(c);
461 	(void)_check_connection_state_with(c, -EAGAIN, ow, 0, POLLIN);
462 
463 	return c->is_connected;
464 }
465 
466 int32_t
qb_ipcc_get_buffer_size(qb_ipcc_connection_t * c)467 qb_ipcc_get_buffer_size(qb_ipcc_connection_t * c)
468 {
469 	if (c == NULL) {
470 		return -EINVAL;
471 	}
472 
473 	return c->event.max_msg_size;
474 }
475