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