/* Copyright (C) 1999 Beau Kuiper This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ftpd.h" /* Note that i may one day replace all this with the poll funcutions */ #define MAXFDNUM 16 /* Set startup maximum file descriptor number */ /* Creates a new select structure */ extern int signumber; SELECTER *select_new(void) { SELECTER *new = mallocwrapper(sizeof(SELECTER)); debuglog("select_new - starting new select structure"); new->fdtable = mallocwrapper(sizeof(struct selectorobj *) * (MAXFDNUM + 1)); memset(new->fdtable, 0, sizeof(struct selectorobj *) * (MAXFDNUM + 1)); new->maxfds = MAXFDNUM; new->firstfd = -1; new->smax = 0; return(new); } /* The following adds a FD to the fdtable. If the table is too small, then increase it. Favours long running times */ void select_addfd(SELECTER *sel, int newport) { debuglog("select_addfd - adding fd %d", newport); if (newport > sel->maxfds) { int temp = newport + 32; /* give some breathing space */ reallocwrapper(sizeof(struct selectorobj *) * (temp + 1), (void *)&(sel->fdtable)); memset(&(sel->fdtable[sel->maxfds + 1]), 0, sizeof(struct selectorobj *) * (temp - sel->maxfds)); sel->maxfds = temp; } if (sel->fdtable[newport] != NULL) errormsg("select.c: select_addfd - trying to add a file descriptor already in table.", __FILE__, __LINE__); else { struct selectorobj *temp = mallocwrapper(sizeof(struct selectorobj)); temp->readsockopt = NULL; temp->writesockopt = NULL; temp->readdata = NULL; temp->writedata = NULL; temp->last = -1; temp->next = sel->firstfd; if (sel->firstfd != -1) sel->fdtable[sel->firstfd]->last = newport; sel->firstfd = newport; sel->fdtable[newport] = temp; } } void select_delfd(SELECTER *sel, int deadport) { int result; struct selectorobj *temp = sel->fdtable[deadport]; debuglog("select_delfd - removing fd %d", deadport); result = close(deadport); /* make sure the port is closed :) */ /* if (result == -1) errormsg(strerror(errno), __FILE__, __LINE__); */ select_takeread(sel, deadport); select_takewrite(sel, deadport); if (sel->firstfd == deadport) { sel->firstfd = temp->next; if (sel->firstfd != -1) sel->fdtable[sel->firstfd]->last = -1; } else { sel->fdtable[temp->last]->next = temp->next; if (temp->next != -1) sel->fdtable[temp->next]->last = temp->last; } freewrapper(temp); sel->fdtable[deadport] = NULL; } void select_addread(SELECTER *sel, int port, int (* proc)(SELECTER *, int, void *), void *dat) { debuglog("select_addread - fd %d", port); sel->fdtable[port]->readsockopt = proc; sel->fdtable[port]->readdata = dat; sel->smax = MAXIMUM(sel->smax, port); } void select_addwrite(SELECTER *sel, int port, int (* proc)(SELECTER *, int, void *), void *dat) { debuglog("select_addwrite - fd %d", port); sel->fdtable[port]->writesockopt = proc; sel->fdtable[port]->writedata = dat; sel->smax = MAXIMUM(sel->smax, port); } void select_takeread(SELECTER *sel, int port) { debuglog("select_takeread - fd %d", port); sel->fdtable[port]->readsockopt = NULL; sel->fdtable[port]->readdata = NULL; /* FD_CLR(port, &(sel->readset)); */ } void select_takewrite(SELECTER *sel, int port) { debuglog("select_takewrite - fd %d", port); sel->fdtable[port]->writesockopt = NULL; sel->fdtable[port]->writedata = NULL; /* FD_CLR(port, &(sel->writeset)); */ } int select_do(SELECTER *sel, int *signum, int timeout) { fd_set readset, writeset; struct timeval tv, *tv2; int result = 0; FD_ZERO(&readset); FD_ZERO(&writeset); debuglog("do_select - waiting on fd's"); *signum = 0; signumber = 0; while (result == 0) { int pos = sel->firstfd; while(pos != -1) { struct selectorobj *temp = sel->fdtable[pos]; if (temp->readsockopt) FD_SET(pos, &readset); if (temp->writesockopt) FD_SET(pos, &writeset); pos = temp->next; } if (timeout != -1) { tv.tv_sec = timeout; tv.tv_usec = 0; tv2 = &tv; } else tv2 = NULL; if (signumber != 0) { *signum = signumber; return(-1); } result = select(sel->smax + 1, &readset, &writeset, NULL, tv2); if (result == 0) return(0); else if (result == -1) { if (errno == EINTR) { *signum = signumber; return(-1); } else { errormsg(strerror(errno), __FILE__, __LINE__); return(-1); } } else { pos = sel->firstfd; result = 0; while((pos != -1) && (result == 0)) { struct selectorobj *temp = sel->fdtable[pos]; if (temp->readsockopt) if (FD_ISSET(pos, &readset)) result = temp->readsockopt(sel, pos, temp->readdata); if ((temp->writesockopt) && (result == 0)) if (FD_ISSET(pos, &writeset)) result = temp->writesockopt(sel, pos, temp->writedata); FD_CLR(pos, &writeset); FD_CLR(pos, &readset); if (result == 2) { int pos2 = temp->next; select_delfd(sel, pos); result = 0; pos = pos2; } else pos = temp->next; } } } return(result); } void select_shutdown(SELECTER *sel) { int count; for (count = 0; count < sel->maxfds; count++) if (sel->fdtable[count]) select_delfd(sel, count); freewrapper(sel->fdtable); freewrapper(sel); }