]> Zhao Yanbai Git Server - minix.git/commitdiff
Started select() implementation.
authorBen Gras <ben@minix3.org>
Fri, 17 Jun 2005 13:41:12 +0000 (13:41 +0000)
committerBen Gras <ben@minix3.org>
Fri, 17 Jun 2005 13:41:12 +0000 (13:41 +0000)
Added interface to select() for pipes (also named pipes), and select()
stubs for regular files.

Added timer library in FS that select() is the first customer of.

This is unfinished, but committed anyway to get a new release out to
Al and testers.

13 files changed:
servers/fs/Makefile
servers/fs/cache.c
servers/fs/const.h
servers/fs/device.c
servers/fs/file.h
servers/fs/filedes.c
servers/fs/fs_timers.h [new file with mode: 0644]
servers/fs/main.c
servers/fs/pipe.c
servers/fs/proto.h
servers/fs/read.c
servers/fs/select.c
servers/fs/select.h [new file with mode: 0644]

index 305a0b21545c68feb01517559b84653315e476ba..23e3b076e114a6a15c675e4644efb6ae686fd6dc 100644 (file)
@@ -11,12 +11,12 @@ h = $i/minix
 CC =   exec cc
 CFLAGS = -I$i
 LDFLAGS = -i
-LIBS = -lsys -lutils
+LIBS = -lsys -lutils -ltimers
 
 OBJ =  main.o open.o read.o write.o pipe.o dmap.o \
        device.o path.o mount.o link.o super.o inode.o \
        cache.o cache2.o filedes.o stadir.o protect.o time.o \
-       cmostime.o lock.o misc.o utility.o select.o table.o 
+       cmostime.o lock.o misc.o utility.o select.o timers.o table.o 
 
 # build local binary 
 all build:     $(SERVER)
index 3f788141198553e0dab0492ba15817cffc7ed944..6fe74c0feb779c6767f8e05e6b68f01adba191ab 100644 (file)
@@ -60,6 +60,7 @@ int only_search;              /* if NO_READ, don't read, else act normal */
                        /* Block needed has been found. */
                        if (bp->b_count == 0) rm_lru(bp);
                        bp->b_count++;  /* record that block is in use */
+
                        return(bp);
                } else {
                        /* This block is not the one sought. */
@@ -114,7 +115,9 @@ int only_search;            /* if NO_READ, don't read, else act normal */
 #endif
        if (only_search == PREFETCH) bp->b_dev = NO_DEV;
        else
-       if (only_search == NORMAL) rw_block(bp, READING);
+       if (only_search == NORMAL) {
+               rw_block(bp, READING);
+       }
   }
   return(bp);                  /* return the newly acquired block */
 }
@@ -175,8 +178,9 @@ int block_type;                     /* INODE_BLOCK, DIRECTORY_BLOCK, or whatever */
    * should be written to the disk immediately to avoid messing up the file
    * system in the event of a crash.
    */
-  if ((block_type & WRITE_IMMED) && bp->b_dirt==DIRTY && bp->b_dev != NO_DEV)
-       rw_block(bp, WRITING);
+  if ((block_type & WRITE_IMMED) && bp->b_dirt==DIRTY && bp->b_dev != NO_DEV) {
+               rw_block(bp, WRITING);
+  } 
 }
 
 
index e8e9141a95d8000378a856e5924b64e14429b645..fd68904fd57ae22583fdd009f592665c1cfe6d0c 100644 (file)
 #define NO_READ            1   /* prevents get_block from doing disk read */
 #define PREFETCH           2   /* tells get_block not to read or mark dev */
 
-#define XPIPE  (-NR_TASKS-1)   /* used in fp_task when susp'd on pipe */
-#define XLOCK  (-NR_TASKS-2)   /* used in fp_task when susp'd on lock */
-#define XPOPEN (-NR_TASKS-3)   /* used in fp_task when susp'd on pipe open */
+#define XPIPE   (-NR_TASKS-1)  /* used in fp_task when susp'd on pipe */
+#define XLOCK   (-NR_TASKS-2)  /* used in fp_task when susp'd on lock */
+#define XPOPEN  (-NR_TASKS-3)  /* used in fp_task when susp'd on pipe open */
+#define XSELECT (-NR_TASKS-4)  /* used in fp_task when susp'd on select */
 
 #define NO_BIT   ((bit_t) 0)   /* returned by alloc_bit() to signal failure */
 
index d6f100304b34d2e6dcce0f1994c1fb7da160ab69..aa810547523aed6ca1af63d6e66630805c7939b6 100644 (file)
@@ -282,14 +282,17 @@ message *mess_ptr;                /* pointer to message for task */
         * trying to send a REVIVE message for an earlier request.
         * Handle it and go try again.
         */
