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