summaryrefslogtreecommitdiff
path: root/libmailutils/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/server.c')
-rw-r--r--libmailutils/server.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/libmailutils/server.c b/libmailutils/server.c
new file mode 100644
index 000000000..3214fb0c0
--- /dev/null
+++ b/libmailutils/server.c
@@ -0,0 +1,293 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <mailutils/server.h>
+#include <mailutils/errno.h>
+
+
+struct _mu_connection
+{
+ struct _mu_connection *next, *prev;
+ int fd;
+ mu_conn_loop_fp f_loop;
+ mu_conn_free_fp f_free;
+ void *data;
+};
+
+#define MU_SERVER_TIMEOUT 0x1
+
+struct _mu_server
+{
+ int nfd;
+ fd_set fdset;
+ int flags;
+ struct timeval timeout;
+ struct _mu_connection *head, *tail;
+ mu_server_idle_fp f_idle;
+ mu_server_free_fp f_free;
+ void *server_data;
+};
+
+void
+recompute_nfd (mu_server_t srv)
+{
+ struct _mu_connection *p;
+ int nfd = 0;
+ for (p = srv->head; p; p = p->next)
+ if (p->fd > nfd)
+ nfd = p->fd;
+ srv->nfd = nfd + 1;
+}
+
+void
+destroy_connection (mu_server_t srv, struct _mu_connection *conn)
+{
+ if (conn->f_free)
+ conn->f_free (conn->data, srv->server_data);
+ free (conn);
+}
+
+void
+remove_connection (mu_server_t srv, struct _mu_connection *conn)
+{
+ struct _mu_connection *p;
+
+ close (conn->fd);
+ FD_CLR (conn->fd, &srv->fdset);
+
+ p = conn->prev;
+ if (p)
+ p->next = conn->next;
+ else /* we're at head */
+ srv->head = conn->next;
+
+ p = conn->next;
+ if (p)
+ p->prev = conn->prev;
+ else /* we're at tail */
+ srv->tail = conn->prev;
+
+ if (conn->fd == srv->nfd - 1)
+ recompute_nfd (srv);
+
+ destroy_connection (srv, conn);
+}
+
+int
+connection_loop (mu_server_t srv, fd_set *fdset)
+{
+ struct _mu_connection *conn;
+ for (conn = srv->head; conn;)
+ {
+ struct _mu_connection *next = conn->next;
+ if (FD_ISSET (conn->fd, fdset))
+ {
+ int rc = conn->f_loop (conn->fd, conn->data, srv->server_data);
+ switch (rc)
+ {
+ case 0:
+ break;
+
+ case MU_SERVER_CLOSE_CONN:
+ default:
+ remove_connection (srv, conn);
+ break;
+
+ case MU_SERVER_SHUTDOWN:
+ return 1;
+ }
+ }
+ conn = next;
+ }
+ return 0;
+}
+
+void
+make_fdset (mu_server_t srv)
+{
+ struct _mu_connection *p;
+ int nfd = 0;
+
+ FD_ZERO (&srv->fdset);
+ for (p = srv->head; p; p = p->next)
+ {
+ FD_SET (p->fd, &srv->fdset);
+ if (p->fd > nfd)
+ nfd = p->fd;
+ }
+ srv->nfd = nfd + 1;
+}
+
+int
+mu_server_run (mu_server_t srv)
+{
+ int status = 0;
+
+ if (!srv)
+ return EINVAL;
+ if (!srv->head)
+ return MU_ERR_NOENT;
+
+ make_fdset (srv);
+
+ while (1)
+ {
+ int rc;
+ fd_set rdset;
+ struct timeval *to;
+
+ rdset = srv->fdset;
+ to = (srv->flags & MU_SERVER_TIMEOUT) ? &srv->timeout : NULL;
+ rc = select (srv->nfd, &rdset, NULL, NULL, to);
+ if (rc == -1 && errno == EINTR)
+ {
+ if (srv->f_idle && srv->f_idle (srv->server_data))
+ break;
+ continue;
+ }
+ if (rc < 0)
+ return errno;
+
+ if (connection_loop (srv, &rdset))
+ {
+ status = MU_ERR_FAILURE;
+ break;
+ }
+ }
+ return status;
+}
+
+int
+mu_server_create (mu_server_t *psrv)
+{
+ mu_server_t srv = calloc (1, sizeof (*srv));
+ if (!srv)
+ return ENOMEM;
+ *psrv = srv;
+ return 0;
+}
+
+int
+mu_server_destroy (mu_server_t *psrv)
+{
+ mu_server_t srv;
+ struct _mu_connection *p;
+
+ if (!psrv)
+ return EINVAL;
+ srv = *psrv;
+ if (!srv)
+ return 0;
+
+ for (p = srv->head; p; )
+ {
+ struct _mu_connection *next = p->next;
+ destroy_connection (srv, p);
+ p = next;
+ }
+
+ if (srv->f_free)
+ srv->f_free (srv->server_data);
+
+ free (srv);
+ *psrv = NULL;
+ return 0;
+}
+
+int
+mu_server_count (mu_server_t srv, size_t *pcount)
+{
+ size_t n = 0;
+ struct _mu_connection *p;
+
+ if (!srv)
+ return EINVAL;
+ for (p = srv->head; p; p = p->next)
+ n++;
+ *pcount = n;
+ return 0;
+}
+
+int
+mu_server_set_idle (mu_server_t srv, mu_server_idle_fp fp)
+{
+ if (!srv)
+ return EINVAL;
+ srv->f_idle = fp;
+ return 0;
+}
+
+int
+mu_server_set_data (mu_server_t srv, void *data, mu_server_free_fp fp)
+{
+ if (!srv)
+ return EINVAL;
+ srv->server_data = data;
+ srv->f_free = fp;
+ return 0;
+}
+
+int
+mu_server_set_timeout (mu_server_t srv, struct timeval *to)
+{
+ if (!srv)
+ return EINVAL;
+ if (!to)
+ srv->flags &= ~MU_SERVER_TIMEOUT;
+ else
+ {
+ srv->timeout = *to;
+ srv->flags |= MU_SERVER_TIMEOUT;
+ }
+ return 0;
+}
+
+int
+mu_server_add_connection (mu_server_t srv,
+ int fd, void *data,
+ mu_conn_loop_fp loop, mu_conn_free_fp free)
+{
+ struct _mu_connection *p;
+
+ if (!srv || !loop)
+ return EINVAL;
+
+ p = malloc (sizeof (*p));
+ if (!p)
+ return ENOMEM;
+ p->fd = fd;
+ p->f_loop = loop;
+ p->f_free = free;
+ p->data = data;
+
+ p->next = NULL;
+ p->prev = srv->tail;
+ if (srv->tail)
+ srv->tail->next = p;
+ else
+ srv->head = p;
+ srv->tail = p;
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.