]> git.proxmox.com Git - mirror_corosync-qdevice.git/blob - qdevices/pr-poll-loop.c
pr-poll-loop: Add support for PR_POLL_EXCEPT
[mirror_corosync-qdevice.git] / qdevices / pr-poll-loop.c
1 /*
2 * Copyright (c) 2015-2020 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Jan Friesse (jfriesse@redhat.com)
7 *
8 * This software licensed under BSD license, the text of which follows:
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Red Hat, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/types.h>
36
37 #include <arpa/inet.h>
38 #include <sys/queue.h>
39
40 #include <assert.h>
41 #include <inttypes.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "pr-poll-array.h"
46 #include "pr-poll-loop.h"
47
48 /*
49 * Needed for creating nspr handle from unix fd
50 */
51 #include <private/pprio.h>
52
53 /*
54 * Helper functions declarations
55 */
56 static PRInt16 poll_events_to_pr_events(short events);
57
58 static short pr_events_to_poll_events(PRInt16 events);
59
60 static int pr_poll_loop_add_fd_int(struct pr_poll_loop *poll_loop,
61 int fd, PRFileDesc *prfd,
62 short events, pr_poll_loop_fd_set_events_cb_fn fd_set_events_cb,
63 pr_poll_loop_prfd_set_events_cb_fn prfd_set_events_cb,
64 pr_poll_loop_fd_read_cb_fn fd_read_cb, pr_poll_loop_prfd_read_cb_fn prfd_read_cb,
65 pr_poll_loop_fd_write_cb_fn fd_write_cb, pr_poll_loop_prfd_write_cb_fn prfd_write_cb,
66 pr_poll_loop_fd_err_cb_fn fd_err_cb, pr_poll_loop_prfd_err_cb_fn prfd_err_cb,
67 void *user_data1, void *user_data2);
68
69 static int pr_poll_loop_del_fd_int(struct pr_poll_loop *poll_loop,
70 int fd, PRFileDesc *prfd);
71
72 static struct pr_poll_loop_fd_entry *pr_poll_loop_find_by_fd(
73 const struct pr_poll_loop *poll_loop, int fd, PRFileDesc *prfd);
74
75 static int prepare_poll_array(struct pr_poll_loop *poll_loop);
76
77 /*
78 * Helper functions definitions
79 */
80 static PRInt16
81 poll_events_to_pr_events(short events)
82 {
83 PRInt16 res;
84
85 res = 0;
86
87 if (events & POLLIN) {
88 res |= PR_POLL_READ;
89 }
90
91 if (events & POLLOUT) {
92 res |= PR_POLL_WRITE;
93 }
94
95 if (events & POLLPRI) {
96 res |= PR_POLL_EXCEPT;
97 }
98
99 return (res);
100 }
101
102 static short
103 pr_events_to_poll_events(PRInt16 events)
104 {
105 short res;
106
107 res = 0;
108
109 if (events & PR_POLL_READ) {
110 res |= POLLIN;
111 }
112
113 if (events & PR_POLL_WRITE) {
114 res |= POLLOUT;
115 }
116
117 if (events & PR_POLL_ERR) {
118 res |= POLLERR;
119 }
120
121 if (events & PR_POLL_NVAL) {
122 res |= POLLNVAL;
123 }
124
125 if (events & PR_POLL_HUP) {
126 res |= POLLHUP;
127 }
128
129 if (events & PR_POLL_EXCEPT) {
130 res |= POLLPRI;
131 }
132
133 return (res);
134 }
135
136 static int
137 pr_poll_loop_add_fd_int(struct pr_poll_loop *poll_loop, int fd, PRFileDesc *prfd,
138 short events, pr_poll_loop_fd_set_events_cb_fn fd_set_events_cb,
139 pr_poll_loop_prfd_set_events_cb_fn prfd_set_events_cb,
140 pr_poll_loop_fd_read_cb_fn fd_read_cb, pr_poll_loop_prfd_read_cb_fn prfd_read_cb,
141 pr_poll_loop_fd_write_cb_fn fd_write_cb, pr_poll_loop_prfd_write_cb_fn prfd_write_cb,
142 pr_poll_loop_fd_err_cb_fn fd_err_cb, pr_poll_loop_prfd_err_cb_fn prfd_err_cb,
143 void *user_data1, void *user_data2)
144 {
145 struct pr_poll_loop_fd_entry *new_entry;
146
147 assert((prfd != NULL && fd == -1) || (fd != -1 && prfd == NULL));
148
149 if ((events & ~(POLLIN|POLLOUT|POLLPRI)) != 0) {
150 return (-1);
151 }
152
153 if (pr_poll_loop_find_by_fd(poll_loop, fd, prfd) != NULL) {
154 return (-1);
155 }
156
157 new_entry = malloc(sizeof(*new_entry));
158 if (new_entry == NULL) {
159 return (-1);
160 }
161
162 memset(new_entry, 0, sizeof(*new_entry));
163
164 new_entry->fd = fd;
165
166 if (fd != -1) {
167 new_entry->prfd = PR_CreateSocketPollFd(fd);
168 if (new_entry->prfd == NULL) {
169 free(new_entry);
170
171 return (-1);
172 }
173 } else {
174 new_entry->prfd = prfd;
175 }
176
177 new_entry->events = events;
178
179 new_entry->fd_set_events_cb = fd_set_events_cb;
180 new_entry->prfd_set_events_cb = prfd_set_events_cb;
181
182 new_entry->fd_read_cb = fd_read_cb;
183 new_entry->prfd_read_cb = prfd_read_cb;
184
185 new_entry->fd_write_cb = fd_write_cb;
186 new_entry->prfd_write_cb = prfd_write_cb;
187
188 new_entry->fd_err_cb = fd_err_cb;
189 new_entry->prfd_err_cb = prfd_err_cb;
190
191 new_entry->user_data1 = user_data1;
192 new_entry->user_data2 = user_data2;
193
194 TAILQ_INSERT_TAIL(&poll_loop->fd_list, new_entry, entries);
195
196 return (0);
197 }
198
199 static int
200 pr_poll_loop_del_fd_int(struct pr_poll_loop *poll_loop, int fd, PRFileDesc *prfd)
201 {
202 struct pr_poll_loop_fd_entry *fd_entry;
203
204 fd_entry = pr_poll_loop_find_by_fd(poll_loop, fd, prfd);
205 if (fd_entry == NULL) {
206 return (-1);
207 }
208
209 TAILQ_REMOVE(&poll_loop->fd_list, fd_entry, entries);
210
211 if (fd_entry->fd != -1) {
212 (void)PR_DestroySocketPollFd(fd_entry->prfd);
213 }
214
215 free(fd_entry);
216
217 return (0);
218 }
219
220 static struct pr_poll_loop_fd_entry *
221 pr_poll_loop_find_by_fd(const struct pr_poll_loop *poll_loop, int fd, PRFileDesc *prfd)
222 {
223 struct pr_poll_loop_fd_entry *fd_entry;
224
225 assert((prfd != NULL && fd == -1) || (fd != -1 && prfd == NULL));
226
227 TAILQ_FOREACH(fd_entry, &poll_loop->fd_list, entries) {
228 if (fd != -1) {
229 if (fd_entry->fd == fd) {
230 return (fd_entry);
231 }
232 } else {
233 if (fd_entry->prfd == prfd) {
234 return (fd_entry);
235 }
236 }
237 }
238
239 return (NULL);
240 }
241
242 static
243 int prepare_poll_array(struct pr_poll_loop *poll_loop)
244 {
245 struct pr_poll_loop_fd_entry *fd_entry;
246 struct pr_poll_loop_fd_entry *fd_entry_next;
247 struct pr_poll_loop_fd_entry **user_data;
248 short events;
249 int res;
250 PRPollDesc *poll_desc;
251 struct pr_poll_array *poll_array;
252
253 poll_array = &poll_loop->poll_array;
254
255 pr_poll_array_clean(poll_array);
256
257 /*
258 * Fill in poll_array
259 */
260 fd_entry = TAILQ_FIRST(&poll_loop->fd_list);
261
262 while (fd_entry != NULL) {
263 fd_entry_next = TAILQ_NEXT(fd_entry, entries);
264
265 events = fd_entry->events;
266
267 if (fd_entry->fd_set_events_cb != NULL || fd_entry->prfd_set_events_cb != NULL) {
268 if (fd_entry->fd_set_events_cb != NULL) {
269 res = fd_entry->fd_set_events_cb(fd_entry->fd, &events,
270 fd_entry->user_data1, fd_entry->user_data2);
271 } else {
272 res = fd_entry->prfd_set_events_cb(fd_entry->prfd, &events,
273 fd_entry->user_data1, fd_entry->user_data2);
274 }
275 } else {
276 /*
277 * Add entry
278 */
279 res = 0;
280 }
281
282 if ((events & ~(POLLIN|POLLOUT|POLLPRI)) != 0) {
283 return (-2);
284 }
285
286 if (events == 0) {
287 /*
288 * Empty events -> do not add entry
289 */
290 res = -1;
291 }
292
293 switch (res) {
294 case 0:
295 /*
296 * Add entry
297 */
298 if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
299 return (-1);
300 }
301
302 poll_desc->fd = fd_entry->prfd;
303 poll_desc->in_flags = poll_events_to_pr_events(events);
304
305 *user_data = fd_entry;
306 break;
307 case -1:
308 /*
309 * Do not add entry
310 */
311 break;
312 case -2:
313 /*
314 * -2 = return immediately
315 */
316 return (-1);
317 break;
318 default:
319 return (-2);
320 break;
321 }
322
323 fd_entry = fd_entry_next;
324 }
325
326 pr_poll_array_gc(poll_array);
327
328 return (0);
329 }
330
331 /*
332 * Exported functions
333 */
334 void
335 pr_poll_loop_init(struct pr_poll_loop *poll_loop)
336 {
337
338 memset(poll_loop, 0, sizeof(*poll_loop));
339
340 TAILQ_INIT(&(poll_loop->fd_list));
341
342 pr_poll_array_init(&poll_loop->poll_array, sizeof(struct pr_poll_loop_fd_entry *));
343 timer_list_init(&poll_loop->tlist);
344 }
345
346 int
347 pr_poll_loop_del_fd(struct pr_poll_loop *poll_loop, int fd)
348 {
349
350 return (pr_poll_loop_del_fd_int(poll_loop, fd, NULL));
351 }
352
353 int
354 pr_poll_loop_del_prfd(struct pr_poll_loop *poll_loop, PRFileDesc *prfd)
355 {
356
357 return (pr_poll_loop_del_fd_int(poll_loop, -1, prfd));
358 }
359
360 int
361 pr_poll_loop_destroy(struct pr_poll_loop *poll_loop)
362 {
363 struct pr_poll_loop_fd_entry *fd_entry;
364 struct pr_poll_loop_fd_entry *fd_entry_next;
365
366 fd_entry = TAILQ_FIRST(&poll_loop->fd_list);
367
368 while (fd_entry != NULL) {
369 fd_entry_next = TAILQ_NEXT(fd_entry, entries);
370
371 if (fd_entry->fd != -1) {
372 (void)PR_DestroySocketPollFd(fd_entry->prfd);
373 }
374
375 free(fd_entry);
376
377 fd_entry = fd_entry_next;
378 }
379
380 TAILQ_INIT(&(poll_loop->fd_list));
381
382 pr_poll_array_destroy(&poll_loop->poll_array);
383 timer_list_free(&poll_loop->tlist);
384
385 return (0);
386 }
387
388 int
389 pr_poll_loop_add_fd(struct pr_poll_loop *poll_loop, int fd,
390 short events, pr_poll_loop_fd_set_events_cb_fn fd_set_events_cb,
391 pr_poll_loop_fd_read_cb_fn fd_read_cb,
392 pr_poll_loop_fd_write_cb_fn fd_write_cb,
393 pr_poll_loop_fd_err_cb_fn fd_err_cb,
394 void *user_data1, void *user_data2)
395 {
396
397 return (pr_poll_loop_add_fd_int(poll_loop, fd, NULL, events,
398 fd_set_events_cb, NULL, fd_read_cb, NULL, fd_write_cb, NULL,
399 fd_err_cb, NULL,
400 user_data1, user_data2));
401 }
402
403 int
404 pr_poll_loop_add_prfd(struct pr_poll_loop *poll_loop, PRFileDesc *prfd,
405 short events, pr_poll_loop_prfd_set_events_cb_fn prfd_set_events_cb,
406 pr_poll_loop_prfd_read_cb_fn prfd_read_cb,
407 pr_poll_loop_prfd_read_cb_fn prfd_write_cb,
408 pr_poll_loop_prfd_err_cb_fn prfd_err_cb,
409 void *user_data1, void *user_data2)
410 {
411
412 return (pr_poll_loop_add_fd_int(poll_loop, -1, prfd, events,
413 NULL, prfd_set_events_cb, NULL, prfd_read_cb, NULL, prfd_write_cb,
414 NULL, prfd_err_cb,
415 user_data1, user_data2));
416 }
417
418 int
419 pr_poll_loop_exec(struct pr_poll_loop *poll_loop)
420 {
421 PRInt32 poll_res;
422 struct pr_poll_loop_fd_entry *fd_entry;
423 struct pr_poll_loop_fd_entry **user_data;
424 ssize_t i;
425 int cb_res;
426 static PRPollDesc *pfds;
427 int res;
428
429 if ((res = prepare_poll_array(poll_loop)) != 0) {
430 return (res);
431 }
432
433 pfds = poll_loop->poll_array.array;
434
435 if ((poll_res = PR_Poll(pfds, pr_poll_array_size(&poll_loop->poll_array),
436 timer_list_time_to_expire(&poll_loop->tlist))) > 0) {
437 for (i = 0; i < pr_poll_array_size(&poll_loop->poll_array); i++) {
438 user_data = pr_poll_array_get_user_data(&poll_loop->poll_array, i);
439 fd_entry = *user_data;
440
441 if (pfds[i].out_flags & PR_POLL_READ &&
442 (fd_entry->fd_read_cb != NULL || fd_entry->prfd_read_cb != NULL)) {
443 if (fd_entry->fd_read_cb) {
444 cb_res = fd_entry->fd_read_cb(fd_entry->fd,
445 fd_entry->user_data1, fd_entry->user_data2);
446 } else {
447 cb_res = fd_entry->prfd_read_cb(fd_entry->prfd,
448 fd_entry->user_data1, fd_entry->user_data2);
449 }
450
451 if (cb_res != 0) {
452 return (-1);
453 }
454 }
455
456 if (pfds[i].out_flags & PR_POLL_WRITE &&
457 (fd_entry->fd_write_cb != NULL || fd_entry->prfd_write_cb != NULL)) {
458 if (fd_entry->fd_write_cb) {
459 cb_res = fd_entry->fd_write_cb(fd_entry->fd,
460 fd_entry->user_data1, fd_entry->user_data2);
461 } else {
462 cb_res = fd_entry->prfd_write_cb(fd_entry->prfd,
463 fd_entry->user_data1, fd_entry->user_data2);
464 }
465
466 if (cb_res != 0) {
467 return (-1);
468 }
469 }
470
471 if ((pfds[i].out_flags & (PR_POLL_ERR|PR_POLL_NVAL|PR_POLL_HUP|PR_POLL_EXCEPT)) &&
472 !(pfds[i].out_flags & (PR_POLL_READ|PR_POLL_WRITE)) &&
473 (fd_entry->fd_err_cb != NULL || fd_entry->prfd_err_cb != NULL)) {
474 if (fd_entry->fd_err_cb) {
475 cb_res = fd_entry->fd_err_cb(fd_entry->fd,
476 pr_events_to_poll_events(pfds[i].out_flags),
477 fd_entry->user_data1, fd_entry->user_data2);
478 } else {
479 cb_res = fd_entry->prfd_err_cb(fd_entry->prfd,
480 pr_events_to_poll_events(pfds[i].out_flags),
481 fd_entry->user_data1, fd_entry->user_data2);
482 }
483
484 if (cb_res != 0) {
485 return (-1);
486 }
487 }
488 }
489 }
490
491 if (poll_res == -1) {
492 return (-3);
493 }
494
495 timer_list_expire(&poll_loop->tlist);
496
497 return (0);
498 }
499
500 struct timer_list *
501 pr_poll_loop_get_timer_list(struct pr_poll_loop *poll_loop)
502 {
503
504 return (&poll_loop->tlist);
505 }