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