-CHANGES.txt - 2006-11-14
+CHANGES.txt - 2006-11-21
------------------------
CHANGES IN CUPS V1.3
+ - The scheduler now uses poll(), epoll(), or /dev/kqueue
+ instead of select() when possible (STR #1261)
+ - Added new cupsArrayGetIndex() and cupsArrayGetInsert()
+ functions to get the current index and insertion
+ positions of an array.
- Added a new --with-max-copies configure option (STR
#2090)
- Added new cupsRemoveDest() and cupsSetDefaultDest()
--- /dev/null
+dnl
+dnl "$Id$"
+dnl
+dnl Select/poll stuff for the Common UNIX Printing System (CUPS).
+dnl
+dnl Copyright 2006 by Easy Software Products, all rights reserved.
+dnl
+dnl These coded instructions, statements, and computer programs are the
+dnl property of Easy Software Products and are protected by Federal
+dnl copyright law. Distribution and use rights are outlined in the file
+dnl "LICENSE.txt" which should have been included with this file. If this
+dnl file is missing or damaged please contact Easy Software Products
+dnl at:
+dnl
+dnl Attn: CUPS Licensing Information
+dnl Easy Software Products
+dnl 44141 Airport View Drive, Suite 204
+dnl Hollywood, Maryland 20636 USA
+dnl
+dnl Voice: (301) 373-9600
+dnl EMail: cups-info@cups.org
+dnl WWW: http://www.cups.org
+dnl
+
+AC_CHECK_FUNC(poll, AC_DEFINE(HAVE_POLL))
+AC_CHECK_FUNC(epoll_create, AC_DEFINE(HAVE_EPOLL))
+AC_CHECK_FUNC(kqueue, AC_DEFINE(HAVE_KQUEUE))
+
+dnl
+dnl End of "$Id$".
+dnl
#define CUPS_DEFAULT_GSSSERVICENAME ""
+/*
+ * Select/poll interfaces...
+ */
+
+#undef HAVE_POLL
+#undef HAVE_EPOLL
+#undef HAVE_KQUEUE
+
+
#endif /* !_CUPS_CONFIG_H_ */
/*
sinclude(config-scripts/cups-image.m4)
sinclude(config-scripts/cups-network.m4)
+sinclude(config-scripts/cups-poll.m4)
sinclude(config-scripts/cups-slp.m4)
sinclude(config-scripts/cups-gssapi.m4)
sinclude(config-scripts/cups-ldap.m4)
*
* Contents:
*
- * cupsArrayAdd() - Add an element to the array.
- * cupsArrayClear() - Clear the array.
- * cupsArrayCount() - Get the number of elements in the array.
- * cupsArrayCurrent() - Return the current element in the array.
- * cupsArrayDelete() - Free all memory used by the array.
- * cupsArrayDup() - Duplicate the array.
- * cupsArrayFind() - Find an element in the array.
- * cupsArrayFirst() - Get the first element in the array.
- * cupsArrayIndex() - Get the N-th element in the array.
- * cupsArrayInsert() - Insert an element in the array.
- * cupsArrayLast() - Get the last element in the array.
- * cupsArrayNew() - Create a new array.
- * cupsArrayNext() - Get the next element in the array.
- * cupsArrayPrev() - Get the previous element in the array.
- * cupsArrayRemove() - Remove an element from the array.
- * cupsArrayRestore() - Reset the current element to the last cupsArraySave.
- * cupsArraySave() - Mark the current element for a later
- * cupsArrayRestore.
- * cupsArrayUserData() - Return the user data for an array.
- * cups_array_add() - Insert or append an element to the array...
- * cups_array_find() - Find an element in the array...
+ * cupsArrayAdd() - Add an element to the array.
+ * cupsArrayClear() - Clear the array.
+ * cupsArrayCount() - Get the number of elements in the array.
+ * cupsArrayCurrent() - Return the current element in the array.
+ * cupsArrayDelete() - Free all memory used by the array.
+ * cupsArrayDup() - Duplicate the array.
+ * cupsArrayFind() - Find an element in the array.
+ * cupsArrayFirst() - Get the first element in the array.
+ * cupsArrayGetIndex() - Get the index of the current element.
+ * cupsArrayGetInsert() - Get the index of the last inserted element.
+ * cupsArrayIndex() - Get the N-th element in the array.
+ * cupsArrayInsert() - Insert an element in the array.
+ * cupsArrayLast() - Get the last element in the array.
+ * cupsArrayNew() - Create a new array.
+ * cupsArrayNext() - Get the next element in the array.
+ * cupsArrayPrev() - Get the previous element in the array.
+ * cupsArrayRemove() - Remove an element from the array.
+ * cupsArrayRestore() - Reset the current element to the last cupsArraySave.
+ * cupsArraySave() - Mark the current element for a later
+ * cupsArrayRestore.
+ * cupsArrayUserData() - Return the user data for an array.
+ * cups_array_add() - Insert or append an element to the array...
+ * cups_array_find() - Find an element in the array...
*/
/*
}
+/*
+ * 'cupsArrayGetIndex()' - Get the index of the current element.
+ *
+ * @since CUPS 1.3@
+ */
+
+int /* O - Index of the current element */
+cupsArrayGetIndex(cups_array_t *a) /* I - Array */
+{
+ if (!a)
+ return (-1);
+ else
+ return (a->current);
+}
+
+
+/*
+ * 'cupsArrayGetInsert()' - Get the index of the last inserted element.
+ *
+ * @since CUPS 1.3@
+ */
+
+int /* O - Index of the last inserted element */
+cupsArrayGetInsert(cups_array_t *a) /* I - Array */
+{
+ if (!a)
+ return (-1);
+ else
+ return (a->insert);
+}
+
+
/*
* 'cupsArrayIndex()' - Get the N-th element in the array.
*/
if (current <= a->current)
a->current --;
- if (current <= a->insert)
+ if (current < a->insert)
a->insert --;
+ else if (current == a->insert)
+ a->insert = -1;
for (i = 0; i < a->num_saved; i ++)
if (current <= a->saved[i])
extern cups_array_t *cupsArrayDup(cups_array_t *a);
extern void *cupsArrayFind(cups_array_t *a, void *e);
extern void *cupsArrayFirst(cups_array_t *a);
+extern int cupsArrayGetIndex(cups_array_t *a);
+extern int cupsArrayGetInsert(cups_array_t *a);
extern void *cupsArrayIndex(cups_array_t *a, int n);
extern int cupsArrayInsert(cups_array_t *a, void *e);
extern void *cupsArrayLast(cups_array_t *a);
void *tls; /* TLS state information */
http_encryption_t encryption; /* Encryption requirements */
/**** New in CUPS 1.1.19 ****/
- fd_set *input_set; /* select() set for httpWait() @since CUPS 1.1.19@ */
+ fd_set *input_set; /* select() set for httpWait() @deprecated@ */
http_status_t expect; /* Expect: header @since CUPS 1.1.19@ */
char *cookie; /* Cookie value(s) @since CUPS 1.1.19@ */
/**** New in CUPS 1.1.20 ****/
# include <sys/time.h>
# include <sys/resource.h>
#endif /* !WIN32 */
+#ifdef HAVE_POLL
+# include <sys/poll.h>
+#endif /* HAVE_POLL */
/*
httpAddrFreeList(http->addrlist);
- if (http->input_set)
- free(http->input_set);
-
if (http->cookie)
free(http->cookie);
int msec, /* I - Milliseconds to wait */
int usessl) /* I - Use SSL context? */
{
-#ifndef WIN32
- struct rlimit limit; /* Runtime limit */
- int set_size; /* Size of select set */
-#endif /* !WIN32 */
+#ifdef HAVE_POLL
+ struct pollfd pfd; /* Polled file descriptor */
+#else
+ fd_set input_set; /* select() input set */
struct timeval timeout; /* Timeout */
- int nfds; /* Result from select() */
+#endif /* HAVE_POLL */
+ int nfds; /* Result from select()/poll() */
DEBUG_printf(("http_wait(http=%p, msec=%d)\n", http, msec));
#endif /* HAVE_SSL */
/*
- * Then try doing a select() to poll the socket...
+ * Then try doing a select() or poll() to poll the socket...
*/
- if (!http->input_set)
- {
-#ifdef WIN32
- /*
- * Windows has a fixed-size select() structure, different (surprise,
- * surprise!) from all UNIX implementations. Just allocate this
- * fixed structure...
- */
-
- http->input_set = calloc(1, sizeof(fd_set));
-#else
- /*
- * Allocate the select() input set based upon the max number of file
- * descriptors available for this process...
- */
-
- getrlimit(RLIMIT_NOFILE, &limit);
+#ifdef HAVE_POLL
+ pfd.fd = http->fd;
+ pfd.events = POLLIN;
- set_size = (limit.rlim_cur + 31) / 8 + 4;
- if (set_size < sizeof(fd_set))
- set_size = sizeof(fd_set);
-
- http->input_set = calloc(1, set_size);
-#endif /* WIN32 */
-
- if (!http->input_set)
- return (0);
- }
+ while ((nfds = poll(&pfd, 1, msec)) < 0 && errno == EINTR);
+#else
do
{
- FD_SET(http->fd, http->input_set);
+ FD_ZERO(&input_set);
+ FD_SET(http->fd, &input_set);
DEBUG_printf(("http_wait: msec=%d, http->fd=%d\n", msec, http->fd));
timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000;
- nfds = select(http->fd + 1, http->input_set, NULL, NULL, &timeout);
+ nfds = select(http->fd + 1, &input_set, NULL, NULL, &timeout);
}
else
- nfds = select(http->fd + 1, http->input_set, NULL, NULL, NULL);
+ nfds = select(http->fd + 1, &input_set, NULL, NULL, NULL);
DEBUG_printf(("http_wait: select() returned %d...\n", nfds));
}
-#ifdef WIN32
+# ifdef WIN32
while (nfds < 0 && WSAGetLastError() == WSAEINTR);
-#else
+# else
while (nfds < 0 && errno == EINTR);
-#endif /* WIN32 */
-
- FD_CLR(http->fd, http->input_set);
+# endif /* WIN32 */
+#endif /* HAVE_POLL */
DEBUG_printf(("http_wait: returning with nfds=%d...\n", nfds));
printers.o \
process.o \
quotas.o \
+ select.o \
server.o \
statbuf.o \
subscriptions.o \
* cupsdSendHeader() - Send an HTTP request.
* cupsdUpdateCGI() - Read status messages from CGI scripts and programs.
* cupsdWriteClient() - Write data to a client as needed.
+ * cupsdWritePipe() - Flag that data is available on the CGI pipe.
* check_if_modified() - Decode an "If-Modified-Since" line.
* encrypt_client() - Enable encryption for the client...
* get_cdsa_server_certs() - Convert a keychain name into the CFArrayRef
* Add the socket to the select() input mask.
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdAcceptClient: Adding fd %d to InputSet...",
- con->http.fd);
- FD_SET(con->http.fd, InputSet);
+ cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
/*
* Temporarily suspend accept()'s until we lose a client...
if (con->file >= 0)
{
- if (FD_ISSET(con->file, InputSet))
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdCloseClient: %d Removing fd %d from InputSet...",
- con->http.fd, con->file);
- FD_CLR(con->file, InputSet);
- }
+ cupsdRemoveSelect(con->file);
cupsdLogMessage(CUPSD_LOG_DEBUG2,
"cupsdCloseClient: %d Closing data file %d.",
* Only do a partial close so that the encrypted client gets everything.
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdCloseClient: Removing fd %d from OutputSet...",
- con->http.fd);
shutdown(con->http.fd, 0);
- FD_CLR(con->http.fd, OutputSet);
+ cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
}
else
{
* Shut the socket down fully...
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdCloseClient: Removing fd %d from InputSet and OutputSet...",
- con->http.fd);
+ cupsdRemoveSelect(con->http.fd);
close(con->http.fd);
- FD_CLR(con->http.fd, InputSet);
- FD_CLR(con->http.fd, OutputSet);
con->http.fd = -1;
}
}
* 'cupsdReadClient()' - Read data from a client.
*/
-int /* O - 1 on success, 0 on error */
+void
cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
{
char line[32768], /* Line from client... */
if (con->http.error)
{
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: http error seen...");
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
#ifdef HAVE_SSL
buf[0] & 255);
if (!encrypt_client(con))
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
- return (1);
+ return;
}
}
#endif /* HAVE_SSL */
{
cupsdLogMessage(CUPSD_LOG_DEBUG2,
"cupsdReadClient: httpGets returned EOF...");
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
/*
"Bad request line \"%s\" from %s!", line,
con->http.hostname);
cupsdSendError(con, HTTP_BAD_REQUEST);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
case 2 :
con->http.version = HTTP_0_9;
break;
"Bad request line \"%s\" from %s!", line,
con->http.hostname);
cupsdSendError(con, HTTP_BAD_REQUEST);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
if (major < 2)
else
{
cupsdSendError(con, HTTP_NOT_SUPPORTED);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
break;
}
cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
con->uri);
cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
/*
{
cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
cupsdSendError(con, HTTP_BAD_REQUEST);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
con->start = time(NULL);
* Parse incoming parameters until the status changes...
*/
- status = httpUpdate(HTTP(con));
+ while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
+ if (con->http.used == 0 ||
+ !memchr(con->http.buffer, '\n', con->http.used))
+ break;
if (status != HTTP_OK && status != HTTP_CONTINUE)
{
cupsdSendError(con, HTTP_BAD_REQUEST);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
break;
*/
if (!cupsdSendError(con, HTTP_BAD_REQUEST))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else if (con->operation == HTTP_OPTIONS)
{
if (con->best && con->best->type != AUTH_NONE)
{
if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
*/
if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
httpPrintf(HTTP(con), "\r\n");
if (cupsdFlushHeader(con) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
if (!encrypt_client(con))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
#else
if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
#endif /* HAVE_SSL */
}
if (!cupsdSendHeader(con, HTTP_OK, NULL))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
httpPrintf(HTTP(con), "Content-Length: 0\r\n");
httpPrintf(HTTP(con), "\r\n");
if (cupsdFlushHeader(con) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else if (!is_path_absolute(con->uri))
{
*/
if (!cupsdSendError(con, HTTP_FORBIDDEN))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else
{
*/
if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
httpPrintf(HTTP(con), "\r\n");
if (cupsdFlushHeader(con) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
if (!encrypt_client(con))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
#else
if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
#endif /* HAVE_SSL */
}
"cupsdReadClient: Unauthorized request for %s...\n",
con->uri);
cupsdSendError(con, status);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
if (con->http.expect &&
*/
if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else
{
*/
if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
httpPrintf(HTTP(con), "Content-Length: 0\r\n");
httpPrintf(HTTP(con), "\r\n");
if (cupsdFlushHeader(con) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
}
else
{
if (!cupsdSendError(con, HTTP_NOT_FOUND))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
if (!cupsdSendCommand(con, con->command, con->options, 0))
{
if (!cupsdSendError(con, HTTP_NOT_FOUND))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else
cupsdLogRequest(con, HTTP_OK);
*/
if (!cupsdSendError(con, HTTP_FORBIDDEN))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
sizeof(buf))) == NULL)
{
if (!cupsdSendError(con, HTTP_NOT_FOUND))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
if (!cupsdSendCommand(con, con->command, con->options, 0))
{
if (!cupsdSendError(con, HTTP_NOT_FOUND))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else
cupsdLogRequest(con, HTTP_OK);
if (!check_if_modified(con, &filestats))
{
if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else
{
snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
if (!write_file(con, HTTP_OK, filename, line, &filestats))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
}
break;
*/
if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
*/
if (!cupsdSendError(con, HTTP_BAD_REQUEST))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
sizeof(buf))) == NULL)
{
if (!cupsdSendError(con, HTTP_NOT_FOUND))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
*/
if (!cupsdSendError(con, HTTP_UNAUTHORIZED))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
}
break;
*/
if (!cupsdSendError(con, HTTP_FORBIDDEN))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
*/
if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
*/
if (!cupsdSendError(con, HTTP_BAD_REQUEST))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
if (con->file < 0)
{
if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
fchmod(con->file, 0640);
case HTTP_DELETE :
case HTTP_TRACE :
cupsdSendError(con, HTTP_NOT_IMPLEMENTED);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
case HTTP_HEAD :
if (!strncmp(con->uri, "/printers/", 10) &&
else
{
if (!cupsdSendError(con, HTTP_NOT_FOUND))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
*/
if (!cupsdSendHeader(con, HTTP_OK, "text/html"))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
if (httpPrintf(HTTP(con), "\r\n") < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
if (cupsdFlushHeader(con) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
cupsdLogRequest(con, HTTP_OK);
}
*/
if (!cupsdSendError(con, HTTP_FORBIDDEN))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
break;
}
sizeof(buf))) == NULL)
{
if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html"))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
cupsdLogRequest(con, HTTP_NOT_FOUND);
}
else if (!check_if_modified(con, &filestats))
{
if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
cupsdLogRequest(con, HTTP_NOT_MODIFIED);
}
snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
if (!cupsdSendHeader(con, HTTP_OK, line))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
httpGetDateString(filestats.st_mtime)) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
(unsigned long)filestats.st_size) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
cupsdLogRequest(con, HTTP_OK);
}
if (httpPrintf(HTTP(con), "\r\n") < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
if (cupsdFlushHeader(con) < 0)
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
con->http.state = HTTP_WAITING;
break;
"CHUNKED" : "LENGTH",
CUPS_LLCAST con->http.data_remaining, con->file);
- if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
- return (cupsdCloseClient(con));
- else if (bytes > 0)
+ do
{
- con->bytes += bytes;
+ if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ else if (bytes > 0)
+ {
+ con->bytes += bytes;
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdReadClient: %d writing %d bytes to %d",
- con->http.fd, bytes, con->file);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdReadClient: %d writing %d bytes to %d",
+ con->http.fd, bytes, con->file);
- if (write(con->file, line, bytes) < bytes)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "cupsdReadClient: Unable to write %d bytes to %s: %s",
- bytes, con->filename, strerror(errno));
+ if (write(con->file, line, bytes) < bytes)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cupsdReadClient: Unable to write %d bytes to %s: %s",
+ bytes, con->filename, strerror(errno));
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdReadClient: Closing data file %d...",
- con->file);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdReadClient: Closing data file %d...",
+ con->file);
- close(con->file);
- con->file = -1;
- unlink(con->filename);
- cupsdClearString(&con->filename);
+ close(con->file);
+ con->file = -1;
+ unlink(con->filename);
+ cupsdClearString(&con->filename);
- if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
+ if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ }
}
- }
+ }
+ while (con->http.state == HTTP_PUT_RECV && con->http.used > 0);
if (con->http.state == HTTP_WAITING)
{
cupsdClearString(&con->filename);
if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
/*
*/
if (!cupsdSendError(con, status))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
break;
"CHUNKED" : "LENGTH",
CUPS_LLCAST con->http.data_remaining, con->file);
- if (con->request)
+ do
{
- /*
- * Grab any request data from the connection...
- */
-
- if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
+ if (con->request)
{
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "cupsdReadClient: %d IPP Read Error!",
- con->http.fd);
+ /*
+ * Grab any request data from the connection...
+ */
- cupsdSendError(con, HTTP_BAD_REQUEST);
- return (cupsdCloseClient(con));
- }
- else if (ipp_state != IPP_DATA)
- {
- if (con->http.state == HTTP_POST_SEND)
+ if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
{
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cupsdReadClient: %d IPP Read Error!",
+ con->http.fd);
+
cupsdSendError(con, HTTP_BAD_REQUEST);
- return (cupsdCloseClient(con));
+ cupsdCloseClient(con);
+ return;
}
+ else if (ipp_state != IPP_DATA)
+ {
+ if (con->http.state == HTTP_POST_SEND)
+ {
+ cupsdSendError(con, HTTP_BAD_REQUEST);
+ cupsdCloseClient(con);
+ return;
+ }
- break;
- }
- else
- con->bytes += ippLength(con->request);
- }
+ break;
+ }
+ else
+ con->bytes += ippLength(con->request);
+ }
- if (con->file < 0 && con->http.state != HTTP_POST_SEND)
- {
- /*
- * Create a file as needed for the request data...
- */
+ if (con->file < 0 && con->http.state != HTTP_POST_SEND)
+ {
+ /*
+ * Create a file as needed for the request data...
+ */
- cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
- con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
+ con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
- cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
- con->filename, con->file);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
+ con->filename, con->file);
- if (con->file < 0)
- {
- if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
- }
+ if (con->file < 0)
+ {
+ if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ }
- fchmod(con->file, 0640);
- fchown(con->file, RunUser, Group);
- fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
- }
+ fchmod(con->file, 0640);
+ fchown(con->file, RunUser, Group);
+ fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
+ }
- if (con->http.state != HTTP_POST_SEND)
- {
- if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
- return (cupsdCloseClient(con));
- else if (bytes > 0)
+ if (con->http.state != HTTP_POST_SEND)
{
- con->bytes += bytes;
+ if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ else if (bytes > 0)
+ {
+ con->bytes += bytes;
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdReadClient: %d writing %d bytes to %d",
- con->http.fd, bytes, con->file);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdReadClient: %d writing %d bytes to %d",
+ con->http.fd, bytes, con->file);
- if (write(con->file, line, bytes) < bytes)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "cupsdReadClient: Unable to write %d bytes to %s: %s",
- bytes, con->filename, strerror(errno));
+ if (write(con->file, line, bytes) < bytes)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cupsdReadClient: Unable to write %d bytes to %s: %s",
+ bytes, con->filename, strerror(errno));
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdReadClient: Closing file %d...",
- con->file);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdReadClient: Closing file %d...",
+ con->file);
- close(con->file);
- con->file = -1;
- unlink(con->filename);
- cupsdClearString(&con->filename);
+ close(con->file);
+ con->file = -1;
+ unlink(con->filename);
+ cupsdClearString(&con->filename);
- if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
+ if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ }
+ }
+ else if (con->http.state == HTTP_POST_RECV)
+ return;
+ else if (con->http.state != HTTP_POST_SEND)
+ {
+ cupsdCloseClient(con);
+ return;
}
}
- else if (con->http.state == HTTP_POST_RECV)
- return (1); /* ??? */
- else if (con->http.state != HTTP_POST_SEND)
- return (cupsdCloseClient(con));
- }
+ }
+ while (con->http.state == HTTP_POST_RECV && con->http.used > 0);
if (con->http.state == HTTP_POST_SEND)
{
}
if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
if (con->command)
if (!cupsdSendCommand(con, con->command, con->options, 0))
{
if (!cupsdSendError(con, HTTP_NOT_FOUND))
- return (cupsdCloseClient(con));
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
else
cupsdLogRequest(con, HTTP_OK);
}
if (con->request)
- return (cupsdProcessIPPRequest(con));
+ {
+ cupsdProcessIPPRequest(con);
+ return;
+ }
}
break;
}
if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
- return (cupsdCloseClient(con));
- else
- return (1);
+ cupsdCloseClient(con);
}
fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdSendCommand: Adding fd %d to InputSet...", con->file);
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdSendCommand: Adding fd %d to OutputSet...",
- con->http.fd);
-
- FD_SET(con->file, InputSet);
- FD_SET(con->http.fd, OutputSet);
+ cupsdAddSelect(con->file, (cupsd_selfunc_t)cupsdWritePipe, NULL, con);
con->sent_header = 0;
con->file_ready = 0;
* 'cupsdWriteClient()' - Write data to a client as needed.
*/
-int /* O - 1 if success, 0 if fail */
+void
cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
{
int bytes; /* Number of bytes written */
if (con->http.state != HTTP_GET_SEND &&
con->http.state != HTTP_POST_SEND)
- return (1);
+ return;
+
+ if (con->pipe_pid)
+ {
+ /*
+ * Make sure we select on the CGI output...
+ */
+
+ cupsdAddSelect(con->file, (cupsd_selfunc_t)cupsdWritePipe, NULL, con);
+
+ if (!con->file_ready)
+ {
+ /*
+ * Try again later when there is CGI output available...
+ */
+
+ cupsdRemoveSelect(con->http.fd);
+ return;
+ }
+
+ con->file_ready = 0;
+ }
if (con->response)
{
con->sent_header = 2;
if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
- return (0);
+ return;
}
else if (!strncasecmp(buf, "Status:", 7))
{
if (con->http.version == HTTP_1_1)
{
if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
- return (0);
+ return;
}
}
}
if (cupsdFlushHeader(con) < 0)
{
cupsdCloseClient(con);
- return (0);
+ return;
}
if (con->http.version == HTTP_1_1)
httpPrintf(HTTP(con), "%s", buf);
con->http.activity = time(NULL);
- return (1);
+ return;
}
else if (bytes == 0)
con->http.activity = time(NULL);
con->http.fd, bytes);
cupsdCloseClient(con);
- return (0);
+ return;
}
con->bytes += bytes;
if (httpWrite2(HTTP(con), "", 0) < 0)
{
cupsdCloseClient(con);
- return (0);
+ return;
}
}
con->http.state = HTTP_WAITING;
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdWriteClient: Removing fd %d from OutputSet...",
- con->http.fd);
-
- FD_CLR(con->http.fd, OutputSet);
+ cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
if (con->file >= 0)
{
- if (FD_ISSET(con->file, InputSet))
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdWriteClient: Removing fd %d from InputSet...",
- con->file);
- FD_CLR(con->file, InputSet);
- }
+ cupsdRemoveSelect(con->file);
if (con->pipe_pid)
cupsdEndProcess(con->pipe_pid, 0);
if (!con->http.keep_alive)
{
cupsdCloseClient(con);
- return (0);
- }
- }
- else
- {
- con->file_ready = 0;
-
- if (con->pipe_pid && !FD_ISSET(con->file, InputSet))
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdWriteClient: Adding fd %d to InputSet...",
- con->file);
- FD_SET(con->file, InputSet);
+ return;
}
}
con->http.activity = time(NULL);
+}
- return (1);
+
+/*
+ * 'cupsdWritePipe()' - Flag that data is available on the CGI pipe.
+ */
+
+void
+cupsdWritePipe(cupsd_client_t *con) /* I - Client connection */
+{
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWritePipe: CGI output on fd %d...",
+ con->file);
+
+ con->file_ready = 1;
+
+ cupsdRemoveSelect(con->file);
+ cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
}
else
con->http._data_remaining = INT_MAX;
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "write_file: Adding fd %d to OutputSet...", con->http.fd);
-
- FD_SET(con->http.fd, OutputSet);
+ cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
+ (cupsd_selfunc_t)cupsdWriteClient, con);
return (1);
}
extern int cupsdFlushHeader(cupsd_client_t *con);
extern void cupsdPauseListening(void);
extern int cupsdProcessIPPRequest(cupsd_client_t *con);
-extern int cupsdReadClient(cupsd_client_t *con);
+extern void cupsdReadClient(cupsd_client_t *con);
extern void cupsdResumeListening(void);
extern int cupsdSendCommand(cupsd_client_t *con, char *command,
char *options, int root);
extern void cupsdStartListening(void);
extern void cupsdStopListening(void);
extern void cupsdUpdateCGI(void);
-extern int cupsdWriteClient(cupsd_client_t *con);
+extern void cupsdWriteClient(cupsd_client_t *con);
+extern void cupsdWritePipe(cupsd_client_t *con);
/*
#define RELOAD_CUPSD 2 /* Reload only cupsd.conf */
+/*
+ * Select callback function type...
+ */
+
+typedef void (*cupsd_selfunc_t)(void *data);
+
+
/*
* Globals...
*/
-VAR int MaxFDs, /* Maximum number of files */
- SetSize; /* The size of the input/output sets */
-VAR fd_set *InputSet, /* Input files for select() */
- *OutputSet; /* Output files for select() */
+VAR int MaxFDs; /* Maximum number of files */
VAR time_t ReloadTime VALUE(0);
/* Time of reload request... */
char *envp[], int infd, int outfd,
int errfd, int backfd, int root, int *pid);
+extern int cupsdAddSelect(int fd, cupsd_selfunc_t read_cb,
+ cupsd_selfunc_t write_cb, void *data);
+extern int cupsdDoSelect(long timeout);
+extern int cupsdIsSelecting(int fd);
+extern void cupsdRemoveSelect(int fd);
+extern void cupsdStartSelect(void);
+extern void cupsdStopSelect(void);
+
/*
* End of "$Id$".
* We only listen if we want remote printers...
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartBrowsing: Adding fd %d to InputSet...",
- BrowseSocket);
-
- FD_SET(BrowseSocket, InputSet);
+ cupsdAddSelect(BrowseSocket, (cupsd_selfunc_t)cupsdUpdateCUPSBrowse,
+ NULL, NULL);
}
}
else
* Finally, add the pipe to the input selection set...
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartPolling: Adding fd %d to InputSet...", PollPipe);
-
- FD_SET(PollPipe, InputSet);
+ cupsdAddSelect(PollPipe, (cupsd_selfunc_t)cupsdUpdatePolling, NULL, NULL);
}
close(BrowseSocket);
#endif /* WIN32 */
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopBrowsing: Removing fd %d from InputSet...",
- BrowseSocket);
-
- FD_CLR(BrowseSocket, InputSet);
+ cupsdRemoveSelect(BrowseSocket);
BrowseSocket = -1;
}
cupsdStatBufDelete(PollStatusBuffer);
close(PollPipe);
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopPolling: removing fd %d from InputSet.", PollPipe);
- FD_CLR(PollPipe, InputSet);
+ cupsdRemoveSelect(PollPipe);
PollPipe = -1;
PollStatusBuffer = NULL;
con->http.data_remaining = length;
}
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdProcessIPPRequest: Adding fd %d to OutputSet...",
- con->http.fd);
-
- FD_SET(con->http.fd, OutputSet);
+ cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
+ (cupsd_selfunc_t)cupsdWriteClient, con);
/*
* Tell the caller the response header was sent successfully...
const char *from, /* I - Source file */
const char *to) /* I - Destination file */
{
- fd_set *input; /* select() input set */
+ fd_set input; /* select() input set */
struct timeval timeout; /* select() timeout */
int maxfd; /* Maximum file descriptor for select() */
char tempfile[1024]; /* Temporary PPD file */
cupsdOpenPipe(temppipe);
- if ((input = calloc(1, SetSize)) == NULL)
- {
- close(tempfd);
- unlink(tempfile);
-
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "copy_model: Unable to allocate %d bytes for select()...",
- SetSize);
- return (-1);
- }
-
cupsdLogMessage(CUPSD_LOG_DEBUG,
"copy_model: Running \"cups-driverd cat %s\"...", from);
if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
-1, 0, &temppid))
{
- free(input);
close(tempfd);
unlink(tempfile);
return (-1);
bytes = 0;
- FD_SET(temppipe[0], input);
- FD_SET(CGIPipes[0], input);
+ FD_ZERO(&input);
+ FD_SET(temppipe[0], &input);
+ FD_SET(CGIPipes[0], &input);
timeout.tv_sec = 30;
timeout.tv_usec = 0;
- if ((i = select(maxfd, input, NULL, NULL, &timeout)) < 0)
+ if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
{
if (errno == EINTR)
continue;
break;
}
- if (FD_ISSET(temppipe[0], input))
+ if (FD_ISSET(temppipe[0], &input))
{
/*
* Read the PPD file from the pipe, and write it to the PPD file.
break;
}
- if (FD_ISSET(CGIPipes[0], input))
+ if (FD_ISSET(CGIPipes[0], &input))
cupsdUpdateCGI();
}
close(temppipe[0]);
close(tempfd);
- free(input);
-
if (!total)
{
/*
* Close the pipe and clear the input bit.
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdFinishJob: Removing fd %d from InputSet...",
- job->status_buffer->fd);
-
- FD_CLR(job->status_buffer->fd, InputSet);
+ cupsdRemoveSelect(job->status_buffer->fd);
cupsdLogMessage(CUPSD_LOG_DEBUG2,
"cupsdFinishJob: Closing status pipes [ %d %d ]...",
* Close the pipe and clear the input bit.
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopJob: Removing fd %d from InputSet...",
- job->status_buffer->fd);
-
- FD_CLR(job->status_buffer->fd, InputSet);
+ cupsdRemoveSelect(job->status_buffer->fd);
cupsdLogMessage(CUPSD_LOG_DEBUG2,
"cupsdStopJob: Closing status pipes [ %d %d ]...",
free(argv);
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "start_job: Adding fd %d to InputSet...",
- job->status_buffer->fd);
-
- FD_SET(job->status_buffer->fd, InputSet);
+ cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)cupsdUpdateJob, NULL,
+ job);
cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
job->id);
for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
lis;
lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
- if (lis->fd >= 0)
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdPauseListening: Removing fd %d from InputSet...",
- lis->fd);
-
- FD_CLR(lis->fd, InputSet);
- }
+ cupsdRemoveSelect(lis->fd);
}
for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
lis;
lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
- if (lis->fd >= 0)
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdResumeListening: Adding fd %d to InputSet...",
- lis->fd);
- FD_SET(lis->fd, InputSet);
- }
+ cupsdAddSelect(lis->fd, (cupsd_selfunc_t)cupsdAcceptClient, NULL, lis);
}
char *opt; /* Option character */
int fg; /* Run in the foreground */
int fds; /* Number of ready descriptors */
- fd_set *input, /* Input set for select() */
- *output; /* Output set for select() */
cupsd_client_t *con; /* Current client */
cupsd_job_t *job; /* Current job */
cupsd_listener_t *lis; /* Current listener */
size_t string_count, /* String count */
alloc_bytes, /* Allocated string bytes */
total_bytes; /* Total string bytes */
- struct timeval timeout; /* select() timeout */
+ long timeout; /* Timeout for cupsdDoSelect() */
struct rlimit limit; /* Runtime limit */
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action; /* Actions for POSIX signals */
getrlimit(RLIMIT_NOFILE, &limit);
+#if !defined(HAVE_POLL) && !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE)
if (limit.rlim_max > FD_SETSIZE)
MaxFDs = FD_SETSIZE;
else
+#endif /* !HAVE_POLL && !HAVE_EPOLL && !HAVE_KQUEUE */
+#ifdef RLIM_INFINITY
+ if (limit.rlim_max == RLIM_INFINITY)
+ MaxFDs = 16384;
+ else
+#endif /* RLIM_INFINITY */
MaxFDs = limit.rlim_max;
limit.rlim_cur = MaxFDs;
setrlimit(RLIMIT_NOFILE, &limit);
- /*
- * Allocate memory for the input and output sets...
- */
-
- SetSize = (MaxFDs + 31) / 8 + 4;
- if (SetSize < sizeof(fd_set))
- SetSize = sizeof(fd_set);
-
- InputSet = (fd_set *)calloc(1, SetSize);
- OutputSet = (fd_set *)calloc(1, SetSize);
- input = (fd_set *)calloc(1, SetSize);
- output = (fd_set *)calloc(1, SetSize);
-
- if (InputSet == NULL || OutputSet == NULL || input == NULL || output == NULL)
- {
- syslog(LOG_LPR, "Unable to allocate memory for select() sets - exiting!");
- return (1);
- }
+ cupsdStartSelect();
/*
* Read configuration...
}
/*
- * Check for available input or ready output. If select() returns
- * 0 or -1, something bad happened and we should exit immediately.
+ * Check for available input or ready output. If cupsdDoSelect()
+ * returns 0 or -1, something bad happened and we should exit
+ * immediately.
*
* Note that we at least have one listening socket open at all
* times.
*/
- memcpy(input, InputSet, SetSize);
- memcpy(output, OutputSet, SetSize);
-
- timeout.tv_sec = select_timeout(fds);
- timeout.tv_usec = 0;
+ timeout = select_timeout(fds);
#if HAVE_LAUNCHD
/*
* inactivity...
*/
- if (timeout.tv_sec == 86400 && Launchd && LaunchdTimeout && !NumPolled &&
+ if (timeout == 86400 && Launchd && LaunchdTimeout && !NumPolled &&
(!Browsing || !(BrowseLocalProtocols & BROWSE_DNSSD) ||
cupsArrayCount(Printers) == 0))
{
- timeout.tv_sec = LaunchdTimeout;
+ timeout = LaunchdTimeout;
launchd_idle_exit = 1;
}
else
launchd_idle_exit = 0;
#endif /* HAVE_LAUNCHD */
- if (timeout.tv_sec < 86400) /* Only use timeout for < 1 day */
- fds = select(MaxFDs, input, output, NULL, &timeout);
- else
- fds = select(MaxFDs, input, output, NULL, NULL);
-
- if (fds < 0)
+ if ((fds = cupsdDoSelect(timeout)) < 0)
{
- char s[16384], /* String buffer */
- *sptr; /* Pointer into buffer */
- int slen; /* Length of string buffer */
-
-
/*
* Got an error from select!
*/
* Log all sorts of debug info to help track down the problem.
*/
- cupsdLogMessage(CUPSD_LOG_EMERG, "select() failed - %s!",
+ cupsdLogMessage(CUPSD_LOG_EMERG, "cupsdDoSelect() failed - %s!",
strerror(errno));
- strcpy(s, "InputSet =");
- slen = 10;
- sptr = s + 10;
-
- for (i = 0; i < MaxFDs; i ++)
- if (FD_ISSET(i, InputSet))
- {
- snprintf(sptr, sizeof(s) - slen, " %d", i);
- slen += strlen(sptr);
- sptr += strlen(sptr);
- }
-
- cupsdLogMessage(CUPSD_LOG_EMERG, "%s", s);
-
- strcpy(s, "OutputSet =");
- slen = 11;
- sptr = s + 11;
-
- for (i = 0; i < MaxFDs; i ++)
- if (FD_ISSET(i, OutputSet))
- {
- snprintf(sptr, sizeof(s) - slen, " %d", i);
- slen += strlen(sptr);
- sptr += strlen(sptr);
- }
-
- cupsdLogMessage(CUPSD_LOG_EMERG, "%s", s);
-
for (i = 0, con = (cupsd_client_t *)cupsArrayFirst(Clients);
con;
i ++, con = (cupsd_client_t *)cupsArrayNext(Clients))
}
#endif /* HAVE_LAUNCHD */
- /*
- * Check for status info from job filters...
- */
-
- for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
- job;
- job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
- if (job->status_buffer && FD_ISSET(job->status_buffer->fd, input))
- {
- /*
- * Clear the input bit to avoid updating the next job
- * using the same status pipe file descriptor...
- */
-
- FD_CLR(job->status_buffer->fd, input);
-
- /*
- * Read any status messages from the filters...
- */
-
- cupsdUpdateJob(job);
- }
-
- /*
- * Update CGI messages as needed...
- */
-
- if (CGIPipes[0] >= 0 && FD_ISSET(CGIPipes[0], input))
- cupsdUpdateCGI();
-
- /*
- * Handle system management events as needed...
- */
-
-#ifdef __APPLE__
- /*
- * Mac OS X provides the SystemConfiguration framework for system
- * configuration change events...
- */
-
- if (SysEventPipes[0] >= 0 && FD_ISSET(SysEventPipes[0], input))
- cupsdUpdateSystemMonitor();
-#else
- /*
- * All other operating systems need to poll for changes...
- */
-
- if ((current_time - netif_time) >= 60)
- {
- NetIFUpdate = 1;
- netif_time = current_time;
- }
-#endif /* __APPLE__ */
-
- /*
- * Update notifier messages as needed...
- */
-
- if (NotifierPipes[0] >= 0 && FD_ISSET(NotifierPipes[0], input))
- cupsdUpdateNotifierStatus();
-
/*
* Expire subscriptions and unload completed jobs as needed...
*/
if (Browsing && BrowseRemoteProtocols)
{
- if (BrowseSocket >= 0 && FD_ISSET(BrowseSocket, input))
- cupsdUpdateCUPSBrowse();
-
- if (PollPipe >= 0 && FD_ISSET(PollPipe, input))
- cupsdUpdatePolling();
-
#ifdef HAVE_LIBSLP
if ((BrowseRemoteProtocols & BROWSE_SLP) &&
BrowseSLPRefresh <= current_time)
browse_time = current_time;
}
- /*
- * Check for new connections on the "listen" sockets...
- */
-
- for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
- lis;
- lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
- if (lis->fd >= 0 && FD_ISSET(lis->fd, input))
- {
- FD_CLR(lis->fd, input);
- cupsdAcceptClient(lis);
- }
-
/*
* Check for new data on the client sockets...
*/
con = (cupsd_client_t *)cupsArrayNext(Clients))
{
/*
- * Process the input buffer...
+ * Process pending data in the input buffer...
*/
- if (FD_ISSET(con->http.fd, input) || con->http.used)
+ if (con->http.used)
{
- int fd = con->file;
-
-
- FD_CLR(con->http.fd, input);
-
- if (!cupsdReadClient(con))
- {
- if (fd >= 0)
- FD_CLR(fd, input);
-
- continue;
- }
- }
-
- /*
- * Write data as needed...
- */
-
- if (con->pipe_pid && FD_ISSET(con->file, input))
- {
- /*
- * Keep track of pending input from the file/pipe separately
- * so that we don't needlessly spin on select() when the web
- * client is not ready to receive data...
- */
-
- FD_CLR(con->file, input);
- con->file_ready = 1;
-
-#ifdef DEBUG
- cupsdLogMessage(CUPSD_LOG_DEBUG2, "main: Data ready file %d!",
- con->file);
-#endif /* DEBUG */
-
- if (!FD_ISSET(con->http.fd, output))
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "main: Removing fd %d from InputSet...", con->file);
- FD_CLR(con->file, input);
- FD_CLR(con->file, InputSet);
- }
- }
-
- if (FD_ISSET(con->http.fd, output))
- {
- FD_CLR(con->http.fd, output);
-
- if (!con->pipe_pid || con->file_ready)
- if (!cupsdWriteClient(con))
- continue;
+ cupsdReadClient(con);
+ continue;
}
/*
unlink("/var/spool/lp/SCHEDLOCK");
#endif /* __sgi */
- /*
- * Free memory used by FD sets and return...
- */
-
- free(InputSet);
- free(OutputSet);
- free(input);
- free(output);
+ cupsdStopSelect();
return (!stop_scheduler);
}
--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Select abstraction functions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2006 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * cupsdAddSelect() - Add a file descriptor to the list.
+ * cupsdDoSelect() - Do a select-like operation.
+ * cupsdIsSelecting() - Determine whether we are monitoring a file
+ * descriptor.
+ * cupsdRemoveSelect() - Remove a file descriptor from the list.
+ * cupsdStartSelect() - Initialize the file polling engine.
+ * cupsdStopSelect() - Shutdown the file polling engine.
+ * compare_fds() - Compare file descriptors.
+ * find_fd() - Find an existing file descriptor record.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+#ifdef HAVE_EPOLL
+# include <sys/epoll.h>
+#elif defined(HAVE_KQUEUE)
+# include <sys/event.h>
+# include <sys/time.h>
+#elif defined(HAVE_POLL)
+# include <sys/poll.h>
+#elif defined(__hpux)
+# include <sys/time.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
+ * IRIX YES YES NO NO NO
+ * Linux YES YES YES NO NO
+ * MacOS X 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.
+ * b. cupsdStartSelect() creates the array
+ * c. cupsdStopSelect() destroys the array and all elements.
+ * d. cupsdAddSelect() adds to the array and allocates a
+ * new callback element.
+ * e. cupsdRemoveSelect() removes from the array and frees
+ * the callback element.
+ *
+ * 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 kqyeue() 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 kqyeye() 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.
+ */
+
+/*
+ * Local structures...
+ */
+
+typedef struct _cupsd_fd_s
+{
+ int fd; /* File descriptor */
+ 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;
+
+#ifdef HAVE_EPOLL
+static int cupsd_epoll_fd = -1;
+static struct epoll_event *cupsd_epoll_events = NULL;
+#elif defined(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;
+#else /* select() */
+static fd_set cupsd_global_input,
+ cupsd_global_output,
+ cupsd_current_input,
+ cupsd_current_output;
+#endif /* HAVE_EPOLL */
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_fds(_cupsd_fd_t *a, _cupsd_fd_t *b);
+static _cupsd_fd_t *find_fd(int fd);
+
+
+/*
+ * '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 */
+ int added; /* 1 if added, 0 if modified */
+
+
+ /*
+ * 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;
+
+ if (!cupsArrayAdd(cupsd_fds, fdptr))
+ {
+ cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to add fd %d to array!", fd);
+ free(fdptr);
+ return (0);
+ }
+
+ added = 1;
+ }
+ else
+ added = 0;
+
+#ifdef HAVE_EPOLL
+ {
+ 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;
+
+ epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
+ &event);
+ }
+
+#elif defined(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_DEBUG2,
+ "cupsdAddSelect: 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_DEBUG2,
+ "cupsdAddSelect: kevent() returned %s",
+ strerror(errno));
+ return (0);
+ }
+ }
+ }
+
+#elif defined(HAVE_POLL)
+ cupsd_update_pollfds = 1;
+
+#else /* select() */
+ /*
+ * Add or remove the file descriptor in the input and output sets
+ * for select()...
+ */
+
+ if (read_cb)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdAddSelect: Adding fd %d to input set...", fd);
+ FD_SET(fd, &cupsd_global_input);
+ }
+ else
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdAddSelect: Removing fd %d from input set...", fd);
+ FD_CLR(fd, &cupsd_global_input);
+ FD_CLR(fd, &cupsd_current_input);
+ }
+
+ if (write_cb)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdAddSelect: Adding fd %d to output set...", fd);
+ FD_SET(fd, &cupsd_global_output);
+ }
+ else
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdAddSelect: Removing fd %d from output set...", fd);
+ FD_CLR(fd, &cupsd_global_output);
+ FD_CLR(fd, &cupsd_current_output);
+ }
+#endif /* HAVE_EPOLL */
+
+ /*
+ * 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_EPOLL
+ int i; /* Looping var */
+ struct epoll_event *event; /* Current event */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdDoSelect: polling %d fds for %ld seconds...",
+ cupsArrayCount(cupsd_fds), timeout);
+
+ 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);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: epoll() returned %d...",
+ nfds);
+
+ for (i = nfds, event = cupsd_epoll_events; i > 0; i --, event ++)
+ {
+ fdptr = (_cupsd_fd_t *)event->data.ptr;
+
+ if (fdptr->read_cb && (event->events & (EPOLLIN | EPOLLERR | EPOLLHUP)))
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
+ fdptr->fd);
+ (*(fdptr->read_cb))(fdptr->data);
+ }
+
+ if (fdptr->write_cb && (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)))
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
+ fdptr->fd);
+ (*(fdptr->write_cb))(fdptr->data);
+ }
+ }
+
+#elif defined(HAVE_KQUEUE)
+ int i; /* Looping var */
+ struct kevent *event; /* Current event */
+ struct timespec ktimeout; /* kevent() timeout */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdDoSelect: polling %d fds for %ld seconds...",
+ cupsArrayCount(cupsd_fds), timeout);
+
+ 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);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdDoSelect: kevent(%d, ..., %d, ...) returned %d...",
+ cupsd_kqueue_fd, MaxFDs, nfds);
+
+ cupsd_kqueue_changes = 0;
+
+ for (i = nfds, event = cupsd_kqueue_events; i > 0; i --, event ++)
+ {
+ fdptr = (_cupsd_fd_t *)event->udata;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "event->filter=%d, event->ident=%d",
+ event->filter, (int)event->ident);
+
+ if (fdptr->read_cb && event->filter == EVFILT_READ)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
+ fdptr->fd);
+ (*(fdptr->read_cb))(fdptr->data);
+ }
+
+ if (fdptr->write_cb && event->filter == EVFILT_WRITE)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
+ fdptr->fd);
+ (*(fdptr->write_cb))(fdptr->data);
+ }
+ }
+
+#elif defined(HAVE_POLL)
+ struct pollfd *pfd; /* Current pollfd structure */
+ int count; /* Number of file descriptors */
+
+
+ count = cupsArrayCount(cupsd_fds);
+
+ if (cupsd_update_pollfds)
+ {
+ /*
+ * Update the cupsd_pollfds array to match the current FD array...
+ */
+
+ cupsd_update_pollfds = 0;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Updating pollfd array...");
+
+ /*
+ * (Re)allocate memory as needed...
+ */
+
+ if (count > cupsd_alloc_pollfds)
+ {
+ int allocfds = count + 16;
+
+
+ if (cupsd_pollfds)
+ pfd = realloc(cupsd_pollfds, allocfds * sizeof(struct pollfd));
+ else
+ pfd = malloc(allocfds * sizeof(struct pollfd));
+
+ if (!pfd)
+ {
+ cupsdLogMessage(CUPSD_LOG_EMERG,
+ "Unable to allocate %d bytes for polling!",
+ (int)(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;
+ }
+ }
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdDoSelect: polling %d fds for %ld seconds...",
+ count, timeout);
+
+ if (timeout >= 0 && timeout < 86400)
+ nfds = poll(cupsd_pollfds, count, timeout * 1000);
+ else
+ nfds = poll(cupsd_pollfds, count, -1);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: poll() returned %d...",
+ nfds);
+
+ if (nfds > 0)
+ {
+ /*
+ * Do callbacks for each file descriptor...
+ */
+
+ for (pfd = cupsd_pollfds; count > 0; pfd ++, count --)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdDoSelect: pollfds[%d]={fd=%d, revents=%x}",
+ pfd - cupsd_pollfds, pfd->fd, pfd->revents);
+
+ if (!pfd->revents)
+ continue;
+
+ if ((fdptr = find_fd(pfd->fd)) == NULL)
+ continue;
+
+ if (fdptr->read_cb && (pfd->revents & (POLLIN | POLLERR | POLLHUP)))
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
+ fdptr->fd);
+ (*(fdptr->read_cb))(fdptr->data);
+ }
+
+ if (fdptr->write_cb && (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
+ fdptr->fd);
+ (*(fdptr->write_cb))(fdptr->data);
+ }
+ }
+ }
+
+#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;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdDoSelect: selecting %d fds for %ld seconds...",
+ maxfd, timeout);
+
+ 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);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: select() returned %d...",
+ nfds);
+
+ 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))
+ {
+ if (fdptr->read_cb && FD_ISSET(fdptr->fd, &cupsd_current_input))
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
+ fdptr->fd);
+ (*(fdptr->read_cb))(fdptr->data);
+ }
+
+ if (fdptr->write_cb && FD_ISSET(fdptr->fd, &cupsd_current_output))
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
+ fdptr->fd);
+ (*(fdptr->write_cb))(fdptr->data);
+ }
+ }
+ }
+
+#endif /* HAVE_EPOLL */
+
+ /*
+ * Return the number of file descriptors handled...
+ */
+
+ return (nfds);
+}
+
+
+/*
+ * '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);
+}
+
+
+/*
+ * '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
+ epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event);
+
+#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_DEBUG2,
+ "cupsdRemoveSelect: kevent() returned %s",
+ strerror(errno));
+ return;
+ }
+ }
+
+ 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_DEBUG2,
+ "cupsdRemoveSelect: kevent() returned %s",
+ strerror(errno));
+ return;
+ }
+ }
+
+
+#elif defined(HAVE_POLL)
+ /*
+ * Update the pollfds array...
+ */
+
+ cupsd_update_pollfds = 1;
+
+#else /* select() */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdRemoveSelect: Removing fd %d from input and output "
+ "sets...", fd);
+ 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 */
+
+ /*
+ * Remove the file descriptor for from the FD array...
+ */
+
+ cupsArrayRemove(cupsd_fds, fdptr);
+ free(fdptr);
+}
+
+
+/*
+ * 'cupsdStartSelect()' - Initialize the file polling engine.
+ */
+
+void
+cupsdStartSelect(void)
+{
+ cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
+
+#ifdef HAVE_EPOLL
+ cupsd_epoll_fd = epoll_create(MaxFDs);
+ cupsd_epoll_events = calloc(MaxFDs, sizeof(struct epoll_event));
+
+#elif defined(HAVE_KQUEUE)
+ cupsd_kqueue_fd = kqueue();
+ cupsd_kqueue_changes = 0;
+ cupsd_kqueue_events = calloc(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 */
+
+
+ for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
+ fdptr;
+ fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
+ free(fdptr);
+
+ cupsArrayDelete(cupsd_fds);
+ cupsd_fds = NULL;
+
+#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;
+ }
+
+#elif defined(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)
+ 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);
+}
+
+
+/*
+ * End of "$Id$".
+ */
{
CGIStatusBuffer = cupsdStatBufNew(CGIPipes[0], "[CGI]");
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartServer: Adding fd %d to InputSet...",
- CGIPipes[0]);
- FD_SET(CGIPipes[0], InputSet);
+ cupsdAddSelect(CGIPipes[0], (cupsd_selfunc_t)cupsdUpdateCGI, NULL, NULL);
}
/*
if (CGIPipes[0] >= 0)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopServer: Removing fd %d from InputSet...",
- CGIPipes[0]);
-
- FD_CLR(CGIPipes[0], InputSet);
+ cupsdRemoveSelect(CGIPipes[0]);
cupsdStatBufDelete(CGIStatusBuffer);
close(CGIPipes[1]);
if (NotifierPipes[0] >= 0)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopAllNotifiers: Removing fd %d from InputSet...",
- NotifierPipes[0]);
- FD_CLR(NotifierPipes[0], InputSet);
+ cupsdRemoveSelect(NotifierPipes[0]);
cupsdStatBufDelete(NotifierStatusBuffer);
NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "start_notifier: Adding fd %d to InputSet...",
- NotifierPipes[0]);
-
- FD_SET(NotifierPipes[0], InputSet);
+ cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsdUpdateNotifierStatus,
+ NULL, NULL);
}
if (cupsdOpenPipe(fds))
return;
}
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartSystemMonitor: Adding fd %d to InputSet...",
- SysEventPipes[0]);
- FD_SET(SysEventPipes[0], InputSet);
+ cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)cupsdUpdateSystemMonitor,
+ NULL, NULL);
/*
* Set non-blocking mode on the descriptor we will be receiving notification
if (SysEventPipes[0] >= 0)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopSystemMonitor: Removing fd %d from InputSet...",
- SysEventPipes[0]);
-
- FD_CLR(SysEventPipes[0], InputSet);
-
+ cupsdRemoveSelect(SysEventPipes[0]);
cupsdClosePipe(SysEventPipes);
}
}