/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2007-2021 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
. */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
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;
}