1 /*
2 * Select abstraction functions for the CUPS scheduler.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright 2007-2016 by Apple Inc.
6 * Copyright 2006-2007 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #include "cupsd.h"
16
17 #ifdef HAVE_EPOLL
18 # include <sys/epoll.h>
19 # include <poll.h>
20 #elif defined(HAVE_KQUEUE)
21 # include <sys/event.h>
22 # include <sys/time.h>
23 #elif defined(HAVE_POLL)
24 # include <poll.h>
25 #else
26 # include <sys/select.h>
27 #endif /* HAVE_EPOLL */
28
29
30 /*
31 * Design Notes for Poll/Select API in CUPSD
32 * -----------------------------------------
33 *
34 * SUPPORTED APIS
35 *
36 * OS select poll epoll kqueue /dev/poll
37 * -------------- ------ ------ ------ ------ ---------
38 * AIX YES YES NO NO NO
39 * FreeBSD YES YES NO YES NO
40 * HP-UX YES YES NO NO NO
41 * Linux YES YES YES NO NO
42 * macOS YES YES NO YES NO
43 * NetBSD YES YES NO YES NO
44 * OpenBSD YES YES NO YES NO
45 * Solaris YES YES NO NO YES
46 * Tru64 YES YES NO NO NO
47 * Windows YES NO NO NO NO
48 *
49 *
50 * HIGH-LEVEL API
51 *
52 * typedef void (*cupsd_selfunc_t)(void *data);
53 *
54 * void cupsdStartSelect(void);
55 * void cupsdStopSelect(void);
56 * void cupsdAddSelect(int fd, cupsd_selfunc_t read_cb,
57 * cupsd_selfunc_t write_cb, void *data);
58 * void cupsdRemoveSelect(int fd);
59 * int cupsdDoSelect(int timeout);
60 *
61 *
62 * IMPLEMENTATION STRATEGY
63 *
64 * 0. Common Stuff
65 * a. CUPS array of file descriptor to callback functions
66 * and data + temporary array of removed fd's.
67 * b. cupsdStartSelect() creates the arrays
68 * c. cupsdStopSelect() destroys the arrays and all elements.
69 * d. cupsdAddSelect() adds to the array and allocates a
70 * new callback element.
71 * e. cupsdRemoveSelect() removes from the active array and
72 * adds to the inactive array.
73 * f. _cupsd_fd_t provides a reference-counted structure for
74 * tracking file descriptors that are monitored.
75 * g. cupsdDoSelect() frees all inactive FDs.
76 *
77 * 1. select() O(n)
78 * a. Input/Output fd_set variables, copied to working
79 * copies and then used with select().
80 * b. Loop through CUPS array, using FD_ISSET and calling
81 * the read/write callbacks as needed.
82 * c. cupsdRemoveSelect() clears fd_set bit from main and
83 * working sets.
84 * d. cupsdStopSelect() frees all of the memory used by the
85 * CUPS array and fd_set's.
86 *
87 * 2. poll() - O(n log n)
88 * a. Regular array of pollfd, sorted the same as the CUPS
89 * array.
90 * b. Loop through pollfd array, call the corresponding
91 * read/write callbacks as needed.
92 * c. cupsdAddSelect() adds first to CUPS array and flags the
93 * pollfd array as invalid.
94 * d. cupsdDoSelect() rebuilds pollfd array as needed, calls
95 * poll(), then loops through the pollfd array looking up
96 * as needed.
97 * e. cupsdRemoveSelect() flags the pollfd array as invalid.
98 * f. cupsdStopSelect() frees all of the memory used by the
99 * CUPS array and pollfd array.
100 *
101 * 3. epoll() - O(n)
102 * a. cupsdStartSelect() creates epoll file descriptor using
103 * epoll_create() with the maximum fd count, and
104 * allocates an events buffer for the maximum fd count.
105 * b. cupsdAdd/RemoveSelect() uses epoll_ctl() to add
106 * (EPOLL_CTL_ADD) or remove (EPOLL_CTL_DEL) a single
107 * event using the level-triggered semantics. The event
108 * user data field is a pointer to the new callback array
109 * element.
110 * c. cupsdDoSelect() uses epoll_wait() with the global event
111 * buffer allocated in cupsdStartSelect() and then loops
112 * through the events, using the user data field to find
113 * the callback record.
114 * d. cupsdStopSelect() closes the epoll file descriptor and
115 * frees all of the memory used by the event buffer.
116 *
117 * 4. kqueue() - O(n)
118 * b. cupsdStartSelect() creates kqueue file descriptor
119 * using kqueue() function and allocates a global event
120 * buffer.
121 * c. cupsdAdd/RemoveSelect() uses EV_SET and kevent() to
122 * register the changes. The event user data field is a
123 * pointer to the new callback array element.
124 * d. cupsdDoSelect() uses kevent() to poll for events and
125 * loops through the events, using the user data field to
126 * find the callback record.
127 * e. cupsdStopSelect() closes the kqueue() file descriptor
128 * and frees all of the memory used by the event buffer.
129 *
130 * 5. /dev/poll - O(n log n) - NOT YET IMPLEMENTED
131 * a. cupsdStartSelect() opens /dev/poll and allocates an
132 * array of pollfd structs; on failure to open /dev/poll,
133 * revert to poll() system call.
134 * b. cupsdAddSelect() writes a single pollfd struct to
135 * /dev/poll with the new file descriptor and the
136 * POLLIN/POLLOUT flags.
137 * c. cupsdRemoveSelect() writes a single pollfd struct to
138 * /dev/poll with the file descriptor and the POLLREMOVE
139 * flag.
140 * d. cupsdDoSelect() uses the DP_POLL ioctl to retrieve
141 * events from /dev/poll and then loops through the
142 * returned pollfd array, looking up the file descriptors
143 * as needed.
144 * e. cupsdStopSelect() closes /dev/poll and frees the
145 * pollfd array.
146 *
147 * PERFORMANCE
148 *
149 * In tests using the "make test" target with option 0 (keep cupsd
150 * running) and the "testspeed" program with "-c 50 -r 1000", epoll()
151 * performed 5.5% slower than select(), followed by kqueue() at 16%
152 * slower than select() and poll() at 18% slower than select(). Similar
153 * results were seen with twice the number of client connections.
154 *
155 * The epoll() and kqueue() performance is likely limited by the
156 * number of system calls used to add/modify/remove file
157 * descriptors dynamically. Further optimizations may be possible
158 * in the area of limiting use of cupsdAddSelect() and
159 * cupsdRemoveSelect(), however extreme care will be needed to avoid
160 * excess CPU usage and deadlock conditions.
161 *
162 * We may be able to improve the poll() implementation simply by
163 * keeping the pollfd array sync'd with the _cupsd_fd_t array, as that
164 * will eliminate the rebuilding of the array whenever there is a
165 * change and eliminate the fd array lookups in the inner loop of
166 * cupsdDoSelect().
167 *
168 * Since /dev/poll will never be able to use a shadow array, it may
169 * not make sense to implement support for it. ioctl() overhead will
170 * impact performance as well, so my guess would be that, for CUPS,
171 * /dev/poll will yield a net performance loss.
172 */
173
174 /*
175 * Local structures...
176 */
177
178 typedef struct _cupsd_fd_s
179 {
180 int fd, /* File descriptor */
181 use; /* Use count */
182 cupsd_selfunc_t read_cb, /* Read callback */
183 write_cb; /* Write callback */
184 void *data; /* Data pointer for callbacks */
185 } _cupsd_fd_t;
186
187
188 /*
189 * Local globals...
190 */
191
192 static cups_array_t *cupsd_fds = NULL;
193 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
194 static cups_array_t *cupsd_inactive_fds = NULL;
195 static int cupsd_in_select = 0;
196 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
197
198 #ifdef HAVE_KQUEUE
199 static int cupsd_kqueue_fd = -1;
200 static struct kevent *cupsd_kqueue_events = NULL;
201 #elif defined(HAVE_POLL)
202 static int cupsd_alloc_pollfds = 0,
203 cupsd_update_pollfds = 0;
204 static struct pollfd *cupsd_pollfds = NULL;
205 # ifdef HAVE_EPOLL
206 static int cupsd_epoll_fd = -1;
207 static struct epoll_event *cupsd_epoll_events = NULL;
208 # endif /* HAVE_EPOLL */
209 #else /* select() */
210 static fd_set cupsd_global_input,
211 cupsd_global_output,
212 cupsd_current_input,
213 cupsd_current_output;
214 #endif /* HAVE_KQUEUE */
215
216
217 /*
218 * Local functions...
219 */
220
221 static int compare_fds(_cupsd_fd_t *a, _cupsd_fd_t *b);
222 static _cupsd_fd_t *find_fd(int fd);
223 #define release_fd(f) { \
224 (f)->use --; \
225 if (!(f)->use) free((f));\
226 }
227 #define retain_fd(f) (f)->use++
228
229
230 /*
231 * 'cupsdAddSelect()' - Add a file descriptor to the list.
232 */
233
234 int /* O - 1 on success, 0 on error */
cupsdAddSelect(int fd,cupsd_selfunc_t read_cb,cupsd_selfunc_t write_cb,void * data)235 cupsdAddSelect(int fd, /* I - File descriptor */
236 cupsd_selfunc_t read_cb, /* I - Read callback */
237 cupsd_selfunc_t write_cb,/* I - Write callback */
238 void *data) /* I - Data to pass to callback */
239 {
240 _cupsd_fd_t *fdptr; /* File descriptor record */
241 #ifdef HAVE_EPOLL
242 int added; /* 1 if added, 0 if modified */
243 #endif /* HAVE_EPOLL */
244
245
246 /*
247 * Range check input...
248 */
249
250 cupsdLogMessage(CUPSD_LOG_DEBUG2,
251 "cupsdAddSelect(fd=%d, read_cb=%p, write_cb=%p, data=%p)",
252 fd, read_cb, write_cb, data);
253
254 if (fd < 0)
255 return (0);
256
257 /*
258 * See if this FD has already been added...
259 */
260
261 if ((fdptr = find_fd(fd)) == NULL)
262 {
263 /*
264 * No, add a new entry...
265 */
266
267 if ((fdptr = calloc(1, sizeof(_cupsd_fd_t))) == NULL)
268 return (0);
269
270 fdptr->fd = fd;
271 fdptr->use = 1;
272
273 if (!cupsArrayAdd(cupsd_fds, fdptr))
274 {
275 cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to add fd %d to array!", fd);
276 free(fdptr);
277 return (0);
278 }
279
280 #ifdef HAVE_EPOLL
281 added = 1;
282 }
283 else
284 added = 0;
285 #else
286 }
287 #endif /* HAVE_EPOLL */
288
289 #ifdef HAVE_KQUEUE
290 {
291 struct kevent event; /* Event data */
292 struct timespec timeout; /* Timeout value */
293
294
295 timeout.tv_sec = 0;
296 timeout.tv_nsec = 0;
297
298 if (fdptr->read_cb != read_cb)
299 {
300 if (read_cb)
301 EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, fdptr);
302 else
303 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
304
305 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
306 {
307 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
308 strerror(errno));
309 return (0);
310 }
311 }
312
313 if (fdptr->write_cb != write_cb)
314 {
315 if (write_cb)
316 EV_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, fdptr);
317 else
318 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
319
320 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
321 {
322 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
323 strerror(errno));
324 return (0);
325 }
326 }
327 }
328
329 #elif defined(HAVE_POLL)
330 # ifdef HAVE_EPOLL
331 if (cupsd_epoll_fd >= 0)
332 {
333 struct epoll_event event; /* Event data */
334
335
336 event.events = 0;
337
338 if (read_cb)
339 event.events |= EPOLLIN;
340
341 if (write_cb)
342 event.events |= EPOLLOUT;
343
344 event.data.ptr = fdptr;
345
346 if (epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
347 &event))
348 {
349 close(cupsd_epoll_fd);
350 cupsd_epoll_fd = -1;
351 cupsd_update_pollfds = 1;
352 }
353 }
354 else
355 # endif /* HAVE_EPOLL */
356
357 cupsd_update_pollfds = 1;
358
359 #else /* select() */
360 /*
361 * Add or remove the file descriptor in the input and output sets
362 * for select()...
363 */
364
365 if (read_cb)
366 FD_SET(fd, &cupsd_global_input);
367 else
368 {
369 FD_CLR(fd, &cupsd_global_input);
370 FD_CLR(fd, &cupsd_current_input);
371 }
372
373 if (write_cb)
374 FD_SET(fd, &cupsd_global_output);
375 else
376 {
377 FD_CLR(fd, &cupsd_global_output);
378 FD_CLR(fd, &cupsd_current_output);
379 }
380 #endif /* HAVE_KQUEUE */
381
382 /*
383 * Save the (new) read and write callbacks...
384 */
385
386 fdptr->read_cb = read_cb;
387 fdptr->write_cb = write_cb;
388 fdptr->data = data;
389
390 return (1);
391 }
392
393
394 /*
395 * 'cupsdDoSelect()' - Do a select-like operation.
396 */
397
398 int /* O - Number of files or -1 on error */
cupsdDoSelect(long timeout)399 cupsdDoSelect(long timeout) /* I - Timeout in seconds */
400 {
401 int nfds; /* Number of file descriptors */
402 _cupsd_fd_t *fdptr; /* Current file descriptor */
403 #ifdef HAVE_KQUEUE
404 int i; /* Looping var */
405 struct kevent *event; /* Current event */
406 struct timespec ktimeout; /* kevent() timeout */
407
408
409 cupsd_in_select = 1;
410
411 if (timeout >= 0 && timeout < 86400)
412 {
413 ktimeout.tv_sec = timeout;
414 ktimeout.tv_nsec = 0;
415
416 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs,
417 &ktimeout);
418 }
419 else
420 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs, NULL);
421
422 for (i = nfds, event = cupsd_kqueue_events; i > 0; i --, event ++)
423 {
424 fdptr = (_cupsd_fd_t *)event->udata;
425
426 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
427 continue;
428
429 retain_fd(fdptr);
430
431 if (fdptr->read_cb && event->filter == EVFILT_READ)
432 (*(fdptr->read_cb))(fdptr->data);
433
434 if (fdptr->use > 1 && fdptr->write_cb && event->filter == EVFILT_WRITE &&
435 !cupsArrayFind(cupsd_inactive_fds, fdptr))
436 (*(fdptr->write_cb))(fdptr->data);
437
438 release_fd(fdptr);
439 }
440
441 #elif defined(HAVE_POLL)
442 struct pollfd *pfd; /* Current pollfd structure */
443 int count; /* Number of file descriptors */
444
445
446 # ifdef HAVE_EPOLL
447 cupsd_in_select = 1;
448
449 if (cupsd_epoll_fd >= 0)
450 {
451 int i; /* Looping var */
452 struct epoll_event *event; /* Current event */
453
454
455 if (timeout >= 0 && timeout < 86400)
456 nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs,
457 timeout * 1000);
458 else
459 nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs, -1);
460
461 if (nfds < 0 && errno != EINTR)
462 {
463 close(cupsd_epoll_fd);
464 cupsd_epoll_fd = -1;
465 }
466 else
467 {
468 for (i = nfds, event = cupsd_epoll_events; i > 0; i --, event ++)
469 {
470 fdptr = (_cupsd_fd_t *)event->data.ptr;
471
472 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
473 continue;
474
475 retain_fd(fdptr);
476
477 if (fdptr->read_cb && (event->events & (EPOLLIN | EPOLLERR | EPOLLHUP)))
478 (*(fdptr->read_cb))(fdptr->data);
479
480 if (fdptr->use > 1 && fdptr->write_cb &&
481 (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) &&
482 !cupsArrayFind(cupsd_inactive_fds, fdptr))
483 (*(fdptr->write_cb))(fdptr->data);
484
485 release_fd(fdptr);
486 }
487
488 goto release_inactive;
489 }
490 }
491 # endif /* HAVE_EPOLL */
492
493 count = cupsArrayCount(cupsd_fds);
494
495 if (cupsd_update_pollfds)
496 {
497 /*
498 * Update the cupsd_pollfds array to match the current FD array...
499 */
500
501 cupsd_update_pollfds = 0;
502
503 /*
504 * (Re)allocate memory as needed...
505 */
506
507 if (count > cupsd_alloc_pollfds)
508 {
509 int allocfds = count + 16;
510
511
512 if (cupsd_pollfds)
513 pfd = realloc(cupsd_pollfds, (size_t)allocfds * sizeof(struct pollfd));
514 else
515 pfd = malloc((size_t)allocfds * sizeof(struct pollfd));
516
517 if (!pfd)
518 {
519 cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to allocate %d bytes for polling.", (int)((size_t)allocfds * sizeof(struct pollfd)));
520
521 return (-1);
522 }
523
524 cupsd_pollfds = pfd;
525 cupsd_alloc_pollfds = allocfds;
526 }
527
528 /*
529 * Rebuild the array...
530 */
531
532 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds), pfd = cupsd_pollfds;
533 fdptr;
534 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds), pfd ++)
535 {
536 pfd->fd = fdptr->fd;
537 pfd->events = 0;
538
539 if (fdptr->read_cb)
540 pfd->events |= POLLIN;
541
542 if (fdptr->write_cb)
543 pfd->events |= POLLOUT;
544 }
545 }
546
547 if (timeout >= 0 && timeout < 86400)
548 nfds = poll(cupsd_pollfds, (nfds_t)count, timeout * 1000);
549 else
550 nfds = poll(cupsd_pollfds, (nfds_t)count, -1);
551
552 if (nfds > 0)
553 {
554 /*
555 * Do callbacks for each file descriptor...
556 */
557
558 for (pfd = cupsd_pollfds; count > 0; pfd ++, count --)
559 {
560 if (!pfd->revents)
561 continue;
562
563 if ((fdptr = find_fd(pfd->fd)) == NULL)
564 continue;
565
566 retain_fd(fdptr);
567
568 if (fdptr->read_cb && (pfd->revents & (POLLIN | POLLERR | POLLHUP)))
569 (*(fdptr->read_cb))(fdptr->data);
570
571 if (fdptr->use > 1 && fdptr->write_cb &&
572 (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
573 (*(fdptr->write_cb))(fdptr->data);
574
575 release_fd(fdptr);
576 }
577 }
578
579 #else /* select() */
580 struct timeval stimeout; /* Timeout for select() */
581 int maxfd; /* Maximum file descriptor */
582
583
584 /*
585 * Figure out the highest file descriptor number...
586 */
587
588 if ((fdptr = (_cupsd_fd_t *)cupsArrayLast(cupsd_fds)) == NULL)
589 maxfd = 1;
590 else
591 maxfd = fdptr->fd + 1;
592
593 /*
594 * Do the select()...
595 */
596
597 cupsd_current_input = cupsd_global_input;
598 cupsd_current_output = cupsd_global_output;
599
600 if (timeout >= 0 && timeout < 86400)
601 {
602 stimeout.tv_sec = timeout;
603 stimeout.tv_usec = 0;
604
605 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
606 &stimeout);
607 }
608 else
609 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
610 NULL);
611
612 if (nfds > 0)
613 {
614 /*
615 * Do callbacks for each file descriptor...
616 */
617
618 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
619 fdptr;
620 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
621 {
622 retain_fd(fdptr);
623
624 if (fdptr->read_cb && FD_ISSET(fdptr->fd, &cupsd_current_input))
625 (*(fdptr->read_cb))(fdptr->data);
626
627 if (fdptr->use > 1 && fdptr->write_cb &&
628 FD_ISSET(fdptr->fd, &cupsd_current_output))
629 (*(fdptr->write_cb))(fdptr->data);
630
631 release_fd(fdptr);
632 }
633 }
634
635 #endif /* HAVE_KQUEUE */
636
637 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
638 /*
639 * Release all inactive file descriptors...
640 */
641
642 # ifndef HAVE_KQUEUE
643 release_inactive:
644 # endif /* !HAVE_KQUEUE */
645
646 cupsd_in_select = 0;
647
648 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_inactive_fds);
649 fdptr;
650 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_inactive_fds))
651 {
652 cupsArrayRemove(cupsd_inactive_fds, fdptr);
653 release_fd(fdptr);
654 }
655 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
656
657 /*
658 * Return the number of file descriptors handled...
659 */
660
661 return (nfds);
662 }
663
664
665 #ifdef CUPSD_IS_SELECTING
666 /*
667 * 'cupsdIsSelecting()' - Determine whether we are monitoring a file
668 * descriptor.
669 */
670
671 int /* O - 1 if selecting, 0 otherwise */
cupsdIsSelecting(int fd)672 cupsdIsSelecting(int fd) /* I - File descriptor */
673 {
674 return (find_fd(fd) != NULL);
675 }
676 #endif /* CUPSD_IS_SELECTING */
677
678
679 /*
680 * 'cupsdRemoveSelect()' - Remove a file descriptor from the list.
681 */
682
683 void
cupsdRemoveSelect(int fd)684 cupsdRemoveSelect(int fd) /* I - File descriptor */
685 {
686 _cupsd_fd_t *fdptr; /* File descriptor record */
687 #ifdef HAVE_EPOLL
688 struct epoll_event event; /* Event data */
689 #elif defined(HAVE_KQUEUE)
690 struct kevent event; /* Event data */
691 struct timespec timeout; /* Timeout value */
692 #elif defined(HAVE_POLL)
693 /* No variables for poll() */
694 #endif /* HAVE_EPOLL */
695
696
697 /*
698 * Range check input...
699 */
700
701 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect(fd=%d)", fd);
702
703 if (fd < 0)
704 return;
705
706 /*
707 * Find the file descriptor...
708 */
709
710 if ((fdptr = find_fd(fd)) == NULL)
711 return;
712
713 #ifdef HAVE_EPOLL
714 if (epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event))
715 {
716 close(cupsd_epoll_fd);
717 cupsd_epoll_fd = -1;
718 cupsd_update_pollfds = 1;
719 }
720
721 #elif defined(HAVE_KQUEUE)
722 timeout.tv_sec = 0;
723 timeout.tv_nsec = 0;
724
725 if (fdptr->read_cb)
726 {
727 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
728
729 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
730 {
731 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
732 strerror(errno));
733 goto cleanup;
734 }
735 }
736
737 if (fdptr->write_cb)
738 {
739 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
740
741 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
742 {
743 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
744 strerror(errno));
745 goto cleanup;
746 }
747 }
748
749 #elif defined(HAVE_POLL)
750 /*
751 * Update the pollfds array...
752 */
753
754 cupsd_update_pollfds = 1;
755
756 #else /* select() */
757 FD_CLR(fd, &cupsd_global_input);
758 FD_CLR(fd, &cupsd_global_output);
759 FD_CLR(fd, &cupsd_current_input);
760 FD_CLR(fd, &cupsd_current_output);
761 #endif /* HAVE_EPOLL */
762
763 #ifdef HAVE_KQUEUE
764 cleanup:
765 #endif /* HAVE_KQUEUE */
766
767 /*
768 * Remove the file descriptor from the active array and add to the
769 * inactive array (or release, if we don't need the inactive array...)
770 */
771
772 cupsArrayRemove(cupsd_fds, fdptr);
773
774 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
775 if (cupsd_in_select)
776 cupsArrayAdd(cupsd_inactive_fds, fdptr);
777 else
778 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
779
780 release_fd(fdptr);
781 }
782
783
784 /*
785 * 'cupsdStartSelect()' - Initialize the file polling engine.
786 */
787
788 void
cupsdStartSelect(void)789 cupsdStartSelect(void)
790 {
791 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartSelect()");
792
793 cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
794
795 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
796 cupsd_inactive_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
797 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
798
799 #ifdef HAVE_EPOLL
800 cupsd_epoll_fd = epoll_create(MaxFDs);
801 cupsd_epoll_events = calloc((size_t)MaxFDs, sizeof(struct epoll_event));
802 cupsd_update_pollfds = 0;
803
804 #elif defined(HAVE_KQUEUE)
805 cupsd_kqueue_fd = kqueue();
806 cupsd_kqueue_events = calloc((size_t)MaxFDs, sizeof(struct kevent));
807
808 #elif defined(HAVE_POLL)
809 cupsd_update_pollfds = 0;
810
811 #else /* select() */
812 FD_ZERO(&cupsd_global_input);
813 FD_ZERO(&cupsd_global_output);
814 #endif /* HAVE_EPOLL */
815 }
816
817
818 /*
819 * 'cupsdStopSelect()' - Shutdown the file polling engine.
820 */
821
822 void
cupsdStopSelect(void)823 cupsdStopSelect(void)
824 {
825 _cupsd_fd_t *fdptr; /* Current file descriptor */
826
827
828 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopSelect()");
829
830 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
831 fdptr;
832 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
833 free(fdptr);
834
835 cupsArrayDelete(cupsd_fds);
836 cupsd_fds = NULL;
837
838 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
839 cupsArrayDelete(cupsd_inactive_fds);
840 cupsd_inactive_fds = NULL;
841 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
842
843 #ifdef HAVE_KQUEUE
844 if (cupsd_kqueue_events)
845 {
846 free(cupsd_kqueue_events);
847 cupsd_kqueue_events = NULL;
848 }
849
850 if (cupsd_kqueue_fd >= 0)
851 {
852 close(cupsd_kqueue_fd);
853 cupsd_kqueue_fd = -1;
854 }
855
856 #elif defined(HAVE_POLL)
857 # ifdef HAVE_EPOLL
858 if (cupsd_epoll_events)
859 {
860 free(cupsd_epoll_events);
861 cupsd_epoll_events = NULL;
862 }
863
864 if (cupsd_epoll_fd >= 0)
865 {
866 close(cupsd_epoll_fd);
867 cupsd_epoll_fd = -1;
868 }
869 # endif /* HAVE_EPOLL */
870
871 if (cupsd_pollfds)
872 {
873 free(cupsd_pollfds);
874 cupsd_pollfds = NULL;
875 cupsd_alloc_pollfds = 0;
876 }
877
878 cupsd_update_pollfds = 0;
879
880 #else /* select() */
881 FD_ZERO(&cupsd_global_input);
882 FD_ZERO(&cupsd_global_output);
883 #endif /* HAVE_EPOLL */
884 }
885
886
887 /*
888 * 'compare_fds()' - Compare file descriptors.
889 */
890
891 static int /* O - Result of comparison */
compare_fds(_cupsd_fd_t * a,_cupsd_fd_t * b)892 compare_fds(_cupsd_fd_t *a, /* I - First file descriptor */
893 _cupsd_fd_t *b) /* I - Second file descriptor */
894 {
895 return (a->fd - b->fd);
896 }
897
898
899 /*
900 * 'find_fd()' - Find an existing file descriptor record.
901 */
902
903 static _cupsd_fd_t * /* O - FD record pointer or NULL */
find_fd(int fd)904 find_fd(int fd) /* I - File descriptor */
905 {
906 _cupsd_fd_t *fdptr, /* Matching record (if any) */
907 key; /* Search key */
908
909
910 cupsArraySave(cupsd_fds);
911
912 key.fd = fd;
913 fdptr = (_cupsd_fd_t *)cupsArrayFind(cupsd_fds, &key);
914
915 cupsArrayRestore(cupsd_fds);
916
917 return (fdptr);
918 }
919