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