[MAJOR] added a new state to listeners
There was a missing state for listeners, when they are not listening
but still attached to the protocol. The LI_ASSIGNED state was added
for this purpose. This permitted to clean up the assignment/release
workflow quite a bit. Generic enable/enable_all/disable/disable_all
primitives were added, and a disable_all entry was added to the
struct protocol.
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 1a09326..4338373 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -61,6 +61,34 @@
#define MAXPATHLEN 128
#endif
+static int uxst_bind_listeners(struct protocol *proto);
+static int uxst_unbind_listeners(struct protocol *proto);
+
+/* Note: must not be declared <const> as its list will be overwritten */
+static struct protocol proto_unix = {
+ .name = "unix_stream",
+ .sock_domain = PF_UNIX,
+ .sock_type = SOCK_STREAM,
+ .sock_prot = 0,
+ .sock_family = AF_UNIX,
+ .sock_addrlen = sizeof(struct sockaddr_un),
+ .l3_addrlen = sizeof(((struct sockaddr_un*)0)->sun_path),/* path len */
+ .read = &stream_sock_read,
+ .write = &stream_sock_write,
+ .bind_all = uxst_bind_listeners,
+ .unbind_all = uxst_unbind_listeners,
+ .enable_all = enable_all_listeners,
+ .disable_all = disable_all_listeners,
+ .listeners = LIST_HEAD_INIT(proto_unix.listeners),
+ .nb_listeners = 0,
+};
+
+
+/********************************
+ * 1) low-level socket functions
+ ********************************/
+
+
/* This function creates a named PF_UNIX stream socket at address <path>. Note
* that the path cannot be NULL nor empty. <uid> and <gid> different of -1 will
* be used to change the socket owner. If <mode> is not 0, it will be used to
@@ -211,6 +239,97 @@
}
+/********************************
+ * 2) listener-oriented functions
+ ********************************/
+
+
+/* This function creates the UNIX socket associated to the listener. It changes
+ * the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling.
+ * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL.
+ */
+static int uxst_bind_listener(struct listener *listener)
+{
+ int fd;
+
+ if (listener->state != LI_ASSIGNED)
+ return ERR_NONE; /* already bound */
+
+ fd = create_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path,
+ listener->perm.ux.uid,
+ listener->perm.ux.gid,
+ listener->perm.ux.mode);
+ if (fd == -1)
+ return ERR_FATAL;
+
+ /* the socket is now listening */
+ listener->fd = fd;
+ listener->state = LI_LISTEN;
+
+ /* the function for the accept() event */
+ fd_insert(fd);
+ fdtab[fd].cb[DIR_RD].f = listener->accept;
+ fdtab[fd].cb[DIR_WR].f = NULL; /* never called */
+ fdtab[fd].cb[DIR_RD].b = fdtab[fd].cb[DIR_WR].b = NULL;
+ fdtab[fd].owner = (struct task *)listener; /* reference the listener instead of a task */
+ fdtab[fd].state = FD_STLISTEN;
+ fdtab[fd].peeraddr = NULL;
+ fdtab[fd].peerlen = 0;
+ fdtab[fd].listener = NULL;
+ fdtab[fd].ev = 0;
+ return ERR_NONE;
+}
+
+/* This function closes the UNIX sockets for the specified listener.
+ * The listener enters the LI_ASSIGNED state. It always returns ERR_NONE.
+ */
+static int uxst_unbind_listener(struct listener *listener)
+{
+ if (listener->state == LI_READY)
+ EV_FD_CLR(listener->fd, DIR_RD);
+
+ if (listener->state >= LI_LISTEN) {
+ close(listener->fd);
+ listener->state = LI_ASSIGNED;
+ destroy_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path);
+ }
+ return ERR_NONE;
+}
+
+/* Add a listener to the list of unix stream listeners. The listener's state
+ * is automatically updated from LI_INIT to LI_ASSIGNED. The number of
+ * listeners is updated. This is the function to use to add a new listener.
+ */
+void uxst_add_listener(struct listener *listener)
+{
+ if (listener->state != LI_INIT)
+ return;
+ listener->state = LI_ASSIGNED;
+ listener->proto = &proto_unix;
+ LIST_ADDQ(&proto_unix.listeners, &listener->proto_list);
+ proto_unix.nb_listeners++;
+}
+
+/* Delete a listener from the list of unix stream listeners. The listener's
+ * state is automatically updated from LI_ASSIGNED to LI_INIT. The number of
+ * listeners is updated. Note that the listener must have previously been
+ * unbound. This is the function to use to remove a listener.
+ */
+void uxst_del_listener(struct listener *listener)
+{
+ if (listener->state != LI_ASSIGNED)
+ return;
+ listener->state = LI_INIT;
+ LIST_DEL(&listener->proto_list);
+ proto_unix.nb_listeners--;
+}
+
+
+/********************************
+ * 3) protocol-oriented functions
+ ********************************/
+
+
/* This function creates all UNIX sockets bound to the protocol entry <proto>.
* It is intended to be used as the protocol's bind_all() function.
* The sockets will be registered but not added to any fd_set, in order not to
@@ -223,58 +342,15 @@
{
struct listener *listener;
int err = ERR_NONE;
- int fd;
list_for_each_entry(listener, &proto->listeners, proto_list) {
- if (listener->state != LI_INIT)
- continue; /* already started */
-
- fd = create_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path,
- listener->perm.ux.uid,
- listener->perm.ux.gid,
- listener->perm.ux.mode);
- if (fd == -1) {
- err |= ERR_FATAL;
+ err |= uxst_bind_listener(listener);
+ if (err != ERR_NONE)
continue;
- }
-
- /* the socket is listening */
- listener->fd = fd;
- listener->state = LI_LISTEN;
-
- /* the function for the accept() event */
- fd_insert(fd);
- fdtab[fd].cb[DIR_RD].f = listener->accept;
- fdtab[fd].cb[DIR_WR].f = NULL; /* never called */
- fdtab[fd].cb[DIR_RD].b = fdtab[fd].cb[DIR_WR].b = NULL;
- fdtab[fd].owner = (struct task *)listener; /* reference the listener instead of a task */
- fdtab[fd].state = FD_STLISTEN;
- fdtab[fd].peeraddr = NULL;
- fdtab[fd].peerlen = 0;
- fdtab[fd].listener = NULL;
- fdtab[fd].ev = 0;
}
-
return err;
}
-/* This function adds the UNIX sockets file descriptors to the polling lists
- * for all listeners in the LI_LISTEN state. It is intended to be used as the
- * protocol's enable_all() primitive, after the fork(). It always returns
- * ERR_NONE.
- */
-static int uxst_enable_listeners(struct protocol *proto)
-{
- struct listener *listener;
-
- list_for_each_entry(listener, &proto->listeners, proto_list) {
- if (listener->state == LI_LISTEN) {
- EV_FD_SET(listener->fd, DIR_RD);
- listener->state = LI_READY;
- }
- }
- return ERR_NONE;
-}
/* This function stops all listening UNIX sockets bound to the protocol
* <proto>. It does not detaches them from the protocol.
@@ -284,17 +360,17 @@
{
struct listener *listener;
- list_for_each_entry(listener, &proto->listeners, proto_list) {
- if (listener->state != LI_INIT) {
- EV_FD_CLR(listener->fd, DIR_RD);
- close(listener->fd);
- listener->state = LI_INIT;
- destroy_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path);
- }
- }
+ list_for_each_entry(listener, &proto->listeners, proto_list)
+ uxst_unbind_listener(listener);
return ERR_NONE;
}
+
+/********************************
+ * 4) high-level functions
+ ********************************/
+
+
/*
* This function is called on a read event from a listen socket, corresponding
* to an accept. It tries to accept as many connections as possible.
@@ -1400,32 +1476,6 @@
tv_eternity(next);
}
-/* Note: must not be declared <const> as its list will be overwritten */
-static struct protocol proto_unix = {
- .name = "unix_stream",
- .sock_domain = PF_UNIX,
- .sock_type = SOCK_STREAM,
- .sock_prot = 0,
- .sock_family = AF_UNIX,
- .sock_addrlen = sizeof(struct sockaddr_un),
- .l3_addrlen = sizeof(((struct sockaddr_un*)0)->sun_path),/* path len */
- .read = &stream_sock_read,
- .write = &stream_sock_write,
- .bind_all = uxst_bind_listeners,
- .unbind_all = uxst_unbind_listeners,
- .enable_all = uxst_enable_listeners,
- .listeners = LIST_HEAD_INIT(proto_unix.listeners),
- .nb_listeners = 0,
-};
-
-/* Adds listener to the list of unix stream listeners */
-void uxst_add_listener(struct listener *listener)
-{
- listener->proto = &proto_unix;
- LIST_ADDQ(&proto_unix.listeners, &listener->proto_list);
- proto_unix.nb_listeners++;
-}
-
__attribute__((constructor))
static void __uxst_protocol_init(void)
{
diff --git a/src/protocols.c b/src/protocols.c
index 49204b8..2412d0e 100644
--- a/src/protocols.c
+++ b/src/protocols.c
@@ -14,6 +14,7 @@
#include <string.h>
#include <common/config.h>
+#include <common/errors.h>
#include <common/mini-clist.h>
#include <common/standard.h>
@@ -22,6 +23,64 @@
/* List head of all registered protocols */
static struct list protocols = LIST_HEAD_INIT(protocols);
+/* This function adds the specified listener's file descriptor to the polling
+ * lists if it is in the LI_LISTEN state. The listener enters LI_READY or
+ * LI_FULL state depending on its number of connections.
+ */
+void enable_listener(struct listener *listener)
+{
+ if (listener->state == LI_LISTEN) {
+ if (listener->nbconn < listener->maxconn) {
+ EV_FD_SET(listener->fd, DIR_RD);
+ listener->state = LI_READY;
+ } else {
+ listener->state = LI_FULL;
+ }
+ }
+}
+
+/* This function removes the specified listener's file descriptor from the
+ * polling lists if it is in the LI_READY or in the LI_FULL state. The listener
+ * enters LI_LISTEN.
+ */
+void disable_listener(struct listener *listener)
+{
+ if (listener->state < LI_READY)
+ return;
+ if (listener->state == LI_READY)
+ EV_FD_CLR(listener->fd, DIR_RD);
+ listener->state = LI_LISTEN;
+}
+
+/* This function adds all of the protocol's listener's file descriptors to the
+ * polling lists when they are in the LI_LISTEN state. It is intended to be
+ * used as a protocol's generic enable_all() primitive, for use after the
+ * fork(). It puts the listeners into LI_READY or LI_FULL states depending on
+ * their number of connections. It always returns ERR_NONE.
+ */
+int enable_all_listeners(struct protocol *proto)
+{
+ struct listener *listener;
+
+ list_for_each_entry(listener, &proto->listeners, proto_list)
+ enable_listener(listener);
+ return ERR_NONE;
+}
+
+/* This function removes all of the protocol's listener's file descriptors from
+ * the polling lists when they are in the LI_READY or LI_FULL states. It is
+ * intended to be used as a protocol's generic disable_all() primitive. It puts
+ * the listeners into LI_LISTEN, and always returns ERR_NONE.
+ */
+int disable_all_listeners(struct protocol *proto)
+{
+ struct listener *listener;
+
+ list_for_each_entry(listener, &proto->listeners, proto_list)
+ disable_listener(listener);
+ return ERR_NONE;
+}
+
/* Registers the protocol <proto> */
void protocol_register(struct protocol *proto)
{
@@ -37,7 +96,7 @@
LIST_INIT(&proto->list);
}
-/* binds all listeneres of all registered protocols. Returns a composition
+/* binds all listeners of all registered protocols. Returns a composition
* of ERR_NONE, ERR_RETRYABLE, ERR_FATAL.
*/
int protocol_bind_all(void)
@@ -88,3 +147,20 @@
return err;
}
+/* disables all listeners of all registered protocols. This may be used before
+ * a fork() to avoid duplicating poll lists. Returns a composition of ERR_NONE,
+ * ERR_RETRYABLE, ERR_FATAL.
+ */
+int protocol_disable_all(void)
+{
+ struct protocol *proto;
+ int err;
+
+ err = 0;
+ list_for_each_entry(proto, &protocols, list) {
+ if (proto->disable_all)
+ err |= proto->disable_all(proto);
+ }
+ return err;
+}
+