Mercurial > nand-ecoscentric
diff packages/net/tcpip/current/src/sys/kern/sys_generic.c @ 97:ced4577552cd ecos-sw-2000-06-06
Merge from eCos master repository on 2000-06-06-08:44:00-BST
| author | jlarmour |
|---|---|
| date | Tue, 06 Jun 2000 08:39:36 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/tcpip/current/src/sys/kern/sys_generic.c @@ -0,0 +1,1048 @@ +//========================================================================== +// +// sys/kern/sys_generic.c +// +// +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD or other sources, +// and are covered by the appropriate copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas +// Date: 2000-01-10 +// Purpose: +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +/* $OpenBSD: sys_generic.c,v 1.22 1999/11/29 22:02:14 deraadt Exp $ */ +/* $NetBSD: sys_generic.c,v 1.24 1996/03/29 00:25:32 cgd Exp $ */ + +/* + * Copyright (c) 1996 Theo de Raadt + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sys_generic.c 8.5 (Berkeley) 1/21/94 + */ + +#include <sys/param.h> +#ifndef __ECOS +#include <sys/systm.h> +#include <sys/filedesc.h> +#endif // __ECOS +#include <sys/ioctl.h> +#ifdef __ECOS +#include <cyg/io/file.h> +#else // __ECOS +#include <sys/file.h> +#include <sys/proc.h> +#include <sys/resourcevar.h> +#endif // __ECOS +#include <sys/socketvar.h> +#ifndef __ECOS +#include <sys/signalvar.h> +#include <sys/kernel.h> +#include <sys/uio.h> +#include <sys/stat.h> +#endif // __ECOS +#include <sys/malloc.h> +#ifndef __ECOS +#include <sys/poll.h> +#endif // __ECOS +#ifdef KTRACE +#include <sys/ktrace.h> +#endif + +#ifndef __ECOS +#include <sys/mount.h> +#endif // __ECOS +#include <sys/syscallargs.h> + +#ifndef __ECOS +int selscan __P((struct proc *, fd_set *, fd_set *, int, register_t *)); +int seltrue __P((dev_t, int, struct proc *)); +void pollscan __P((struct proc *, struct pollfd *, int, register_t *)); +#endif // __ECOS + +/* + * Read system call. + */ +#ifdef __ECOS +int +sys_read(struct sys_read_args *uap, register_t *retval) +#else +/* ARGSUSED */ +int +sys_read(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +#endif +{ +#ifndef __ECOS + register struct sys_read_args /* { + syscallarg(int) fd; + syscallarg(void *) buf; + syscallarg(size_t) nbyte; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; +#endif + struct file *fp; + struct uio auio; + struct iovec aiov; + long cnt, error = 0; +#ifdef KTRACE + struct iovec ktriov; +#endif + +#ifdef __ECOS + if (getfp((u_int)SCARG(uap, fd), &fp) || +#else + if (((u_int)SCARG(uap, fd)) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || +#endif + (fp->f_flag & FREAD) == 0) + return (EBADF); + /* Don't allow nbyte to be larger than max return val */ + if (SCARG(uap, nbyte) > SSIZE_MAX) + return(EINVAL); + aiov.iov_base = (caddr_t)SCARG(uap, buf); + aiov.iov_len = SCARG(uap, nbyte); + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_resid = SCARG(uap, nbyte); + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; +#ifndef __ECOS + auio.uio_procp = p; +#endif +#ifdef KTRACE + /* + * if tracing, save a copy of iovec + */ + if (KTRPOINT(p, KTR_GENIO)) + ktriov = aiov; +#endif + cnt = SCARG(uap, nbyte); +#ifdef __ECOS + error = (*fp->f_ops->fo_read)(fp, &auio); +#else + error = (*fp->f_ops->fo_read)(fp, &auio, fp->f_cred); +#endif + if (error) +#ifdef __ECOS + if (auio.uio_resid != cnt && ( +#else + if (auio.uio_resid != cnt && (error == ERESTART || +#endif + error == EINTR || error == EWOULDBLOCK)) + error = 0; + cnt -= auio.uio_resid; +#ifdef KTRACE + if (KTRPOINT(p, KTR_GENIO) && error == 0) + ktrgenio(p->p_tracep, SCARG(uap, fd), UIO_READ, &ktriov, + cnt, error); +#endif + *retval = cnt; + return (error); +} + + +#ifndef __ECOS +/* + * Scatter read system call. + */ +int +sys_readv(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_readv_args /* { + syscallarg(int) fd; + syscallarg(struct iovec *) iovp; + syscallarg(int) iovcnt; + } */ *uap = v; + register struct file *fp; + register struct filedesc *fdp = p->p_fd; + struct uio auio; + register struct iovec *iov; + struct iovec *needfree; + struct iovec aiov[UIO_SMALLIOV]; + long i, cnt, error = 0; + u_int iovlen; +#ifdef KTRACE + struct iovec *ktriov = NULL; +#endif + + if (((u_int)SCARG(uap, fd)) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || + (fp->f_flag & FREAD) == 0) + return (EBADF); + if (SCARG(uap, iovcnt) <= 0) + return (EINVAL); + /* note: can't use iovlen until iovcnt is validated */ + iovlen = SCARG(uap, iovcnt) * sizeof (struct iovec); + if (SCARG(uap, iovcnt) > UIO_SMALLIOV) { + if (SCARG(uap, iovcnt) > IOV_MAX) + return (EINVAL); + MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); + needfree = iov; + } else { + iov = aiov; + needfree = NULL; + } + auio.uio_iov = iov; + auio.uio_iovcnt = SCARG(uap, iovcnt); + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + error = copyin((caddr_t)SCARG(uap, iovp), (caddr_t)iov, iovlen); + if (error) + goto done; + auio.uio_resid = 0; + for (i = 0; i < SCARG(uap, iovcnt); i++, iov++) { + /* Don't allow sum > SSIZE_MAX */ + if (iov->iov_len > SSIZE_MAX || + (auio.uio_resid += iov->iov_len) > SSIZE_MAX) { + error = EINVAL; + goto done; + } + } +#ifdef KTRACE + /* + * if tracing, save a copy of iovec + */ + if (KTRPOINT(p, KTR_GENIO)) { + MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK); + bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen); + } +#endif + cnt = auio.uio_resid; + error = (*fp->f_ops->fo_read)(fp, &auio, fp->f_cred); + if (error) + if (auio.uio_resid != cnt && (error == ERESTART || + error == EINTR || error == EWOULDBLOCK)) + error = 0; + cnt -= auio.uio_resid; +#ifdef KTRACE + if (ktriov != NULL) { + if (error == 0) + ktrgenio(p->p_tracep, SCARG(uap, fd), UIO_READ, ktriov, + cnt, error); + FREE(ktriov, M_TEMP); + } +#endif + *retval = cnt; +done: + if (needfree) + FREE(needfree, M_IOV); + return (error); +} +#endif + +/* + * Write system call + */ +#ifdef __ECOS +int +sys_write(struct sys_write_args *uap, register_t *retval) +#else +int +sys_write(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +#endif +{ +#ifndef __ECOS + register struct sys_write_args /* { + syscallarg(int) fd; + syscallarg(void *) buf; + syscallarg(size_t) nbyte; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; +#endif + struct file *fp; + struct uio auio; + struct iovec aiov; + long cnt, error = 0; +#ifdef KTRACE + struct iovec ktriov; +#endif + +#ifdef __ECOS + if (getfp((u_int)SCARG(uap, fd), &fp) || +#else + if (((u_int)SCARG(uap, fd)) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || +#endif + (fp->f_flag & FWRITE) == 0) + return (EBADF); + /* Don't allow nbyte to be larger than max return val */ + if (SCARG(uap, nbyte) > SSIZE_MAX) + return(EINVAL); + aiov.iov_base = (caddr_t)SCARG(uap, buf); + aiov.iov_len = SCARG(uap, nbyte); + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_resid = SCARG(uap, nbyte); + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_USERSPACE; +#ifndef __ECOS + auio.uio_procp = p; +#endif +#ifdef KTRACE + /* + * if tracing, save a copy of iovec + */ + if (KTRPOINT(p, KTR_GENIO)) + ktriov = aiov; +#endif + cnt = SCARG(uap, nbyte); +#ifdef __ECOS + error = (*fp->f_ops->fo_write)(fp, &auio); +#else + error = (*fp->f_ops->fo_write)(fp, &auio, fp->f_cred); +#endif + if (error) { +#ifdef __ECOS + if (auio.uio_resid != cnt && + (error == EINTR || error == EWOULDBLOCK)) + error = 0; +#else + if (auio.uio_resid != cnt && (error == ERESTART || + error == EINTR || error == EWOULDBLOCK)) + error = 0; + if (error == EPIPE) + psignal(p, SIGPIPE); +#endif + } + cnt -= auio.uio_resid; +#ifdef KTRACE + if (KTRPOINT(p, KTR_GENIO) && error == 0) + ktrgenio(p->p_tracep, SCARG(uap, fd), UIO_WRITE, + &ktriov, cnt, error); +#endif + *retval = cnt; + return (error); +} + +#ifndef __ECOS +/* + * Gather write system call + */ +int +sys_writev(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_writev_args /* { + syscallarg(int) fd; + syscallarg(struct iovec *) iovp; + syscallarg(int) iovcnt; + } */ *uap = v; + register struct file *fp; + register struct filedesc *fdp = p->p_fd; + struct uio auio; + register struct iovec *iov; + struct iovec *needfree; + struct iovec aiov[UIO_SMALLIOV]; + long i, cnt, error = 0; + u_int iovlen; +#ifdef KTRACE + struct iovec *ktriov = NULL; +#endif + + if (((u_int)SCARG(uap, fd)) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || + (fp->f_flag & FWRITE) == 0) + return (EBADF); + if (SCARG(uap, iovcnt) <= 0) + return (EINVAL); + /* note: can't use iovlen until iovcnt is validated */ + iovlen = SCARG(uap, iovcnt) * sizeof (struct iovec); + if (SCARG(uap, iovcnt) > UIO_SMALLIOV) { + if (SCARG(uap, iovcnt) > IOV_MAX) + return (EINVAL); + MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); + needfree = iov; + } else { + iov = aiov; + needfree = NULL; + } + auio.uio_iov = iov; + auio.uio_iovcnt = SCARG(uap, iovcnt); + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + error = copyin((caddr_t)SCARG(uap, iovp), (caddr_t)iov, iovlen); + if (error) + goto done; + auio.uio_resid = 0; + for (i = 0; i < SCARG(uap, iovcnt); i++, iov++) { + /* Don't allow sum > SSIZE_MAX */ + if (iov->iov_len > SSIZE_MAX || + (auio.uio_resid += iov->iov_len) > SSIZE_MAX) { + error = EINVAL; + goto done; + } + } +#ifdef KTRACE + /* + * if tracing, save a copy of iovec + */ + if (KTRPOINT(p, KTR_GENIO)) { + MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK); + bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen); + } +#endif + cnt = auio.uio_resid; + error = (*fp->f_ops->fo_write)(fp, &auio, fp->f_cred); + if (error) { + if (auio.uio_resid != cnt && (error == ERESTART || + error == EINTR || error == EWOULDBLOCK)) + error = 0; + if (error == EPIPE) + psignal(p, SIGPIPE); + } + cnt -= auio.uio_resid; +#ifdef KTRACE + if (ktriov != NULL) { + if (error == 0) + ktrgenio(p->p_tracep, SCARG(uap, fd), UIO_WRITE, + ktriov, cnt, error); + FREE(ktriov, M_TEMP); + } +#endif + *retval = cnt; +done: + if (needfree) + FREE(needfree, M_IOV); + return (error); +} +#endif + +/* + * Ioctl system call + */ +#ifdef __ECOS +int +sys_ioctl(struct sys_ioctl_args *uap, register_t *retval) +#else +/* ARGSUSED */ +int +sys_ioctl(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +#endif +{ +#ifndef __ECOS + register struct sys_ioctl_args /* { + syscallarg(int) fd; + syscallarg(u_long) com; + syscallarg(caddr_t) data; + } */ *uap = v; + register struct filedesc *fdp; +#endif + int tmp; + struct file *fp; + register u_long com; + register int error; + register u_int size; + caddr_t data, memp; +#define STK_PARAMS 128 + char stkbuf[STK_PARAMS]; + +#ifdef __ECOS + if (getfp(SCARG(uap, fd), &fp)) +#else + fdp = p->p_fd; + if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) +#endif + return (EBADF); + + if ((fp->f_flag & (FREAD | FWRITE)) == 0) + return (EBADF); + +#ifdef __ECOS + com = SCARG(uap, com); +#else + switch (com = SCARG(uap, com)) { + case FIONCLEX: + fdp->fd_ofileflags[SCARG(uap, fd)] &= ~UF_EXCLOSE; + return (0); + case FIOCLEX: + fdp->fd_ofileflags[SCARG(uap, fd)] |= UF_EXCLOSE; + return (0); + } +#endif + + /* + * Interpret high order word to find amount of data to be + * copied to/from the user's address space. + */ + size = IOCPARM_LEN(com); +#ifndef __ECOS + if (size > IOCPARM_MAX) + return (ENOTTY); +#endif + memp = NULL; + if (size > sizeof (stkbuf)) { + memp = (caddr_t)malloc((u_long)size, M_IOCTLOPS, M_WAITOK); + data = memp; + } else + data = stkbuf; + if (com&IOC_IN) { + if (size) { + error = copyin(SCARG(uap, data), data, (u_int)size); + if (error) { + if (memp) + free(memp, M_IOCTLOPS); + return (error); + } + } else + *(caddr_t *)data = SCARG(uap, data); + } else if ((com&IOC_OUT) && size) + /* + * Zero the buffer so the user always + * gets back something deterministic. + */ + bzero(data, size); + else if (com&IOC_VOID) + *(caddr_t *)data = SCARG(uap, data); + + switch (com) { + + case FIONBIO: + if ((tmp = *(int *)data) != 0) + fp->f_flag |= FNONBLOCK; + else + fp->f_flag &= ~FNONBLOCK; +#ifdef __ECOS + error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (CYG_ADDRWORD)&tmp); +#else + error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p); +#endif + break; + + case FIOASYNC: + if ((tmp = *(int *)data) != 0) + fp->f_flag |= FASYNC; + else + fp->f_flag &= ~FASYNC; +#ifdef __ECOS + error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (CYG_ADDRWORD)&tmp); +#else + error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (caddr_t)&tmp, p); +#endif + break; + +#ifndef __ECOS + case FIOSETOWN: + tmp = *(int *)data; + if (fp->f_type == DTYPE_SOCKET) { + struct socket *so = (struct socket *)fp->f_data; + + so->so_pgid = tmp; + so->so_siguid = p->p_cred->p_ruid; + so->so_sigeuid = p->p_ucred->cr_uid; + error = 0; + break; + } + if (tmp <= 0) { + tmp = -tmp; + } else { + struct proc *p1 = pfind(tmp); + if (p1 == 0) { + error = ESRCH; + break; + } + tmp = p1->p_pgrp->pg_id; + } + error = (*fp->f_ops->fo_ioctl) + (fp, TIOCSPGRP, (caddr_t)&tmp, p); + break; + + case FIOGETOWN: + if (fp->f_type == DTYPE_SOCKET) { + error = 0; + *(int *)data = ((struct socket *)fp->f_data)->so_pgid; + break; + } + error = (*fp->f_ops->fo_ioctl)(fp, TIOCGPGRP, data, p); + *(int *)data = -*(int *)data; + break; +#endif + default: +#ifdef __ECOS + error = (*fp->f_ops->fo_ioctl)(fp, com, (CYG_ADDRWORD)data); +#else + error = (*fp->f_ops->fo_ioctl)(fp, com, data, p); +#endif + /* + * Copy any data to user, size was + * already set and checked above. + */ + if (error == 0 && (com&IOC_OUT) && size) + error = copyout(data, SCARG(uap, data), (u_int)size); + break; + } + if (memp) + free(memp, M_IOCTLOPS); + return (error); +} + +#ifndef __ECOS +int selwait, nselcoll; + +/* + * Select system call. + */ +int +sys_select(p, v, retval) + register struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_select_args /* { + syscallarg(int) nd; + syscallarg(fd_set *) in; + syscallarg(fd_set *) ou; + syscallarg(fd_set *) ex; + syscallarg(struct timeval *) tv; + } */ *uap = v; + fd_set bits[6], *pibits[3], *pobits[3]; + struct timeval atv; + int s, ncoll, error = 0, timo; + u_int ni; + + if (SCARG(uap, nd) > p->p_fd->fd_nfiles) { + /* forgiving; slightly wrong */ + SCARG(uap, nd) = p->p_fd->fd_nfiles; + } + ni = howmany(SCARG(uap, nd), NFDBITS) * sizeof(fd_mask); + if (SCARG(uap, nd) > FD_SETSIZE) { + caddr_t mbits; + + if ((mbits = malloc(ni * 6, M_TEMP, M_WAITOK)) == NULL) { + error = EINVAL; + goto cleanup; + } + bzero(mbits, ni * 6); + pibits[0] = (fd_set *)&mbits[ni * 0]; + pibits[1] = (fd_set *)&mbits[ni * 1]; + pibits[2] = (fd_set *)&mbits[ni * 2]; + pobits[0] = (fd_set *)&mbits[ni * 3]; + pobits[1] = (fd_set *)&mbits[ni * 4]; + pobits[2] = (fd_set *)&mbits[ni * 5]; + } else { + bzero((caddr_t)bits, sizeof(bits)); + pibits[0] = &bits[0]; + pibits[1] = &bits[1]; + pibits[2] = &bits[2]; + pobits[0] = &bits[3]; + pobits[1] = &bits[4]; + pobits[2] = &bits[5]; + } + +#define getbits(name, x) \ + if (SCARG(uap, name) && (error = copyin((caddr_t)SCARG(uap, name), \ + (caddr_t)pibits[x], ni))) \ + goto done; + getbits(in, 0); + getbits(ou, 1); + getbits(ex, 2); +#undef getbits + + if (SCARG(uap, tv)) { + error = copyin((caddr_t)SCARG(uap, tv), (caddr_t)&atv, + sizeof (atv)); + if (error) + goto done; + if (itimerfix(&atv)) { + error = EINVAL; + goto done; + } + s = splclock(); + timeradd(&atv, &time, &atv); + timo = hzto(&atv); + /* + * Avoid inadvertently sleeping forever. + */ + if (timo == 0) + timo = 1; + splx(s); + } else + timo = 0; +retry: + ncoll = nselcoll; + p->p_flag |= P_SELECT; + error = selscan(p, pibits[0], pobits[0], SCARG(uap, nd), retval); + if (error || *retval) + goto done; + s = splhigh(); + /* this should be timercmp(&time, &atv, >=) */ + if (SCARG(uap, tv) && (time.tv_sec > atv.tv_sec || + (time.tv_sec == atv.tv_sec && time.tv_usec >= atv.tv_usec))) { + splx(s); + goto done; + } + if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) { + splx(s); + goto retry; + } + p->p_flag &= ~P_SELECT; + error = tsleep((caddr_t)&selwait, PSOCK | PCATCH, "select", timo); + splx(s); + if (error == 0) + goto retry; +done: + p->p_flag &= ~P_SELECT; + /* select is not restarted after signals... */ + if (error == ERESTART) + error = EINTR; + if (error == EWOULDBLOCK) + error = 0; +#define putbits(name, x) \ + if (SCARG(uap, name) && (error2 = copyout((caddr_t)pobits[x], \ + (caddr_t)SCARG(uap, name), ni))) \ + error = error2; + if (error == 0) { + int error2; + + putbits(in, 0); + putbits(ou, 1); + putbits(ex, 2); +#undef putbits + } + +cleanup: + if (pibits[0] != &bits[0]) + free(pibits[0], M_TEMP); + return (error); +} + +int +selscan(p, ibits, obits, nfd, retval) + struct proc *p; + fd_set *ibits, *obits; + int nfd; + register_t *retval; +{ + caddr_t cibits = (caddr_t)ibits, cobits = (caddr_t)obits; + register struct filedesc *fdp = p->p_fd; + register int msk, i, j, fd; + register fd_mask bits; + struct file *fp; + int ni, n = 0; + static int flag[3] = { FREAD, FWRITE, 0 }; + + /* + * if nfd > FD_SETSIZE then the fd_set's contain nfd bits (rounded + * up to the next byte) otherwise the fd_set's are normal sized. + */ + ni = sizeof(fd_set); + if (nfd > FD_SETSIZE) + ni = howmany(nfd, NFDBITS) * sizeof(fd_mask); + + for (msk = 0; msk < 3; msk++) { + fd_set *pibits = (fd_set *)&cibits[msk*ni]; + fd_set *pobits = (fd_set *)&cobits[msk*ni]; + + for (i = 0; i < nfd; i += NFDBITS) { + bits = pibits->fds_bits[i/NFDBITS]; + while ((j = ffs(bits)) && (fd = i + --j) < nfd) { + bits &= ~(1 << j); + fp = fdp->fd_ofiles[fd]; + if (fp == NULL) + return (EBADF); + if ((*fp->f_ops->fo_select)(fp, flag[msk], p)) { + FD_SET(fd, pobits); + n++; + } + } + } + } + *retval = n; + return (0); +} + +/*ARGSUSED*/ +int +seltrue(dev, flag, p) + dev_t dev; + int flag; + struct proc *p; +{ + + return (1); +} + +/* + * Record a select request. + */ +void +selrecord(selector, sip) + struct proc *selector; + struct selinfo *sip; +{ + struct proc *p; + pid_t mypid; + + mypid = selector->p_pid; + if (sip->si_selpid == mypid) + return; + if (sip->si_selpid && (p = pfind(sip->si_selpid)) && + p->p_wchan == (caddr_t)&selwait) + sip->si_flags |= SI_COLL; + else + sip->si_selpid = mypid; +} + +/* + * Do a wakeup when a selectable event occurs. + */ +void +selwakeup(sip) + register struct selinfo *sip; +{ + register struct proc *p; + int s; + + if (sip->si_selpid == 0) + return; + if (sip->si_flags & SI_COLL) { + nselcoll++; + sip->si_flags &= ~SI_COLL; + wakeup((caddr_t)&selwait); + } + p = pfind(sip->si_selpid); + sip->si_selpid = 0; + if (p != NULL) { + s = splhigh(); + if (p->p_wchan == (caddr_t)&selwait) { + if (p->p_stat == SSLEEP) + setrunnable(p); + else + unsleep(p); + } else if (p->p_flag & P_SELECT) + p->p_flag &= ~P_SELECT; + splx(s); + } +} + +void +pollscan(p, pl, nfd, retval) + struct proc *p; + struct pollfd *pl; + int nfd; + register_t *retval; +{ + register struct filedesc *fdp = p->p_fd; + register int msk, i; + struct file *fp; + int x, n = 0; + static int flag[3] = { FREAD, FWRITE, 0 }; + static int pflag[3] = { POLLIN|POLLRDNORM, POLLOUT, POLLERR }; + + /* + * XXX: We need to implement the rest of the flags. + */ + for (i = 0; i < nfd; i++) { + /* Check the file descriptor. */ + if (pl[i].fd < 0) + continue; + if (pl[i].fd >= fdp->fd_nfiles) { + pl[i].revents = POLLNVAL; + n++; + continue; + } + + fp = fdp->fd_ofiles[pl[i].fd]; + if (fp == NULL) { + pl[i].revents = POLLNVAL; + n++; + continue; + } + for (x = msk = 0; msk < 3; msk++) { + if (pl[i].events & pflag[msk]) { + if ((*fp->f_ops->fo_select)(fp, flag[msk], p)) { + pl[i].revents |= pflag[msk] & + pl[i].events; + x++; + } + } + } + if (x) + n++; + } + *retval = n; +} + +/* + * We are using the same mechanism as select only we encode/decode args + * differently. + */ +int +sys_poll(p, v, retval) + register struct proc *p; + void *v; + register_t *retval; +{ + struct sys_poll_args *uap = v; + size_t sz; + struct pollfd pfds[4], *pl = pfds; + int msec = SCARG(uap, timeout); + struct timeval atv; + int timo, ncoll, i, s, error, error2; + extern int nselcoll, selwait; + + /* Standards say no more than MAX_OPEN; this is possibly better. */ + if (SCARG(uap, nfds) > min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, + maxfiles)) + return (EINVAL); + + sz = sizeof(struct pollfd) * SCARG(uap, nfds); + + /* optimize for the default case, of a small nfds value */ + if (sz > sizeof(pfds)) + pl = (struct pollfd *) malloc(sz, M_TEMP, M_WAITOK); + + if ((error = copyin(SCARG(uap, fds), pl, sz)) != 0) + goto bad; + + for (i = 0; i < SCARG(uap, nfds); i++) + pl[i].revents = 0; + + if (msec != -1) { + atv.tv_sec = msec / 1000; + atv.tv_usec = (msec - (atv.tv_sec * 1000)) * 1000; + + if (itimerfix(&atv)) { + error = EINVAL; + goto done; + } + s = splclock(); + timeradd(&atv, &time, &atv); + timo = hzto(&atv); + /* + * Avoid inadvertently sleeping forever. + */ + if (timo == 0) + timo = 1; + splx(s); + } else + timo = 0; + +retry: + ncoll = nselcoll; + p->p_flag |= P_SELECT; + pollscan(p, pl, SCARG(uap, nfds), retval); + if (*retval) + goto done; + s = splhigh(); + if (timo && timercmp(&time, &atv, >=)) { + splx(s); + goto done; + } + if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) { + splx(s); + goto retry; + } + p->p_flag &= ~P_SELECT; + error = tsleep((caddr_t)&selwait, PSOCK | PCATCH, "poll", timo); + splx(s); + if (error == 0) + goto retry; + +done: + p->p_flag &= ~P_SELECT; + /* poll is not restarted after signals... */ + if (error == ERESTART) + error = EINTR; + if (error == EWOULDBLOCK) + error = 0; + if ((error2 = copyout(pl, SCARG(uap, fds), sz)) != 0) + error = error2; +bad: + if (pl != pfds) + free((char *) pl, M_TEMP); + return (error); +} +#endif
