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