-       if ((r = receive(task_nr, &local_m)) != OK) break;
+       if ((r = receive(task_nr, &local_m)) != OK) {
+               break;
+       }
 
        /* If we're trying to send a cancel message to a task which has just
         * sent a completion reply, ignore the reply and abort the cancel
         * request. The caller will do the revive for the process.
         */
-       if (mess_ptr->m_type == CANCEL && local_m.REP_PROC_NR == proc_nr)
+       if (mess_ptr->m_type == CANCEL && local_m.REP_PROC_NR == proc_nr) {
                return;
+       }
 
        /* Otherwise it should be a REVIVE. */
        if (local_m.m_type != REVIVE) {
@@ -313,7 +316,9 @@ message *mess_ptr;          /* pointer to message for task */
        }
 
        /* Did the process we did the sendrec() for get a result? */
-       if (mess_ptr->REP_PROC_NR == proc_nr) break;
+       if (mess_ptr->REP_PROC_NR == proc_nr) {
+               break;
+       }
 
        /* Otherwise it should be a REVIVE. */
        if (mess_ptr->m_type != REVIVE) {
index 04ea3d41b95af897b5a944f7275a9a475ea58e03..32bcba113ff068b1aef4b5c8835ad7c484d6c59f 100644 (file)
@@ -8,6 +8,15 @@ EXTERN struct filp {
   int filp_count;              /* how many file descriptors share this slot?*/
   struct inode *filp_ino;      /* pointer to the inode */
   off_t filp_pos;              /* file position */
+
+  /* the following fields are for select() and are owned by the generic
+   * select() code (i.e., fd-type-specific select() code can't touch these).
+   */
+  int filp_selectors;          /* select()ing processes blocking on this fd */
+  int filp_select_ops;         /* interested in these SEL_* operations */
+
+  /* following are for fd-type-specific select() */
+  int filp_pipe_select_ops;
 } filp[NR_FILPS];
 
 #define FILP_CLOSED    0       /* filp_mode: associated device closed */
index b0dce3d645fed6b6589349763c99b70bc9fcedc4..739fdd2ed7efbb93b19fe4946bb7b714f44e4145 100644 (file)
@@ -47,6 +47,9 @@ struct filp **fpt;            /* place to return filp slot */
        if (f->filp_count == 0) {
                f->filp_mode = bits;
                f->filp_pos = 0L;
+               f->filp_selectors = 0;
+               f->filp_select_ops = 0;
+               f->filp_pipe_select_ops = 0;
                f->filp_flags = 0;
                *fpt = f;
                return(OK);
diff --git a/servers/fs/fs_timers.h b/servers/fs/fs_timers.h
new file mode 100644 (file)
index 0000000..b6c53eb
--- /dev/null
@@ -0,0 +1,3 @@
+
+#include <timers.h>
+
index 2832bdf73e5fb0a921dcafcc98c741eb611b6463..12eb70baad82d70d54e113131ad4470e6150a1c3 100644 (file)
@@ -64,22 +64,29 @@ PUBLIC void main()
         if (call_nr == HARD_STOP) { 
                do_sync();
                sys_exit(0);            /* never returns */
-        }
-
-       /* Call the internal function that does the work. */
-       if (call_nr < 0 || call_nr >= NCALLS) { 
-               error = ENOSYS;
-               printf("FS, warning illegal %d system call by %d\n", call_nr, who);
-       } else {
-               error = (*call_vec[call_nr])();
-       }
+        } else if(call_nr == SYN_ALARM) {
+               /* Not a user request; system has expired one of our timers,
+                * currently only in use for select(). Check it.
+                */
+               fs_expire_timers(m_in.NOTIFY_ARG);
+        } else if(call_nr == DEV_SELECTED) {
+               /* device notify()s us of fd that has become usable */
+               select_notified(&m_in);
+        } else {
+               /* Call the internal function that does the work. */
+               if (call_nr < 0 || call_nr >= NCALLS) { 
+                       error = ENOSYS;
+                       printf("FS, warning illegal %d system call by %d\n", call_nr, who);
+               } else {
+                       error = (*call_vec[call_nr])();
+               }
 
-       /* Copy the results back to the user and send reply. */
-       if (error != SUSPEND) { reply(who, error); }
-       if (rdahed_inode != NIL_INODE) {
-               read_ahead(); /* do block read ahead */
+               /* Copy the results back to the user and send reply. */
+               if (error != SUSPEND) { reply(who, error); }
+               if (rdahed_inode != NIL_INODE) {
+                       read_ahead(); /* do block read ahead */
+               }
        }
-
   }
 }
 
