123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- #define _BSDTIME_EXTENSION
- #define _LOCK_EXTENSION
- #include "lib.h"
- #include <stdlib.h>
- #include <errno.h>
- #include <unistd.h>
- #include <signal.h>
- #include <string.h>
- #include <stdio.h>
- #include <lock.h>
- #include <sys/time.h>
- #include <sys/select.h>
- #include <unistd.h>
- #include "sys9.h"
- typedef struct Muxseg {
- Lock lock; /* for mutual exclusion access to buffer variables */
- int curfds; /* number of fds currently buffered */
- int selwait; /* true if selecting process is waiting */
- int waittime; /* time for timer process to wait */
- fd_set rwant; /* fd's that select wants to read */
- fd_set ewant; /* fd's that select wants to know eof info on */
- Muxbuf bufs[INITBUFS]; /* can grow, via segbrk() */
- } Muxseg;
- #define MUXADDR ((void*)0x6000000)
- static Muxseg *mux = 0; /* shared memory segment */
- /* _muxsid and _killmuxsid are known in libbsd's listen.c */
- int _muxsid = -1; /* group id of copy processes */
- static int _mainpid = -1;
- static int timerpid = -1; /* pid of a timer process */
- void _killmuxsid(void);
- static void _copyproc(int, Muxbuf*);
- static void _timerproc(void);
- static void _resettimer(void);
- static int copynotehandler(void *, char *);
- /* assume FD_SETSIZE is 96 */
- #define FD_ANYSET(p) ((p)->fds_bits[0] || (p)->fds_bits[1] || (p)->fds_bits[2])
- /*
- * Start making fd read-buffered: make the shared segment, if necessary,
- * allocate a slot (index into mux->bufs), and fork a child to read the fd
- * and write into the slot-indexed buffer.
- * Return -1 if we can't do it.
- */
- int
- _startbuf(int fd)
- {
- long i, n, slot;
- int pid, sid;
- Fdinfo *f;
- Muxbuf *b;
- if(mux == 0){
- _RFORK(RFREND);
- mux = (Muxseg*)_SEGATTACH(0, "shared", MUXADDR, sizeof(Muxseg));
- if((long)mux == -1){
- _syserrno();
- return -1;
- }
- /* segattach has returned zeroed memory */
- atexit(_killmuxsid);
- }
- if(fd == -1)
- return 0;
- slot = mux->curfds++;
- if(mux->curfds > INITBUFS) {
- if(_SEGBRK(mux, mux->bufs+mux->curfds) < 0){
- _syserrno();
- return -1;
- }
- }
- f = &_fdinfo[fd];
- b = &mux->bufs[slot];
- b->n = 0;
- b->putnext = b->data;
- b->getnext = b->data;
- b->eof = 0;
- b->fd = fd;
- if(_mainpid == -1)
- _mainpid = getpid();
- if((pid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){
- /* copy process ... */
- if(_muxsid == -1) {
- _RFORK(RFNOTEG);
- _muxsid = getpgrp();
- } else
- setpgid(getpid(), _muxsid);
- _NOTIFY(copynotehandler);
- for(i=0; i<OPEN_MAX; i++)
- if(i!=fd && (_fdinfo[i].flags&FD_ISOPEN))
- _CLOSE(i);
- _RENDEZVOUS(0, _muxsid);
- _copyproc(fd, b);
- }
- /* parent process continues ... */
- b->copypid = pid;
- f->buf = b;
- f->flags |= FD_BUFFERED;
- _muxsid = _RENDEZVOUS(0, 0);
- /* leave fd open in parent so system doesn't reuse it */
- return 0;
- }
- /*
- * The given buffered fd is being closed.
- * Set the fd field in the shared buffer to -1 to tell copyproc
- * to exit, and kill the copyproc.
- */
- void
- _closebuf(int fd)
- {
- Muxbuf *b;
- b = _fdinfo[fd].buf;
- if(!b)
- return;
- lock(&mux->lock);
- b->fd = -1;
- unlock(&mux->lock);
- kill(b->copypid, SIGKILL);
- }
- /* child copy procs execute this until eof */
- static void
- _copyproc(int fd, Muxbuf *b)
- {
- unsigned char *e;
- int n;
- int nzeros;
- e = &b->data[PERFDMAX];
- for(;;) {
- /* make sure there's room */
- lock(&mux->lock);
- if(e - b->putnext < READMAX) {
- if(b->getnext == b->putnext) {
- b->getnext = b->putnext = b->data;
- unlock(&mux->lock);
- } else {
- /* sleep until there's room */
- b->roomwait = 1;
- unlock(&mux->lock);
- _RENDEZVOUS((unsigned long)&b->roomwait, 0);
- }
- } else
- unlock(&mux->lock);
- /*
- * A Zero-length _READ might mean a zero-length write
- * happened, or it might mean eof; try several times to
- * disambiguate (posix read() discards 0-length messages)
- */
- nzeros = 0;
- do {
- n = _READ(fd, b->putnext, READMAX);
- if(b->fd == -1) {
- _exit(0); /* we've been closed */
- }
- } while(n == 0 && ++nzeros < 3);
- lock(&mux->lock);
- if(n <= 0) {
- b->eof = 1;
- if(mux->selwait && FD_ISSET(fd, &mux->ewant)) {
- mux->selwait = 0;
- unlock(&mux->lock);
- _RENDEZVOUS((unsigned long)&mux->selwait, fd);
- } else if(b->datawait) {
- b->datawait = 0;
- unlock(&mux->lock);
- _RENDEZVOUS((unsigned long)&b->datawait, 0);
- } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) {
- mux->selwait = 0;
- unlock(&mux->lock);
- _RENDEZVOUS((unsigned long)&mux->selwait, fd);
- } else
- unlock(&mux->lock);
- _exit(0);
- } else {
- b->putnext += n;
- b->n += n;
- if(b->n > 0) {
- /* parent process cannot be both in datawait and selwait */
- if(b->datawait) {
- b->datawait = 0;
- unlock(&mux->lock);
- /* wake up _bufreading process */
- _RENDEZVOUS((unsigned long)&b->datawait, 0);
- } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) {
- mux->selwait = 0;
- unlock(&mux->lock);
- /* wake up selecting process */
- _RENDEZVOUS((unsigned long)&mux->selwait, fd);
- } else
- unlock(&mux->lock);
- } else
- unlock(&mux->lock);
- }
- }
- }
- /* like read(), for a buffered fd; extra arg noblock says don't wait for data if true */
- int
- _readbuf(int fd, void *addr, int nwant, int noblock)
- {
- Muxbuf *b;
- int ngot;
- b = _fdinfo[fd].buf;
- if(b->eof && b->n == 0) {
- goteof:
- return 0;
- }
- if(b->n == 0 && noblock) {
- errno = EAGAIN;
- return -1;
- }
- /* make sure there's data */
- lock(&mux->lock);
- ngot = b->putnext - b->getnext;
- if(ngot == 0) {
- /* maybe EOF just happened */
- if(b->eof) {
- unlock(&mux->lock);
- goto goteof;
- }
- /* sleep until there's data */
- b->datawait = 1;
- unlock(&mux->lock);
- _RENDEZVOUS((unsigned long)&b->datawait, 0);
- lock(&mux->lock);
- ngot = b->putnext - b->getnext;
- }
- if(ngot == 0) {
- unlock(&mux->lock);
- goto goteof;
- }
- if(ngot > nwant)
- ngot = nwant;
- memcpy(addr, b->getnext, ngot);
- b->getnext += ngot;
- b->n -= ngot;
- if(b->getnext == b->putnext && b->roomwait) {
- b->getnext = b->putnext = b->data;
- b->roomwait = 0;
- unlock(&mux->lock);
- /* wake up copy process */
- _RENDEZVOUS((unsigned long)&b->roomwait, 0);
- } else
- unlock(&mux->lock);
- return ngot;
- }
- int
- select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout)
- {
- int n, i, tmp, t, slots, fd;
- Fdinfo *f;
- Muxbuf *b;
- if(timeout)
- t = timeout->tv_sec*1000 + (timeout->tv_usec+999)/1000;
- else
- t = -1;
- if(!((rfds && FD_ANYSET(rfds)) || (wfds && FD_ANYSET(wfds))
- || (efds && FD_ANYSET(efds)))) {
- /* no requested fds */
- if(t > 0)
- _SLEEP(t);
- return 0;
- }
- _startbuf(-1);
- /* make sure all requested rfds and efds are buffered */
- if(nfds >= OPEN_MAX)
- nfds = OPEN_MAX-1;
- for(i = 0; i<= nfds; i++)
- if((rfds && FD_ISSET(i, rfds)) || (efds && FD_ISSET(i, efds))){
- f = &_fdinfo[i];
- if(!(f->flags&FD_BUFFERED))
- if(_startbuf(i) != 0) {
- return -1;
- }
- b = f->buf;
- if(rfds && FD_ISSET(i,rfds) && b->eof && b->n == 0) {
- errno = EBADF; /* how X tells a client is gone */
- return -1;
- }
- }
- /* check wfds; for now, we'll say they are all ready */
- n = 0;
- if(wfds && FD_ANYSET(wfds)){
- for(i = 0; i<nfds; i++)
- if(FD_ISSET(i, wfds)) {
- n++;
- }
- }
- lock(&mux->lock);
- slots = mux->curfds;
- FD_ZERO(&mux->rwant);
- FD_ZERO(&mux->ewant);
- for(i = 0; i<slots; i++) {
- b = &mux->bufs[i];
- fd = b->fd;
- if(fd == -1)
- continue;
- if(rfds && FD_ISSET(fd, rfds)) {
- if(b->n > 0 || b->eof)
- n++;
- else{
- FD_CLR(fd, rfds);
- FD_SET(fd, &mux->rwant);
- }
- }
- if(efds && FD_ISSET(fd, efds)) {
- if(b->eof && b->n == 0)
- n++;
- else{
- FD_CLR(fd, efds);
- FD_SET(fd, &mux->ewant);
- }
- }
- }
- if(n || !(FD_ANYSET(&mux->rwant) || FD_ANYSET(&mux->ewant)) || t == 0) {
- FD_ZERO(&mux->rwant);
- FD_ZERO(&mux->ewant);
- unlock(&mux->lock);
- return n;
- }
- if(timeout) {
- mux->waittime = t;
- if(timerpid == -1)
- _timerproc();
- else
- _resettimer();
- }
- mux->selwait = 1;
- unlock(&mux->lock);
- fd = _RENDEZVOUS((unsigned long)&mux->selwait, 0);
- if(fd >= 0) {
- b = _fdinfo[fd].buf;
- if(FD_ISSET(fd, &mux->rwant)) {
- FD_SET(fd, rfds);
- n = 1;
- } else if(FD_ISSET(fd, &mux->ewant) && b->eof && b->n == 0) {
- FD_SET(fd, efds);
- n = 1;
- }
- }
- FD_ZERO(&mux->rwant);
- FD_ZERO(&mux->ewant);
- return n;
- }
- static int timerreset;
- static void
- alarmed(int v)
- {
- timerreset = 1;
- }
- /* a little over an hour */
- #define LONGWAIT 4000001
- static void
- _timerproc(void)
- {
- int i;
- if((timerpid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){
- /* timer process */
- setpgid(getpid(), _muxsid);
- signal(SIGALRM, alarmed);
- for(i=0; i<OPEN_MAX; i++)
- _CLOSE(i);
- _RENDEZVOUS(1, 0);
- for(;;) {
- _SLEEP(mux->waittime);
- if(timerreset) {
- timerreset = 0;
- } else {
- lock(&mux->lock);
- if(mux->selwait && mux->waittime != LONGWAIT) {
- mux->selwait = 0;
- mux->waittime = LONGWAIT;
- unlock(&mux->lock);
- _RENDEZVOUS((unsigned long)&mux->selwait, -2);
- } else {
- mux->waittime = LONGWAIT;
- unlock(&mux->lock);
- }
- }
- }
- }
- /* parent process continues */
- _RENDEZVOUS(1, 0);
- }
- static void
- _resettimer(void)
- {
- kill(timerpid, SIGALRM);
- }
- void
- _killmuxsid(void)
- {
- if(_muxsid != -1 && (_mainpid == getpid() || _mainpid == -1))
- kill(-_muxsid,SIGTERM);
- }
- /* call this on fork(), because reading a BUFFERED fd won't work in child */
- void
- _detachbuf(void)
- {
- int i;
- Fdinfo *f;
- if(mux == 0)
- return;
- _SEGDETACH(mux);
- for(i = 0; i < OPEN_MAX; i++){
- f = &_fdinfo[i];
- if(f->flags&FD_BUFFERED)
- f->flags = (f->flags&~FD_BUFFERED) | FD_BUFFEREDX;
- /* mark 'poisoned' */
- }
- mux = 0;
- _muxsid = -1;
- _mainpid = -1;
- timerpid = -1;
- }
- static int
- copynotehandler(void *u, char *msg)
- {
- int i;
- void(*f)(int);
- if(_finishing)
- _finish(0, 0);
- _NOTED(1);
- }
|