You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
924 lines
23 KiB
924 lines
23 KiB
/*
|
|
* Select abstraction functions for the CUPS scheduler.
|
|
*
|
|
* Copyright 2007-2016 by Apple Inc.
|
|
* Copyright 2006-2007 by Easy Software Products.
|
|
*
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more information.
|
|
*/
|
|
|
|
/*
|
|
* Include necessary headers...
|
|
*/
|
|
|
|
#include "cupsd.h"
|
|
|
|
#ifdef HAVE_EPOLL
|
|
# include <sys/epoll.h>
|
|
# include <poll.h>
|
|
#elif defined(HAVE_KQUEUE)
|
|
# include <sys/event.h>
|
|
# include <sys/time.h>
|
|
#elif defined(HAVE_POLL)
|
|
# include <poll.h>
|
|
#else
|
|
# include <sys/select.h>
|
|
#endif /* HAVE_EPOLL */
|
|
|
|
|
|
/*
|
|
* Design Notes for Poll/Select API in CUPSD
|
|
* -----------------------------------------
|
|
*
|
|
* SUPPORTED APIS
|
|
*
|
|
* OS select poll epoll kqueue /dev/poll
|
|
* -------------- ------ ------ ------ ------ ---------
|
|
* AIX YES YES NO NO NO
|
|
* FreeBSD YES YES NO YES NO
|
|
* HP-UX YES YES NO NO NO
|
|
* Linux YES YES YES NO NO
|
|
* macOS YES YES NO YES NO
|
|
* NetBSD YES YES NO YES NO
|
|
* OpenBSD YES YES NO YES NO
|
|
* Solaris YES YES NO NO YES
|
|
* Tru64 YES YES NO NO NO
|
|
* Windows YES NO NO NO NO
|
|
*
|
|
*
|
|
* HIGH-LEVEL API
|
|
*
|
|
* typedef void (*cupsd_selfunc_t)(void *data);
|
|
*
|
|
* void cupsdStartSelect(void);
|
|
* void cupsdStopSelect(void);
|
|
* void cupsdAddSelect(int fd, cupsd_selfunc_t read_cb,
|
|
* cupsd_selfunc_t write_cb, void *data);
|
|
* void cupsdRemoveSelect(int fd);
|
|
* int cupsdDoSelect(int timeout);
|
|
*
|
|
*
|
|
* IMPLEMENTATION STRATEGY
|
|
*
|
|
* 0. Common Stuff
|
|
* a. CUPS array of file descriptor to callback functions
|
|
* and data + temporary array of removed fd's.
|
|
* b. cupsdStartSelect() creates the arrays
|
|
* c. cupsdStopSelect() destroys the arrays and all elements.
|
|
* d. cupsdAddSelect() adds to the array and allocates a
|
|
* new callback element.
|
|
* e. cupsdRemoveSelect() removes from the active array and
|
|
* adds to the inactive array.
|
|
* f. _cupsd_fd_t provides a reference-counted structure for
|
|
* tracking file descriptors that are monitored.
|
|
* g. cupsdDoSelect() frees all inactive FDs.
|
|
*
|
|
* 1. select() O(n)
|
|
* a. Input/Output fd_set variables, copied to working
|
|
* copies and then used with select().
|
|
* b. Loop through CUPS array, using FD_ISSET and calling
|
|
* the read/write callbacks as needed.
|
|
* c. cupsdRemoveSelect() clears fd_set bit from main and
|
|
* working sets.
|
|
* d. cupsdStopSelect() frees all of the memory used by the
|
|
* CUPS array and fd_set's.
|
|
*
|
|
* 2. poll() - O(n log n)
|
|
* a. Regular array of pollfd, sorted the same as the CUPS
|
|
* array.
|
|
* b. Loop through pollfd array, call the corresponding
|
|
* read/write callbacks as needed.
|
|
* c. cupsdAddSelect() adds first to CUPS array and flags the
|
|
* pollfd array as invalid.
|
|
* d. cupsdDoSelect() rebuilds pollfd array as needed, calls
|
|
* poll(), then loops through the pollfd array looking up
|
|
* as needed.
|
|
* e. cupsdRemoveSelect() flags the pollfd array as invalid.
|
|
* f. cupsdStopSelect() frees all of the memory used by the
|
|
* CUPS array and pollfd array.
|
|
*
|
|
* 3. epoll() - O(n)
|
|
* a. cupsdStartSelect() creates epoll file descriptor using
|
|
* epoll_create() with the maximum fd count, and
|
|
* allocates an events buffer for the maximum fd count.
|
|
* b. cupsdAdd/RemoveSelect() uses epoll_ctl() to add
|
|
* (EPOLL_CTL_ADD) or remove (EPOLL_CTL_DEL) a single
|
|
* event using the level-triggered semantics. The event
|
|
* user data field is a pointer to the new callback array
|
|
* element.
|
|
* c. cupsdDoSelect() uses epoll_wait() with the global event
|
|
* buffer allocated in cupsdStartSelect() and then loops
|
|
* through the events, using the user data field to find
|
|
* the callback record.
|
|
* d. cupsdStopSelect() closes the epoll file descriptor and
|
|
* frees all of the memory used by the event buffer.
|
|
*
|
|
* 4. kqueue() - O(n)
|
|
* b. cupsdStartSelect() creates kqueue file descriptor
|
|
* using kqueue() function and allocates a global event
|
|
* buffer.
|
|
* c. cupsdAdd/RemoveSelect() uses EV_SET and kevent() to
|
|
* register the changes. The event user data field is a
|
|
* pointer to the new callback array element.
|
|
* d. cupsdDoSelect() uses kevent() to poll for events and
|
|
* loops through the events, using the user data field to
|
|
* find the callback record.
|
|
* e. cupsdStopSelect() closes the kqueue() file descriptor
|
|
* and frees all of the memory used by the event buffer.
|
|
*
|
|
* 5. /dev/poll - O(n log n) - NOT YET IMPLEMENTED
|
|
* a. cupsdStartSelect() opens /dev/poll and allocates an
|
|
* array of pollfd structs; on failure to open /dev/poll,
|
|
* revert to poll() system call.
|
|
* b. cupsdAddSelect() writes a single pollfd struct to
|
|
* /dev/poll with the new file descriptor and the
|
|
* POLLIN/POLLOUT flags.
|
|
* c. cupsdRemoveSelect() writes a single pollfd struct to
|
|
* /dev/poll with the file descriptor and the POLLREMOVE
|
|
* flag.
|
|
* d. cupsdDoSelect() uses the DP_POLL ioctl to retrieve
|
|
* events from /dev/poll and then loops through the
|
|
* returned pollfd array, looking up the file descriptors
|
|
* as needed.
|
|
* e. cupsdStopSelect() closes /dev/poll and frees the
|
|
* pollfd array.
|
|
*
|
|
* PERFORMANCE
|
|
*
|
|
* In tests using the "make test" target with option 0 (keep cupsd
|
|
* running) and the "testspeed" program with "-c 50 -r 1000", epoll()
|
|
* performed 5.5% slower than select(), followed by kqueue() at 16%
|
|
* slower than select() and poll() at 18% slower than select(). Similar
|
|
* results were seen with twice the number of client connections.
|
|
*
|
|
* The epoll() and kqueue() performance is likely limited by the
|
|
* number of system calls used to add/modify/remove file
|
|
* descriptors dynamically. Further optimizations may be possible
|
|
* in the area of limiting use of cupsdAddSelect() and
|
|
* cupsdRemoveSelect(), however extreme care will be needed to avoid
|
|
* excess CPU usage and deadlock conditions.
|
|
*
|
|
* We may be able to improve the poll() implementation simply by
|
|
* keeping the pollfd array sync'd with the _cupsd_fd_t array, as that
|
|
* will eliminate the rebuilding of the array whenever there is a
|
|
* change and eliminate the fd array lookups in the inner loop of
|
|
* cupsdDoSelect().
|
|
*
|
|
* Since /dev/poll will never be able to use a shadow array, it may
|
|
* not make sense to implement support for it. ioctl() overhead will
|
|
* impact performance as well, so my guess would be that, for CUPS,
|
|
* /dev/poll will yield a net performance loss.
|
|
*/
|
|
|
|
/*
|
|
* Local structures...
|
|
*/
|
|
|
|
typedef struct _cupsd_fd_s
|
|
{
|
|
int fd, /* File descriptor */
|
|
use; /* Use count */
|
|
cupsd_selfunc_t read_cb, /* Read callback */
|
|
write_cb; /* Write callback */
|
|
void *data; /* Data pointer for callbacks */
|
|
} _cupsd_fd_t;
|
|
|
|
|
|
/*
|
|
* Local globals...
|
|
*/
|
|
|
|
static cups_array_t *cupsd_fds = NULL;
|
|
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
|
|
static cups_array_t *cupsd_inactive_fds = NULL;
|
|
static int cupsd_in_select = 0;
|
|
#endif /* HAVE_EPOLL || HAVE_KQUEUE */
|
|
|
|
#ifdef HAVE_KQUEUE
|
|
static int cupsd_kqueue_fd = -1,
|
|
cupsd_kqueue_changes = 0;
|
|
static struct kevent *cupsd_kqueue_events = NULL;
|
|
#elif defined(HAVE_POLL)
|
|
static int cupsd_alloc_pollfds = 0,
|
|
cupsd_update_pollfds = 0;
|
|
static struct pollfd *cupsd_pollfds = NULL;
|
|
# ifdef HAVE_EPOLL
|
|
static int cupsd_epoll_fd = -1;
|
|
static struct epoll_event *cupsd_epoll_events = NULL;
|
|
# endif /* HAVE_EPOLL */
|
|
#else /* select() */
|
|
static fd_set cupsd_global_input,
|
|
cupsd_global_output,
|
|
cupsd_current_input,
|
|
cupsd_current_output;
|
|
#endif /* HAVE_KQUEUE */
|
|
|
|
|
|
/*
|
|
* Local functions...
|
|
*/
|
|
|
|
static int compare_fds(_cupsd_fd_t *a, _cupsd_fd_t *b);
|
|
static _cupsd_fd_t *find_fd(int fd);
|
|
#define release_fd(f) { \
|
|
(f)->use --; \
|
|
if (!(f)->use) free((f));\
|
|
}
|
|
#define retain_fd(f) (f)->use++
|
|
|
|
|
|
/*
|
|
* 'cupsdAddSelect()' - Add a file descriptor to the list.
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on error */
|
|
cupsdAddSelect(int fd, /* I - File descriptor */
|
|
cupsd_selfunc_t read_cb, /* I - Read callback */
|
|
cupsd_selfunc_t write_cb,/* I - Write callback */
|
|
void *data) /* I - Data to pass to callback */
|
|
{
|
|
_cupsd_fd_t *fdptr; /* File descriptor record */
|
|
#ifdef HAVE_EPOLL
|
|
int added; /* 1 if added, 0 if modified */
|
|
#endif /* HAVE_EPOLL */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG2,
|
|
"cupsdAddSelect(fd=%d, read_cb=%p, write_cb=%p, data=%p)",
|
|
fd, read_cb, write_cb, data);
|
|
|
|
if (fd < 0)
|
|
return (0);
|
|
|
|
/*
|
|
* See if this FD has already been added...
|
|
*/
|
|
|
|
if ((fdptr = find_fd(fd)) == NULL)
|
|
{
|
|
/*
|
|
* No, add a new entry...
|
|
*/
|
|
|
|
if ((fdptr = calloc(1, sizeof(_cupsd_fd_t))) == NULL)
|
|
return (0);
|
|
|
|
fdptr->fd = fd;
|
|
fdptr->use = 1;
|
|
|
|
if (!cupsArrayAdd(cupsd_fds, fdptr))
|
|
{
|
|
cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to add fd %d to array!", fd);
|
|
free(fdptr);
|
|
return (0);
|
|
}
|
|
|
|
#ifdef HAVE_EPOLL
|
|
added = 1;
|
|
}
|
|
else
|
|
added = 0;
|
|
#else
|
|
}
|
|
#endif /* HAVE_EPOLL */
|
|
|
|
#ifdef HAVE_KQUEUE
|
|
{
|
|
struct kevent event; /* Event data */
|
|
struct timespec timeout; /* Timeout value */
|
|
|
|
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_nsec = 0;
|
|
|
|
if (fdptr->read_cb != read_cb)
|
|
{
|
|
if (read_cb)
|
|
EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, fdptr);
|
|
else
|
|
EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
|
|
|
|
if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
|
|
{
|
|
cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
|
|
strerror(errno));
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
if (fdptr->write_cb != write_cb)
|
|
{
|
|
if (write_cb)
|
|
EV_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, fdptr);
|
|
else
|
|
EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
|
|
|
|
if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
|
|
{
|
|
cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
|
|
strerror(errno));
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#elif defined(HAVE_POLL)
|
|
# ifdef HAVE_EPOLL
|
|
if (cupsd_epoll_fd >= 0)
|
|
{
|
|
struct epoll_event event; /* Event data */
|
|
|
|
|
|
event.events = 0;
|
|
|
|
if (read_cb)
|
|
event.events |= EPOLLIN;
|
|
|
|
if (write_cb)
|
|
event.events |= EPOLLOUT;
|
|
|
|
event.data.ptr = fdptr;
|
|
|
|
if (epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
|
|
&event))
|
|
{
|
|
close(cupsd_epoll_fd);
|
|
cupsd_epoll_fd = -1;
|
|
cupsd_update_pollfds = 1;
|
|
}
|
|
}
|
|
else
|
|
# endif /* HAVE_EPOLL */
|
|
|
|
cupsd_update_pollfds = 1;
|
|
|
|
#else /* select() */
|
|
/*
|
|
* Add or remove the file descriptor in the input and output sets
|
|
* for select()...
|
|
*/
|
|
|
|
if (read_cb)
|
|
FD_SET(fd, &cupsd_global_input);
|
|
else
|
|
{
|
|
FD_CLR(fd, &cupsd_global_input);
|
|
FD_CLR(fd, &cupsd_current_input);
|
|
}
|
|
|
|
if (write_cb)
|
|
FD_SET(fd, &cupsd_global_output);
|
|
else
|
|
{
|
|
FD_CLR(fd, &cupsd_global_output);
|
|
FD_CLR(fd, &cupsd_current_output);
|
|
}
|
|
#endif /* HAVE_KQUEUE */
|
|
|
|
/*
|
|
* Save the (new) read and write callbacks...
|
|
*/
|
|
|
|
fdptr->read_cb = read_cb;
|
|
fdptr->write_cb = write_cb;
|
|
fdptr->data = data;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsdDoSelect()' - Do a select-like operation.
|
|
*/
|
|
|
|
int /* O - Number of files or -1 on error */
|
|
cupsdDoSelect(long timeout) /* I - Timeout in seconds */
|
|
{
|
|
int nfds; /* Number of file descriptors */
|
|
_cupsd_fd_t *fdptr; /* Current file descriptor */
|
|
#ifdef HAVE_KQUEUE
|
|
int i; /* Looping var */
|
|
struct kevent *event; /* Current event */
|
|
struct timespec ktimeout; /* kevent() timeout */
|
|
|
|
|
|
cupsd_in_select = 1;
|
|
|
|
if (timeout >= 0 && timeout < 86400)
|
|
{
|
|
ktimeout.tv_sec = timeout;
|
|
ktimeout.tv_nsec = 0;
|
|
|
|
nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs,
|
|
&ktimeout);
|
|
}
|
|
else
|
|
nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs, NULL);
|
|
|
|
cupsd_kqueue_changes = 0;
|
|
|
|
for (i = nfds, event = cupsd_kqueue_events; i > 0; i --, event ++)
|
|
{
|
|
fdptr = (_cupsd_fd_t *)event->udata;
|
|
|
|
if (cupsArrayFind(cupsd_inactive_fds, fdptr))
|
|
continue;
|
|
|
|
retain_fd(fdptr);
|
|
|
|
if (fdptr->read_cb && event->filter == EVFILT_READ)
|
|
(*(fdptr->read_cb))(fdptr->data);
|
|
|
|
if (fdptr->use > 1 && fdptr->write_cb && event->filter == EVFILT_WRITE &&
|
|
!cupsArrayFind(cupsd_inactive_fds, fdptr))
|
|
(*(fdptr->write_cb))(fdptr->data);
|
|
|
|
release_fd(fdptr);
|
|
}
|
|
|
|
#elif defined(HAVE_POLL)
|
|
struct pollfd *pfd; /* Current pollfd structure */
|
|
int count; /* Number of file descriptors */
|
|
|
|
|
|
# ifdef HAVE_EPOLL
|
|
cupsd_in_select = 1;
|
|
|
|
if (cupsd_epoll_fd >= 0)
|
|
{
|
|
int i; /* Looping var */
|
|
struct epoll_event *event; /* Current event */
|
|
|
|
|
|
if (timeout >= 0 && timeout < 86400)
|
|
nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs,
|
|
timeout * 1000);
|
|
else
|
|
nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs, -1);
|
|
|
|
if (nfds < 0 && errno != EINTR)
|
|
{
|
|
close(cupsd_epoll_fd);
|
|
cupsd_epoll_fd = -1;
|
|
}
|
|
else
|
|
{
|
|
for (i = nfds, event = cupsd_epoll_events; i > 0; i --, event ++)
|
|
{
|
|
fdptr = (_cupsd_fd_t *)event->data.ptr;
|
|
|
|
if (cupsArrayFind(cupsd_inactive_fds, fdptr))
|
|
continue;
|
|
|
|
retain_fd(fdptr);
|
|
|
|
if (fdptr->read_cb && (event->events & (EPOLLIN | EPOLLERR | EPOLLHUP)))
|
|
(*(fdptr->read_cb))(fdptr->data);
|
|
|
|
if (fdptr->use > 1 && fdptr->write_cb &&
|
|
(event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) &&
|
|
!cupsArrayFind(cupsd_inactive_fds, fdptr))
|
|
(*(fdptr->write_cb))(fdptr->data);
|
|
|
|
release_fd(fdptr);
|
|
}
|
|
|
|
goto release_inactive;
|
|
}
|
|
}
|
|
# endif /* HAVE_EPOLL */
|
|
|
|
count = cupsArrayCount(cupsd_fds);
|
|
|
|
if (cupsd_update_pollfds)
|
|
{
|
|
/*
|
|
* Update the cupsd_pollfds array to match the current FD array...
|
|
*/
|
|
|
|
cupsd_update_pollfds = 0;
|
|
|
|
/*
|
|
* (Re)allocate memory as needed...
|
|
*/
|
|
|
|
if (count > cupsd_alloc_pollfds)
|
|
{
|
|
int allocfds = count + 16;
|
|
|
|
|
|
if (cupsd_pollfds)
|
|
pfd = realloc(cupsd_pollfds, (size_t)allocfds * sizeof(struct pollfd));
|
|
else
|
|
pfd = malloc((size_t)allocfds * sizeof(struct pollfd));
|
|
|
|
if (!pfd)
|
|
{
|
|
cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to allocate %d bytes for polling.", (int)((size_t)allocfds * sizeof(struct pollfd)));
|
|
|
|
return (-1);
|
|
}
|
|
|
|
cupsd_pollfds = pfd;
|
|
cupsd_alloc_pollfds = allocfds;
|
|
}
|
|
|
|
/*
|
|
* Rebuild the array...
|
|
*/
|
|
|
|
for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds), pfd = cupsd_pollfds;
|
|
fdptr;
|
|
fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds), pfd ++)
|
|
{
|
|
pfd->fd = fdptr->fd;
|
|
pfd->events = 0;
|
|
|
|
if (fdptr->read_cb)
|
|
pfd->events |= POLLIN;
|
|
|
|
if (fdptr->write_cb)
|
|
pfd->events |= POLLOUT;
|
|
}
|
|
}
|
|
|
|
if (timeout >= 0 && timeout < 86400)
|
|
nfds = poll(cupsd_pollfds, (nfds_t)count, timeout * 1000);
|
|
else
|
|
nfds = poll(cupsd_pollfds, (nfds_t)count, -1);
|
|
|
|
if (nfds > 0)
|
|
{
|
|
/*
|
|
* Do callbacks for each file descriptor...
|
|
*/
|
|
|
|
for (pfd = cupsd_pollfds; count > 0; pfd ++, count --)
|
|
{
|
|
if (!pfd->revents)
|
|
continue;
|
|
|
|
if ((fdptr = find_fd(pfd->fd)) == NULL)
|
|
continue;
|
|
|
|
retain_fd(fdptr);
|
|
|
|
if (fdptr->read_cb && (pfd->revents & (POLLIN | POLLERR | POLLHUP)))
|
|
(*(fdptr->read_cb))(fdptr->data);
|
|
|
|
if (fdptr->use > 1 && fdptr->write_cb &&
|
|
(pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
|
|
(*(fdptr->write_cb))(fdptr->data);
|
|
|
|
release_fd(fdptr);
|
|
}
|
|
}
|
|
|
|
#else /* select() */
|
|
struct timeval stimeout; /* Timeout for select() */
|
|
int maxfd; /* Maximum file descriptor */
|
|
|
|
|
|
/*
|
|
* Figure out the highest file descriptor number...
|
|
*/
|
|
|
|
if ((fdptr = (_cupsd_fd_t *)cupsArrayLast(cupsd_fds)) == NULL)
|
|
maxfd = 1;
|
|
else
|
|
maxfd = fdptr->fd + 1;
|
|
|
|
/*
|
|
* Do the select()...
|
|
*/
|
|
|
|
cupsd_current_input = cupsd_global_input;
|
|
cupsd_current_output = cupsd_global_output;
|
|
|
|
if (timeout >= 0 && timeout < 86400)
|
|
{
|
|
stimeout.tv_sec = timeout;
|
|
stimeout.tv_usec = 0;
|
|
|
|
nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
|
|
&stimeout);
|
|
}
|
|
else
|
|
nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
|
|
NULL);
|
|
|
|
if (nfds > 0)
|
|
{
|
|
/*
|
|
* Do callbacks for each file descriptor...
|
|
*/
|
|
|
|
for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
|
|
fdptr;
|
|
fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
|
|
{
|
|
retain_fd(fdptr);
|
|
|
|
if (fdptr->read_cb && FD_ISSET(fdptr->fd, &cupsd_current_input))
|
|
(*(fdptr->read_cb))(fdptr->data);
|
|
|
|
if (fdptr->use > 1 && fdptr->write_cb &&
|
|
FD_ISSET(fdptr->fd, &cupsd_current_output))
|
|
(*(fdptr->write_cb))(fdptr->data);
|
|
|
|
release_fd(fdptr);
|
|
}
|
|
}
|
|
|
|
#endif /* HAVE_KQUEUE */
|
|
|
|
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
|
|
/*
|
|
* Release all inactive file descriptors...
|
|
*/
|
|
|
|
# ifndef HAVE_KQUEUE
|
|
release_inactive:
|
|
# endif /* !HAVE_KQUEUE */
|
|
|
|
cupsd_in_select = 0;
|
|
|
|
for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_inactive_fds);
|
|
fdptr;
|
|
fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_inactive_fds))
|
|
{
|
|
cupsArrayRemove(cupsd_inactive_fds, fdptr);
|
|
release_fd(fdptr);
|
|
}
|
|
#endif /* HAVE_EPOLL || HAVE_KQUEUE */
|
|
|
|
/*
|
|
* Return the number of file descriptors handled...
|
|
*/
|
|
|
|
return (nfds);
|
|
}
|
|
|
|
|
|
#ifdef CUPSD_IS_SELECTING
|
|
/*
|
|
* 'cupsdIsSelecting()' - Determine whether we are monitoring a file
|
|
* descriptor.
|
|
*/
|
|
|
|
int /* O - 1 if selecting, 0 otherwise */
|
|
cupsdIsSelecting(int fd) /* I - File descriptor */
|
|
{
|
|
return (find_fd(fd) != NULL);
|
|
}
|
|
#endif /* CUPSD_IS_SELECTING */
|
|
|
|
|
|
/*
|
|
* 'cupsdRemoveSelect()' - Remove a file descriptor from the list.
|
|
*/
|
|
|
|
void
|
|
cupsdRemoveSelect(int fd) /* I - File descriptor */
|
|
{
|
|
_cupsd_fd_t *fdptr; /* File descriptor record */
|
|
#ifdef HAVE_EPOLL
|
|
struct epoll_event event; /* Event data */
|
|
#elif defined(HAVE_KQUEUE)
|
|
struct kevent event; /* Event data */
|
|
struct timespec timeout; /* Timeout value */
|
|
#elif defined(HAVE_POLL)
|
|
/* No variables for poll() */
|
|
#endif /* HAVE_EPOLL */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect(fd=%d)", fd);
|
|
|
|
if (fd < 0)
|
|
return;
|
|
|
|
/*
|
|
* Find the file descriptor...
|
|
*/
|
|
|
|
if ((fdptr = find_fd(fd)) == NULL)
|
|
return;
|
|
|
|
#ifdef HAVE_EPOLL
|
|
if (epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event))
|
|
{
|
|
close(cupsd_epoll_fd);
|
|
cupsd_epoll_fd = -1;
|
|
cupsd_update_pollfds = 1;
|
|
}
|
|
|
|
#elif defined(HAVE_KQUEUE)
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_nsec = 0;
|
|
|
|
if (fdptr->read_cb)
|
|
{
|
|
EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
|
|
|
|
if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
|
|
{
|
|
cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
|
|
strerror(errno));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (fdptr->write_cb)
|
|
{
|
|
EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
|
|
|
|
if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
|
|
{
|
|
cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
|
|
strerror(errno));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
#elif defined(HAVE_POLL)
|
|
/*
|
|
* Update the pollfds array...
|
|
*/
|
|
|
|
cupsd_update_pollfds = 1;
|
|
|
|
#else /* select() */
|
|
FD_CLR(fd, &cupsd_global_input);
|
|
FD_CLR(fd, &cupsd_global_output);
|
|
FD_CLR(fd, &cupsd_current_input);
|
|
FD_CLR(fd, &cupsd_current_output);
|
|
#endif /* HAVE_EPOLL */
|
|
|
|
#ifdef HAVE_KQUEUE
|
|
cleanup:
|
|
#endif /* HAVE_KQUEUE */
|
|
|
|
/*
|
|
* Remove the file descriptor from the active array and add to the
|
|
* inactive array (or release, if we don't need the inactive array...)
|
|
*/
|
|
|
|
cupsArrayRemove(cupsd_fds, fdptr);
|
|
|
|
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
|
|
if (cupsd_in_select)
|
|
cupsArrayAdd(cupsd_inactive_fds, fdptr);
|
|
else
|
|
#endif /* HAVE_EPOLL || HAVE_KQUEUE */
|
|
|
|
release_fd(fdptr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsdStartSelect()' - Initialize the file polling engine.
|
|
*/
|
|
|
|
void
|
|
cupsdStartSelect(void)
|
|
{
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartSelect()");
|
|
|
|
cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
|
|
|
|
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
|
|
cupsd_inactive_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
|
|
#endif /* HAVE_EPOLL || HAVE_KQUEUE */
|
|
|
|
#ifdef HAVE_EPOLL
|
|
cupsd_epoll_fd = epoll_create(MaxFDs);
|
|
cupsd_epoll_events = calloc((size_t)MaxFDs, sizeof(struct epoll_event));
|
|
cupsd_update_pollfds = 0;
|
|
|
|
#elif defined(HAVE_KQUEUE)
|
|
cupsd_kqueue_fd = kqueue();
|
|
cupsd_kqueue_changes = 0;
|
|
cupsd_kqueue_events = calloc((size_t)MaxFDs, sizeof(struct kevent));
|
|
|
|
#elif defined(HAVE_POLL)
|
|
cupsd_update_pollfds = 0;
|
|
|
|
#else /* select() */
|
|
FD_ZERO(&cupsd_global_input);
|
|
FD_ZERO(&cupsd_global_output);
|
|
#endif /* HAVE_EPOLL */
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsdStopSelect()' - Shutdown the file polling engine.
|
|
*/
|
|
|
|
void
|
|
cupsdStopSelect(void)
|
|
{
|
|
_cupsd_fd_t *fdptr; /* Current file descriptor */
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopSelect()");
|
|
|
|
for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
|
|
fdptr;
|
|
fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
|
|
free(fdptr);
|
|
|
|
cupsArrayDelete(cupsd_fds);
|
|
cupsd_fds = NULL;
|
|
|
|
#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
|
|
cupsArrayDelete(cupsd_inactive_fds);
|
|
cupsd_inactive_fds = NULL;
|
|
#endif /* HAVE_EPOLL || HAVE_KQUEUE */
|
|
|
|
#ifdef HAVE_KQUEUE
|
|
if (cupsd_kqueue_events)
|
|
{
|
|
free(cupsd_kqueue_events);
|
|
cupsd_kqueue_events = NULL;
|
|
}
|
|
|
|
if (cupsd_kqueue_fd >= 0)
|
|
{
|
|
close(cupsd_kqueue_fd);
|
|
cupsd_kqueue_fd = -1;
|
|
}
|
|
|
|
cupsd_kqueue_changes = 0;
|
|
|
|
#elif defined(HAVE_POLL)
|
|
# ifdef HAVE_EPOLL
|
|
if (cupsd_epoll_events)
|
|
{
|
|
free(cupsd_epoll_events);
|
|
cupsd_epoll_events = NULL;
|
|
}
|
|
|
|
if (cupsd_epoll_fd >= 0)
|
|
{
|
|
close(cupsd_epoll_fd);
|
|
cupsd_epoll_fd = -1;
|
|
}
|
|
# endif /* HAVE_EPOLL */
|
|
|
|
if (cupsd_pollfds)
|
|
{
|
|
free(cupsd_pollfds);
|
|
cupsd_pollfds = NULL;
|
|
cupsd_alloc_pollfds = 0;
|
|
}
|
|
|
|
cupsd_update_pollfds = 0;
|
|
|
|
#else /* select() */
|
|
FD_ZERO(&cupsd_global_input);
|
|
FD_ZERO(&cupsd_global_output);
|
|
#endif /* HAVE_EPOLL */
|
|
}
|
|
|
|
|
|
/*
|
|
* 'compare_fds()' - Compare file descriptors.
|
|
*/
|
|
|
|
static int /* O - Result of comparison */
|
|
compare_fds(_cupsd_fd_t *a, /* I - First file descriptor */
|
|
_cupsd_fd_t *b) /* I - Second file descriptor */
|
|
{
|
|
return (a->fd - b->fd);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'find_fd()' - Find an existing file descriptor record.
|
|
*/
|
|
|
|
static _cupsd_fd_t * /* O - FD record pointer or NULL */
|
|
find_fd(int fd) /* I - File descriptor */
|
|
{
|
|
_cupsd_fd_t *fdptr, /* Matching record (if any) */
|
|
key; /* Search key */
|
|
|
|
|
|
cupsArraySave(cupsd_fds);
|
|
|
|
key.fd = fd;
|
|
fdptr = (_cupsd_fd_t *)cupsArrayFind(cupsd_fds, &key);
|
|
|
|
cupsArrayRestore(cupsd_fds);
|
|
|
|
return (fdptr);
|
|
}
|