@@ -158,7 +165,7 @@ int result;                 /* result of the call (usually OK or error #) */
   int s;
   m_out.reply_type = result;
   s = send(whom, &m_out);
-  if (s != OK) printf("FS: couldn't send reply: %d\n", s);
+  if (s != OK) printf("FS: couldn't send reply %d: %d\n", result, s);
 }
 
 
index 678676ee034f7e0a3dd1ae238fcc23c4b89e3c27..6d03d60b835e358dcd2ae259c06b71bb93070168 100644 (file)
 #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                                      *
@@ -82,13 +84,14 @@ PUBLIC int 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
@@ -106,11 +109,13 @@ int *canwrite;                    /* return: number of bytes we can write */
                        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);
        }
@@ -118,7 +123,8 @@ int *canwrite;                      /* return: number of bytes we can write */
        /* 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);
        }
 
@@ -128,7 +134,8 @@ int *canwrite;                      /* return: number of bytes we can write */
                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);
@@ -143,12 +150,14 @@ int *canwrite;                    /* return: number of bytes we can write */
                                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;
@@ -197,6 +206,25 @@ int count;                 /* max number of processes to release */
  */
 
   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++) {
@@ -215,9 +243,9 @@ int count;                  /* max number of processes to release */
 /*===========================================================================*
  *                             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.
@@ -232,8 +260,8 @@ int bytes;                  /* if hanging on task, how many bytes read */
 
   /* 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) {
@@ -244,10 +272,12 @@ int bytes;                        /* if hanging on task, how many bytes read */
        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 */
        }
   }
 }
@@ -282,6 +312,10 @@ PUBLIC int do_unpause()
        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;
 
@@ -305,3 +339,46 @@ PUBLIC int do_unpause()
   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;
+}
+
index 4dfec6e1fbcab76d31a02faf9b5b8cb99f3acfa0..f7e10ba249954432eb99a18820682f3aa042d4a7 100644 (file)
@@ -1,5 +1,7 @@
 /* Function prototypes. */
 
+#include "timers.h"
+
 /* Structs used in prototypes must be declared as such first. */
 struct buf;
 struct filp;           
@@ -113,10 +115,13 @@ _PROTOTYPE( struct inode *last_dir, (char *path, char string [NAME_MAX]));
 _PROTOTYPE( int do_pipe, (void)                                                );
 _PROTOTYPE( int do_unpause, (void)                                     );
 _PROTOTYPE( int pipe_check, (struct inode *rip, int rw_flag,
-                       int oflags, int bytes, off_t position, int *canwrite));
+                       int oflags, int bytes, off_t position, int *canwrite, int notouch));
 _PROTOTYPE( void release, (struct inode *ip, int call_nr, int count)   );
 _PROTOTYPE( void revive, (int proc_nr, int bytes)                      );
 _PROTOTYPE( void suspend, (int task)                                   );
+_PROTOTYPE( int select_request_pipe, (struct filp *f, int *ops, int bl)                        );
+_PROTOTYPE( int select_cancel_pipe, (struct filp *f)                   );
+_PROTOTYPE( int select_match_pipe, (struct filp *f)                    );
 
 /* protect.c */
 _PROTOTYPE( int do_access, (void)                                      );
@@ -174,4 +179,12 @@ _PROTOTYPE( void zero_block, (struct buf *bp)                              );
 
 /* select.c */
 _PROTOTYPE( int do_select, (void)                                      );
-
+_PROTOTYPE( int select_callback, (struct filp *, int ops)              );
+_PROTOTYPE( void select_forget, (int fproc)                            );
+_PROTOTYPE( void select_timeout_check, (timer_t *)                     );
+_PROTOTYPE( int select_notified, (message *)                           );
+
+/* timers.c */
+_PROTOTYPE( void fs_set_timer, (timer_t *tp, int delta, tmr_func_t watchdog, int arg));
+_PROTOTYPE( void fs_expire_timers, (clock_t now));
+_PROTOTYPE( void fs_cancel_timer, (timer_t *tp));
index e867405d0905aa75e27ae1806e17700bc1a6a168..9792cd4b07cb008ce9cea5644af2c68980d369d4 100644 (file)
 #include "param.h"
 #include "super.h"
 
