• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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