#include <signal.h>
#include <minix/callnr.h>
#include <minix/com.h>
+#include <sys/select.h>
#include "dmap.h"
#include "file.h"
#include "fproc.h"
#include "inode.h"
#include "param.h"
#include "super.h"
+#include "select.h"
/*===========================================================================*
* do_pipe *
/*===========================================================================*
* pipe_check *
*===========================================================================*/
-PUBLIC int pipe_check(rip, rw_flag, oflags, bytes, position, canwrite)
+PUBLIC int pipe_check(rip, rw_flag, oflags, bytes, position, canwrite, notouch)
register struct inode *rip; /* the inode of the pipe */
int rw_flag; /* READING or WRITING */
int oflags; /* flags set by open or fcntl */
register int bytes; /* bytes to be read or written (all chunks) */
register off_t position; /* current file position */
int *canwrite; /* return: number of bytes we can write */
+int notouch; /* check only */
{
/* Pipes are a little different. If a process reads from an empty pipe for
* which a writer still exists, suspend the reader. If the pipe is empty
if (oflags & O_NONBLOCK) {
r = EAGAIN;
} else {
- suspend(XPIPE); /* block reader */
+ if(!notouch)
+ suspend(XPIPE); /* block reader */
r = SUSPEND;
}
/* If need be, activate sleeping writers. */
- if (susp_count > 0) release(rip, WRITE, susp_count);
+ if (susp_count > 0 && !notouch)
+ release(rip, WRITE, susp_count);
}
return(r);
}
/* Process is writing to a pipe. */
if (find_filp(rip, R_BIT) == NIL_FILP) {
/* Tell kernel to generate a SIGPIPE signal. */
- sys_kill((int)(fp - fproc), SIGPIPE);
+ if(!notouch)
+ sys_kill((int)(fp - fproc), SIGPIPE);
return(EPIPE);
}
else if ((oflags & O_NONBLOCK) && bytes > PIPE_SIZE(rip->i_sp->s_block_size)) {
if ( (*canwrite = (PIPE_SIZE(rip->i_sp->s_block_size) - position)) > 0) {
/* Do a partial write. Need to wakeup reader */
- release(rip, READ, susp_count);
+ if(!notouch)
+ release(rip, READ, susp_count);
return(1);
} else {
return(EAGAIN);
return(1);
}
}
- suspend(XPIPE); /* stop writer -- pipe full */
+ if(!notouch)
+ suspend(XPIPE); /* stop writer -- pipe full */
return(SUSPEND);
}
/* Writing to an empty pipe. Search for suspended reader. */
- if (position == 0) release(rip, READ, susp_count);
+ if (position == 0 && !notouch)
+ release(rip, READ, susp_count);
}
*canwrite = 0;
*/
register struct fproc *rp;
+ struct filp *f;
+
+ /* Trying to perform the call also includes SELECTing on it with that
+ * operation.
+ */
+ if(call_nr == READ || call_nr == WRITE) {
+ int op;
+ if(call_nr == READ)
+ op = SEL_RD;
+ else
+ op = SEL_WR;
+ for(f = &filp[0]; f < &filp[NR_FILPS]; f++) {
+ if(f->filp_count < 1 || !(f->filp_pipe_select_ops & op) ||
+ f->filp_ino != ip)
+ continue;
+ select_callback(f, op);
+ f->filp_pipe_select_ops &= ~op;
+ }
+ }
/* Search the proc table. */
for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) {
/*===========================================================================*
* revive *
*===========================================================================*/
-PUBLIC void revive(proc_nr, bytes)
+PUBLIC void revive(proc_nr, returned)
int proc_nr; /* process to revive */
-int bytes; /* if hanging on task, how many bytes read */
+int returned; /* if hanging on task, how many bytes read */
{
/* Revive a previously blocked process. When a process hangs on tty, this
* is the way it is eventually released.
/* The 'reviving' flag only applies to pipes. Processes waiting for TTY get
* a message right away. The revival process is different for TTY and pipes.
- * For TTY revival, the work is already done, for pipes it is not: the proc
- * must be restarted so it can try again.
+ * For select and TTY revival, the work is already done, for pipes it is not:
+ * the proc must be restarted so it can try again.
*/
task = -rfp->fp_task;
if (task == XPIPE || task == XLOCK) {
rfp->fp_suspended = NOT_SUSPENDED;
if (task == XPOPEN) /* process blocked in open or create */
reply(proc_nr, rfp->fp_fd>>8);
- else {
+ else if(task == XSELECT) {
+ reply(proc_nr, returned);
+ } else {
/* Revive a process suspended on TTY or other device. */
- rfp->fp_nbytes = bytes; /*pretend it wants only what there is*/
- reply(proc_nr, bytes); /* unblock the process */
+ rfp->fp_nbytes = returned; /*pretend it wants only what there is*/
+ reply(proc_nr, returned); /* unblock the process */
}
}
}
case XLOCK: /* process trying to set a lock with FCNTL */
break;
+ case XSELECT: /* process blocking on select() */
+ select_forget(proc_nr);
+ break;
+
case XPOPEN: /* process trying to open a fifo */
break;
reply(proc_nr, EINTR); /* signal interrupted call */
return(OK);
}
+
+/*===========================================================================*
+ * select_request_pipe *
+ *===========================================================================*/
+PUBLIC int select_request_pipe(struct filp *f, int *ops, int block)
+{
+ int orig_ops, r = 0, err, canwrite;
+ orig_ops = *ops;
+ if((*ops & SEL_RD)) {
+ if((err = pipe_check(f->filp_ino, READING, 0,
+ 1, f->filp_pos, &canwrite, 1)) != SUSPEND)
+ r |= SEL_RD;
+ if(err < 0 && err != SUSPEND && (*ops & SEL_ERR))
+ r |= SEL_ERR;
+ }
+ if((*ops & SEL_WR)) {
+ if((err = pipe_check(f->filp_ino, WRITING, 0,
+ 1, f->filp_pos, &canwrite, 1)) != SUSPEND)
+ r |= SEL_WR;
+ if(err < 0 && err != SUSPEND && (*ops & SEL_ERR))
+ r |= SEL_ERR;
+ }
+
+ *ops = r;
+
+ if(!r && block) {
+ f->filp_pipe_select_ops |= orig_ops;
+ }
+
+ return SEL_OK;
+}
+
+/*===========================================================================*
+ * select_match_pipe *
+ *===========================================================================*/
+PUBLIC int select_match_pipe(struct filp *f)
+{
+ /* recognize either pipe or named pipe (FIFO) */
+ if(f && f->filp_ino && (f->filp_ino->i_mode & I_NAMED_PIPE))
+ return 1;
+ return 0;
+}
+
/* Implement entry point to select system call.
*
* The entry points into this file are
- * do_select: perform the SELECT system call
+ * do_select: perform the SELECT system call
+ * select_callback: notify select system of possible fd operation
+ * select_notified: low-level entry for device notifying select
*/
+
+ /* TODO: check if close (pipe?) / exit works;
+ * some printf()s are serious errors;
+ * check combinations of cases listen in open group select
+ * spec (various NULLs and behaviours);
+ * pty support in tty
+ * make select cancel disappearing fp's
+ */
+
+#define DEBUG_SELECT 1
+
#include "fs.h"
+#include "select.h"
+#include "file.h"
+#include "inode.h"
+#include "fs_timers.h"
+
+#include <sys/time.h>
+#include <sys/select.h>
+#include <minix/com.h>
+#include <string.h>
+
+/* max. number of simultaneously pending select() calls */
+#define MAXSELECTS 25
+
+PRIVATE struct selectentry {
+ struct fproc *requestor; /* slot is free iff this is NULL */
+ int req_procnr;
+ fd_set readfds, writefds, errorfds;
+ fd_set ready_readfds, ready_writefds, ready_errorfds;
+ fd_set *vir_readfds, *vir_writefds, *vir_errorfds;
+ struct filp *filps[FD_SETSIZE];
+ int type[FD_SETSIZE];
+ int nfds, nreadyfds;
+ clock_t expiry;
+ timer_t timer; /* if expiry > 0 */
+} selecttab[MAXSELECTS];
+
+#define SELFD_FILE 0
+#define SELFD_PIPE 1
+#define SELFD_TTY 2
+#define SELFD_INET 3
+#define SEL_FDS 4
+
+FORWARD _PROTOTYPE(int select_reevaluate, (struct filp *fp));
+
+FORWARD _PROTOTYPE(int select_request_file, (struct filp *f, int *ops, int block));
+FORWARD _PROTOTYPE(int select_match_file, (struct filp *f));
+
+FORWARD _PROTOTYPE(int select_request_tty, (struct filp *f, int *ops, int block));
+FORWARD _PROTOTYPE(int select_match_tty, (struct filp *f));
+
+FORWARD _PROTOTYPE(int select_request_inet, (struct filp *f, int *ops, int block));
+FORWARD _PROTOTYPE(int select_match_inet, (struct filp *f));
+
+FORWARD _PROTOTYPE(void select_cancel_all, (struct selectentry *e));
+FORWARD _PROTOTYPE(int select_wakeup, (struct selectentry *e));
+
+/* The Open Group:
+ * "The pselect() and select() functions shall support
+ * regular files, terminal and pseudo-terminal devices,
+ * STREAMS-based files, FIFOs, pipes, and sockets."
+ */
+
+PRIVATE struct fdtype {
+ int (*select_request)(struct filp *, int *ops, int block);
+ int (*select_match)(struct filp *);
+} fdtypes[SEL_FDS] = {
+ /* SELFD_FILE */
+ { select_request_file, select_match_file },
+ /* SELFD_TTY (also PTY) */
+ { select_request_tty, select_match_tty },
+ /* SELFD_INET */
+ { select_request_inet, select_match_inet },
+ /* SELFD_PIPE (pipe(2) pipes and FS FIFOs) */
+ { select_request_pipe, select_match_pipe },
+};
+
+/* Open Group:
+ * "File descriptors associated with regular files shall always select true
+ * for ready to read, ready to write, and error conditions."
+ */
+
+/*===========================================================================*
+ * select_request_file *
+ *===========================================================================*/
+PRIVATE int select_request_file(struct filp *f, int *ops, int block)
+{
+ /* output *ops is input *ops */
+ return SEL_OK;
+}
+
+/*===========================================================================*
+ * select_match_file *
+ *===========================================================================*/
+PRIVATE int select_match_file(struct filp *file)
+{
+ if(file && file->filp_ino && (file->filp_ino->i_mode & I_REGULAR))
+ return 1;
+ return 0;
+}
+
+/*===========================================================================*
+ * select_request_tty *
+ *===========================================================================*/
+PRIVATE int select_request_tty(struct filp *f, int *ops, int block)
+{
+ int r, rops;
+ rops = *ops;
+ if(block) rops |= SEL_NOTIFY;
+ *ops = dev_io(DEV_SELECT, f->filp_ino->i_zone[0], rops, NULL, 0, 0, 0);
+ if(*ops < 0)
+ return SEL_ERR;
+ return SEL_OK;
+}
+
+/*===========================================================================*
+ * select_match_tty *
+ *===========================================================================*/
+PRIVATE int select_match_tty(struct filp *file)
+{
+ int major;
+ if(!(file && file->filp_ino &&
+ (file->filp_ino->i_mode & I_TYPE) == I_CHAR_SPECIAL))
+ return 0;
+ major = (file->filp_ino->i_zone[0] >> MAJOR) & BYTE;
+ if(major == TTY_MAJOR || major == CTTY_MAJOR)
+ return 1;
+ return 0;
+}
+
+/*===========================================================================*
+ * select_request_inet *
+ *===========================================================================*/
+PRIVATE int select_request_inet(struct filp *f, int *ops, int block)
+{
+ int r, rops;
+ rops = *ops;
+ if(block) rops |= SEL_NOTIFY;
+ *ops = dev_io(DEV_SELECT, f->filp_ino->i_zone[0], rops, NULL, 0, 0, 0);
+ if(*ops < 0)
+ return SEL_ERR;
+ return SEL_OK;
+}
+
+/*===========================================================================*
+ * select_match_inet *
+ *===========================================================================*/
+PRIVATE int select_match_inet(struct filp *file)
+{
+ int major;
+ if(!(file && file->filp_ino &&
+ (file->filp_ino->i_mode & I_TYPE) == I_CHAR_SPECIAL))
+ return 0;
+ major = (file->filp_ino->i_zone[0] >> MAJOR) & BYTE;
+ if(major == INET_MAJOR)
+ printf("inet minor: %d\n",
+ (file->filp_ino->i_zone[0] & BYTE));
+ /* return 1; */
+ return 0;
+}
+
+
+PRIVATE int tab2ops(int fd, struct selectentry *e)
+{
+ return (FD_ISSET(fd, &e->readfds) ? SEL_RD : 0) |
+ (FD_ISSET(fd, &e->writefds) ? SEL_WR : 0) |
+ (FD_ISSET(fd, &e->errorfds) ? SEL_ERR : 0);
+}
+
+PRIVATE void ops2tab(int ops, int fd, struct selectentry *e)
+{
+ if((ops & SEL_RD) && e->vir_readfds && FD_ISSET(fd, &e->readfds)
+ && !FD_ISSET(fd, &e->ready_readfds)) {
+ FD_SET(fd, &e->ready_readfds);
+ e->nreadyfds++;
+ }
+ if((ops & SEL_WR) && e->vir_writefds && FD_ISSET(fd, &e->writefds)
+ && !FD_ISSET(fd, &e->ready_writefds)) {
+ FD_SET(fd, &e->ready_writefds);
+ e->nreadyfds++;
+ }
+ if((ops & SEL_ERR) && e->vir_errorfds && FD_ISSET(fd, &e->errorfds)
+ && !FD_ISSET(fd, &e->ready_errorfds)) {
+ FD_SET(fd, &e->ready_errorfds);
+ e->nreadyfds++;
+ }
+
+ return;
+}
+
+PRIVATE void copy_fdsets(struct selectentry *e)
+{
+ if(e->vir_readfds)
+ sys_vircopy(SELF, D, (vir_bytes) &e->ready_readfds,
+ e->req_procnr, D, (vir_bytes) e->vir_readfds, sizeof(fd_set));
+ if(e->vir_writefds)
+ sys_vircopy(SELF, D, (vir_bytes) &e->ready_writefds,
+ e->req_procnr, D, (vir_bytes) e->vir_writefds, sizeof(fd_set));
+ if(e->vir_errorfds)
+ sys_vircopy(SELF, D, (vir_bytes) &e->ready_errorfds,
+ e->req_procnr, D, (vir_bytes) e->vir_errorfds, sizeof(fd_set));
+
+ return;
+}
/*===========================================================================*
* do_select *
*===========================================================================*/
PUBLIC int do_select(void)
{
+ int r, nfds, is_timeout = 1, nonzero_timeout = 0,
+ fd, s, block = 0;
+ struct timeval timeout;
+ nfds = m_in.SEL_NFDS;
+
+ if(nfds < 0 || nfds > FD_SETSIZE)
+ return EINVAL;
+
+ for(s = 0; s < MAXSELECTS; s++)
+ if(!selecttab[s].requestor)
+ break;
+
+ if(s >= MAXSELECTS)
+ return ENOSPC;
+
+ selecttab[s].req_procnr = who;
+ selecttab[s].nfds = 0;
+ selecttab[s].nreadyfds = 0;
+ memset(selecttab[s].filps, 0, sizeof(selecttab[s].filps));
+
+ /* defaults */
+ FD_ZERO(&selecttab[s].readfds);
+ FD_ZERO(&selecttab[s].writefds);
+ FD_ZERO(&selecttab[s].errorfds);
+ FD_ZERO(&selecttab[s].ready_readfds);
+ FD_ZERO(&selecttab[s].ready_writefds);
+ FD_ZERO(&selecttab[s].ready_errorfds);
+
+ selecttab[s].vir_readfds = (fd_set *) m_in.SEL_READFDS;
+ selecttab[s].vir_writefds = (fd_set *) m_in.SEL_WRITEFDS;
+ selecttab[s].vir_errorfds = (fd_set *) m_in.SEL_ERRORFDS;
+
+ /* copy args */
+ if(selecttab[s].vir_readfds && (r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_READFDS,
+ SELF, D, (vir_bytes) &selecttab[s].readfds, sizeof(fd_set))) != OK)
+ return r;
+
+ if(selecttab[s].vir_writefds && (r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_WRITEFDS,
+ SELF, D, (vir_bytes) &selecttab[s].writefds, sizeof(fd_set))) != OK)
+ return r;
+
+ if(selecttab[s].vir_errorfds && (r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_ERRORFDS,
+ SELF, D, (vir_bytes) &selecttab[s].errorfds, sizeof(fd_set))) != OK)
+ return r;
+
+ if(!m_in.SEL_TIMEOUT)
+ is_timeout = nonzero_timeout = 0;
+ else
+ if((r=sys_vircopy(who, D, (vir_bytes) m_in.SEL_TIMEOUT,
+ SELF, D, (vir_bytes) &timeout, sizeof(timeout))) != OK)
+ return r;
+
+ /* No nonsense in the timeval please. */
+ if(is_timeout && (timeout.tv_sec < 0 || timeout.tv_usec < 0))
+ return EINVAL;
+
+ /* if is_timeout if 0, we block forever. otherwise, if nonzero_timeout
+ * is 0, we do a poll (don't block). otherwise, we block up to the
+ * specified time interval.
+ */
+ if(is_timeout && (timeout.tv_sec > 0 || timeout.tv_usec > 0))
+ nonzero_timeout = 1;
+
+ if(nonzero_timeout || !is_timeout)
+ block = 1;
+ else
+ block = 0; /* timeout set as (0,0) - this effects a poll */
+
+ /* no timeout set (yet) */
+ selecttab[s].expiry = 0;
+
+ for(fd = 0; fd < nfds; fd++) {
+ int orig_ops, ops, t, type = -1, r;
+ struct filp *filp;
+
+ if(!(orig_ops = ops = tab2ops(fd, &selecttab[s])))
+ continue;
+ if(!(filp = selecttab[s].filps[fd] = get_filp(fd))) {
+ select_cancel_all(&selecttab[s]);
+ return EBADF;
+ }
+
+ for(t = 0; t < SEL_FDS; t++) {
+ if(fdtypes[t].select_match(filp)) {
+#if DEBUG_SELECT
+ printf("select: fd %d is type %d ", fd, t);
+#endif
+ if(type != -1)
+ printf("select: double match\n");
+ type = t;
+ }
+ }
+
+ /* Open Group:
+ * "The pselect() and select() functions shall support
+ * regular files, terminal and pseudo-terminal devices,
+ * STREAMS-based files, FIFOs, pipes, and sockets. The
+ * behavior of pselect() and select() on file descriptors
+ * that refer to other types of file is unspecified."
+ *
+ * If all types are implemented, then this is another
+ * type of file and we get to do whatever we want.
+ */
+ if(type == -1)
+ return EBADF;
+
+ selecttab[s].type[fd] = type;
+
+ if((selecttab[s].filps[fd]->filp_select_ops & ops) != ops) {
+ int wantops;
+ /* Request the select on this fd. */
+#if DEBUG_SELECT
+ printf("%p requesting ops %d -> ",
+ selecttab[s].filps[fd],
+ selecttab[s].filps[fd]->filp_select_ops);
+#endif
+ wantops = (selecttab[s].filps[fd]->filp_select_ops |= ops);
+#if DEBUG_SELECT
+ printf("%d\n", selecttab[s].filps[fd]->filp_select_ops);
+#endif
+ if((r = fdtypes[type].select_request(filp,
+ &wantops, block)) != SEL_OK) {
+ /* error or bogus return code.. backpaddle */
+ select_cancel_all(&selecttab[s]);
+ printf("select: select_request returned error\n");
+ return EINVAL;
+ }
+ if(wantops) {
+ if(wantops & ops) {
+ /* operations that were just requested
+ * are ready to go right away
+ */
+ ops2tab(wantops, fd, &selecttab[s]);
+ }
+ /* if there are any other select()s blocking
+ * on these operations of this fp, they can
+ * be awoken too
+ */
+ select_callback(filp, ops);
+ }
+#if DEBUG_SELECT
+ printf("select request ok; ops returned %d\n", wantops);
+#endif
+ } else {
+#if DEBUG_SELECT
+ printf("select already happening on that filp\n");
+#endif
+ }
+
+ selecttab[s].nfds = fd+1;
+ selecttab[s].filps[fd]->filp_selectors++;
+
+#if DEBUG_SELECT
+ printf("[fd %d ops: %d] ", fd, ops);
+#endif
+ }
+
+ if(selecttab[s].nreadyfds > 0 || !block) {
+ /* fd's were found that were ready to go right away, and/or
+ * we were instructed not to block at all. Must return
+ * immediately.
+ */
+ copy_fdsets(&selecttab[s]);
+ select_cancel_all(&selecttab[s]);
+ selecttab[s].requestor = NULL;
+
+ /* Open Group:
+ * "Upon successful completion, the pselect() and select()
+ * functions shall return the total number of bits
+ * set in the bit masks."
+ */
+
+ return selecttab[s].nreadyfds;
+ }
+
+ /* Convert timeval to ticks and set the timer. If it fails, undo
+ * all, return error.
+ */
+ if(is_timeout) {
+ int ticks;
+ /* Open Group:
+ * "If the requested timeout interval requires a finer
+ * granularity than the implementation supports, the
+ * actual timeout interval shall be rounded up to the next
+ * supported value."
+ */
+#define USECPERSEC 1000000
+ while(timeout.tv_usec >= USECPERSEC) {
+ /* this is to avoid overflow with *HZ below */
+ timeout.tv_usec -= USECPERSEC;
+ timeout.tv_sec++;
+ }
+ ticks = timeout.tv_sec * HZ +
+ (timeout.tv_usec * HZ + USECPERSEC-1) / USECPERSEC;
+ selecttab[s].expiry = ticks;
+ fs_set_timer(&selecttab[s].timer, ticks, select_timeout_check, s);
+#if DEBUG_SELECT
+ printf("%d: blocking %d ticks\n", s, ticks);
+#endif
+ }
+
+ /* if we're blocking, the table entry is now valid. */
+ selecttab[s].requestor = fp;
+
+ /* process now blocked */
+ suspend(XSELECT);
+ return SUSPEND;
+}
+
+PRIVATE void select_cancel_all(struct selectentry *e)
+{
+ int fd;
+
+ for(fd = 0; fd < e->nfds; fd++) {
+ struct filp *fp;
+ fp = e->filps[fd];
+ if(!fp) {
+#if DEBUG_SELECT
+ printf("[ fd %d/%d NULL ] ", fd, e->nfds);
+#endif
+ continue;
+ }
+ if(fp->filp_selectors < 1) {
+#if DEBUG_SELECT
+ printf("select: %d selectors?!\n", fp->filp_selectors);
+#endif
+ continue;
+ }
+ fp->filp_selectors--;
+ e->filps[fd] = NULL;
+ select_reevaluate(fp);
+ }
+
+ if(e->expiry > 0) {
+#if DEBUG_SELECT
+ printf("cancelling timer %d\n", e - selecttab);
+#endif
+ fs_cancel_timer(&e->timer);
+ e->expiry = 0;
+ }
+
+ return;
+}
+
+PRIVATE int select_wakeup(struct selectentry *e)
+{
+ /* Open Group:
+ * "Upon successful completion, the pselect() and select()
+ * functions shall return the total number of bits
+ * set in the bit masks."
+ */
+ revive(e->req_procnr, e->nreadyfds);
+ return;
+}
+
+PRIVATE int select_reevaluate(struct filp *fp)
+{
+ int r;
+ int s, remain_ops = 0, fd, type = -1;
+ int want_ops;
+
+ if(!fp) {
+ printf("fs: select: reevalute NULL fp\n");
+ return 0;
+ }
+
+ for(s = 0; s < MAXSELECTS; s++) {
+ if(!selecttab[s].requestor)
+ continue;
+ for(fd = 0; fd < selecttab[s].nfds; fd++)
+ if(fp == selecttab[s].filps[fd]) {
+ remain_ops |= tab2ops(fd, &selecttab[s]);
+ type = selecttab[s].type[fd];
+ }
+ }
+
+ /* If there are any select()s open that want any operations on
+ * this fd that haven't been satisfied by this callback, then we're
+ * still in the market for it.
+ */
+ fp->filp_select_ops = remain_ops;
+#if DEBUG_SELECT
+ printf("remaining operations on fp are %d\n", fp->filp_select_ops);
+#endif
+
+ return remain_ops;
+}
+
+/*===========================================================================*
+ * int select_callback *
+ *===========================================================================*/
+PUBLIC int select_callback(struct filp *fp, int ops)
+{
+ int s, f, fd, want_ops, remain_ops, type;
+
+ /* We are being notified that file pointer fp is available for
+ * operations 'ops'. We must re-register the select for
+ * operations that we are still interested in, if any.
+ */
+
+restart_callback:
+ want_ops = 0;
+ type = -1;
+ for(s = 0; s < MAXSELECTS; s++) {
+ int wakehim = 0;
+ if(!selecttab[s].requestor)
+ continue;
+ for(fd = 0; fd < selecttab[s].nfds; fd++) {
+ if(!selecttab[s].filps[fd])
+ continue;
+ if(selecttab[s].filps[fd] == fp) {
+ int this_want_ops;
+ this_want_ops = tab2ops(fd, &selecttab[s]);
+ want_ops |= this_want_ops;
+ if(this_want_ops & ops) {
+ /* this select() has been satisfied. */
+ ops2tab(ops, fd, &selecttab[s]);
+ wakehim = 1;
+ }
+ type = selecttab[s].type[fd];
+ }
+ }
+ if(wakehim) {
+ select_cancel_all(&selecttab[s]);
+ copy_fdsets(&selecttab[s]);
+ selecttab[s].requestor = NULL;
+ select_wakeup(&selecttab[s]);
+ }
+ }
+
+ return 0;
+}
+
+/*===========================================================================*
+ * int select_notified *
+ *===========================================================================*/
+PUBLIC int select_notified(message *m)
+{
+ int s, f;
+
+ switch(m->m_source) {
+ case TTY:
+#if DEBUG_SELECT
+ printf("fs: select: tty notification\n");
+#endif
+ for(s = 0; s < MAXSELECTS; s++) {
+ int line, ops;
+ if(!selecttab[s].requestor)
+ continue;
+ for(f = 0; f < selecttab[s].nfds; f++) {
+ if(!selecttab[s].filps[f] ||
+ !select_match_tty(selecttab[s].filps[f]))
+ continue;
+ ops = tab2ops(f, &selecttab[s]);
+ line = selecttab[s].filps[f]->filp_ino->i_zone[0] & BYTE;
+ if((line == m->NOTIFY_ARG) &&
+ (m->NOTIFY_FLAGS & ops)) {
+#if DEBUG_SELECT
+ printf("fs: select: tty notification matched\n");
+#endif
+ select_callback(selecttab[s].filps[f], ops);
+ }
+ }
+ }
+ break;
+ default:
+ printf("fs: select: unrecognized select reply\n");
+ }
return OK;
}
+/*===========================================================================*
+ * int select_forget *
+ *===========================================================================*/
+PUBLIC void select_forget(int proc)
+{
+ /* something has happened (e.g. signal delivered that interrupts
+ * select()). totally forget about the select().
+ */
+ int s;
+
+ for(s = 0; s < MAXSELECTS; s++) {
+ if(selecttab[s].requestor &&
+ selecttab[s].req_procnr == proc) {
+ break;
+ }
+
+ }
+
+ if(s >= MAXSELECTS) {
+ printf("select: cancelled select() not found");
+ return;
+ }
+
+ select_cancel_all(&selecttab[s]);
+ selecttab[s].requestor = NULL;
+
+ return;
+}
+
+/*===========================================================================*
+ * int select_timeout_check *
+ *===========================================================================*/
+PUBLIC void select_timeout_check(timer_t *timer)
+{
+ int s, r;
+ clock_t now;
+
+ s = tmr_arg(timer)->ta_int;
+
+ if(s < 0 || s >= MAXSELECTS) {
+ printf("select: bogus slot arg to watchdog %d\n", s);
+ return;
+ }
+
+ if(!selecttab[s].requestor) {
+ printf("select: no requestor in watchdog\n");
+ return;
+ }
+
+ if(selecttab[s].expiry <= 0) {
+ printf("select: strange expiry value in watchdog\n", s);
+ return;
+ }
+
+ selecttab[s].expiry = 0;
+ copy_fdsets(&selecttab[s]);
+ select_cancel_all(&selecttab[s]);
+ selecttab[s].requestor = NULL;
+ select_wakeup(&selecttab[s]);
+
+ return;
+}
+