444 lines
9.7 KiB
C
444 lines
9.7 KiB
C
/* @(#)str_syscalls.c 1.1 94/10/31 SMI; from S5R3 os/sys2.c 10.19 */
|
|
|
|
/* Copyright (c) 1984 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
/*
|
|
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/user.h>
|
|
#include <sys/file.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/debug.h>
|
|
|
|
/*
|
|
* getmsg system call
|
|
*/
|
|
getmsg()
|
|
{
|
|
msgio(FREAD);
|
|
}
|
|
|
|
/*
|
|
* putmsg system call
|
|
*/
|
|
putmsg()
|
|
{
|
|
msgio(FWRITE);
|
|
}
|
|
|
|
/*
|
|
* common code for getmsg and putmsg calls:
|
|
* check permissions, copy in args, preliminary setup,
|
|
* and switch to appropriate stream routine
|
|
*/
|
|
msgio(mode)
|
|
register mode;
|
|
{
|
|
register struct file *fp;
|
|
register struct a {
|
|
int fdes;
|
|
struct strbuf *ctl;
|
|
struct strbuf *data;
|
|
union {
|
|
int flags;
|
|
int *flagsp;
|
|
} flags_un;
|
|
} *uap;
|
|
struct strbuf msgctl, msgdata;
|
|
register struct vnode *vp;
|
|
|
|
uap = (struct a *)u.u_ap;
|
|
GETF(fp, uap->fdes);
|
|
if (!(fp->f_flag&mode)) {
|
|
u.u_error = EBADF;
|
|
return;
|
|
}
|
|
if (fp->f_type != DTYPE_VNODE) {
|
|
u.u_error = ENOSTR;
|
|
return;
|
|
}
|
|
vp = (struct vnode *)fp->f_data;
|
|
if ((vp->v_type != VCHR) || (vp->v_stream == NULL)) {
|
|
u.u_error = ENOSTR;
|
|
return;
|
|
}
|
|
if (uap->ctl) {
|
|
u.u_error = copyin((caddr_t)uap->ctl, (caddr_t)&msgctl,
|
|
sizeof (struct strbuf));
|
|
if (u.u_error)
|
|
return;
|
|
}
|
|
if (uap->data) {
|
|
u.u_error = copyin((caddr_t)uap->data, (caddr_t)&msgdata,
|
|
sizeof (struct strbuf));
|
|
if (u.u_error)
|
|
return;
|
|
}
|
|
if (mode == FREAD) {
|
|
if (!uap->ctl)
|
|
msgctl.maxlen = -1;
|
|
if (!uap->data)
|
|
msgdata.maxlen = -1;
|
|
u.u_r.r_val1 = strgetmsg(vp, &msgctl, &msgdata,
|
|
(caddr_t)uap->flags_un.flagsp, fp->f_flag);
|
|
if (u.u_error)
|
|
return;
|
|
if (uap->ctl) {
|
|
u.u_error = copyout((caddr_t)&msgctl,
|
|
(caddr_t)uap->ctl, sizeof (struct strbuf));
|
|
if (u.u_error)
|
|
return;
|
|
}
|
|
if (uap->data) {
|
|
u.u_error = copyout((caddr_t)&msgdata,
|
|
(caddr_t)uap->data, sizeof (struct strbuf));
|
|
if (u.u_error)
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
/*
|
|
* FWRITE case
|
|
*/
|
|
if (!uap->ctl)
|
|
msgctl.len = -1;
|
|
if (!uap->data)
|
|
msgdata.len = -1;
|
|
strputmsg(vp, &msgctl, &msgdata, uap->flags_un.flags, fp->f_flag);
|
|
}
|
|
|
|
/*
|
|
* Poll system call
|
|
*/
|
|
poll()
|
|
{
|
|
register struct uap {
|
|
struct pollfd *fdp;
|
|
unsigned long nfds;
|
|
long timo;
|
|
} *uap = (struct uap *)u.u_ap;
|
|
struct pollfd pollfd[NPOLLFILE];
|
|
register struct pollfd *pfd;
|
|
caddr_t fdp;
|
|
register int fdcnt = 0;
|
|
register int i, j, s;
|
|
struct timeval atv;
|
|
register struct file *fp;
|
|
int polltime();
|
|
struct strevent *timeproc;
|
|
int size;
|
|
int mark;
|
|
register struct vnode *vp;
|
|
char newu_error;
|
|
|
|
if (uap->nfds > NOFILE) {
|
|
u.u_error = EINVAL;
|
|
return;
|
|
}
|
|
if (uap->timo > 0) {
|
|
/*
|
|
* Convert milliseconds to wait to an
|
|
* absolute time in timeval format.
|
|
*/
|
|
atv.tv_sec = uap->timo / 1000;
|
|
atv.tv_usec = (uap->timo % 1000) * 1000;
|
|
if (itimerfix(&atv)) {
|
|
u.u_error = EINVAL;
|
|
return;
|
|
}
|
|
s = splclock();
|
|
timevaladd(&atv, &time);
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* retry scan of fds until an event is found or until the
|
|
* timeout is reached.
|
|
*/
|
|
retry:
|
|
|
|
/*
|
|
* Polling the fds is a relatively long process. Set up the SPOLL
|
|
* flag so that we can see if something happened
|
|
* to an fd after we checked it but before we go to sleep.
|
|
*/
|
|
u.u_procp->p_flag |= SPOLL;
|
|
|
|
/*
|
|
* Check fd's for specified events.
|
|
* Read in pollfd records in blocks of NPOLLFILE. Test each fd in the
|
|
* block and store the result of the test in the event field of the
|
|
* in-core record. After a block of fds is finished, write the result
|
|
* out to the user. Note that if no event is found, the whole
|
|
* procedure will be repeated after awakenening from the sleep
|
|
* (subject to timeout).
|
|
*/
|
|
|
|
mark = uap->nfds;
|
|
size = 0;
|
|
fdp = (caddr_t)uap->fdp;
|
|
for (i = 0, pfd = pollfd; i < uap->nfds; i++, pfd++) {
|
|
j = i % NPOLLFILE;
|
|
/*
|
|
* If we have looped back around to the base of pollfd,
|
|
* write out the results of the strpoll calls kept in pollfd
|
|
* to the user fdp. Read in the next batch of fds to check.
|
|
*/
|
|
if (!j) {
|
|
if (i > 0) {
|
|
ASSERT(size == NPOLLFILE *
|
|
sizeof (struct pollfd));
|
|
u.u_error = copyout((caddr_t)pollfd, fdp,
|
|
(u_int)size);
|
|
if (u.u_error)
|
|
return;
|
|
fdp += size;
|
|
}
|
|
size = MIN(uap->nfds - i, NPOLLFILE) *
|
|
sizeof (struct pollfd);
|
|
u.u_error =
|
|
copyin((caddr_t)fdp, (caddr_t)pollfd,
|
|
(u_int)size);
|
|
if (u.u_error)
|
|
return;
|
|
pfd = pollfd;
|
|
}
|
|
|
|
if (pfd->fd < 0)
|
|
pfd->revents = 0;
|
|
else if ((fp = getf(pfd->fd)) == NULL){
|
|
u.u_error = 0;
|
|
pfd->revents = POLLNVAL;
|
|
}
|
|
else if ((fp->f_type != DTYPE_VNODE) ||
|
|
!(vp = (struct vnode *)fp->f_data) ||
|
|
(vp->v_type != VCHR) ||
|
|
!vp->v_stream) {
|
|
pfd->revents = pollsel(fp, pfd->events);
|
|
if (u.u_error) {
|
|
if (fdcnt == 0)
|
|
mark = i;
|
|
goto pollout;
|
|
}
|
|
} else {
|
|
pfd->revents = strpoll(vp->v_stream,
|
|
pfd->events, fdcnt);
|
|
if (u.u_error) {
|
|
if (fdcnt == 0)
|
|
mark = i;
|
|
goto pollout;
|
|
}
|
|
}
|
|
if (pfd->revents && fdcnt++ == 0)
|
|
mark = i;
|
|
}
|
|
|
|
/*
|
|
* Poll of fds completed.
|
|
* Copy out the last batch of events. If the poll was successful,
|
|
* return fdcnt to user.
|
|
*/
|
|
u.u_r.r_val1 = fdcnt;
|
|
u.u_error = copyout((caddr_t)pollfd, fdp, (u_int)size);
|
|
if (u.u_error)
|
|
return;
|
|
if (fdcnt)
|
|
goto pollout;
|
|
|
|
/*
|
|
* If you get here, the poll of fds was unsuccessful.
|
|
* First make sure your timeout hasn't been reached.
|
|
* If not then sleep and wait until some fd becomes
|
|
* readable, writeable, or gets an exception.
|
|
*/
|
|
if (uap->timo == 0)
|
|
goto pollout;
|
|
s = splclock();
|
|
/* this should be timercmp(&time, &atv, >=) */
|
|
if (uap->timo > 0 && (time.tv_sec > atv.tv_sec ||
|
|
time.tv_sec == atv.tv_sec && time.tv_usec >= atv.tv_usec)) {
|
|
(void) splx(s);
|
|
goto pollout;
|
|
}
|
|
|
|
/*
|
|
* If anything has happened on an fd since it was checked, it will
|
|
* have turned off SPOLL. Check this and rescan if so.
|
|
*/
|
|
if (!(u.u_procp->p_flag & SPOLL)) {
|
|
(void) splx(s);
|
|
goto retry;
|
|
}
|
|
u.u_procp->p_flag &= ~SPOLL;
|
|
|
|
if (!(timeproc = sealloc(SE_SLEEP))) {
|
|
(void) splx(s);
|
|
u.u_error = EAGAIN;
|
|
goto pollout;
|
|
}
|
|
timeproc->se_procp = u.u_procp;
|
|
if (uap->timo > 0)
|
|
timeout(polltime, (caddr_t)timeproc, hzto(&atv));
|
|
|
|
/*
|
|
* The sleep will be awakened either by this poll's timeout (which
|
|
* will have nulled timeproc), or by the strwakepoll function called
|
|
* from a stream head.
|
|
*/
|
|
if (sleep((caddr_t)&pollwait, (PZERO+1)|PCATCH)) {
|
|
if (uap->timo > 0)
|
|
untimeout(polltime, (caddr_t)timeproc);
|
|
(void) splx(s);
|
|
u.u_error = EINTR;
|
|
sefree(timeproc);
|
|
goto pollout;
|
|
}
|
|
(void) splx(s);
|
|
if (uap->timo > 0)
|
|
untimeout(polltime, (caddr_t)timeproc);
|
|
|
|
/*
|
|
* If timeproc is not NULL, you know you were woken because
|
|
* write queue emptied, read queue got data, or an exception
|
|
* condition occurred. If so go back up and poll fds again.
|
|
* Otherwise, you've timed out so you will fall thru and return.
|
|
*/
|
|
if (timeproc->se_procp) {
|
|
sefree(timeproc);
|
|
goto retry;
|
|
}
|
|
sefree(timeproc);
|
|
|
|
pollout:
|
|
|
|
/*
|
|
* Poll general cleanup code. Go back to all of the streams
|
|
* before the mark and reset the wakeup mechanisms that were
|
|
* set up during the poll.
|
|
*/
|
|
u.u_procp->p_flag &= ~SPOLL;
|
|
fdp = (caddr_t)uap->fdp;
|
|
for (i = 0, pfd = pollfd; i < mark; i++, pfd++) {
|
|
j = i % NPOLLFILE;
|
|
/*
|
|
* Read in next block of pollfds. If the total number of
|
|
* pollfds is less than NPOLLFILE, don't bother because the
|
|
* pollfds of interest are still in the pollfd[] array.
|
|
*/
|
|
if (!j && (uap->nfds > NPOLLFILE)) {
|
|
size = MIN(uap->nfds - i, NPOLLFILE) *
|
|
sizeof (struct pollfd);
|
|
newu_error =
|
|
copyin((caddr_t)fdp, (caddr_t)pollfd,
|
|
(u_int)size);
|
|
if (newu_error) {
|
|
u.u_error = newu_error;
|
|
return;
|
|
}
|
|
fdp += size;
|
|
pfd = pollfd;
|
|
}
|
|
|
|
if (pfd->fd < 0)
|
|
continue;
|
|
if ((fp = getf(pfd->fd)) == NULL)
|
|
continue;
|
|
if (fp->f_type != DTYPE_VNODE)
|
|
continue;
|
|
vp = (struct vnode *)fp->f_data;
|
|
if (vp->v_type != VCHR || vp->v_stream == NULL)
|
|
continue;
|
|
pollreset(vp->v_stream);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Removes all event cells that refer to the current process in the
|
|
* given stream's poll list.
|
|
*/
|
|
pollreset(stp)
|
|
register struct stdata *stp;
|
|
{
|
|
register struct strevent *psep, *sep, *tmp;
|
|
register int s;
|
|
|
|
s = splstr();
|
|
sep = stp->sd_pollist;
|
|
psep = NULL;
|
|
while (sep) {
|
|
tmp = sep->se_next;
|
|
if (sep->se_procp == u.u_procp) {
|
|
if (psep)
|
|
psep->se_next = tmp;
|
|
else
|
|
stp->sd_pollist = tmp;
|
|
sefree(sep);
|
|
}
|
|
sep = tmp;
|
|
}
|
|
/*
|
|
* Recalculate pollflags
|
|
*/
|
|
stp->sd_pollflags = 0;
|
|
for (sep = stp->sd_pollist; sep; sep = sep->se_next)
|
|
stp->sd_pollflags |= sep->se_events;
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* This function is placed in the callout table to time out a process
|
|
* waiting on poll. If the poll completes, this function is removed
|
|
* from the table. Its argument is a pointer to a variable which holds
|
|
* the process table pointer for the process to be awakened. This
|
|
* variable is nulled to indicate that polltime ran.
|
|
*/
|
|
polltime(timeproc)
|
|
struct strevent *timeproc;
|
|
{
|
|
register struct proc *p = timeproc->se_procp;
|
|
int s;
|
|
|
|
s = spl6();
|
|
if ((p->p_wchan == (caddr_t)&pollwait) && (p->p_stat == SSLEEP)) {
|
|
setrun(p);
|
|
timeproc->se_procp = NULL;
|
|
}
|
|
splx(s);
|
|
|
|
}
|
|
|
|
int
|
|
pollsel(fp, events)
|
|
struct file *fp;
|
|
int events;
|
|
{
|
|
int revents = 0;
|
|
|
|
if ((events & POLLIN) &&
|
|
(*fp->f_ops->fo_select)(fp, FREAD)) {
|
|
revents |= POLLIN;
|
|
}
|
|
if ((events & POLLOUT) &&
|
|
(*fp->f_ops->fo_select)(fp, FWRITE)) {
|
|
revents |= POLLOUT;
|
|
}
|
|
if ((events & POLLPRI) &&
|
|
(*fp->f_ops->fo_select)(fp, 0)) {
|
|
revents |= POLLPRI;
|
|
}
|
|
return (revents);
|
|
}
|