summaryrefslogtreecommitdiff
path: root/apps/netutils/thttpd/fdwatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/netutils/thttpd/fdwatch.c')
-rw-r--r--apps/netutils/thttpd/fdwatch.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/apps/netutils/thttpd/fdwatch.c b/apps/netutils/thttpd/fdwatch.c
new file mode 100644
index 000000000..24ea7a1e0
--- /dev/null
+++ b/apps/netutils/thttpd/fdwatch.c
@@ -0,0 +1,371 @@
+/****************************************************************************
+ * netutils/thttpd/timers.c
+ * FD watcher routines for poll()
+ *
+ * Copyright (C) 2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <spudmonkey@racsa.co.cr>
+ *
+ * Derived from the file of the same name in the original THTTPD package:
+ *
+ * Copyright © 1999,2000 by Jef Poskanzer <jef@mail.acme.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <poll.h>
+#include <debug.h>
+
+#include "config.h"
+#include "thttpd_alloc.h"
+#include "fdwatch.h"
+
+#ifdef CONFIG_THTTPD
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* Debug output from this file is normally suppressed. If enabled, be aware
+ * that output to stdout will interfere with CGI programs (you could use the
+ * the low-level debug (lldbg) functions which probably do not use stdout
+ */
+
+#ifdef CONFIG_THTTPD_FDWATCH_DEBUG
+# ifdef CONFIG_CPP_HAVE_VARARGS
+# define fwdbg(format, arg...) ndbg(format, ##arg)
+# define fwlldbg(format, arg...) nlldbg(format, ##arg)
+# define fwvdbg(format, arg...) nvdbg(format, ##arg)
+# define fwllvdbg(format, arg...) nllvdbg(format, ##arg)
+# else
+# define fwdbg ndbg
+# define fwlldbg nlldbg
+# define fwvdbg nvdbg
+# define fwllvdbg nllvdbg
+# endif
+#else
+# ifdef CONFIG_CPP_HAVE_VARARGS
+# define fwdbg(x...)
+# define fwlldbg(x...)
+# define fwvdbg(x...)
+# define fwllvdbg(x...)
+# else
+# define fwdbg (void)
+# define fwlldbg (void)
+# define fwvdbg (void)
+# define fwllvdbg (void)
+# endif
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#ifdef CONFIG_THTTPD_FDWATCH_DEBUG
+static void fdwatch_dump(const char *msg, FAR struct fdwatch_s *fw)
+{
+ int i;
+
+ fwvdbg("%s\n", msg);
+ fwvdbg("nwatched: %d nfds: %d\n", fw->nwatched, fw->nfds);
+ for (i = 0; i < fw->nwatched; i++)
+ {
+ fwvdbg("%2d. pollfds: {fd: %d events: %02x revents: %02x} client: %p\n",
+ i+1, fw->pollfds[i].fd, fw->pollfds[i].events,
+ fw->pollfds[i].revents, fw->client[i]);
+ }
+ fwvdbg("nactive: %d next: %d\n", fw->nactive, fw->next);
+ for (i = 0; i < fw->nactive; i++)
+ {
+ fwvdbg("%2d. %d active\n", i, fw->ready[i]);
+ }
+}
+#else
+# define fdwatch_dump(m,f)
+#endif
+
+static int fdwatch_pollndx(FAR struct fdwatch_s *fw, int fd)
+{
+ int pollndx;
+
+ /* Get the index associated with the fd */
+
+ for (pollndx = 0; pollndx < fw->nwatched; pollndx++)
+ {
+ if (fw->pollfds[pollndx].fd == fd)
+ {
+ fwvdbg("pollndx: %d\n", pollndx);
+ return pollndx;
+ }
+ }
+
+ fwdbg("No poll index for fd %d: %d\n", fd);
+ return -1;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/* Initialize the fdwatch data structures. Returns -1 on failure. */
+
+struct fdwatch_s *fdwatch_initialize(int nfds)
+{
+ FAR struct fdwatch_s *fw;
+
+ /* Allocate the fdwatch data structure */
+
+ fw = (struct fdwatch_s*)zalloc(sizeof(struct fdwatch_s));
+ if (!fw)
+ {
+ fwdbg("Failed to allocate fdwatch\n");
+ return NULL;
+ }
+
+ /* Initialize the fdwatch data structures. */
+
+ fw->nfds = nfds;
+
+ fw->client = (void**)httpd_malloc(sizeof(void*) * nfds);
+ if (!fw->client)
+ {
+ goto errout_with_allocations;
+ }
+
+ fw->pollfds = (struct pollfd*)httpd_malloc(sizeof(struct pollfd) * nfds);
+ if (!fw->pollfds)
+ {
+ goto errout_with_allocations;
+ }
+
+ fw->ready = (uint8_t*)httpd_malloc(sizeof(uint8_t) * nfds);
+ if (!fw->ready)
+ {
+ goto errout_with_allocations;
+ }
+
+ fdwatch_dump("Initial state:", fw);
+ return fw;
+
+errout_with_allocations:
+ fdwatch_uninitialize(fw);
+ return NULL;
+}
+
+/* Uninitialize the fwdatch data structure */
+
+void fdwatch_uninitialize(struct fdwatch_s *fw)
+{
+ if (fw)
+ {
+ fdwatch_dump("Uninitializing:", fw);
+ if (fw->client)
+ {
+ httpd_free(fw->client);
+ }
+
+ if (fw->pollfds)
+ {
+ httpd_free(fw->pollfds);
+ }
+
+ if (fw->ready)
+ {
+ httpd_free(fw->ready);
+ }
+
+ httpd_free(fw);
+ }
+}
+
+/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */
+
+void fdwatch_add_fd(struct fdwatch_s *fw, int fd, void *client_data)
+{
+ fwvdbg("fd: %d client_data: %p\n", fd, client_data);
+ fdwatch_dump("Before adding:", fw);
+
+ if (fw->nwatched >= fw->nfds)
+ {
+ fwdbg("too many fds\n");
+ return;
+ }
+
+ /* Save the new fd at the end of the list */
+
+ fw->pollfds[fw->nwatched].fd = fd;
+ fw->pollfds[fw->nwatched].events = POLLIN;
+ fw->client[fw->nwatched] = client_data;
+
+ /* Increment the count of watched descriptors */
+
+ fw->nwatched++;
+ fdwatch_dump("After adding:", fw);
+}
+
+/* Remove a descriptor from the watch list. */
+
+void fdwatch_del_fd(struct fdwatch_s *fw, int fd)
+{
+ int pollndx;
+
+ fwvdbg("fd: %d\n", fd);
+ fdwatch_dump("Before deleting:", fw);
+
+ /* Get the index associated with the fd */
+
+ pollndx = fdwatch_pollndx(fw, fd);
+ if (pollndx >= 0)
+ {
+ /* Decrement the number of fds in the poll table */
+
+ fw->nwatched--;
+
+ /* Replace the deleted one with the one at the the end
+ * of the list.
+ */
+
+ if (pollndx != fw->nwatched)
+ {
+ fw->pollfds[pollndx] = fw->pollfds[fw->nwatched];
+ fw->client[pollndx] = fw->client[fw->nwatched];
+ }
+ }
+ fdwatch_dump("After deleting:", fw);
+}
+
+/* Do the watch. Return value is the number of descriptors that are ready,
+ * or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means
+ * wait indefinitely.
+ */
+
+int fdwatch(struct fdwatch_s *fw, long timeout_msecs)
+{
+ int ret;
+ int i;
+
+ /* Wait for activity on any of the desciptors. When poll() returns, ret
+ * will hold the number of descriptors with activity (or zero on a timeout
+ * or <0 on an error.
+ */
+
+ fdwatch_dump("Before waiting:", fw);
+ fwvdbg("Waiting... (timeout %d)\n", timeout_msecs);
+ fw->nactive = 0;
+ fw->next = 0;
+ ret = poll(fw->pollfds, fw->nwatched, (int)timeout_msecs);
+ fwvdbg("Awakened: %d\n", ret);
+
+ /* Look through all of the descriptors and make a list of all of them than
+ * have activity.
+ */
+
+ if (ret > 0)
+ {
+ for (i = 0; i < fw->nwatched; i++)
+ {
+ /* Is there activity on this descriptor? */
+
+ if (fw->pollfds[i].revents & (POLLIN | POLLERR | POLLHUP | POLLNVAL))
+ {
+ /* Yes... save it in a shorter list */
+
+ fwvdbg("pollndx: %d fd: %d revents: %04x\n",
+ i, fw->pollfds[i].fd, fw->pollfds[i].revents);
+
+ fw->ready[fw->nactive++] = fw->pollfds[i].fd;
+ if (fw->nactive == ret)
+ {
+ /* We have all of them, break out early */
+
+ break;
+ }
+ }
+ }
+ }
+
+ /* Return the number of descriptors with activity */
+
+ fwvdbg("nactive: %d\n", fw->nactive);
+ fdwatch_dump("After wakeup:", fw);
+ return ret;
+}
+
+/* Check if a descriptor was ready. */
+
+int fdwatch_check_fd(struct fdwatch_s *fw, int fd)
+{
+ int pollndx;
+
+ fwvdbg("fd: %d\n", fd);
+ fdwatch_dump("Checking:", fw);
+
+ /* Get the index associated with the fd */
+
+ pollndx = fdwatch_pollndx(fw, fd);
+ if (pollndx >= 0 && (fw->pollfds[pollndx].revents & POLLERR) == 0)
+ {
+ return fw->pollfds[pollndx].revents & (POLLIN | POLLHUP | POLLNVAL);
+ }
+
+ fwvdbg("POLLERR fd: %d\n", fd);
+ return 0;
+}
+
+void *fdwatch_get_next_client_data(struct fdwatch_s *fw)
+{
+ fdwatch_dump("Before getting client data:", fw);
+ if (fw->next >= fw->nwatched)
+ {
+ fwvdbg("All client data returned: %d\n", fw->next);
+ return (void*)-1;
+ }
+
+ fwvdbg("client_data[%d]: %p\n", fw->next, fw->client[fw->next]);
+ return fw->client[fw->next++];
+}
+
+#endif /* CONFIG_THTTPD */
+