src/irc.c \
src/ircfmt.c \
src/net.c \
+ src/netpoll.c \
src/netsock.c \
src/passwd.c \
src/trie.c \
src/irc.h \
src/ircfmt.h \
src/net.h \
+ src/netpoll.h \
src/netsock.h \
src/passwd.h \
src/trie.h \
#ifndef COMMON_H_
#define COMMON_H_
#include "netsock.h"
+#include "netpoll.h"
#include "config.h"
#include "trie.h"
#include <time.h>
unsigned want_write_for_close :1;
unsigned dying :1;
unsigned write_error :1;
+ unsigned poll_reg :2;
};
struct channel {
char *key;
extern struct trie_node *autojoin;
extern struct client_data *clients;
extern struct client_data *dyingclients;
-extern fd_set readfds;
-extern struct sockaddr_in serverAddrV4;
-extern struct sockaddr_in6 serverAddrV6;
-extern size_t serverAddrV4len;
-extern size_t serverAddrV6len;
#endif
goto err;
if (net_nonblock(self_sock) == -1)
goto err;
+ net_poll_add(self_sock, SOCKTYPE_SELFPIPE, 0);
+ net_poll_reg(self_sock, NETPOLL_IN);
return;
err:
net_perror("ERROR CREATING SELF SOCK");
// Client linked list
struct client_data *clients = NULL, *dyingclients = NULL;
-// FDSET List
-fd_set readfds, writefds;
FILE *fchanlog = NULL;
cl->peerport = port;
if (cctx)
cl->umode |= UMODE_SECURE;
+ net_poll_add(cl->FD, SOCKTYPE_CLIENT, cl);
+ update_poll_reg(cl);
extern const char *hostname;
copy_strf(&databuf, ":%s NOTICE * :Hello welcome to PatchyIRCD!\r\n", hostname);
sendbuf(cl);
fprintf(stderr, "[PatchyIRCD]: Starting up...\n");
net_init();
+ net_poll_init();
set_handler();
databuf.buf = malloc(sizeof(char) * 512);
databuf.cap = 512;
#endif
}
- for (int i = 0; i < listener_count; i++)
+ for (int i = 0; i < listener_count; i++) {
net_nonblock(listeners[i].sock);
+ net_poll_add(listeners[i].sock, SOCKTYPE_LISTENER, &listeners[i]);
+ net_poll_reg(listeners[i].sock, NETPOLL_IN);
+ }
FILE *f = fopen(getdatafile("init.txt"), "rb");
if (f) {
struct client_data *fake = allocClient();
+ fake->FD = INVALID_SOCKET;
fake->umode |= UMODE_OPER | UMODE_SECURE;
strcpy_lit(fake->buf, "NICK :root\r\n");
fake->bufLen = strlen(fake->buf);
while (is_running || dyingclients) {
if (is_siginted) {
fprintf(stderr, "[PatchyIRCD]: Cleaning up...\n");
- for (int i = 0; i < listener_count; i++)
+ for (int i = 0; i < listener_count; i++) {
+ net_poll_del(listeners[i].sock);
closesocket(listeners[i].sock);
+ }
listener_count = 0;
for (struct client_data *cl = clients; cl;) {
struct client_data *next = cl->next;
if (!dyingclients)
break;
}
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
- // Add listening SOCKETS to FDSET
- for (int i = 0; i < listener_count; i++)
- FD_SET(listeners[i].sock, &readfds);
-#ifdef _WIN32
- if (self_sock != INVALID_SOCKET)
- FD_SET(self_sock, &readfds);
-#endif
- for (struct client_data *cl = clients; cl; cl = cl->next) {
- if (!cl->want_write_for_read || cl->want_read_for_write)
- FD_SET(cl->FD, &readfds);
- if (cl->want_write_for_read || cl->want_write_for_write)
- FD_SET(cl->FD, &writefds);
- }
- for (struct client_data *cl = dyingclients; cl; cl = cl->next) {
- if (cl->want_read_for_write || cl->want_read_for_close)
- FD_SET(cl->FD, &readfds);
- if (cl->want_write_for_write || cl->want_write_for_close)
- FD_SET(cl->FD, &writefds);
- }
- // Set highestFD
-#ifdef _WIN32
- int highestFD = -1;
-#else
- SOCKET highestFD = -1;
- for (int i = 0; i < listener_count; i++)
- if (highestFD < listeners[i].sock)
- highestFD = listeners[i].sock;
- for (struct client_data *cl = clients; cl; cl = cl->next)
- if (highestFD < cl->FD)
- highestFD = cl->FD;
- for (struct client_data *cl = dyingclients; cl; cl = cl->next)
- if (highestFD < cl->FD)
- highestFD = cl->FD;
-#endif
- // Wait until activity on sockets
- if(select(highestFD+1,&readfds,&writefds,NULL,NULL) == -1) {
+ if (net_poll_begin() == -1) {
#ifndef _WIN32
if (errno == EINTR)
continue;
#endif
net_perror("ERROR SELECT");
}
+ int ev;
+ struct net_pollfd pfd;
+ while ((ev = net_poll_get(&pfd))) {
+ switch (pfd.type) {
#ifdef _WIN32
- if (self_sock != INVALID_SOCKET && FD_ISSET(self_sock, &readfds)) {
- char x;
- while (recv(self_sock, &x, 0, 0) != -1)
- ;
- }
+ case SOCKTYPE_SELFPIPE:
+ if (ev & NETPOLL_IN) {
+ char x;
+ while (recv(pfd.fd, &x, 0, 0) != -1)
+ ;
+ }
+ break;
#endif
+ case SOCKTYPE_LISTENER:
+ if (ev & NETPOLL_IN)
+ accept_from_sock((struct listener *)pfd.assoc);
+ break;
+ case SOCKTYPE_CLIENT: {
+ struct client_data *cl = pfd.assoc;
+ int isr = ev & NETPOLL_IN;
+ int isw = ev & NETPOLL_OUT;
+ /* NOTE: recvProc can kill the client, so writing should be done first. */
+ if ((cl->want_read_for_write && isr) || (cl->want_write_for_write && isw))
+ sendClient(cl, 0, 0);
+ if (cl->want_write_for_read ? isw : isr)
+ recvProc(cl);
+ break;
+ }
+ case SOCKTYPE_DYING: {
+ struct client_data *cl = pfd.assoc;
+ int isr = ev & NETPOLL_IN;
+ int isw = ev & NETPOLL_OUT;
+ if (cl->wbufLen)
+ if ((cl->want_read_for_write && isr) || (cl->want_write_for_write && isw))
+ sendClient(cl, 0, 0);
+ if (!cl->wbufLen) {
+ if (!cl->want_read_for_close && !cl->want_write_for_close)
+ killConnectionPart2(cl);
+ else if ((cl->want_read_for_close && isr) || (cl->want_write_for_close && isw))
+ killConnectionPart2(cl);
+ }
+ break;
+ }
+ }
+ }
- /* Accept new connections */
- for (int i = 0; i < listener_count; i++)
- if (FD_ISSET(listeners[i].sock, &readfds))
- accept_from_sock(&listeners[i]);
- /* Respond to already connected clients */
+ /* Clean up write error clients */
for (struct client_data *cl = clients; cl;) {
struct client_data *next = cl->next;
- int isr = FD_ISSET(cl->FD, &readfds);
- int isw = FD_ISSET(cl->FD, &writefds);
- /* NOTE: recvProc can kill the client, so writing should be done first. */
- if ((cl->want_read_for_write && isr) || (cl->want_write_for_write && isw))
- sendClient(cl, 0, 0);
if (cl->write_error)
killConnection(cl, 0);
- else if (cl->want_write_for_read ? isw : isr)
- recvProc(cl);
- cl = next;
- }
- /* Close dying clients */
- for (struct client_data *cl = dyingclients; cl;) {
- struct client_data *next = cl->next;
- int isr = FD_ISSET(cl->FD, &readfds);
- int isw = FD_ISSET(cl->FD, &writefds);
- if (cl->wbufLen)
- if ((cl->want_read_for_write && isr) || (cl->want_write_for_write && isw))
- sendClient(cl, 0, 0);
- if (!cl->wbufLen) {
- if (!cl->want_read_for_close && !cl->want_write_for_close)
- killConnectionPart2(cl);
- else if ((cl->want_read_for_close && isr) || (cl->want_write_for_close && isw))
- killConnectionPart2(cl);
- }
cl = next;
}
}
+#ifdef _WIN32
+ if (self_sock != INVALID_SOCKET) {
+ net_poll_del(self_sock);
+ closesocket(self_sock);
+ }
+#endif
+ net_poll_fini();
net_cleanup();
#ifdef HAS_TLS
tls_free(tls_ctx);
return calloc(1, sizeof(struct client_data));
}
+void update_poll_reg(struct client_data *cl)
+{
+ if (cl->FD == INVALID_SOCKET)
+ return;
+ int nreg = 0;
+ /* Shorthand:
+ * r = (!d && !w4r) || r4w || (d && r4c)
+ * w = (!d && w4r) || w4w || (d && w4c)
+ */
+ if (!cl->dying) {
+ if (!cl->want_write_for_read || cl->want_read_for_write)
+ nreg |= NETPOLL_IN;
+ if (cl->want_write_for_read || cl->want_write_for_write)
+ nreg |= NETPOLL_OUT;
+ } else {
+ if (cl->want_read_for_write || cl->want_read_for_close)
+ nreg |= NETPOLL_IN;
+ if (cl->want_write_for_write || cl->want_write_for_close)
+ nreg |= NETPOLL_OUT;
+ }
+ if (nreg != cl->poll_reg) {
+ net_poll_reg(cl->FD, nreg);
+ cl->poll_reg = nreg;
+ }
+}
+
void killConnection(struct client_data *cl, int byChoice)
{
if (!cl)
return;
if (cl->dying)
return;
- cl->dying = 1; /* NOTE: this recursion guard isn't necessary, but it's there just in case */
+ cl->dying = 1;
if (cl->peername)
fprintf(stderr, "Client disconnected from Socket: %"PRISocket", IP: %s PORT: %d\n", cl->FD, cl->peername, cl->peerport);
if (!byChoice && cl->nick && cl->username) {
}
cl->next = dyingclients;
dyingclients = cl;
+ if (cl->FD != INVALID_SOCKET) {
+ net_poll_del(cl->FD);
+ net_poll_add(cl->FD, SOCKTYPE_DYING, cl);
+ }
+ update_poll_reg(cl);
/* Part all channels */
struct trie_find *f = NULL;
if (trie_findfirst(&f, &cl->channels) == 1)
int rv = tls_close(cl->cctx);
if (rv == TLS_WANT_POLLIN) {
cl->want_read_for_close = 1;
+ update_poll_reg(cl);
return;
}
if (rv == TLS_WANT_POLLOUT) {
cl->want_write_for_close = 1;
+ update_poll_reg(cl);
return;
}
if (rv == -1) {
}
#endif
shutdown(cl->FD, SHUT_RDWR);
+ net_poll_del(cl->FD);
closesocket(cl->FD);
}
/* Remove the client from the dyingclients chain */
killConnection(cl, 0);
return;
case TLS_WANT_POLLIN:
+ update_poll_reg(cl);
return;
case TLS_WANT_POLLOUT:
cl->want_write_for_read = 1;
+ update_poll_reg(cl);
return;
}
}
if(!net_wouldblock()){
net_perror("RECV");
killConnection(cl, 0);
+ } else {
+ update_poll_reg(cl);
}
return;
}
if(!x) break;
packetReturn = parsePacket(cl);
// Client QUIT
- if (packetReturn == 1 || cl->write_error)
+ if (packetReturn == 1)
return;
+ if (cl->write_error) {
+ update_poll_reg(cl);
+ return;
+ }
memmove(cl->buf, x+1, cl->buf + cl->bufLen-(x+1));
cl->bufLen -= x+1 - cl->buf;
}
if (!cl->wbufLen) {
if (len) {
int left = sendClientRaw(cl, buf, len);
+ update_poll_reg(cl);
if (left == -1) {
/* Write error happened, discard the buffer and close the
* connection. sendClient is used from a ton of places,
cl->wbufLen += len;
}
int left = sendClientRaw(cl, cl->wbuf, cl->wbufLen);
+ update_poll_reg(cl);
if (left == -1) {
cl->wbufLen = 0;
cl->write_error = 1;
void sendbuf(struct client_data *client);
void sendbuf_visible(struct client_data *client);
struct client_data *allocClient();
+void update_poll_reg(struct client_data *cl);
void killConnection(struct client_data *client, int byChoice);
void killConnectionPart2(struct client_data *cl);
void recvProc(struct client_data *cl);
--- /dev/null
+#include "netpoll.h"
+#define NETPOLL_MAXFDS 1024
+static struct net_pollfd fds[NETPOLL_MAXFDS] = {0};
+static int nfds = 0;
+
+static int search(SOCKET fd) {
+ int l = 0, r = nfds;
+ while (l != r) {
+ int m = l + (r-l)/2;
+ if (fds[m].fd < fd)
+ l = m+1;
+ else if (fd < fds[m].fd)
+ r = m;
+ else
+ l = r = m;
+ }
+ return l;
+}
+
+#if defined(NETPOLL_USE_SELECT)
+static fd_set readfds, writefds, xreadfds, xwritefds;
+static int lastptr = 0;
+void net_poll_init(void)
+{
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+}
+void net_poll_fini(void)
+{
+}
+void net_poll_add_impl(int i)
+{
+ if (fds[i].fd >= FD_SETSIZE) {
+ fprintf(stderr, "net_poll_add_impl: FD_SETSIZE limit\n");
+ abort();
+ }
+ if (lastptr >= i)
+ lastptr++;
+}
+void net_poll_del_impl(int i)
+{
+ if (lastptr > i)
+ lastptr--;
+}
+void net_poll_reg_impl(int i, int ev)
+{
+ if (ev & NETPOLL_IN)
+ FD_SET(fds[i].fd, &readfds);
+ else
+ FD_CLR(fds[i].fd, &readfds);
+ if (ev & NETPOLL_OUT)
+ FD_SET(fds[i].fd, &writefds);
+ else
+ FD_CLR(fds[i].fd, &writefds);
+}
+int net_poll_begin(void)
+{
+ memcpy(&xreadfds, &readfds, sizeof(readfds));
+ memcpy(&xwritefds, &writefds, sizeof(writefds));
+#ifdef _WIN32
+ int highfd = 0;
+#else
+ int highfd = nfds ? fds[nfds-1].fd+1 : 0;
+#endif
+ int rv = select(highfd, &xreadfds, &xwritefds, 0, 0);
+ lastptr = 0;
+ return rv;
+}
+int net_poll_get(struct net_pollfd *out)
+{
+ int ev = 0;
+ while (!ev) {
+ if (lastptr == nfds)
+ return 0;
+ if (FD_ISSET(fds[lastptr].fd, &xreadfds))
+ ev |= NETPOLL_IN;
+ if (FD_ISSET(fds[lastptr].fd, &xwritefds))
+ ev |= NETPOLL_OUT;
+ lastptr++;
+ }
+ *out = fds[lastptr-1];
+ return ev;
+}
+#else
+#error pick a netpoll implementation
+#endif
+void net_poll_add(SOCKET fd, int type, void *assoc)
+{
+ if (fd == INVALID_SOCKET) {
+ fprintf(stderr, "net_poll_add: INVALID_SOCKET\n");
+ abort();
+ }
+ int i = search(fd);
+ if (i < nfds && fds[i].fd == fd) {
+ fprintf(stderr, "net_poll_add: socket already added\n");
+ abort();
+ }
+ if (i == NETPOLL_MAXFDS) {
+ fprintf(stderr, "net_poll_add: NETPOLL_MAXFDS limit\n");
+ abort();
+ }
+ memmove(fds+i+1, fds+i, sizeof(fds[0])*(nfds-i));
+ nfds++;
+ fds[i].fd = fd;
+ fds[i].type = type;
+ fds[i].assoc = assoc;
+ net_poll_add_impl(i);
+}
+void net_poll_del(SOCKET fd)
+{
+ if (fd == INVALID_SOCKET) {
+ fprintf(stderr, "net_poll_del: INVALID_SOCKET\n");
+ abort();
+ }
+ int i = search(fd);
+ if (i >= nfds || fds[i].fd != fd) {
+ fprintf(stderr, "net_poll_del: socket not added\n");
+ abort();
+ }
+ net_poll_reg_impl(i, 0);
+ net_poll_del_impl(i);
+ memmove(fds+i, fds+i+1, sizeof(fds[0])*(nfds-(i+1)));
+ nfds--;
+}
+void net_poll_reg(SOCKET fd, int ev)
+{
+ if (fd == INVALID_SOCKET) {
+ fprintf(stderr, "net_poll_reg: INVALID_SOCKET\n");
+ abort();
+ }
+ int i = search(fd);
+ if (i >= nfds || fds[i].fd != fd) {
+ fprintf(stderr, "net_poll_reg: socket not added\n");
+ abort();
+ }
+ net_poll_reg_impl(i, ev);
+}
--- /dev/null
+#ifndef NETPOLL_H_
+#define NETPOLL_H_
+#include "netsock.h"
+#define NETPOLL_USE_SELECT
+struct net_pollfd {
+ void *assoc;
+ SOCKET fd;
+ int type;
+};
+void net_poll_init(void);
+void net_poll_fini(void);
+void net_poll_add(SOCKET fd, int type, void *assoc);
+void net_poll_del(SOCKET fd);
+void net_poll_reg(SOCKET fd, int ev);
+int net_poll_begin(void);
+int net_poll_get(struct net_pollfd *out);
+#define NETPOLL_IN 1
+#define NETPOLL_OUT 2
+
+#define SOCKTYPE_LISTENER 0
+#define SOCKTYPE_SELFPIPE 1
+#define SOCKTYPE_CLIENT 2
+#define SOCKTYPE_DYING 3
+#define SOCKTYPE_MAX 4
+
+#endif