2024-05-08 15:39:18 +02:00

285 lines
8.1 KiB
C

#include "stdio.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <devices/timer.h>
#include <dos/dosextens.h>
#include <proto/exec.h>
#include "debuglib.h"
#include "select.h"
#include "socket.h"
#include "stabs.h"
/*
**
*/
static struct MsgPort *_tport=NULL;
static struct timerequest *_treq=NULL;
/*
**
*/
void __setupselect(void)
{
if (((_tport=CreateMsgPort())==NULL) ||
((_treq=CreateIORequest(_tport,sizeof(*_treq)))==NULL))
exit(20);
}
ADD2INIT(__setupselect,-10);
void __cleanupselect(void)
{
if (_treq) {
DeleteIORequest(_treq); _treq=NULL;
}
if (_tport) {
DeleteMsgPort(_tport); _tport=NULL;
}
}
ADD2EXIT(__cleanupselect,-10);
/*
**
*/
#if 0
static inline void handle_select_port(void)
{ extern struct MsgPort *__selport;
struct StandardPacket *pkt;
while((pkt=GetPacket(__selport))) {
DB( BUG("handle_select_port got packet %lx\n", pkt); )
pkt->sp_Pkt.dp_Port = NULL;
}
}
#endif
static inline void setcopy(int nfd, u_int *ifd, u_int *ofd)
{
/* this procedure is here, because it's "normal" that if you only
* want to select on fd 0,1,2 eg. you only pass a long to select,
* not a whole fd_set, so we can't simply copy over results in the
* full size of an fd_set.. */
/* we have to copy that many longs... */
for (nfd=(nfd+31)>>5; nfd; *ofd++=*ifd++,--nfd);
}
static inline int net_select(int s, fd_set *in, fd_set *out, fd_set *exc, struct timeval *timeout, u_long *sigs)
{ struct SocketSettings *lss;
int rc;
switch (lss=_lx_get_socket_settings(),lss->lx_network_type) {
case LX_AS225:
rc = SOCK_selectwait(s, in, out, exc, timeout, sigs);
break;
case LX_AMITCP:
rc = TCP_WaitSelect(s, in, out, exc, timeout, (ULONG*)sigs);
break;
default:
rc = -1;
break;
}
return rc;
}
static inline int lx_select(int nfd, fd_set *ifd, fd_set *ofd, fd_set *efd, struct timeval *timeout, u_long *mask)
{ int i, waitin, waitout, waitexc, dotout, result, skipped_wait;
u_long net_nfds, wait_sigs, recv_wait_sigs=0, origmask = mask ? *mask : 0;
StdFileDes *f;
/* as long as I don't support anything similar to a network, I surely
* won't get any exceptional conditions, so *efd is mapped into
* *ifd, if it's set.
*/
/* first check, that all included descriptors are valid and support
* the requested operation. If the user included a request to wait
* for a descriptor to be ready to read, while the descriptor was only
* opened for writing, the requested bit is immediately cleared
*/
waitin = waitout = waitexc = 0;
/*if (nfd > NOFILE) nfd = NOFILE;*/
for (i = 0; i < nfd; i++) {
if (ifd && FD_ISSET(i, ifd) && (f = _lx_fhfromfd(i))) {
if (!f->lx_fx)
FD_CLR(i, ifd);
else
++waitin;
}
if (ofd && FD_ISSET(i, ofd) && (f = _lx_fhfromfd(i))) {
if (!f->lx_fx)
FD_CLR(i, ofd);
else
++waitout;
}
if (efd && FD_ISSET(i, efd) && (f = _lx_fhfromfd(i))) {
/* question: can an exceptional condition also occur on a
* write-only fd?? */
if (!f->lx_fx)
FD_CLR(i, efd);
else
++waitexc;
}
}
dotout = (timeout && timerisset(timeout));
for (skipped_wait = 0; ; skipped_wait=1) {
fd_set readyin, readyout, readyexc;
fd_set netin, netout, netexc;
extern int network_installed;
int tout, readydesc, cmd;
FD_ZERO(&readyin);
FD_ZERO(&readyout);
FD_ZERO(&readyexc);
if (network_installed) {
FD_ZERO(&netin);
FD_ZERO(&netout);
FD_ZERO(&netexc);
net_nfds = 0;
}
// handle_select_port();
tout = readydesc = 0;
wait_sigs = SIGBREAKF_CTRL_C | origmask;
if ((cmd=SELCMD_POLL,skipped_wait) != 0) {
if (dotout) {
_treq->tr_node.io_Command = TR_ADDREQUEST;
_treq->tr_time.tv_sec = timeout->tv_sec;
_treq->tr_time.tv_usec = timeout->tv_usec;
SendIO(&_treq->tr_node);
/* clear the bit, it's used for sync packets too, and might be set */
SetSignal (0, 1 << _tport->mp_SigBit);
wait_sigs |= 1 << _tport->mp_SigBit;
}
/* have all watched files get prepared for selecting */
for (i = 0; i < nfd; i++) {
if (ifd && FD_ISSET (i, ifd) && (f = _lx_fhfromfd(i)))
wait_sigs |= f->lx_fx->lx_select (f, SELCMD_PREPARE, SELMODE_IN, &netin, &net_nfds);
if (ofd && FD_ISSET (i, ofd) && (f = _lx_fhfromfd(i)))
wait_sigs |= f->lx_fx->lx_select (f, SELCMD_PREPARE, SELMODE_OUT, &netout, &net_nfds);
if (efd && FD_ISSET (i, efd) && (f = _lx_fhfromfd(i)))
wait_sigs |= f->lx_fx->lx_select (f, SELCMD_PREPARE, SELMODE_EXC, &netexc, &net_nfds);
}
/* now wait for all legally possible signals, this includes BSD
* signals (but want at least one signal set!)
*/
if (network_installed) {
int res;
DB( BUG("Calling _net_select, signals are %lx\n", wait_sigs); )
res = net_select(net_nfds, &netin, &netout, &netexc, NULL, &wait_sigs);
recv_wait_sigs = wait_sigs;
DB( BUG("Net select returns %ld, wait_sigs are %lx\n", res, wait_sigs); )
}
else
while (!(recv_wait_sigs = Wait (wait_sigs))) ;
if (mask)
*mask = recv_wait_sigs & origmask;
if (dotout) {
/* IMPORTANT: unqueue the timer request BEFORE polling the fd's,
* or __wait_packet() will treat the timer request
* as a packet...
*/
if (!CheckIO(&_treq->tr_node))
AbortIO(&_treq->tr_node);
else
recv_wait_sigs |= 1 << _tport->mp_SigBit;
WaitIO(&_treq->tr_node);
}
// handle_select_port();
cmd = SELCMD_CHECK;
}
/* no matter what caused Wait() to return, wait for all requests
* to complete (we CAN'T abort a DOS packet, sigh...)
*/
/* collect information from the file descriptors */
for (i = 0; i < nfd; i++) {
if (ifd && FD_ISSET (i, ifd) && (f = _lx_fhfromfd(i))
&& f->lx_fx->lx_select (f, cmd, SELMODE_IN, &netin, NULL)) {
DB( BUG("Select: fd %ld, has data ready for reading\n", i); )
FD_SET (i, &readyin);
++readydesc;
}
if (ofd && FD_ISSET (i, ofd) && (f = _lx_fhfromfd(i))
&& f->lx_fx->lx_select (f, cmd, SELMODE_OUT, &netout, NULL)) {
DB( BUG("Select: fd %ld, has data ready for writing\n", i); )
FD_SET (i, &readyout);
++readydesc;
}
if (efd && FD_ISSET (i, efd) && (f = _lx_fhfromfd(i))
&& f->lx_fx->lx_select (f, cmd, SELMODE_EXC, &netexc, NULL)) {
DB( BUG("Select: fd %ld, has an exception\n", i); )
FD_SET (i, &readyexc);
++readydesc;
}
}
/* we have a timeout condition, if readydesc == 0, dotout == 1 and
* end_time < current time
*/
DB( BUG("After loop, readydesc is %ld\n", readydesc); )
if (!readydesc && dotout)
tout = recv_wait_sigs & (1 << _tport->mp_SigBit);
DB( BUG("readydesc is %ld, tout is %ld, timeout is %lx\n", readydesc, tout, timeout); )
if (readydesc || tout || (timeout && !timerisset(timeout))) {
if (ifd) setcopy(nfd, (u_int *)&readyin, (u_int *)ifd);
if (ofd) setcopy(nfd, (u_int *)&readyout, (u_int *)ofd);
if (efd) setcopy(nfd, (u_int *)&readyexc, (u_int *)efd);
result = readydesc; /* ok for tout, since then readydesc is already 0 */
DB( BUG("Breaking out of loop, result is %ld\n", result); )
break;
}
if (recv_wait_sigs & SIGBREAKF_CTRL_C) {
DB( BUG("Found CTRL-C\n"); )
/* clear it and resend it so the outer stuff can catch it */
SetSignal(0, SIGBREAKF_CTRL_C);
Signal(FindTask(NULL), SIGBREAKF_CTRL_C);
result = -1;
break;
}
}
if (recv_wait_sigs == (u_long)-1)
return -1;
/* have to set this here, since errno can be changed in signal handlers */
if (result == -1)
errno = EINTR;
DB( BUG("SELECT: returning %ld\n", result); )
return result;
}
int select(int nfd, fd_set *ifd, fd_set *ofd, fd_set *efd, struct timeval *timeout)
{ return lx_select(nfd, ifd, ofd, efd, timeout, NULL); }