1 #define _BSDTIME_EXTENSION
2 #define _LOCK_EXTENSION
12 #include <sys/select.h>
16 typedef struct Muxseg {
17 Lock lock; /* for mutual exclusion access to buffer variables */
18 int curfds; /* number of fds currently buffered */
19 int selwait; /* true if selecting process is waiting */
20 int waittime; /* time for timer process to wait */
21 fd_set rwant; /* fd's that select wants to read */
22 fd_set ewant; /* fd's that select wants to know eof info on */
23 Muxbuf bufs[INITBUFS]; /* can grow, via segbrk() */
26 #define MUXADDR ((void*)0x6000000)
27 static Muxseg *mux = 0; /* shared memory segment */
29 /* _muxsid and _killmuxsid are known in libbsd's listen.c */
30 int _muxsid = -1; /* group id of copy processes */
31 static int _mainpid = -1;
32 static int timerpid = -1; /* pid of a timer process */
34 void _killmuxsid(void);
35 static void _copyproc(int, Muxbuf*);
36 static void _timerproc(void);
37 static void _resettimer(void);
39 static int copynotehandler(void *, char *);
41 /* assume FD_SETSIZE is 96 */
42 #define FD_ANYSET(p) ((p)->fds_bits[0] || (p)->fds_bits[1] || (p)->fds_bits[2])
45 * Start making fd read-buffered: make the shared segment, if necessary,
46 * allocate a slot (index into mux->bufs), and fork a child to read the fd
47 * and write into the slot-indexed buffer.
48 * Return -1 if we can't do it.
60 mux = (Muxseg*)_SEGATTACH(0, "shared", MUXADDR, sizeof(Muxseg));
65 /* segattach has returned zeroed memory */
74 if(mux->curfds > INITBUFS) {
75 if(_SEGBRK(mux, mux->bufs+mux->curfds) < 0){
91 if((pid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){
92 /* copy process ... */
97 setpgid(getpid(), _muxsid);
98 _NOTIFY(copynotehandler);
99 for(i=0; i<OPEN_MAX; i++)
100 if(i!=fd && (_fdinfo[i].flags&FD_ISOPEN))
102 _RENDEZVOUS(0, _muxsid);
106 /* parent process continues ... */
109 f->flags |= FD_BUFFERED;
111 _muxsid = _RENDEZVOUS(0, 0);
112 /* leave fd open in parent so system doesn't reuse it */
117 * The given buffered fd is being closed.
118 * Set the fd field in the shared buffer to -1 to tell copyproc
119 * to exit, and kill the copyproc.
132 kill(b->copypid, SIGKILL);
135 /* child copy procs execute this until eof */
137 _copyproc(int fd, Muxbuf *b)
143 e = &b->data[PERFDMAX];
145 /* make sure there's room */
147 if(e - b->putnext < READMAX) {
148 if(b->getnext == b->putnext) {
149 b->getnext = b->putnext = b->data;
152 /* sleep until there's room */
155 _RENDEZVOUS((unsigned long)&b->roomwait, 0);
160 * A Zero-length _READ might mean a zero-length write
161 * happened, or it might mean eof; try several times to
162 * disambiguate (posix read() discards 0-length messages)
166 n = _READ(fd, b->putnext, READMAX);
168 _exit(0); /* we've been closed */
170 } while(n == 0 && ++nzeros < 3);
174 if(mux->selwait && FD_ISSET(fd, &mux->ewant)) {
177 _RENDEZVOUS((unsigned long)&mux->selwait, fd);
178 } else if(b->datawait) {
181 _RENDEZVOUS((unsigned long)&b->datawait, 0);
182 } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) {
185 _RENDEZVOUS((unsigned long)&mux->selwait, fd);
193 /* parent process cannot be both in datawait and selwait */
197 /* wake up _bufreading process */
198 _RENDEZVOUS((unsigned long)&b->datawait, 0);
199 } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) {
202 /* wake up selecting process */
203 _RENDEZVOUS((unsigned long)&mux->selwait, fd);
212 /* like read(), for a buffered fd; extra arg noblock says don't wait for data if true */
214 _readbuf(int fd, void *addr, int nwant, int noblock)
220 if(b->eof && b->n == 0) {
224 if(b->n == 0 && noblock) {
228 /* make sure there's data */
230 ngot = b->putnext - b->getnext;
232 /* maybe EOF just happened */
237 /* sleep until there's data */
240 _RENDEZVOUS((unsigned long)&b->datawait, 0);
242 ngot = b->putnext - b->getnext;
250 memcpy(addr, b->getnext, ngot);
253 if(b->getnext == b->putnext && b->roomwait) {
254 b->getnext = b->putnext = b->data;
257 /* wake up copy process */
258 _RENDEZVOUS((unsigned long)&b->roomwait, 0);
265 select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout)
267 int n, i, tmp, t, slots, fd, err;
272 t = timeout->tv_sec*1000 + (timeout->tv_usec+999)/1000;
275 if(!((rfds && FD_ANYSET(rfds)) || (wfds && FD_ANYSET(wfds))
276 || (efds && FD_ANYSET(efds)))) {
277 /* no requested fds */
285 /* make sure all requested rfds and efds are buffered */
288 for(i = 0; i < nfds; i++)
289 if((rfds && FD_ISSET(i, rfds)) || (efds && FD_ISSET(i, efds))){
291 if(!(f->flags&FD_BUFFERED))
292 if(_startbuf(i) != 0) {
296 if(rfds && FD_ISSET(i,rfds) && b->eof && b->n == 0)
297 if(efds == 0 || !FD_ISSET(i,efds)) {
298 errno = EBADF; /* how X tells a client is gone */
303 /* check wfds; for now, we'll say they are all ready */
305 if(wfds && FD_ANYSET(wfds)){
306 for(i = 0; i<nfds; i++)
307 if(FD_ISSET(i, wfds)) {
315 FD_ZERO(&mux->rwant);
316 FD_ZERO(&mux->ewant);
318 for(i = 0; i<slots; i++) {
324 if(efds && FD_ISSET(fd, efds)) {
325 if(b->eof && b->n == 0){
330 FD_SET(fd, &mux->ewant);
333 if(rfds && FD_ISSET(fd, rfds)) {
334 if(!err && (b->n > 0 || b->eof))
338 FD_SET(fd, &mux->rwant);
342 if(n || !(FD_ANYSET(&mux->rwant) || FD_ANYSET(&mux->ewant)) || t == 0) {
343 FD_ZERO(&mux->rwant);
344 FD_ZERO(&mux->ewant);
358 fd = _RENDEZVOUS((unsigned long)&mux->selwait, 0);
361 if(FD_ISSET(fd, &mux->rwant)) {
364 } else if(FD_ISSET(fd, &mux->ewant) && b->eof && b->n == 0) {
369 FD_ZERO(&mux->rwant);
370 FD_ZERO(&mux->ewant);
374 static int timerreset;
383 /* a little over an hour */
384 #define LONGWAIT 4000001
390 kill(timerpid, SIGKILL);
398 if((timerpid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){
400 setpgid(getpid(), _muxsid);
401 signal(SIGALRM, alarmed);
402 for(i=0; i<OPEN_MAX; i++)
406 _SLEEP(mux->waittime);
411 if(mux->selwait && mux->waittime != LONGWAIT) {
413 mux->waittime = LONGWAIT;
415 _RENDEZVOUS((unsigned long)&mux->selwait, -2);
417 mux->waittime = LONGWAIT;
423 atexit(_killtimerproc);
424 /* parent process continues */
431 kill(timerpid, SIGALRM);
437 if(_muxsid != -1 && (_mainpid == getpid() || _mainpid == -1))
438 kill(-_muxsid,SIGTERM);
441 /* call this on fork(), because reading a BUFFERED fd won't work in child */
451 for(i = 0; i < OPEN_MAX; i++){
453 if(f->flags&FD_BUFFERED)
454 f->flags = (f->flags&~FD_BUFFERED) | FD_BUFFEREDX;
455 /* mark 'poisoned' */
464 copynotehandler(void *u, char *msg)