+#define VVIRCOPY 0
+
+#if VVIRCOPY
+#define NOVVIRCOPY     0
+#else
+#define NOVVIRCOPY     1
+#endif
 
 FORWARD _PROTOTYPE( int rw_chunk, (struct inode *rip, off_t position,
                        unsigned off, int chunk, unsigned left, int rw_flag,
@@ -79,8 +86,10 @@ int rw_flag;                 /* READING or WRITING */
    * it means something has gone wrong we can't repair now.
    */
   if(copy_queue_used != 0) {
-       panic(__FILE__,"copy queue size nonzero when entering read_write().",
-               copy_queue_used);
+       panic(__FILE__,"start - copy queue size nonzero", copy_queue_used);
+  }
+  if(bufs_in_use < 0) {
+       panic(__FILE__,"start - bufs_in_use negative", bufs_in_use);
   }
 
   /* MM loads segments by putting funny things in upper 10 bits of 'fd'. */
@@ -167,8 +176,9 @@ int rw_flag;                        /* READING or WRITING */
 
        /* Pipes are a little different.  Check. */
        if (rip->i_pipe == I_PIPE) {
-              r = pipe_check(rip,rw_flag,oflags,
-                       m_in.nbytes,position,&partial_cnt);
+               struct filp *other_end;
+              r = pipe_check(rip, rw_flag, oflags,
+                       m_in.nbytes, position, &partial_cnt, 0);
               if (r <= 0) return(r);
        }
 
@@ -210,7 +220,7 @@ int rw_flag;                        /* READING or WRITING */
        }
   }
 
-#if 0
+#if VVIRCOPY
   /* do copying to/from user space */
   r2 = rw_chunk_finish(&completed);
 #endif
@@ -221,11 +231,14 @@ int rw_flag;                      /* READING or WRITING */
                if (position > f_size) rip->i_size = position;
        }
   } else {
-       if (rip->i_pipe == I_PIPE && position >= rip->i_size) {
-               /* Reset pipe pointers. */
-               rip->i_size = 0;        /* no data left */
-               position = 0;           /* reset reader(s) */
-               if ( (wf = find_filp(rip, W_BIT)) != NIL_FILP) wf->filp_pos =0;
+       if (rip->i_pipe == I_PIPE) {
+               if( position >= rip->i_size) {
+                       /* Reset pipe pointers. */
+                       rip->i_size = 0;        /* no data left */
+                       position = 0;           /* reset reader(s) */
+                       wf = find_filp(rip, W_BIT);
+                       if (wf != NIL_FILP) wf->filp_pos = 0;
+               }
        }
   }
   f->filp_pos = position;
@@ -262,6 +275,12 @@ int rw_flag;                       /* READING or WRITING */
        fp->fp_cum_io_partial = 0;
        return(cum_io);
   }
+  if(copy_queue_used != 0) {
+       panic(__FILE__,"end - copy queue size nonzero", copy_queue_used);
+  }
+  if(bufs_in_use < 0) {
+       panic(__FILE__,"end - bufs_in_use negative", bufs_in_use);
+  }
   return(r);
 }
 
@@ -332,7 +351,7 @@ int *completed;                     /* number of bytes copied */
        zero_block(bp);
   }
 
-#if 1
+#if NOVVIRCOPY
   if (rw_flag == READING) {
        /* Copy a chunk from the block buffer to user space. */
        r = sys_vircopy(FS_PROC_NR, D, (phys_bytes) (bp->b_data+off),
@@ -416,7 +435,6 @@ PRIVATE int rw_chunk_finish(int *completed)
                user->segment = copy_queue[i].user_seg;
                user->offset = copy_queue[i].user_offset;
                total += copy_queue[i].chunk;
-               put_block(copy_queue[i].bp, copy_queue[i].blocktype);
        }
 
        m.m_type = SYS_VIRVCOPY;
@@ -427,6 +445,10 @@ PRIVATE int rw_chunk_finish(int *completed)
                panic(__FILE__,"rw_chunk_finish: virvcopy sendrec failed", r);
        }
 
+       for(i = 0; i < copy_queue_used; i++) {
+               put_block(copy_queue[i].bp, copy_queue[i].blocktype);
+       }
+
        *completed = total;
 
        copy_queue_used = 0;
index 5bf065d945a8d1cd9b2dccc208df5c840353acff..156ef494f5d09080358715e481f2bb41b0c059b7 100644 (file)
 /* 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;
+}
+
diff --git a/servers/fs/select.h b/servers/fs/select.h
new file mode 100644 (file)
index 0000000..6fef611
--- /dev/null
@@ -0,0 +1,10 @@
+
+#ifndef _FS_SELECT_H
+#define _FS_SELECT_H 1
+
+/* return codes for select_request_* and select_cancel_* */
+#define SEL_OK         0       /* ready */
+#define SEL_ERROR      1       /* failed */
+
+#endif
+