OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/rtos/ecos-2.0/packages/net/tcpip/v2_0/src/sys/kern
    from Rev 27 to Rev 174
    Reverse comparison

Rev 27 → Rev 174

/uipc_proto.c
0,0 → 1,108
//==========================================================================
//
// sys/kern/uipc_proto.c
//
//
//
//==========================================================================
//####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: uipc_proto.c,v 1.3 1998/04/26 22:40:42 millert Exp $ */
/* $NetBSD: uipc_proto.c,v 1.8 1996/02/13 21:10:47 christos Exp $ */
 
/*-
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)uipc_proto.c 8.1 (Berkeley) 6/10/93
*/
 
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/mbuf.h>
#include <sys/un.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <net/raw_cb.h>
 
/*
* Definitions of protocols supported in the UNIX domain.
*/
 
extern struct domain unixdomain; /* or at least forward */
 
struct protosw unixsw[] = {
{ SOCK_STREAM, &unixdomain, PF_LOCAL, PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS,
0, 0, 0, 0,
uipc_usrreq,
0, 0, 0, 0,
},
{ SOCK_DGRAM, &unixdomain, PF_LOCAL, PR_ATOMIC|PR_ADDR|PR_RIGHTS,
0, 0, 0, 0,
uipc_usrreq,
0, 0, 0, 0,
},
{ 0, 0, 0, 0,
raw_input, 0, raw_ctlinput, 0,
raw_usrreq,
raw_init, 0, 0, 0,
}
};
 
struct domain unixdomain =
{ AF_LOCAL, "unix", 0, unp_externalize, unp_dispose,
unixsw, &unixsw[sizeof(unixsw)/sizeof(unixsw[0])] };
/sys_socket.c
0,0 → 1,282
//==========================================================================
//
// sys/kern/sys_socket.c
//
//
//
//==========================================================================
//####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_socket.c,v 1.3 1997/08/31 20:42:23 deraadt Exp $ */
/* $NetBSD: sys_socket.c,v 1.13 1995/08/12 23:59:09 mycroft Exp $ */
 
/*
* Copyright (c) 1982, 1986, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* 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_socket.c 8.1 (Berkeley) 6/10/93
*/
 
#include <sys/param.h>
#ifdef __ECOS
#include <cyg/io/file.h>
#else
#include <sys/systm.h>
#include <sys/file.h>
#include <sys/proc.h>
#endif
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
#ifndef __ECOS
#include <sys/stat.h>
#endif
 
#include <net/if.h>
#include <net/route.h>
 
struct fileops socketops =
{ soo_read, soo_write, soo_ioctl, soo_select, soo_close };
 
#ifdef __ECOS
/* ARGSUSED */
int
soo_read(struct file *fp, struct uio *uio)
#else
/* ARGSUSED */
int
soo_read(fp, uio, cred)
struct file *fp;
struct uio *uio;
struct ucred *cred;
#endif
{
 
return (soreceive((struct socket *)fp->f_data, (struct mbuf **)0,
uio, (struct mbuf **)0, (struct mbuf **)0, (int *)0));
}
 
#ifdef __ECOS
int
soo_write(struct file *fp, struct uio *uio)
#else
/* ARGSUSED */
int
soo_write(fp, uio, cred)
struct file *fp;
struct uio *uio;
struct ucred *cred;
#endif
{
 
return (sosend((struct socket *)fp->f_data, (struct mbuf *)0,
uio, (struct mbuf *)0, (struct mbuf *)0, 0));
}
 
#ifdef __ECOS
int
soo_ioctl(struct file *fp, CYG_ADDRWORD cmd, CYG_ADDRWORD data)
#else
int
soo_ioctl(fp, cmd, data, p)
struct file *fp;
u_long cmd;
register caddr_t data;
struct proc *p;
#endif
{
register struct socket *so = (struct socket *)fp->f_data;
#ifdef __ECOS
void *p = 0;
#endif
 
switch (cmd) {
 
case FIONBIO:
if (*(int *)data)
so->so_state |= SS_NBIO;
else
so->so_state &= ~SS_NBIO;
return (0);
 
case FIOASYNC:
if (*(int *)data) {
so->so_state |= SS_ASYNC;
so->so_rcv.sb_flags |= SB_ASYNC;
so->so_snd.sb_flags |= SB_ASYNC;
} else {
so->so_state &= ~SS_ASYNC;
so->so_rcv.sb_flags &= ~SB_ASYNC;
so->so_snd.sb_flags &= ~SB_ASYNC;
}
return (0);
 
case FIONREAD:
*(int *)data = so->so_rcv.sb_cc;
return (0);
 
#ifndef __ECOS
case SIOCSPGRP:
so->so_pgid = *(int *)data;
so->so_siguid = p->p_cred->p_ruid;
so->so_sigeuid = p->p_ucred->cr_uid;
return (0);
 
case SIOCGPGRP:
*(int *)data = so->so_pgid;
return (0);
#endif
 
case SIOCATMARK:
*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
return (0);
}
/*
* Interface/routing/protocol specific ioctls:
* interface and routing ioctls should have a
* different entry since a socket's unnecessary
*/
if (IOCGROUP(cmd) == 'i')
return (ifioctl(so, (u_long)cmd, (caddr_t)data, p));
if (IOCGROUP(cmd) == 'r')
return (rtioctl((u_long)cmd, (caddr_t)data, p));
return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
(struct mbuf *)cmd, (struct mbuf *)data, (struct mbuf *)0));
}
 
#ifdef __ECOS
int
soo_select(struct file *fp, int which)
#else
int
soo_select(fp, which, p)
struct file *fp;
int which;
struct proc *p;
#endif
{
register struct socket *so = (struct socket *)fp->f_data;
register int s = splsoftnet();
#ifdef __ECOS
void *p = 0;
#endif
 
switch (which) {
 
case FREAD:
if (soreadable(so)) {
splx(s);
return (1);
}
selrecord(p, &so->so_rcv.sb_sel);
so->so_rcv.sb_flags |= SB_SEL;
break;
 
case FWRITE:
if (sowriteable(so)) {
splx(s);
return (1);
}
selrecord(p, &so->so_snd.sb_sel);
so->so_snd.sb_flags |= SB_SEL;
break;
 
case 0:
if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
splx(s);
return (1);
}
selrecord(p, &so->so_rcv.sb_sel);
so->so_rcv.sb_flags |= SB_SEL;
break;
}
splx(s);
return (0);
}
 
#ifndef __ECOS
int
soo_stat(so, ub)
register struct socket *so;
register struct stat *ub;
{
 
bzero((caddr_t)ub, sizeof (*ub));
ub->st_mode = S_IFSOCK;
return ((*so->so_proto->pr_usrreq)(so, PRU_SENSE,
(struct mbuf *)ub, (struct mbuf *)0,
(struct mbuf *)0));
}
#endif
 
#ifdef __ECOS
int
soo_close(struct file *fp)
#else
/* ARGSUSED */
int
soo_close(fp, p)
struct file *fp;
struct proc *p;
#endif
{
int error = 0;
 
if (fp->f_data)
error = soclose((struct socket *)fp->f_data);
fp->f_data = 0;
return (error);
}
/sockio.c
0,0 → 1,953
//==========================================================================
//
// sys/kern/sockio.c
//
// Socket interface to Fileio subsystem
//
//==========================================================================
//####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): nickg
// Contributors: nickg
// Date: 2000-06-06
// Purpose:
// Description:
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
/*
* Copyright (c) 1982, 1986, 1991, 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.
*
*/
//==========================================================================
 
#include <pkgconf/net.h>
#include <pkgconf/io_fileio.h>
 
#include <sys/types.h>
 
#include <cyg/io/file.h>
 
#include <cyg/fileio/fileio.h>
#include <cyg/fileio/sockio.h>
 
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
 
#include <net/if.h>
#include <net/route.h>
 
//==========================================================================
// Forward definitions
 
static int bsd_init( cyg_nstab_entry *nste );
static int bsd_socket( cyg_nstab_entry *nste, int domain, int type,
int protocol, cyg_file *file );
 
static int bsd_bind ( cyg_file *fp, const sockaddr *sa, socklen_t len );
static int bsd_connect ( cyg_file *fp, const sockaddr *sa, socklen_t len );
static int bsd_accept ( cyg_file *fp, cyg_file *new_fp,
struct sockaddr *name, socklen_t *anamelen );
static int bsd_listen ( cyg_file *fp, int len );
static int bsd_getname ( cyg_file *fp, sockaddr *sa, socklen_t *len, int peer );
static int bsd_shutdown ( cyg_file *fp, int flags );
static int bsd_getsockopt( cyg_file *fp, int level, int optname,
void *optval, socklen_t *optlen);
static int bsd_setsockopt( cyg_file *fp, int level, int optname,
const void *optval, socklen_t optlen);
static int bsd_sendmsg ( cyg_file *fp, const struct msghdr *m,
int flags, ssize_t *retsize );
static int bsd_recvmsg ( cyg_file *fp, struct msghdr *m,
socklen_t *namelen, ssize_t *retsize );
 
 
// File operations
static int bsd_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int bsd_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
static int bsd_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
static int bsd_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
CYG_ADDRWORD data);
static int bsd_select (struct CYG_FILE_TAG *fp, int which, CYG_ADDRWORD info);
static int bsd_fsync (struct CYG_FILE_TAG *fp, int mode );
static int bsd_close (struct CYG_FILE_TAG *fp);
static int bsd_fstat (struct CYG_FILE_TAG *fp, struct stat *buf );
static int bsd_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
static int bsd_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
 
static int
bsd_recvit(cyg_file *fp, struct msghdr *mp, socklen_t *namelenp, ssize_t *retsize);
static int
bsd_sendit(cyg_file *fp, const struct msghdr *mp, int flags, ssize_t *retsize);
 
 
//==========================================================================
// Table entrys
 
NSTAB_ENTRY( bsd_nste, 0,
"bsd_tcpip",
"",
0,
bsd_init,
bsd_socket);
 
struct cyg_sock_ops bsd_sockops =
{
bsd_bind,
bsd_connect,
bsd_accept,
bsd_listen,
bsd_getname,
bsd_shutdown,
bsd_getsockopt,
bsd_setsockopt,
bsd_sendmsg,
bsd_recvmsg
};
 
cyg_fileops bsd_sock_fileops =
{
bsd_read,
bsd_write,
bsd_lseek,
bsd_ioctl,
bsd_select,
bsd_fsync,
bsd_close,
bsd_fstat,
bsd_getinfo,
bsd_setinfo
};
 
//==========================================================================
// NStab functions
 
 
 
// -------------------------------------------------------------------------
 
static int bsd_init( cyg_nstab_entry *nste )
{
// Initialization already handled via constructor
return ENOERR;
}
 
// -------------------------------------------------------------------------
 
static int bsd_socket( cyg_nstab_entry *nste, int domain, int type,
int protocol, cyg_file *file )
{
int error = 0;
struct socket *so;
 
error = socreate( domain, &so, type, protocol );
 
if( error == ENOERR )
{
 
cyg_selinit(&so->so_rcv.sb_sel);
cyg_selinit(&so->so_snd.sb_sel);
file->f_flag |= CYG_FREAD|CYG_FWRITE;
file->f_type = CYG_FILE_TYPE_SOCKET;
file->f_ops = &bsd_sock_fileops;
file->f_offset = 0;
file->f_data = (CYG_ADDRWORD)so;
file->f_xops = (CYG_ADDRWORD)&bsd_sockops;
}
return error;
}
 
 
//==========================================================================
// Sockops functions
 
// -------------------------------------------------------------------------
 
static int bsd_bind ( cyg_file *fp, const sockaddr *sa, socklen_t len )
{
struct mbuf *nam;
int error;
 
error = sockargs(&nam, (caddr_t)sa, len, MT_SONAME);
 
if (error)
return (error);
 
error = sobind((struct socket *)fp->f_data, nam);
 
m_freem(nam);
return error;
}
 
// -------------------------------------------------------------------------
 
static int bsd_connect ( cyg_file *fp, const sockaddr *sa, socklen_t len )
{
register struct socket *so;
struct mbuf *nam;
int error, s;
 
so = (struct socket *)fp->f_data;
 
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING))
return (EALREADY);
 
error = sockargs(&nam, (caddr_t)sa, len, MT_SONAME);
 
if (error)
return (error);
 
error = soconnect(so, nam);
if (error)
goto bad;
 
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
m_freem(nam);
return (EINPROGRESS);
}
 
s = splsoftnet();
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
netcon, 0);
if (error)
break;
}
 
if (error == 0) {
error = so->so_error;
so->so_error = 0;
}
 
splx(s);
 
bad:
so->so_state &= ~SS_ISCONNECTING;
m_freem(nam);
 
return error;
}
 
// -------------------------------------------------------------------------
 
static int bsd_accept ( cyg_file *fp, cyg_file *new_fp,
struct sockaddr *name, socklen_t *anamelen )
{
struct mbuf *nam;
socklen_t namelen = 0;
int error = 0, s;
register struct socket *so;
 
if( anamelen != NULL )
namelen = *anamelen;
 
s = splsoftnet();
so = (struct socket *)fp->f_data;
 
if ((so->so_options & SO_ACCEPTCONN) == 0) {
splx(s);
return (EINVAL);
}
 
if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
splx(s);
return (EWOULDBLOCK);
}
 
while (so->so_qlen == 0 && so->so_error == 0) {
if (so->so_state & SS_CANTRCVMORE) {
so->so_error = ECONNABORTED;
break;
}
error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
netcon, 0);
if (error) {
splx(s);
return (error);
}
}
 
if (so->so_error) {
error = so->so_error;
so->so_error = 0;
splx(s);
return (error);
}
 
{
struct socket *aso = so->so_q;
if (soqremque(aso, 1) == 0)
panic("accept");
so = aso;
}
 
cyg_selinit(&so->so_rcv.sb_sel);
cyg_selinit(&so->so_snd.sb_sel);
new_fp->f_type = DTYPE_SOCKET;
new_fp->f_flag |= FREAD|FWRITE;
new_fp->f_offset = 0;
new_fp->f_ops = &bsd_sock_fileops;
new_fp->f_data = (CYG_ADDRWORD)so;
new_fp->f_xops = (CYG_ADDRWORD)&bsd_sockops;
nam = m_get(M_WAIT, MT_SONAME);
(void) soaccept(so, nam);
if (name) {
if (namelen > nam->m_len)
namelen = nam->m_len;
/* SHOULD COPY OUT A CHAIN HERE */
if ((error = copyout(mtod(nam, caddr_t),
(caddr_t)name, namelen)) == 0)
*anamelen = namelen;
}
m_freem(nam);
splx(s);
return (error);
}
 
// -------------------------------------------------------------------------
 
static int bsd_listen ( cyg_file *fp, int backlog )
{
return (solisten((struct socket *)fp->f_data, backlog));
}
 
// -------------------------------------------------------------------------
 
static int bsd_getname ( cyg_file *fp, sockaddr *asa, socklen_t *alen, int peer )
{
register struct socket *so;
struct mbuf *m;
socklen_t len = 0;
int error;
int type = peer ? PRU_PEERADDR : PRU_SOCKADDR;
if( alen != NULL )
len = *alen;
so = (struct socket *)fp->f_data;
 
if ( peer && (so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0)
return (ENOTCONN);
 
m = m_getclr(M_WAIT, MT_SONAME);
if (m == NULL)
return (ENOBUFS);
error = (*so->so_proto->pr_usrreq)(so, type, 0, m, 0);
if (error)
goto bad;
 
if (len > m->m_len)
len = m->m_len;
 
error = copyout(mtod(m, caddr_t), (caddr_t)asa, len);
if (error == 0)
*alen = len;
 
bad:
m_freem(m);
 
return (error);
}
 
// -------------------------------------------------------------------------
 
static int bsd_shutdown ( cyg_file *fp, int how )
{
return (soshutdown((struct socket *)fp->f_data, how));
}
 
// -------------------------------------------------------------------------
 
static int bsd_getsockopt( cyg_file *fp, int level, int optname,
void *optval, socklen_t *optlen)
{
struct mbuf *m = NULL;
socklen_t valsize = 0;
int error;
 
if( optval != NULL && optlen != NULL )
valsize = *optlen;
error = sogetopt((struct socket *)fp->f_data, level, optname, &m);
 
if( error == ENOERR && valsize != 0 && m != NULL)
{
if (valsize > m->m_len)
valsize = m->m_len;
 
error = copyout(mtod(m, caddr_t), optval, valsize);
 
if( error == ENOERR )
*optlen = valsize;
}
 
if (m != NULL)
(void) m_free(m);
return (error);
}
 
// -------------------------------------------------------------------------
 
static int bsd_setsockopt( cyg_file *fp, int level, int optname,
const void *optval, socklen_t optlen)
{
int error;
struct mbuf *m = NULL;
if( optlen > MCLBYTES )
return EINVAL;
 
if (optval != NULL) {
m = m_get(M_WAIT, MT_SOOPTS);
if (optlen > MLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
return (ENOBUFS);
}
}
if (m == NULL)
return (ENOBUFS);
error = copyin(optval, mtod(m, caddr_t), optlen);
if (error) {
(void) m_free(m);
return (error);
}
m->m_len = optlen;
}
return (sosetopt((struct socket *)fp->f_data, level, optname, m));
}
 
// -------------------------------------------------------------------------
 
static int bsd_sendmsg ( cyg_file *fp, const struct msghdr *m, int flags, ssize_t *retsize )
{
return bsd_sendit( fp, m, flags, retsize);
}
 
// -------------------------------------------------------------------------
 
static int bsd_recvmsg ( cyg_file *fp, struct msghdr *m, socklen_t *namelen, ssize_t *retsize )
{
return bsd_recvit( fp, m, namelen, retsize);
}
 
//==========================================================================
// File system call functions
 
// -------------------------------------------------------------------------
 
static int bsd_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
{
return (soreceive((struct socket *)fp->f_data, (struct mbuf **)0,
uio, (struct mbuf **)0, (struct mbuf **)0, (int *)0));
}
 
// -------------------------------------------------------------------------
 
static int bsd_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
{
return (sosend((struct socket *)fp->f_data, (struct mbuf *)0,
uio, (struct mbuf *)0, (struct mbuf *)0, 0));
}
 
// -------------------------------------------------------------------------
 
static int bsd_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence )
{
return ESPIPE;
}
 
// -------------------------------------------------------------------------
 
static int bsd_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD cmd,
CYG_ADDRWORD data)
{
register struct socket *so = (struct socket *)fp->f_data;
void *p = 0;
 
switch (cmd) {
 
case FIONBIO:
if (*(int *)data)
so->so_state |= SS_NBIO;
else
so->so_state &= ~SS_NBIO;
return (0);
 
case FIOASYNC:
if (*(int *)data) {
so->so_state |= SS_ASYNC;
so->so_rcv.sb_flags |= SB_ASYNC;
so->so_snd.sb_flags |= SB_ASYNC;
} else {
so->so_state &= ~SS_ASYNC;
so->so_rcv.sb_flags &= ~SB_ASYNC;
so->so_snd.sb_flags &= ~SB_ASYNC;
}
return (0);
 
case FIONREAD:
*(int *)data = so->so_rcv.sb_cc;
return (0);
 
case SIOCATMARK:
*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
return (0);
}
/*
* Interface/routing/protocol specific ioctls:
* interface and routing ioctls should have a
* different entry since a socket's unnecessary
*/
if (IOCGROUP(cmd) == 'i')
return (ifioctl(so, (u_long)cmd, (caddr_t)data, p));
if (IOCGROUP(cmd) == 'r')
return (rtioctl((u_long)cmd, (caddr_t)data, p));
return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
(struct mbuf *)cmd, (struct mbuf *)data, (struct mbuf *)0));
 
}
 
// -------------------------------------------------------------------------
 
static int bsd_select (struct CYG_FILE_TAG *fp, int which, CYG_ADDRWORD info)
{
register struct socket *so = (struct socket *)fp->f_data;
register int s = splsoftnet();
 
switch (which) {
 
case FREAD:
if (soreadable(so)) {
splx(s);
return (1);
}
cyg_selrecord(info, &so->so_rcv.sb_sel);
so->so_rcv.sb_flags |= SB_SEL;
break;
 
case FWRITE:
if (sowriteable(so)) {
splx(s);
return (1);
}
cyg_selrecord(info, &so->so_snd.sb_sel);
so->so_snd.sb_flags |= SB_SEL;
break;
 
case 0:
if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
splx(s);
return (1);
}
cyg_selrecord(info, &so->so_rcv.sb_sel);
so->so_rcv.sb_flags |= SB_SEL;
break;
}
splx(s);
return ENOERR;
}
 
// -------------------------------------------------------------------------
 
static int bsd_fsync (struct CYG_FILE_TAG *fp, int mode )
{
// FIXME: call some sort of flush IOCTL?
return 0;
}
 
// -------------------------------------------------------------------------
 
static int bsd_close (struct CYG_FILE_TAG *fp)
{
int error = 0;
 
if (fp->f_data)
error = soclose((struct socket *)fp->f_data);
fp->f_data = 0;
return (error);
}
 
// -------------------------------------------------------------------------
 
static int bsd_fstat (struct CYG_FILE_TAG *fp, struct stat *buf )
{
register struct socket *so = (struct socket *)fp->f_data;
 
bzero((caddr_t)buf, sizeof (*buf));
 
// Mark socket as a fifo for now. We need to add socket types to
// sys/stat.h.
buf->st_mode = __stat_mode_FIFO;
return ((*so->so_proto->pr_usrreq)(so, PRU_SENSE,
(struct mbuf *)buf,
(struct mbuf *)0,
(struct mbuf *)0));
}
 
// -------------------------------------------------------------------------
 
static int bsd_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
{
return ENOSYS;
}
 
// -------------------------------------------------------------------------
 
static int bsd_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
{
return ENOSYS;
}
 
 
 
//==========================================================================
// Select support
 
// -------------------------------------------------------------------------
// This function is called by the lower layers to record the
// fact that a particular 'select' event is being requested.
//
 
void
selrecord(void *selector, struct selinfo *info)
{
// Unused by this implementation
}
 
// -------------------------------------------------------------------------
// This function is called to indicate that a 'select' event
// may have occurred.
//
 
void
selwakeup(struct selinfo *info)
{
cyg_selwakeup( info );
}
 
//==========================================================================
// Misc support functions
 
int
sockargs(mp, buf, buflen, type)
struct mbuf **mp;
caddr_t buf;
socklen_t buflen;
int type;
{
register struct sockaddr *sa;
register struct mbuf *m;
int error;
 
if (buflen > MLEN) {
#ifdef COMPAT_OLDSOCK
if (type == MT_SONAME && buflen <= 112)
buflen = MLEN; /* unix domain compat. hack */
else
#endif
return (EINVAL);
}
m = m_get(M_WAIT, type);
if (m == NULL)
return (ENOBUFS);
m->m_len = buflen;
error = copyin(buf, mtod(m, caddr_t), buflen);
if (error) {
(void) m_free(m);
return (error);
}
*mp = m;
if (type == MT_SONAME) {
sa = mtod(m, struct sockaddr *);
 
#if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN
if (sa->sa_family == 0 && sa->sa_len < AF_MAX)
sa->sa_family = sa->sa_len;
#endif
sa->sa_len = buflen;
}
return (0);
}
 
 
// -------------------------------------------------------------------------
// bsd_recvit()
// Support for message reception. This is a lightly edited version of the
// recvit() function is uipc_syscalls.c.
 
static int
bsd_recvit(cyg_file *fp, struct msghdr *mp, socklen_t *namelenp, ssize_t *retsize)
{
struct uio auio;
register struct iovec *iov;
register int i;
size_t len;
int error;
struct mbuf *from = 0, *control = 0;
 
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_READ;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
/* Don't allow sum > SSIZE_MAX */
if (iov->iov_len > SSIZE_MAX ||
(auio.uio_resid += iov->iov_len) > SSIZE_MAX)
return (EINVAL);
}
 
len = auio.uio_resid;
error = soreceive((struct socket *)fp->f_data, &from, &auio,
NULL, mp->msg_control ? &control : NULL,
&mp->msg_flags);
if (error) {
if (auio.uio_resid != len &&
(error == EINTR || error == EWOULDBLOCK))
error = 0;
}
if (error)
goto out;
*retsize = len - auio.uio_resid;
if (mp->msg_name) {
len = mp->msg_namelen;
if (len <= 0 || from == 0)
len = 0;
else {
/* save sa_len before it is destroyed by MSG_COMPAT */
if (len > from->m_len)
len = from->m_len;
/* else if len < from->m_len ??? */
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags & MSG_COMPAT)
mtod(from, struct osockaddr *)->sa_family =
mtod(from, struct sockaddr *)->sa_family;
#endif
error = copyout(mtod(from, caddr_t),
(caddr_t)mp->msg_name, (unsigned)len);
if (error)
goto out;
}
mp->msg_namelen = len;
if (namelenp ) {
*namelenp = len;
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags & MSG_COMPAT)
error = 0; /* old recvfrom didn't check */
else
#endif
goto out;
}
}
if (mp->msg_control) {
#ifdef COMPAT_OLDSOCK
/*
* We assume that old recvmsg calls won't receive access
* rights and other control info, esp. as control info
* is always optional and those options didn't exist in 4.3.
* If we receive rights, trim the cmsghdr; anything else
* is tossed.
*/
if (control && mp->msg_flags & MSG_COMPAT) {
if (mtod(control, struct cmsghdr *)->cmsg_level !=
SOL_SOCKET ||
mtod(control, struct cmsghdr *)->cmsg_type !=
SCM_RIGHTS) {
mp->msg_controllen = 0;
goto out;
}
control->m_len -= sizeof (struct cmsghdr);
control->m_data += sizeof (struct cmsghdr);
}
#endif
len = mp->msg_controllen;
if (len <= 0 || control == 0)
len = 0;
else {
struct mbuf *m = control;
caddr_t p = (caddr_t)mp->msg_control;
 
do {
i = m->m_len;
if (len < i) {
mp->msg_flags |= MSG_CTRUNC;
i = len;
}
error = copyout(mtod(m, caddr_t), p,
(unsigned)i);
if (m->m_next)
i = ALIGN(i);
p += i;
len -= i;
if (error != 0 || len <= 0)
break;
} while ((m = m->m_next) != NULL);
len = p - (caddr_t)mp->msg_control;
}
mp->msg_controllen = len;
}
out:
if (from)
m_freem(from);
if (control)
m_freem(control);
return (error);
}
 
// -------------------------------------------------------------------------
// sendit()
// Support for message transmission. This is a lightly edited version of the
// synonymous function is uipc_syscalls.c.
 
static int
bsd_sendit(cyg_file *fp, const struct msghdr *mp, int flags, ssize_t *retsize)
{
struct uio auio;
register struct iovec *iov;
register int i;
struct mbuf *to, *control;
int len, error;
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
/* Don't allow sum > SSIZE_MAX */
if (iov->iov_len > SSIZE_MAX ||
(auio.uio_resid += iov->iov_len) > SSIZE_MAX)
return (EINVAL);
}
if (mp->msg_name) {
error = sockargs(&to, mp->msg_name, mp->msg_namelen,
MT_SONAME);
if (error)
return (error);
} else
to = 0;
if (mp->msg_control) {
if (mp->msg_controllen < sizeof(struct cmsghdr)
#ifdef COMPAT_OLDSOCK
&& mp->msg_flags != MSG_COMPAT
#endif
) {
error = EINVAL;
goto bad;
}
error = sockargs(&control, mp->msg_control,
mp->msg_controllen, MT_CONTROL);
if (error)
goto bad;
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags == MSG_COMPAT) {
register struct cmsghdr *cm;
 
M_PREPEND(control, sizeof(*cm), M_WAIT);
if (control == 0) {
error = ENOBUFS;
goto bad;
} else {
cm = mtod(control, struct cmsghdr *);
cm->cmsg_len = control->m_len;
cm->cmsg_level = SOL_SOCKET;
cm->cmsg_type = SCM_RIGHTS;
}
}
#endif
} else
control = 0;
 
len = auio.uio_resid;
error = sosend((struct socket *)fp->f_data, to, &auio,
NULL, control, flags);
if (error) {
if (auio.uio_resid != len &&
(error == EINTR || error == EWOULDBLOCK))
error = 0;
#ifndef __ECOS
if (error == EPIPE)
psignal(p, SIGPIPE);
#endif
}
if (error == 0)
*retsize = len - auio.uio_resid;
bad:
if (to)
m_freem(to);
return (error);
}
 
 
//==========================================================================
// End of sockio.c
/uipc_domain.c
0,0 → 1,311
//==========================================================================
//
// sys/kern/uipc_domain.c
//
//
//
//==========================================================================
//####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: uipc_domain.c,v 1.9 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: uipc_domain.c,v 1.14 1996/02/09 19:00:44 christos Exp $ */
 
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)uipc_domain.c 8.2 (Berkeley) 10/18/93
*/
 
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/mbuf.h>
#include <sys/time.h>
#include <sys/kernel.h>
#ifndef __ECOS
#include <sys/systm.h>
#include <sys/proc.h>
 
#include <vm/vm.h>
#include <sys/sysctl.h>
#endif
 
void pffasttimo __P((void *));
void pfslowtimo __P((void *));
#if defined (KEY) || defined (IPSEC)
int pfkey_init __P((void));
#endif /* KEY || IPSEC */
 
#define ADDDOMAIN(x) { \
extern struct domain __CONCAT(x,domain); \
__CONCAT(x,domain.dom_next) = domains; \
domains = &__CONCAT(x,domain); \
}
 
void
domaininit()
{
register struct domain *dp;
register struct protosw *pr;
 
#undef unix
/*
* KAME NOTE: ADDDOMAIN(route) is moved to the last part so that
* it will be initialized as the *first* element. confusing!
*/
#ifndef lint
#ifndef __ECOS
ADDDOMAIN(unix);
#endif
#ifdef INET
ADDDOMAIN(inet);
#endif
#ifdef INET6
ADDDOMAIN(inet6);
#endif /* INET6 */
#if defined (KEY) || defined (IPSEC)
pfkey_init();
#endif /* KEY || IPSEC */
#ifdef IPX
ADDDOMAIN(ipx);
#endif
#ifdef NETATALK
ADDDOMAIN(atalk);
#endif
#ifdef NS
ADDDOMAIN(ns);
#endif
#ifdef ISO
ADDDOMAIN(iso);
#endif
#ifdef CCITT
ADDDOMAIN(ccitt);
#endif
#ifdef notdef /* XXXX */
#include "imp.h"
#if NIMP > 0
ADDDOMAIN(imp);
#endif
#endif
#ifdef IPSEC
#ifdef __KAME__
ADDDOMAIN(key);
#endif
#endif
ADDDOMAIN(route);
#endif
 
for (dp = domains; dp; dp = dp->dom_next) {
if (dp->dom_init)
(*dp->dom_init)();
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_init)
(*pr->pr_init)();
}
 
if (max_linkhdr < 16) /* XXX */
max_linkhdr = 16;
max_hdr = max_linkhdr + max_protohdr;
max_datalen = MHLEN - max_hdr;
timeout(pffasttimo, NULL, 1);
timeout(pfslowtimo, NULL, 1);
}
 
struct protosw *
pffindtype(family, type)
int family, type;
{
register struct domain *dp;
register struct protosw *pr;
 
for (dp = domains; dp; dp = dp->dom_next)
if (dp->dom_family == family)
goto found;
return (0);
found:
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_type && pr->pr_type == type)
return (pr);
return (0);
}
 
struct protosw *
pffindproto(family, protocol, type)
int family, protocol, type;
{
register struct domain *dp;
register struct protosw *pr;
struct protosw *maybe = 0;
 
if (family == 0)
return (0);
for (dp = domains; dp; dp = dp->dom_next)
if (dp->dom_family == family)
goto found;
return (0);
found:
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
if ((pr->pr_protocol == protocol) && (pr->pr_type == type))
return (pr);
 
if (type == SOCK_RAW && pr->pr_type == SOCK_RAW &&
pr->pr_protocol == 0 && maybe == (struct protosw *)0)
maybe = pr;
}
return (maybe);
}
 
#ifndef __ECOS
int
net_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
int *name;
u_int namelen;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
struct proc *p;
{
register struct domain *dp;
register struct protosw *pr;
int family, protocol;
 
/*
* All sysctl names at this level are nonterminal.
* PF_KEY: next component is protocol family, and then at least one
* additional component.
* usually: next two components are protocol family and protocol
* number, then at least one addition component.
*/
if (namelen < 2)
return (EISDIR); /* overloaded */
family = name[0];
 
if (family == 0)
return (0);
for (dp = domains; dp; dp = dp->dom_next)
if (dp->dom_family == family)
goto found;
return (ENOPROTOOPT);
found:
switch (family) {
#ifdef IPSEC
#ifdef __KAME__
case PF_KEY:
pr = dp->dom_protosw;
if (pr->pr_sysctl)
return ((*pr->pr_sysctl)(name + 1, namelen - 1,
oldp, oldlenp, newp, newlen));
return (ENOPROTOOPT);
#endif
#endif
default:
break;
}
if (namelen < 3)
return (EISDIR); /* overloaded */
protocol = name[1];
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_protocol == protocol && pr->pr_sysctl)
return ((*pr->pr_sysctl)(name + 2, namelen - 2,
oldp, oldlenp, newp, newlen));
return (ENOPROTOOPT);
}
#endif
 
void
pfctlinput(cmd, sa)
int cmd;
struct sockaddr *sa;
{
register struct domain *dp;
register struct protosw *pr;
 
for (dp = domains; dp; dp = dp->dom_next)
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_ctlinput)
(*pr->pr_ctlinput)(cmd, sa, NULL);
}
 
void
pfslowtimo(arg)
void *arg;
{
register struct domain *dp;
register struct protosw *pr;
 
for (dp = domains; dp; dp = dp->dom_next)
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_slowtimo) {
(*pr->pr_slowtimo)();
}
timeout(pfslowtimo, NULL, hz/2);
}
 
void
pffasttimo(arg)
void *arg;
{
register struct domain *dp;
register struct protosw *pr;
 
for (dp = domains; dp; dp = dp->dom_next)
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_fasttimo) {
(*pr->pr_fasttimo)();
}
timeout(pffasttimo, NULL, hz/5);
}
/uipc_socket.c
0,0 → 1,1147
//==========================================================================
//
// sys/kern/uipc_socket.c
//
//
//
//==========================================================================
//####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: uipc_socket.c,v 1.27 1999/10/14 08:18:49 cmetz Exp $ */
/* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */
 
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94
*/
 
#include <sys/param.h>
#ifdef __ECOS
#include <cyg/io/file.h>
#else
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/file.h>
#endif
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/kernel.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#ifndef __ECOS
#include <sys/signalvar.h>
#include <sys/resourcevar.h>
#endif
 
#ifndef SOMINCONN
#define SOMINCONN 80
#endif /* SOMINCONN */
 
int somaxconn = SOMAXCONN;
int sominconn = SOMINCONN;
 
/*
* Socket operation routines.
* These routines are called by the routines in
* sys_socket.c or from a system process, and
* implement the semantics of socket operations by
* switching out to the protocol specific routines.
*/
/*ARGSUSED*/
int
socreate(dom, aso, type, proto)
int dom;
struct socket **aso;
register int type;
int proto;
{
#ifndef __ECOS
struct proc *p = curproc; /* XXX */
#endif
register struct protosw *prp;
register struct socket *so;
register int error;
 
if (proto)
prp = pffindproto(dom, proto, type);
else
prp = pffindtype(dom, type);
if (prp == 0 || prp->pr_usrreq == 0)
return (EPROTONOSUPPORT);
if (prp->pr_type != type)
return (EPROTOTYPE);
MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_WAIT);
bzero((caddr_t)so, sizeof(*so));
so->so_type = type;
#ifdef __ECOS
so->so_state = SS_PRIV;
so->so_ruid = 0; // FIXME
so->so_euid = 0; // FIXME
#else
if (p->p_ucred->cr_uid == 0)
so->so_state = SS_PRIV;
so->so_ruid = p->p_cred->p_ruid;
so->so_euid = p->p_ucred->cr_uid;
#endif
so->so_proto = prp;
error =
(*prp->pr_usrreq)(so, PRU_ATTACH, NULL, (struct mbuf *)(long)proto,
NULL);
if (error) {
so->so_state |= SS_NOFDREF;
sofree(so);
return (error);
}
#ifdef COMPAT_SUNOS
{
extern struct emul emul_sunos;
if (p->p_emul == &emul_sunos && type == SOCK_DGRAM)
so->so_options |= SO_BROADCAST;
}
#endif
*aso = so;
return (0);
}
 
int
sobind(so, nam)
struct socket *so;
struct mbuf *nam;
{
int s = splsoftnet();
int error;
 
error = (*so->so_proto->pr_usrreq)(so, PRU_BIND, NULL, nam, NULL);
splx(s);
return (error);
}
 
int
solisten(so, backlog)
register struct socket *so;
int backlog;
{
int s = splsoftnet(), error;
 
error = (*so->so_proto->pr_usrreq)(so, PRU_LISTEN, NULL, NULL, NULL);
if (error) {
splx(s);
return (error);
}
if (so->so_q == 0)
so->so_options |= SO_ACCEPTCONN;
if (backlog < 0 || backlog > somaxconn)
backlog = somaxconn;
if (backlog < sominconn)
backlog = sominconn;
so->so_qlimit = backlog;
splx(s);
return (0);
}
 
void
sofree(so)
register struct socket *so;
{
 
if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0)
return;
if (so->so_head) {
/*
* We must not decommission a socket that's on the accept(2)
* queue. If we do, then accept(2) may hang after select(2)
* indicated that the listening socket was ready.
*/
if (!soqremque(so, 0))
return;
}
sbrelease(&so->so_snd);
sorflush(so);
FREE(so, M_SOCKET);
}
 
/*
* Close a socket on last file table reference removal.
* Initiate disconnect if connected.
* Free socket when disconnect complete.
*/
int
soclose(so)
register struct socket *so;
{
struct socket *so2;
int s = splsoftnet(); /* conservative */
int error = 0;
 
if (so->so_options & SO_ACCEPTCONN) {
while ((so2 = so->so_q0) != NULL) {
(void) soqremque(so2, 0);
(void) soabort(so2);
}
while ((so2 = so->so_q) != NULL) {
(void) soqremque(so2, 1);
(void) soabort(so2);
}
}
if (so->so_pcb == 0)
goto discard;
if (so->so_state & SS_ISCONNECTED) {
if ((so->so_state & SS_ISDISCONNECTING) == 0) {
error = sodisconnect(so);
if (error)
goto drop;
}
if (so->so_options & SO_LINGER) {
if ((so->so_state & SS_ISDISCONNECTING) &&
(so->so_state & SS_NBIO))
goto drop;
while (so->so_state & SS_ISCONNECTED) {
error = tsleep((caddr_t)&so->so_timeo,
PSOCK | PCATCH, netcls,
so->so_linger * hz);
if (error)
break;
}
}
}
drop:
if (so->so_pcb) {
int error2 = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, NULL,
NULL, NULL);
if (error == 0)
error = error2;
}
discard:
if (so->so_state & SS_NOFDREF)
panic("soclose: NOFDREF");
so->so_state |= SS_NOFDREF;
sofree(so);
splx(s);
return (error);
}
 
/*
* Must be called at splsoftnet...
*/
int
soabort(so)
struct socket *so;
{
 
return (*so->so_proto->pr_usrreq)(so, PRU_ABORT, NULL, NULL, NULL);
}
 
int
soaccept(so, nam)
register struct socket *so;
struct mbuf *nam;
{
int s = splsoftnet();
int error = 0;
 
if ((so->so_state & SS_NOFDREF) == 0)
panic("soaccept: !NOFDREF");
so->so_state &= ~SS_NOFDREF;
if ((so->so_state & SS_ISDISCONNECTED) == 0)
error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, NULL,
nam, NULL);
splx(s);
return (error);
}
 
int
soconnect(so, nam)
register struct socket *so;
struct mbuf *nam;
{
int s;
int error;
 
if (so->so_options & SO_ACCEPTCONN)
return (EOPNOTSUPP);
s = splsoftnet();
/*
* If protocol is connection-based, can only connect once.
* Otherwise, if connected, try to disconnect first.
* This allows user to disconnect by connecting to, e.g.,
* a null address.
*/
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
(error = sodisconnect(so))))
error = EISCONN;
else
error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT,
NULL, nam, NULL);
splx(s);
return (error);
}
 
int
soconnect2(so1, so2)
register struct socket *so1;
struct socket *so2;
{
int s = splsoftnet();
int error;
 
error = (*so1->so_proto->pr_usrreq)(so1, PRU_CONNECT2, NULL,
(struct mbuf *)so2, NULL);
splx(s);
return (error);
}
 
int
sodisconnect(so)
register struct socket *so;
{
int s = splsoftnet();
int error;
 
if ((so->so_state & SS_ISCONNECTED) == 0) {
error = ENOTCONN;
goto bad;
}
if (so->so_state & SS_ISDISCONNECTING) {
error = EALREADY;
goto bad;
}
error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, NULL, NULL,
NULL);
bad:
splx(s);
return (error);
}
 
#define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
/*
* Send on a socket.
* If send must go all at once and message is larger than
* send buffering, then hard error.
* Lock against other senders.
* If must go all at once and not enough room now, then
* inform user that this would block and do nothing.
* Otherwise, if nonblocking, send as much as possible.
* The data to be sent is described by "uio" if nonzero,
* otherwise by the mbuf chain "top" (which must be null
* if uio is not). Data provided in mbuf chain must be small
* enough to send all at once.
*
* Returns nonzero on error, timeout or signal; callers
* must check for short counts if EINTR/ERESTART are returned.
* Data and control buffers are freed on return.
*/
int
sosend(so, addr, uio, top, control, flags)
register struct socket *so;
struct mbuf *addr;
struct uio *uio;
struct mbuf *top;
struct mbuf *control;
int flags;
{
#ifndef __ECOS
struct proc *p = curproc; /* XXX */
#endif
struct mbuf **mp;
register struct mbuf *m;
register long space, len;
register quad_t resid;
int clen = 0, error, s, dontroute, mlen;
int atomic = sosendallatonce(so) || top;
 
if (uio)
resid = uio->uio_resid;
else
resid = top->m_pkthdr.len;
/*
* In theory resid should be unsigned (since uio->uio_resid is).
* However, space must be signed, as it might be less than 0
* if we over-committed, and we must use a signed comparison
* of space and resid. On the other hand, a negative resid
* causes us to loop sending 0-length segments to the protocol.
* MSG_EOR on a SOCK_STREAM socket is also invalid.
*/
if (resid < 0 ||
(so->so_type == SOCK_STREAM && (flags & MSG_EOR))) {
error = EINVAL;
goto out;
}
dontroute =
(flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
(so->so_proto->pr_flags & PR_ATOMIC);
#ifndef __ECOS
p->p_stats->p_ru.ru_msgsnd++;
#endif
if (control)
clen = control->m_len;
#define snderr(errno) { error = errno; splx(s); goto release; }
 
restart:
if ((error = sblock(&so->so_snd, SBLOCKWAIT(flags))) != 0)
goto out;
do {
s = splsoftnet();
if (so->so_state & SS_CANTSENDMORE)
snderr(EPIPE);
if (so->so_error)
snderr(so->so_error);
if ((so->so_state & SS_ISCONNECTED) == 0) {
if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
if ((so->so_state & SS_ISCONFIRMING) == 0 &&
!(resid == 0 && clen != 0))
snderr(ENOTCONN);
} else if (addr == 0)
snderr(EDESTADDRREQ);
}
space = sbspace(&so->so_snd);
if (flags & MSG_OOB)
space += 1024;
if ((atomic && resid > so->so_snd.sb_hiwat) ||
clen > so->so_snd.sb_hiwat)
snderr(EMSGSIZE);
if (space < resid + clen && uio &&
(atomic || space < so->so_snd.sb_lowat || space < clen)) {
if (so->so_state & SS_NBIO)
snderr(EWOULDBLOCK);
sbunlock(&so->so_snd);
error = sbwait(&so->so_snd);
splx(s);
if (error)
goto out;
goto restart;
}
splx(s);
mp = &top;
space -= clen;
do {
if (uio == NULL) {
/*
* Data is prepackaged in "top".
*/
resid = 0;
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
} else do {
if (top == 0) {
MGETHDR(m, M_WAIT, MT_DATA);
mlen = MHLEN;
m->m_pkthdr.len = 0;
m->m_pkthdr.rcvif = (struct ifnet *)0;
} else {
MGET(m, M_WAIT, MT_DATA);
mlen = MLEN;
}
if (resid >= MINCLSIZE && space >= MCLBYTES) {
MCLGET(m, M_WAIT);
if ((m->m_flags & M_EXT) == 0)
goto nopages;
mlen = MCLBYTES;
#ifdef MAPPED_MBUFS
len = min(MCLBYTES, resid);
#else
if (atomic && top == 0) {
len = min(MCLBYTES - max_hdr, resid);
m->m_data += max_hdr;
} else
len = min(MCLBYTES, resid);
#endif
space -= len;
} else {
nopages:
len = min(min(mlen, resid), space);
space -= len;
/*
* For datagram protocols, leave room
* for protocol headers in first mbuf.
*/
if (atomic && top == 0 && len < mlen)
MH_ALIGN(m, len);
}
error = uiomove(mtod(m, caddr_t), (int)len, uio);
resid = uio->uio_resid;
m->m_len = len;
*mp = m;
top->m_pkthdr.len += len;
if (error)
goto release;
mp = &m->m_next;
if (resid <= 0) {
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
break;
}
} while (space > 0 && atomic);
if (dontroute)
so->so_options |= SO_DONTROUTE;
s = splsoftnet(); /* XXX */
error = (*so->so_proto->pr_usrreq)(so, (flags & MSG_OOB) ?
PRU_SENDOOB : PRU_SEND,
top, addr, control);
splx(s);
if (dontroute)
so->so_options &= ~SO_DONTROUTE;
clen = 0;
control = 0;
top = 0;
mp = &top;
if (error)
goto release;
} while (resid && space > 0);
} while (resid);
 
release:
sbunlock(&so->so_snd);
out:
if (top)
m_freem(top);
if (control)
m_freem(control);
return (error);
}
 
/*
* Implement receive operations on a socket.
* We depend on the way that records are added to the sockbuf
* by sbappend*. In particular, each record (mbufs linked through m_next)
* must begin with an address if the protocol so specifies,
* followed by an optional mbuf or mbufs containing ancillary data,
* and then zero or more mbufs of data.
* In order to avoid blocking network interrupts for the entire time here,
* we splx() while doing the actual copy to user space.
* Although the sockbuf is locked, new data may still be appended,
* and thus we must maintain consistency of the sockbuf during that time.
*
* The caller may receive the data as a single mbuf chain by supplying
* an mbuf **mp0 for use in returning the chain. The uio is then used
* only for the count in uio_resid.
*/
int
soreceive(so, paddr, uio, mp0, controlp, flagsp)
register struct socket *so;
struct mbuf **paddr;
struct uio *uio;
struct mbuf **mp0;
struct mbuf **controlp;
int *flagsp;
{
register struct mbuf *m, **mp;
register int flags, len, error, s, offset;
struct protosw *pr = so->so_proto;
struct mbuf *nextrecord;
int moff, type = 0;
size_t orig_resid = uio->uio_resid;
int uio_error = 0;
int resid;
 
mp = mp0;
if (paddr)
*paddr = 0;
if (controlp)
*controlp = 0;
if (flagsp)
flags = *flagsp &~ MSG_EOR;
else
flags = 0;
if (so->so_state & SS_NBIO)
flags |= MSG_DONTWAIT;
if (flags & MSG_OOB) {
m = m_get(M_WAIT, MT_DATA);
error = (*pr->pr_usrreq)(so, PRU_RCVOOB, m,
(struct mbuf *)(long)(flags & MSG_PEEK), NULL);
if (error)
goto bad;
do {
error = uiomove(mtod(m, caddr_t),
(int) min(uio->uio_resid, m->m_len), uio);
m = m_free(m);
} while (uio->uio_resid && error == 0 && m);
bad:
if (m)
m_freem(m);
return (error);
}
if (mp)
*mp = (struct mbuf *)0;
if (so->so_state & SS_ISCONFIRMING && uio->uio_resid)
(*pr->pr_usrreq)(so, PRU_RCVD, NULL, NULL, NULL);
 
restart:
if ((error = sblock(&so->so_rcv, SBLOCKWAIT(flags))) != 0)
return (error);
s = splsoftnet();
 
m = so->so_rcv.sb_mb;
/*
* If we have less data than requested, block awaiting more
* (subject to any timeout) if:
* 1. the current count is less than the low water mark,
* 2. MSG_WAITALL is set, and it is possible to do the entire
* receive operation at once if we block (resid <= hiwat), or
* 3. MSG_DONTWAIT is not set.
* If MSG_WAITALL is set but resid is larger than the receive buffer,
* we have to do the receive in sections, and thus risk returning
* a short count if a timeout or signal occurs after we start.
*/
if (m == 0 || (((flags & MSG_DONTWAIT) == 0 &&
so->so_rcv.sb_cc < uio->uio_resid) &&
(so->so_rcv.sb_cc < so->so_rcv.sb_lowat ||
((flags & MSG_WAITALL) && uio->uio_resid <= so->so_rcv.sb_hiwat)) &&
m->m_nextpkt == 0 && (pr->pr_flags & PR_ATOMIC) == 0)) {
#ifdef DIAGNOSTIC
if (m == 0 && so->so_rcv.sb_cc)
panic("receive 1");
#endif
if (so->so_error) {
if (m)
goto dontblock;
error = so->so_error;
if ((flags & MSG_PEEK) == 0)
so->so_error = 0;
goto release;
}
if (so->so_state & SS_CANTRCVMORE) {
if (m)
goto dontblock;
else
goto release;
}
for (; m; m = m->m_next)
if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) {
m = so->so_rcv.sb_mb;
goto dontblock;
}
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
(so->so_proto->pr_flags & PR_CONNREQUIRED)) {
error = ENOTCONN;
goto release;
}
if (uio->uio_resid == 0 && controlp == NULL)
goto release;
if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) {
error = EWOULDBLOCK;
goto release;
}
sbunlock(&so->so_rcv);
error = sbwait(&so->so_rcv);
splx(s);
if (error)
return (error);
goto restart;
}
dontblock:
#ifdef notyet /* XXXX */
if (uio->uio_procp)
uio->uio_procp->p_stats->p_ru.ru_msgrcv++;
#endif
nextrecord = m->m_nextpkt;
if (pr->pr_flags & PR_ADDR) {
#ifdef DIAGNOSTIC
if (m->m_type != MT_SONAME)
panic("receive 1a");
#endif
orig_resid = 0;
if (flags & MSG_PEEK) {
if (paddr)
*paddr = m_copy(m, 0, m->m_len);
m = m->m_next;
} else {
sbfree(&so->so_rcv, m);
if (paddr) {
*paddr = m;
so->so_rcv.sb_mb = m->m_next;
m->m_next = 0;
m = so->so_rcv.sb_mb;
} else {
MFREE(m, so->so_rcv.sb_mb);
m = so->so_rcv.sb_mb;
}
}
}
while (m && m->m_type == MT_CONTROL && error == 0) {
if (flags & MSG_PEEK) {
if (controlp)
*controlp = m_copy(m, 0, m->m_len);
m = m->m_next;
} else {
sbfree(&so->so_rcv, m);
if (controlp) {
if (pr->pr_domain->dom_externalize &&
mtod(m, struct cmsghdr *)->cmsg_type ==
SCM_RIGHTS)
error = (*pr->pr_domain->dom_externalize)(m);
*controlp = m;
so->so_rcv.sb_mb = m->m_next;
m->m_next = 0;
m = so->so_rcv.sb_mb;
} else {
MFREE(m, so->so_rcv.sb_mb);
m = so->so_rcv.sb_mb;
}
}
if (controlp) {
orig_resid = 0;
controlp = &(*controlp)->m_next;
}
}
if (m) {
if ((flags & MSG_PEEK) == 0)
m->m_nextpkt = nextrecord;
type = m->m_type;
if (type == MT_OOBDATA)
flags |= MSG_OOB;
if (m->m_flags & M_BCAST)
flags |= MSG_BCAST;
if (m->m_flags & M_MCAST)
flags |= MSG_MCAST;
}
moff = 0;
offset = 0;
while (m && uio->uio_resid > 0 && error == 0) {
if (m->m_type == MT_OOBDATA) {
if (type != MT_OOBDATA)
break;
} else if (type == MT_OOBDATA)
break;
#ifdef DIAGNOSTIC
else if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
panic("receive 3");
#endif
so->so_state &= ~SS_RCVATMARK;
len = uio->uio_resid;
if (so->so_oobmark && len > so->so_oobmark - offset)
len = so->so_oobmark - offset;
if (len > m->m_len - moff)
len = m->m_len - moff;
/*
* If mp is set, just pass back the mbufs.
* Otherwise copy them out via the uio, then free.
* Sockbuf must be consistent here (points to current mbuf,
* it points to next record) when we drop priority;
* we must note any additions to the sockbuf when we
* block interrupts again.
*/
if (mp == 0 && uio_error == 0) {
resid = uio->uio_resid;
splx(s);
uio_error =
uiomove(mtod(m, caddr_t) + moff, (int)len,
uio);
s = splsoftnet();
if (uio_error)
uio->uio_resid = resid - len;
} else
uio->uio_resid -= len;
if (len == m->m_len - moff) {
if (m->m_flags & M_EOR)
flags |= MSG_EOR;
if (flags & MSG_PEEK) {
m = m->m_next;
moff = 0;
} else {
nextrecord = m->m_nextpkt;
sbfree(&so->so_rcv, m);
if (mp) {
*mp = m;
mp = &m->m_next;
so->so_rcv.sb_mb = m = m->m_next;
*mp = (struct mbuf *)0;
} else {
MFREE(m, so->so_rcv.sb_mb);
m = so->so_rcv.sb_mb;
}
if (m)
m->m_nextpkt = nextrecord;
}
} else {
if (flags & MSG_PEEK)
moff += len;
else {
if (mp)
*mp = m_copym(m, 0, len, M_WAIT);
m->m_data += len;
m->m_len -= len;
so->so_rcv.sb_cc -= len;
}
}
if (so->so_oobmark) {
if ((flags & MSG_PEEK) == 0) {
so->so_oobmark -= len;
if (so->so_oobmark == 0) {
so->so_state |= SS_RCVATMARK;
break;
}
} else {
offset += len;
if (offset == so->so_oobmark)
break;
}
}
if (flags & MSG_EOR)
break;
/*
* If the MSG_WAITALL flag is set (for non-atomic socket),
* we must not quit until "uio->uio_resid == 0" or an error
* termination. If a signal/timeout occurs, return
* with a short count but without error.
* Keep sockbuf locked against other readers.
*/
while (flags & MSG_WAITALL && m == 0 && uio->uio_resid > 0 &&
!sosendallatonce(so) && !nextrecord) {
if (so->so_error || so->so_state & SS_CANTRCVMORE)
break;
error = sbwait(&so->so_rcv);
if (error) {
sbunlock(&so->so_rcv);
splx(s);
return (0);
}
if ((m = so->so_rcv.sb_mb) != NULL)
nextrecord = m->m_nextpkt;
}
}
 
if (m && pr->pr_flags & PR_ATOMIC) {
flags |= MSG_TRUNC;
if ((flags & MSG_PEEK) == 0)
(void) sbdroprecord(&so->so_rcv);
}
if ((flags & MSG_PEEK) == 0) {
if (m == 0)
so->so_rcv.sb_mb = nextrecord;
if (pr->pr_flags & PR_WANTRCVD && so->so_pcb)
(*pr->pr_usrreq)(so, PRU_RCVD, NULL,
(struct mbuf *)(long)flags, NULL);
}
if (orig_resid == uio->uio_resid && orig_resid &&
(flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) {
sbunlock(&so->so_rcv);
splx(s);
goto restart;
}
 
if (uio_error)
error = uio_error;
if (flagsp)
*flagsp |= flags;
release:
sbunlock(&so->so_rcv);
splx(s);
return (error);
}
 
int
soshutdown(so, how)
register struct socket *so;
register int how;
{
register struct protosw *pr = so->so_proto;
 
how++;
if (how & ~(FREAD|FWRITE))
return (EINVAL);
if (how & FREAD)
sorflush(so);
if (how & FWRITE)
return (*pr->pr_usrreq)(so, PRU_SHUTDOWN, NULL, NULL, NULL);
return (0);
}
 
void
sorflush(so)
register struct socket *so;
{
register struct sockbuf *sb = &so->so_rcv;
register struct protosw *pr = so->so_proto;
register int s;
struct sockbuf asb;
 
sb->sb_flags |= SB_NOINTR;
(void) sblock(sb, M_WAITOK);
s = splimp();
socantrcvmore(so);
sbunlock(sb);
asb = *sb;
bzero((caddr_t)sb, sizeof (*sb));
splx(s);
if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose)
(*pr->pr_domain->dom_dispose)(asb.sb_mb);
sbrelease(&asb);
}
 
int
sosetopt(so, level, optname, m0)
register struct socket *so;
int level, optname;
struct mbuf *m0;
{
int error = 0;
register struct mbuf *m = m0;
 
if (level != SOL_SOCKET) {
if (so->so_proto && so->so_proto->pr_ctloutput)
return ((*so->so_proto->pr_ctloutput)
(PRCO_SETOPT, so, level, optname, &m0));
error = ENOPROTOOPT;
} else {
switch (optname) {
 
case SO_LINGER:
if (m == NULL || m->m_len != sizeof (struct linger)) {
error = EINVAL;
goto bad;
}
so->so_linger = mtod(m, struct linger *)->l_linger;
/* fall thru... */
 
case SO_DEBUG:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_USELOOPBACK:
case SO_BROADCAST:
case SO_REUSEADDR:
case SO_REUSEPORT:
case SO_OOBINLINE:
if (m == NULL || m->m_len < sizeof (int)) {
error = EINVAL;
goto bad;
}
if (*mtod(m, int *))
so->so_options |= optname;
else
so->so_options &= ~optname;
break;
 
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
{
u_long cnt;
 
if (m == NULL || m->m_len < sizeof (int)) {
error = EINVAL;
goto bad;
}
cnt = *mtod(m, int *);
if ((long)cnt <= 0)
cnt = 1;
switch (optname) {
 
case SO_SNDBUF:
case SO_RCVBUF:
if (sbreserve(optname == SO_SNDBUF ?
&so->so_snd : &so->so_rcv,
cnt) == 0) {
error = ENOBUFS;
goto bad;
}
break;
 
case SO_SNDLOWAT:
so->so_snd.sb_lowat = (cnt > so->so_snd.sb_hiwat) ?
so->so_snd.sb_hiwat : cnt;
break;
case SO_RCVLOWAT:
so->so_rcv.sb_lowat = (cnt > so->so_rcv.sb_hiwat) ?
so->so_rcv.sb_hiwat : cnt;
break;
}
break;
}
 
case SO_SNDTIMEO:
case SO_RCVTIMEO:
{
struct timeval *tv;
short val;
 
if (m == NULL || m->m_len < sizeof (*tv)) {
error = EINVAL;
goto bad;
}
tv = mtod(m, struct timeval *);
if (tv->tv_sec * hz + tv->tv_usec / tick > SHRT_MAX) {
error = EDOM;
goto bad;
}
val = tv->tv_sec * hz + tv->tv_usec / tick;
 
switch (optname) {
 
case SO_SNDTIMEO:
so->so_snd.sb_timeo = val;
break;
case SO_RCVTIMEO:
so->so_rcv.sb_timeo = val;
break;
}
break;
}
 
default:
error = ENOPROTOOPT;
break;
}
if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) {
(void) ((*so->so_proto->pr_ctloutput)
(PRCO_SETOPT, so, level, optname, &m0));
m = NULL; /* freed by protocol */
}
}
bad:
if (m)
(void) m_free(m);
return (error);
}
 
int
sogetopt(so, level, optname, mp)
register struct socket *so;
int level, optname;
struct mbuf **mp;
{
register struct mbuf *m;
 
if (level != SOL_SOCKET) {
if (so->so_proto && so->so_proto->pr_ctloutput) {
return ((*so->so_proto->pr_ctloutput)
(PRCO_GETOPT, so, level, optname, mp));
} else
return (ENOPROTOOPT);
} else {
m = m_get(M_WAIT, MT_SOOPTS);
m->m_len = sizeof (int);
 
switch (optname) {
 
case SO_LINGER:
m->m_len = sizeof (struct linger);
mtod(m, struct linger *)->l_onoff =
so->so_options & SO_LINGER;
mtod(m, struct linger *)->l_linger = so->so_linger;
break;
 
case SO_USELOOPBACK:
case SO_DONTROUTE:
case SO_DEBUG:
case SO_KEEPALIVE:
case SO_REUSEADDR:
case SO_REUSEPORT:
case SO_BROADCAST:
case SO_OOBINLINE:
*mtod(m, int *) = so->so_options & optname;
break;
 
case SO_TYPE:
*mtod(m, int *) = so->so_type;
break;
 
case SO_ERROR:
*mtod(m, int *) = so->so_error;
so->so_error = 0;
break;
 
case SO_SNDBUF:
*mtod(m, int *) = so->so_snd.sb_hiwat;
break;
 
case SO_RCVBUF:
*mtod(m, int *) = so->so_rcv.sb_hiwat;
break;
 
case SO_SNDLOWAT:
*mtod(m, int *) = so->so_snd.sb_lowat;
break;
 
case SO_RCVLOWAT:
*mtod(m, int *) = so->so_rcv.sb_lowat;
break;
 
case SO_SNDTIMEO:
case SO_RCVTIMEO:
{
int val = (optname == SO_SNDTIMEO ?
so->so_snd.sb_timeo : so->so_rcv.sb_timeo);
 
m->m_len = sizeof(struct timeval);
mtod(m, struct timeval *)->tv_sec = val / hz;
mtod(m, struct timeval *)->tv_usec =
(val % hz) * tick;
break;
}
 
default:
(void)m_free(m);
return (ENOPROTOOPT);
}
*mp = m;
return (0);
}
}
 
void
sohasoutofband(so)
register struct socket *so;
{
#ifndef __ECOS
csignal(so->so_pgid, SIGURG, so->so_siguid, so->so_sigeuid);
#endif
selwakeup(&so->so_rcv.sb_sel);
}
/uipc_mbuf.c
0,0 → 1,1096
//==========================================================================
//
// sys/kern/uipc_mbuf.c
//
//
//
//==========================================================================
//####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: uipc_mbuf.c,v 1.18 1999/12/05 07:30:31 angelos Exp $ */
/* $NetBSD: uipc_mbuf.c,v 1.15.4.1 1996/06/13 17:11:44 cgd Exp $ */
 
/*
* Copyright (c) 1982, 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
*/
 
/*
%%% portions-copyright-nrl-95
Portions of this software are Copyright 1995-1998 by Randall Atkinson,
Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights
Reserved. All rights under this copyright have been assigned to the US
Naval Research Laboratory (NRL). The NRL Copyright Notice and License
Agreement Version 1.1 (January 17, 1995) applies to these portions of the
software.
You should have received a copy of the license with this software. If you
didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
*/
 
#include <sys/param.h>
#ifndef __ECOS
#include <sys/systm.h>
#include <sys/proc.h>
#endif
#include <sys/malloc.h>
#ifndef __ECOS
#include <sys/map.h>
#endif
#define MBTYPES
#include <sys/mbuf.h>
#include <sys/kernel.h>
#ifndef __ECOS
#include <sys/syslog.h>
#endif
#include <sys/domain.h>
#include <sys/protosw.h>
 
#include <machine/cpu.h>
 
#ifndef __ECOS
#include <vm/vm.h>
#endif
 
#if defined(UVM)
#include <uvm/uvm_extern.h>
#endif
 
#ifndef __ECOS
extern vm_map_t mb_map;
#endif
struct mbuf *mbutl;
char *mclrefcnt;
int needqueuedrain;
 
#ifdef __ECOS
extern void setsoftnet(void);
extern void *cyg_net_cluster_alloc(void);
#endif
 
/* Declarations of variables move from mbuf.h to keep g++ happy */
#ifdef __ECOS
struct mbstat mbstat;
union mcluster *mclfree;
int max_linkhdr; /* largest link-level header */
int max_protohdr; /* largest protocol header */
int max_hdr; /* largest link+protocol header */
int max_datalen; /* MHLEN - max_hdr */
#ifdef MBTYPES
int mbtypes[] = { /* XXX */
M_FREE, /* MT_FREE 0 should be on free list */
M_MBUF, /* MT_DATA 1 dynamic (data) allocation */
M_MBUF, /* MT_HEADER 2 packet header */
M_SOCKET, /* MT_SOCKET 3 socket structure */
M_PCB, /* MT_PCB 4 protocol control block */
M_RTABLE, /* MT_RTABLE 5 routing tables */
M_HTABLE, /* MT_HTABLE 6 IMP host tables */
0, /* MT_ATABLE 7 address resolution tables */
M_MBUF, /* MT_SONAME 8 socket name */
0, /* 9 */
M_SOOPTS, /* MT_SOOPTS 10 socket options */
M_FTABLE, /* MT_FTABLE 11 fragment reassembly header */
M_MBUF, /* MT_RIGHTS 12 access rights */
M_IFADDR, /* MT_IFADDR 13 interface address */
M_MBUF, /* MT_CONTROL 14 extra-data protocol message */
M_MBUF, /* MT_OOBDATA 15 expedited data */
#ifdef DATAKIT
25, 26, 27, 28, 29, 30, 31, 32 /* datakit ugliness */
#endif
};
#endif
#endif // __ECOS
 
void
mbinit()
{
int s;
 
s = splimp();
#ifdef __ECOS
if (m_clalloc(1, M_DONTWAIT) == 0)
#else
if (m_clalloc(max(4096 / CLBYTES, 1), M_DONTWAIT) == 0)
#endif
goto bad;
splx(s);
return;
bad:
panic("mbinit");
}
 
/*
* Allocate some number of mbuf clusters
* and place on cluster free list.
* Must be called at splimp.
*/
/* ARGSUSED */
int
m_clalloc(ncl, nowait)
register int ncl;
int nowait;
{
#ifdef __ECOS
caddr_t p;
int i;
 
if (ncl != 1) {
panic("Allocate multiple clusters!");
}
p = (caddr_t)cyg_net_cluster_alloc();
if (p == NULL) {
m_reclaim();
return (mclfree != NULL);
}
for (i = 0; i < ncl; i++) {
((union mcluster *)p)->mcl_next = mclfree;
mclfree = (union mcluster *)p;
p += MCLBYTES;
mbstat.m_clfree++;
}
mbstat.m_clusters += ncl;
return (1);
#else // __ECOS
volatile static struct timeval lastlogged;
struct timeval curtime, logdiff;
register caddr_t p;
register int i;
int npg, s;
 
npg = ncl * CLSIZE;
#if defined(UVM)
p = (caddr_t)uvm_km_kmemalloc(mb_map, uvmexp.mb_object, ctob(npg),
nowait ? 0 : UVM_KMF_NOWAIT);
#else
p = (caddr_t)kmem_malloc(mb_map, ctob(npg), !nowait);
#endif
if (p == NULL) {
s = splclock();
curtime = time;
splx(s);
timersub(&curtime, &lastlogged, &logdiff);
if (logdiff.tv_sec >= 60) {
lastlogged = curtime;
log(LOG_ERR, "mb_map full\n");
}
m_reclaim();
return (mclfree != NULL);
}
ncl = ncl * CLBYTES / MCLBYTES;
for (i = 0; i < ncl; i++) {
((union mcluster *)p)->mcl_next = mclfree;
mclfree = (union mcluster *)p;
p += MCLBYTES;
mbstat.m_clfree++;
}
mbstat.m_clusters += ncl;
return (1);
#endif // __ECOS
}
 
/*
* When MGET failes, ask protocols to free space when short of memory,
* then re-attempt to allocate an mbuf.
*/
struct mbuf *
m_retry(i, t)
int i, t;
{
register struct mbuf *m;
 
if (i & M_DONTWAIT) {
needqueuedrain = 1;
setsoftnet();
return (NULL);
}
m_reclaim();
#define m_retry(i, t) NULL
MGET(m, i, t);
#undef m_retry
return (m);
}
 
/*
* As above; retry an MGETHDR.
*/
struct mbuf *
m_retryhdr(i, t)
int i, t;
{
register struct mbuf *m;
 
if (i & M_DONTWAIT) {
needqueuedrain = 1;
setsoftnet();
return (NULL);
}
m_reclaim();
#define m_retryhdr(i, t) NULL
MGETHDR(m, i, t);
#undef m_retryhdr
return (m);
}
 
void
m_reclaim()
{
register struct domain *dp;
register struct protosw *pr;
int s = splimp();
 
needqueuedrain = 0;
for (dp = domains; dp; dp = dp->dom_next)
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_drain)
(*pr->pr_drain)();
splx(s);
mbstat.m_drain++;
}
 
/*
* Space allocation routines.
* These are also available as macros
* for critical paths.
*/
struct mbuf *
m_get(nowait, type)
int nowait, type;
{
register struct mbuf *m;
 
MGET(m, nowait, type);
return (m);
}
 
struct mbuf *
m_gethdr(nowait, type)
int nowait, type;
{
register struct mbuf *m;
 
MGETHDR(m, nowait, type);
return (m);
}
 
struct mbuf *
m_getclr(nowait, type)
int nowait, type;
{
register struct mbuf *m;
 
MGET(m, nowait, type);
if (m == NULL)
return (NULL);
bzero(mtod(m, caddr_t), MLEN);
return (m);
}
 
struct mbuf *
m_free(m)
struct mbuf *m;
{
register struct mbuf *n;
 
MFREE(m, n);
return (n);
}
 
void
m_freem(m)
register struct mbuf *m;
{
register struct mbuf *n;
 
if (m == NULL)
return;
do {
MFREE(m, n);
} while ((m = n) != NULL);
}
 
/*
* Mbuffer utility routines.
*/
 
/*
* Lesser-used path for M_PREPEND:
* allocate new mbuf to prepend to chain,
* copy junk along.
*/
struct mbuf *
m_prepend(m, len, how)
register struct mbuf *m;
int len, how;
{
struct mbuf *mn;
 
MGET(mn, how, m->m_type);
if (mn == NULL) {
m_freem(m);
return (NULL);
}
if (m->m_flags & M_PKTHDR) {
M_COPY_PKTHDR(mn, m);
m->m_flags &= ~M_PKTHDR;
}
mn->m_next = m;
m = mn;
if (len < MHLEN)
MH_ALIGN(m, len);
m->m_len = len;
return (m);
}
 
/*
* Make a copy of an mbuf chain starting "off0" bytes from the beginning,
* continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf.
* The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller.
*/
int MCFail;
 
struct mbuf *
m_copym(m, off0, len, wait)
register struct mbuf *m;
int off0, wait;
register int len;
{
register struct mbuf *n, **np;
register int off = off0;
struct mbuf *top;
int copyhdr = 0;
 
if (off < 0)
panic("m_copym: off %d < 0", off);
if (len < 0)
panic("m_copym: len %d < 0", len);
if (off == 0 && m->m_flags & M_PKTHDR)
copyhdr = 1;
while (off > 0) {
if (m == NULL)
panic("m_copym: null mbuf");
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
np = &top;
top = NULL;
while (len > 0) {
if (m == NULL) {
if (len != M_COPYALL)
panic("m_copym: %d not M_COPYALL", len);
break;
}
MGET(n, wait, m->m_type);
*np = n;
if (n == NULL)
goto nospace;
if (copyhdr) {
M_COPY_PKTHDR(n, m);
if (len == M_COPYALL)
n->m_pkthdr.len -= off0;
else
n->m_pkthdr.len = len;
copyhdr = 0;
}
n->m_len = min(len, m->m_len - off);
if (m->m_flags & M_EXT) {
n->m_data = m->m_data + off;
if (!m->m_ext.ext_ref)
mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
else
(*(m->m_ext.ext_ref))(m);
n->m_ext = m->m_ext;
n->m_flags |= M_EXT;
} else
bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
(unsigned)n->m_len);
if (len != M_COPYALL)
len -= n->m_len;
off = 0;
m = m->m_next;
np = &n->m_next;
}
if (top == NULL)
MCFail++;
return (top);
nospace:
m_freem(top);
MCFail++;
return (NULL);
}
 
/*
* m_copym2() is like m_copym(), except it COPIES cluster mbufs, instead
* of merely bumping the reference count.
*/
struct mbuf *
m_copym2(m, off0, len, wait)
register struct mbuf *m;
int off0, wait;
register int len;
{
register struct mbuf *n, **np;
register int off = off0;
struct mbuf *top;
int copyhdr = 0;
 
if (len < 0)
panic("m_copym2: len %d < 0", len);
if (off < 0)
panic("m_copym2: off %d < 0", off);
if (off == 0 && m->m_flags & M_PKTHDR)
copyhdr = 1;
while (off > 0) {
if (m == NULL)
panic("m_copym2: null mbuf");
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
np = &top;
top = NULL;
while (len > 0) {
if (m == NULL) {
if (len != M_COPYALL)
panic("m_copym2: %d != M_COPYALL", len);
break;
}
MGET(n, wait, m->m_type);
*np = n;
if (n == NULL)
goto nospace;
if (copyhdr) {
M_COPY_PKTHDR(n, m);
if (len == M_COPYALL)
n->m_pkthdr.len -= off0;
else
n->m_pkthdr.len = len;
copyhdr = 0;
}
n->m_len = min(len, m->m_len - off);
if ((m->m_flags & M_EXT) && (n->m_len > MHLEN)) {
/* This is a cheesy hack. */
MCLGET(n, wait);
if (n->m_flags & M_EXT)
bcopy(mtod(m, caddr_t) + off, mtod(n, caddr_t),
(unsigned)n->m_len);
else
goto nospace;
} else
bcopy(mtod(m, caddr_t) + off, mtod(n, caddr_t),
(unsigned)n->m_len);
if (len != M_COPYALL)
len -= n->m_len;
off = 0;
m = m->m_next;
np = &n->m_next;
}
if (top == NULL)
MCFail++;
return (top);
nospace:
m_freem(top);
MCFail++;
return (NULL);
}
 
/*
* Copy data from an mbuf chain starting "off" bytes from the beginning,
* continuing for "len" bytes, into the indicated buffer.
*/
void
m_copydata(m, off, len, cp)
register struct mbuf *m;
register int off;
register int len;
caddr_t cp;
{
register unsigned count;
 
if (off < 0)
panic("m_copydata: off %d < 0", off);
if (len < 0)
panic("m_copydata: len %d < 0", len);
while (off > 0) {
if (m == NULL)
panic("m_copydata: null mbuf in skip");
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
while (len > 0) {
if (m == NULL)
panic("m_copydata: null mbuf");
count = min(m->m_len - off, len);
bcopy(mtod(m, caddr_t) + off, cp, count);
len -= count;
cp += count;
off = 0;
m = m->m_next;
}
}
 
/*
* Concatenate mbuf chain n to m.
* Both chains must be of the same type (e.g. MT_DATA).
* Any m_pkthdr is not updated.
*/
void
m_cat(m, n)
register struct mbuf *m, *n;
{
while (m->m_next)
m = m->m_next;
while (n) {
if (m->m_flags & M_EXT ||
m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) {
/* just join the two chains */
m->m_next = n;
return;
}
/* splat the data from one into the other */
bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
(u_int)n->m_len);
m->m_len += n->m_len;
n = m_free(n);
}
}
 
void
m_adj(mp, req_len)
struct mbuf *mp;
int req_len;
{
register int len = req_len;
register struct mbuf *m;
register int count;
 
if ((m = mp) == NULL)
return;
if (len >= 0) {
/*
* Trim from head.
*/
while (m != NULL && len > 0) {
if (m->m_len <= len) {
len -= m->m_len;
m->m_len = 0;
m = m->m_next;
} else {
m->m_len -= len;
m->m_data += len;
len = 0;
}
}
m = mp;
if (mp->m_flags & M_PKTHDR)
m->m_pkthdr.len -= (req_len - len);
} else {
/*
* Trim from tail. Scan the mbuf chain,
* calculating its length and finding the last mbuf.
* If the adjustment only affects this mbuf, then just
* adjust and return. Otherwise, rescan and truncate
* after the remaining size.
*/
len = -len;
count = 0;
for (;;) {
count += m->m_len;
if (m->m_next == NULL)
break;
m = m->m_next;
}
if (m->m_len >= len) {
m->m_len -= len;
if (mp->m_flags & M_PKTHDR)
mp->m_pkthdr.len -= len;
return;
}
count -= len;
if (count < 0)
count = 0;
/*
* Correct length for chain is "count".
* Find the mbuf with last data, adjust its length,
* and toss data from remaining mbufs on chain.
*/
m = mp;
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len = count;
for (; m; m = m->m_next) {
if (m->m_len >= count) {
m->m_len = count;
break;
}
count -= m->m_len;
}
while ((m = m->m_next) != NULL)
m->m_len = 0;
}
}
 
/*
* Rearange an mbuf chain so that len bytes are contiguous
* and in the data area of an mbuf (so that mtod and dtom
* will work for a structure of size len). Returns the resulting
* mbuf chain on success, frees it and returns null on failure.
* If there is room, it will add up to max_protohdr-len extra bytes to the
* contiguous region in an attempt to avoid being called next time.
*/
int MPFail;
 
struct mbuf *
m_pullup(n, len)
register struct mbuf *n;
int len;
{
register struct mbuf *m;
register int count;
int space;
 
/*
* If first mbuf has no cluster, and has room for len bytes
* without shifting current data, pullup into it,
* otherwise allocate a new mbuf to prepend to the chain.
*/
if ((n->m_flags & M_EXT) == 0 &&
n->m_data + len < &n->m_dat[MLEN] && n->m_next) {
if (n->m_len >= len)
return (n);
m = n;
n = n->m_next;
len -= m->m_len;
} else {
if (len > MHLEN)
goto bad;
MGET(m, M_DONTWAIT, n->m_type);
if (m == NULL)
goto bad;
m->m_len = 0;
if (n->m_flags & M_PKTHDR) {
M_COPY_PKTHDR(m, n);
n->m_flags &= ~M_PKTHDR;
}
}
space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
do {
count = min(min(max(len, max_protohdr), space), n->m_len);
bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
(unsigned)count);
len -= count;
m->m_len += count;
n->m_len -= count;
space -= count;
if (n->m_len)
n->m_data += count;
else
n = m_free(n);
} while (len > 0 && n);
if (len > 0) {
(void)m_free(m);
goto bad;
}
m->m_next = n;
return (m);
bad:
m_freem(n);
MPFail++;
return (NULL);
}
 
/*
* m_pullup2() works like m_pullup, save that len can be <= MCLBYTES.
* m_pullup2() only works on values of len such that MHLEN < len <= MCLBYTES,
* it calls m_pullup() for values <= MHLEN. It also only coagulates the
* reqested number of bytes. (For those of us who expect unwieldly option
* headers.
*
* KEBE SAYS: Remember that dtom() calls with data in clusters does not work!
*/
struct mbuf *
m_pullup2(n, len)
register struct mbuf *n;
int len;
{
register struct mbuf *m;
register int count;
int space;
if (len <= MHLEN)
return m_pullup(n, len);
if ((n->m_flags & M_EXT) != 0 &&
n->m_data + len < &n->m_data[MCLBYTES] && n->m_next) {
if (n->m_len >= len)
return (n);
m = n;
n = n->m_next;
len -= m->m_len;
} else {
if (len > MCLBYTES)
goto bad;
MGET(m, M_DONTWAIT, n->m_type);
if (m == NULL)
goto bad;
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0)
goto bad;
m->m_len = 0;
if (n->m_flags & M_PKTHDR) {
/* M_COPY_PKTHDR(m, n);*//* Too many adverse side effects. */
m->m_pkthdr = n->m_pkthdr;
m->m_flags = (n->m_flags & M_COPYFLAGS) | M_EXT;
n->m_flags &= ~M_PKTHDR;
/* n->m_data is cool. */
}
}
 
do {
count = min(len, n->m_len);
bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
(unsigned)count);
len -= count;
m->m_len += count;
n->m_len -= count;
space -= count;
if (n->m_len)
n->m_data += count;
else
n = m_free(n);
} while (len > 0 && n);
if (len > 0) {
(void)m_free(m);
goto bad;
}
m->m_next = n;
return (m);
bad:
m_freem(n);
MPFail++;
return (NULL);
}
 
/*
* Inject a new mbuf chain of length siz in mbuf chain m0 at
* position len0. Returns a pointer to the first injected mbuf, or
* NULL on failure (m0 is left undisturbed). Note that if there is
* enough space for an object of size siz in the appropriate position,
* no memory will be allocated. Also, there will be no data movement in
* the first len0 bytes (pointers to that will remain valid).
*
* XXX It is assumed that siz is less than the size of an mbuf at the moment.
*/
struct mbuf *
m_inject(m0, len0, siz, wait)
register struct mbuf *m0;
int len0, siz, wait;
{
register struct mbuf *m, *n, *n2 = NULL, *n3;
unsigned len = len0, remain;
 
if ((siz >= MHLEN) || (len0 <= 0))
return (NULL);
for (m = m0; m && len > m->m_len; m = m->m_next)
len -= m->m_len;
if (m == NULL)
return (NULL);
remain = m->m_len - len;
if (remain == 0) {
if ((m->m_next) && (M_LEADINGSPACE(m->m_next) >= siz)) {
m->m_next->m_len += siz;
m0->m_pkthdr.len += siz;
m->m_next->m_data -= siz;
return m->m_next;
}
} else {
n2 = m_copym2(m, len, remain, wait);
if (n2 == NULL)
return (NULL);
}
 
MGET(n, wait, MT_DATA);
if (n == NULL) {
if (n2)
m_freem(n2);
return (NULL);
}
 
n->m_len = siz;
m0->m_pkthdr.len += siz;
m->m_len -= remain; /* Trim */
if (n2) {
for (n3 = n; n3->m_next != NULL; n3 = n3->m_next)
;
n3->m_next = n2;
} else
n3 = n;
for (; n3->m_next != NULL; n3 = n3->m_next)
;
n3->m_next = m->m_next;
m->m_next = n;
return n;
}
 
/*
* Partition an mbuf chain in two pieces, returning the tail --
* all but the first len0 bytes. In case of failure, it returns NULL and
* attempts to restore the chain to its original state.
*/
struct mbuf *
m_split(m0, len0, wait)
register struct mbuf *m0;
int len0, wait;
{
register struct mbuf *m, *n;
unsigned len = len0, remain, olen;
 
for (m = m0; m && len > m->m_len; m = m->m_next)
len -= m->m_len;
if (m == NULL)
return (NULL);
remain = m->m_len - len;
if (m0->m_flags & M_PKTHDR) {
MGETHDR(n, wait, m0->m_type);
if (n == NULL)
return (NULL);
n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif;
n->m_pkthdr.len = m0->m_pkthdr.len - len0;
olen = m0->m_pkthdr.len;
m0->m_pkthdr.len = len0;
if (m->m_flags & M_EXT)
goto extpacket;
if (remain > MHLEN) {
/* m can't be the lead packet */
MH_ALIGN(n, 0);
n->m_next = m_split(m, len, wait);
if (n->m_next == NULL) {
(void) m_free(n);
m0->m_pkthdr.len = olen;
return (NULL);
} else
return (n);
} else
MH_ALIGN(n, remain);
} else if (remain == 0) {
n = m->m_next;
m->m_next = NULL;
return (n);
} else {
MGET(n, wait, m->m_type);
if (n == NULL)
return (NULL);
M_ALIGN(n, remain);
}
extpacket:
if (m->m_flags & M_EXT) {
n->m_flags |= M_EXT;
n->m_ext = m->m_ext;
if(!m->m_ext.ext_ref)
mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
else
(*(m->m_ext.ext_ref))(m);
m->m_ext.ext_size = 0; /* For Accounting XXXXXX danger */
n->m_data = m->m_data + len;
} else {
bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain);
}
n->m_len = remain;
m->m_len = len;
n->m_next = m->m_next;
m->m_next = NULL;
return (n);
}
/*
* Routine to copy from device local memory into mbufs.
*/
struct mbuf *
m_devget(buf, totlen, off0, ifp, copy)
char *buf;
int totlen, off0;
struct ifnet *ifp;
void (*copy) __P((const void *, void *, size_t));
{
register struct mbuf *m;
struct mbuf *top = NULL, **mp = &top;
register int off = off0, len;
register char *cp;
char *epkt;
 
cp = buf;
epkt = cp + totlen;
if (off) {
/*
* If 'off' is non-zero, packet is trailer-encapsulated,
* so we have to skip the type and length fields.
*/
cp += off + 2 * sizeof(u_int16_t);
totlen -= 2 * sizeof(u_int16_t);
}
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return (NULL);
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = totlen;
m->m_len = MHLEN;
 
while (totlen > 0) {
if (top != NULL) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
m_freem(top);
return (NULL);
}
m->m_len = MLEN;
}
len = min(totlen, epkt - cp);
if (len >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (m->m_flags & M_EXT)
m->m_len = len = min(len, MCLBYTES);
else
len = m->m_len;
} else {
/*
* Place initial small packet/header at end of mbuf.
*/
if (len < m->m_len) {
if (top == NULL &&
len + max_linkhdr <= m->m_len)
m->m_data += max_linkhdr;
m->m_len = len;
} else
len = m->m_len;
}
if (copy)
copy(cp, mtod(m, caddr_t), (size_t)len);
else
bcopy(cp, mtod(m, caddr_t), (size_t)len);
cp += len;
*mp = m;
mp = &m->m_next;
totlen -= len;
if (cp == epkt)
cp = buf;
}
return (top);
}
 
void
m_zero(m)
struct mbuf *m;
{
while (m) {
if (m->m_flags & M_PKTHDR)
bzero((unsigned char *)m + sizeof(struct m_hdr) +
sizeof(struct pkthdr), MHLEN);
else
bzero((unsigned char *)m + sizeof(struct m_hdr), MLEN);
if ((m->m_flags & M_EXT) &&
(m->m_ext.ext_free == NULL) &&
!mclrefcnt[mtocl((m)->m_ext.ext_buf)])
bzero(m->m_ext.ext_buf, m->m_ext.ext_size);
m = m->m_next;
}
}
 
/*
* Apply function f to the data in an mbuf chain starting "off" bytes from the
* beginning, continuing for "len" bytes.
*/
int
m_apply(m, off, len, f, fstate)
struct mbuf *m;
int off;
int len;
/* fstate, data, len */
int (*f)(caddr_t, caddr_t, unsigned int);
caddr_t fstate;
{
int rval;
unsigned int count;
 
if (len < 0)
panic("m_apply: len %d < 0", len);
if (off < 0)
panic("m_apply: off %d < 0", off);
while (off > 0) {
if (m == NULL)
panic("m_apply: null mbuf in skip");
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
while (len > 0) {
if (m == NULL)
panic("m_apply: null mbuf");
count = min(m->m_len - off, len);
 
rval = f(fstate, mtod(m, caddr_t) + off, count);
if (rval)
return (rval);
 
len -= count;
off = 0;
m = m->m_next;
}
 
return (0);
}
 
/kern_subr.c
0,0 → 1,360
//==========================================================================
//
// sys/kern/kern_subr.c
//
//
//
//==========================================================================
//####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: kern_subr.c,v 1.10 1999/11/07 17:39:14 provos Exp $ */
/* $NetBSD: kern_subr.c,v 1.15 1996/04/09 17:21:56 ragge Exp $ */
 
/*
* Copyright (c) 1982, 1986, 1991, 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.
*
* @(#)kern_subr.c 8.3 (Berkeley) 1/21/94
*/
 
#include <sys/param.h>
#ifndef __ECOS
#include <sys/systm.h>
#include <sys/proc.h>
#endif // __ECOS
#include <sys/malloc.h>
#include <sys/queue.h>
 
int
uiomove(cp, n, uio)
register caddr_t cp;
register int n;
register struct uio *uio;
{
register struct iovec *iov;
u_int cnt;
int error = 0;
 
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE)
panic("uiomove: mode");
if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
panic("uiomove proc");
#endif
while (n > 0 && uio->uio_resid) {
iov = uio->uio_iov;
cnt = iov->iov_len;
if (cnt == 0) {
uio->uio_iov++;
uio->uio_iovcnt--;
continue;
}
if (cnt > n)
cnt = n;
switch (uio->uio_segflg) {
 
case UIO_USERSPACE:
if (uio->uio_rw == UIO_READ)
error = copyout(cp, iov->iov_base, cnt);
else
error = copyin(iov->iov_base, cp, cnt);
if (error)
return (error);
break;
 
case UIO_SYSSPACE:
#if defined(UVM)
if (uio->uio_rw == UIO_READ)
error = kcopy(cp, iov->iov_base, cnt);
else
error = kcopy(iov->iov_base, cp, cnt);
if (error)
return(error);
#else
if (uio->uio_rw == UIO_READ)
bcopy((caddr_t)cp, iov->iov_base, cnt);
else
bcopy(iov->iov_base, (caddr_t)cp, cnt);
break;
#endif
}
(char *)(iov->iov_base) += cnt;
iov->iov_len -= cnt;
uio->uio_resid -= cnt;
uio->uio_offset += cnt;
cp += cnt;
n -= cnt;
}
return (error);
}
 
#ifndef __ECOS
/*
* Give next character to user as result of read.
*/
int
ureadc(c, uio)
register int c;
register struct uio *uio;
{
register struct iovec *iov;
 
if (uio->uio_resid == 0)
#ifdef DIAGNOSTIC
panic("ureadc: zero resid");
#else
return (EINVAL);
#endif
again:
if (uio->uio_iovcnt <= 0)
#ifdef DIAGNOSTIC
panic("ureadc: non-positive iovcnt");
#else
return (EINVAL);
#endif
iov = uio->uio_iov;
if (iov->iov_len <= 0) {
uio->uio_iovcnt--;
uio->uio_iov++;
goto again;
}
switch (uio->uio_segflg) {
 
case UIO_USERSPACE:
if (subyte(iov->iov_base, c) < 0)
return (EFAULT);
break;
 
case UIO_SYSSPACE:
*(char *)iov->iov_base = c;
break;
}
iov->iov_base++;
iov->iov_len--;
uio->uio_resid--;
uio->uio_offset++;
return (0);
}
#endif // __ECOS
 
/*
* General routine to allocate a hash table.
*/
#ifdef __ECOS
void *
hashinit(int elements, int type, int flags, u_long *hashmask)
#else
void *
hashinit(elements, type, flags, hashmask)
int elements, type, flags;
u_long *hashmask;
#endif
{
long hashsize;
LIST_HEAD(generic, generic) *hashtbl;
int i;
 
if (elements <= 0)
panic("hashinit: bad cnt");
for (hashsize = 1; hashsize <= elements; hashsize <<= 1)
continue;
hashsize >>= 1;
hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, flags);
for (i = 0; i < hashsize; i++)
LIST_INIT(&hashtbl[i]);
*hashmask = hashsize - 1;
return (hashtbl);
}
 
#ifndef __ECOS
/*
* "Shutdown hook" types, functions, and variables.
*/
 
struct shutdownhook_desc {
LIST_ENTRY(shutdownhook_desc) sfd_list;
void (*sfd_fn) __P((void *));
void *sfd_arg;
};
 
LIST_HEAD(, shutdownhook_desc) shutdownhook_list;
 
int shutdownhooks_done;
 
void *
shutdownhook_establish(fn, arg)
void (*fn) __P((void *));
void *arg;
{
struct shutdownhook_desc *ndp;
 
ndp = (struct shutdownhook_desc *)
malloc(sizeof (*ndp), M_DEVBUF, M_NOWAIT);
if (ndp == NULL)
return NULL;
 
ndp->sfd_fn = fn;
ndp->sfd_arg = arg;
LIST_INSERT_HEAD(&shutdownhook_list, ndp, sfd_list);
 
return (ndp);
}
 
void
shutdownhook_disestablish(vhook)
void *vhook;
{
#ifdef DIAGNOSTIC
struct shutdownhook_desc *dp;
 
for (dp = shutdownhook_list.lh_first; dp != NULL;
dp = dp->sfd_list.le_next)
if (dp == vhook)
break;
if (dp == NULL)
panic("shutdownhook_disestablish: hook not established");
#endif
 
LIST_REMOVE((struct shutdownhook_desc *)vhook, sfd_list);
}
 
/*
* Run shutdown hooks. Should be invoked immediately before the
* system is halted or rebooted, i.e. after file systems unmounted,
* after crash dump done, etc.
*/
void
doshutdownhooks()
{
struct shutdownhook_desc *dp;
 
if (shutdownhooks_done)
return;
 
for (dp = shutdownhook_list.lh_first; dp != NULL; dp =
dp->sfd_list.le_next)
(*dp->sfd_fn)(dp->sfd_arg);
}
 
/*
* "Power hook" types, functions, and variables.
*/
 
struct powerhook_desc {
LIST_ENTRY(powerhook_desc) sfd_list;
void (*sfd_fn) __P((int, void *));
void *sfd_arg;
};
 
LIST_HEAD(, powerhook_desc) powerhook_list;
 
void *
powerhook_establish(fn, arg)
void (*fn) __P((int, void *));
void *arg;
{
struct powerhook_desc *ndp;
 
ndp = (struct powerhook_desc *)
malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT);
if (ndp == NULL)
return NULL;
 
ndp->sfd_fn = fn;
ndp->sfd_arg = arg;
LIST_INSERT_HEAD(&powerhook_list, ndp, sfd_list);
 
return (ndp);
}
 
void
powerhook_disestablish(vhook)
void *vhook;
{
#ifdef DIAGNOSTIC
struct powerhook_desc *dp;
 
for (dp = powerhook_list.lh_first; dp != NULL;
dp = dp->sfd_list.le_next)
if (dp == vhook)
break;
if (dp == NULL)
panic("powerhook_disestablish: hook not established");
#endif
 
LIST_REMOVE((struct powerhook_desc *)vhook, sfd_list);
free(vhook, M_DEVBUF);
}
 
/*
* Run power hooks.
*/
void
dopowerhooks(why)
int why;
{
struct powerhook_desc *dp;
 
for (dp = LIST_FIRST(&powerhook_list);
dp != NULL;
dp = LIST_NEXT(dp, sfd_list)) {
(*dp->sfd_fn)(why, dp->sfd_arg);
}
}
#endif // __ECOS
/sys_generic.c
0,0 → 1,1025
//==========================================================================
//
// sys/kern/sys_generic.c
//
//
//
//==========================================================================
//####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
/uipc_socket2.c
0,0 → 1,909
//==========================================================================
//
// sys/kern/uipc_socket2.c
//
//
//
//==========================================================================
//####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: uipc_socket2.c,v 1.11 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */
 
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)uipc_socket2.c 8.1 (Berkeley) 6/10/93
*/
 
#include <sys/param.h>
#ifndef __ECOS
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/buf.h>
#endif
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#ifndef __ECOS
#include <sys/signalvar.h>
#endif
 
#ifdef __ECOS
#include <cyg/infra/diag.h>
#endif
 
/*
* Primitive routines for operating on sockets and socket buffers
*/
 
/* strings for sleep message: */
char netio[] = "netio";
char netcon[] = "netcon";
char netcls[] = "netcls";
 
u_long sb_max = SB_MAX; /* patchable */
 
/*
* Procedures to manipulate state flags of socket
* and do appropriate wakeups. Normal sequence from the
* active (originating) side is that soisconnecting() is
* called during processing of connect() call,
* resulting in an eventual call to soisconnected() if/when the
* connection is established. When the connection is torn down
* soisdisconnecting() is called during processing of disconnect() call,
* and soisdisconnected() is called when the connection to the peer
* is totally severed. The semantics of these routines are such that
* connectionless protocols can call soisconnected() and soisdisconnected()
* only, bypassing the in-progress calls when setting up a ``connection''
* takes no time.
*
* From the passive side, a socket is created with
* two queues of sockets: so_q0 for connections in progress
* and so_q for connections already made and awaiting user acceptance.
* As a protocol is preparing incoming connections, it creates a socket
* structure queued on so_q0 by calling sonewconn(). When the connection
* is established, soisconnected() is called, and transfers the
* socket structure to so_q, making it available to accept().
*
* If a socket is closed with sockets on either
* so_q0 or so_q, these sockets are dropped.
*
* If higher level protocols are implemented in
* the kernel, the wakeups done here will sometimes
* cause software-interrupt process scheduling.
*/
 
void
soisconnecting(so)
register struct socket *so;
{
 
so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING);
so->so_state |= SS_ISCONNECTING;
}
 
void
soisconnected(so)
register struct socket *so;
{
register struct socket *head = so->so_head;
 
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
so->so_state |= SS_ISCONNECTED;
if (head && soqremque(so, 0)) {
soqinsque(head, so, 1);
sorwakeup(head);
wakeup((caddr_t)&head->so_timeo);
} else {
wakeup((caddr_t)&so->so_timeo);
sorwakeup(so);
sowwakeup(so);
}
}
 
void
soisdisconnecting(so)
register struct socket *so;
{
 
so->so_state &= ~SS_ISCONNECTING;
so->so_state |= (SS_ISDISCONNECTING|SS_CANTRCVMORE|SS_CANTSENDMORE);
wakeup((caddr_t)&so->so_timeo);
sowwakeup(so);
sorwakeup(so);
}
 
void
soisdisconnected(so)
register struct socket *so;
{
 
so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
so->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE|SS_ISDISCONNECTED);
wakeup((caddr_t)&so->so_timeo);
sowwakeup(so);
sorwakeup(so);
}
 
/*
* When an attempt at a new connection is noted on a socket
* which accepts connections, sonewconn is called. If the
* connection is possible (subject to space constraints, etc.)
* then we allocate a new structure, propoerly linked into the
* data structure of the original socket, and return this.
* Connstatus may be 0, or SO_ISCONFIRMING, or SO_ISCONNECTED.
*
* Currently, sonewconn() is defined as sonewconn1() in socketvar.h
* to catch calls that are missing the (new) second parameter.
*/
struct socket *
sonewconn1(head, connstatus)
register struct socket *head;
int connstatus;
{
register struct socket *so;
int soqueue = connstatus ? 1 : 0;
 
if (head->so_qlen + head->so_q0len > head->so_qlimit * 3)
return ((struct socket *)0);
MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_DONTWAIT);
if (so == NULL)
return ((struct socket *)0);
bzero((caddr_t)so, sizeof(*so));
so->so_type = head->so_type;
so->so_options = head->so_options &~ SO_ACCEPTCONN;
so->so_linger = head->so_linger;
so->so_state = head->so_state | SS_NOFDREF;
so->so_proto = head->so_proto;
so->so_timeo = head->so_timeo;
so->so_pgid = head->so_pgid;
so->so_euid = head->so_euid;
so->so_ruid = head->so_ruid;
(void) soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat);
soqinsque(head, so, soqueue);
if ((*so->so_proto->pr_usrreq)(so, PRU_ATTACH,
(struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)) {
(void) soqremque(so, soqueue);
(void) free((caddr_t)so, M_SOCKET);
return ((struct socket *)0);
}
if (connstatus) {
sorwakeup(head);
wakeup((caddr_t)&head->so_timeo);
so->so_state |= connstatus;
}
return (so);
}
 
void
soqinsque(head, so, q)
register struct socket *head, *so;
int q;
{
 
register struct socket **prev;
so->so_head = head;
if (q == 0) {
head->so_q0len++;
so->so_q0 = 0;
for (prev = &(head->so_q0); *prev; )
prev = &((*prev)->so_q0);
} else {
head->so_qlen++;
so->so_q = 0;
for (prev = &(head->so_q); *prev; )
prev = &((*prev)->so_q);
}
*prev = so;
}
 
int
soqremque(so, q)
register struct socket *so;
int q;
{
register struct socket *head, *prev, *next;
 
head = so->so_head;
prev = head;
for (;;) {
next = q ? prev->so_q : prev->so_q0;
if (next == so)
break;
if (next == 0)
return (0);
prev = next;
}
if (q == 0) {
prev->so_q0 = next->so_q0;
head->so_q0len--;
} else {
prev->so_q = next->so_q;
head->so_qlen--;
}
next->so_q0 = next->so_q = 0;
next->so_head = 0;
return (1);
}
 
/*
* Socantsendmore indicates that no more data will be sent on the
* socket; it would normally be applied to a socket when the user
* informs the system that no more data is to be sent, by the protocol
* code (in case PRU_SHUTDOWN). Socantrcvmore indicates that no more data
* will be received, and will normally be applied to the socket by a
* protocol when it detects that the peer will send no more data.
* Data queued for reading in the socket may yet be read.
*/
 
void
socantsendmore(so)
struct socket *so;
{
 
so->so_state |= SS_CANTSENDMORE;
sowwakeup(so);
}
 
void
socantrcvmore(so)
struct socket *so;
{
 
so->so_state |= SS_CANTRCVMORE;
sorwakeup(so);
}
 
/*
* Wait for data to arrive at/drain from a socket buffer.
*/
int
sbwait(sb)
struct sockbuf *sb;
{
 
sb->sb_flags |= SB_WAIT;
return (tsleep((caddr_t)&sb->sb_cc,
(sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH, netio,
sb->sb_timeo));
}
 
/*
* Lock a sockbuf already known to be locked;
* return any error returned from sleep (EINTR).
*/
int
sb_lock(sb)
register struct sockbuf *sb;
{
int error;
 
while (sb->sb_flags & SB_LOCK) {
sb->sb_flags |= SB_WANT;
error = tsleep((caddr_t)&sb->sb_flags,
(sb->sb_flags & SB_NOINTR) ?
PSOCK : PSOCK|PCATCH, netio, 0);
if (error)
return (error);
}
sb->sb_flags |= SB_LOCK;
return (0);
}
 
#ifdef __ECOS
/*
* Set lock on sockbuf sb; sleep if lock is already held.
* Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
* Returns error without lock if sleep is interrupted.
*/
int
sblock(struct sockbuf *sb, int wf)
{
int res;
cyg_scheduler_safe_lock();
if (sb->sb_flags & SB_LOCK) {
// Already locked by another thread
if (wf == M_WAITOK) {
res = sb_lock(sb);
// Note: scheduler unlocked by 'sb_lock()'
} else {
res = EWOULDBLOCK;
cyg_scheduler_unlock();
}
} else {
sb->sb_flags |= SB_LOCK;
res = 0;
cyg_scheduler_unlock();
}
return res;
}
 
/* release lock on sockbuf sb */
void
sbunlock(struct sockbuf *sb)
{
cyg_scheduler_lock();
sb->sb_flags &= ~SB_LOCK;
if (sb->sb_flags & SB_WANT) {
sb->sb_flags &= ~SB_WANT;
wakeup((caddr_t)&sb->sb_flags);
}
cyg_scheduler_unlock();
}
#endif
 
/*
* Wakeup processes waiting on a socket buffer.
* Do asynchronous notification via SIGIO
* if the socket has the SS_ASYNC flag set.
*/
void
sowakeup(so, sb)
register struct socket *so;
register struct sockbuf *sb;
{
selwakeup(&sb->sb_sel);
sb->sb_flags &= ~SB_SEL;
if (sb->sb_flags & SB_WAIT) {
sb->sb_flags &= ~SB_WAIT;
wakeup((caddr_t)&sb->sb_cc);
}
#ifndef __ECOS
if (so->so_state & SS_ASYNC)
csignal(so->so_pgid, SIGIO, so->so_siguid, so->so_sigeuid);
#endif
}
 
/*
* Socket buffer (struct sockbuf) utility routines.
*
* Each socket contains two socket buffers: one for sending data and
* one for receiving data. Each buffer contains a queue of mbufs,
* information about the number of mbufs and amount of data in the
* queue, and other fields allowing select() statements and notification
* on data availability to be implemented.
*
* Data stored in a socket buffer is maintained as a list of records.
* Each record is a list of mbufs chained together with the m_next
* field. Records are chained together with the m_nextpkt field. The upper
* level routine soreceive() expects the following conventions to be
* observed when placing information in the receive buffer:
*
* 1. If the protocol requires each message be preceded by the sender's
* name, then a record containing that name must be present before
* any associated data (mbuf's must be of type MT_SONAME).
* 2. If the protocol supports the exchange of ``access rights'' (really
* just additional data associated with the message), and there are
* ``rights'' to be received, then a record containing this data
* should be present (mbuf's must be of type MT_CONTROL).
* 3. If a name or rights record exists, then it must be followed by
* a data record, perhaps of zero length.
*
* Before using a new socket structure it is first necessary to reserve
* buffer space to the socket, by calling sbreserve(). This should commit
* some of the available buffer space in the system buffer pool for the
* socket (currently, it does nothing but enforce limits). The space
* should be released by calling sbrelease() when the socket is destroyed.
*/
 
int
soreserve(so, sndcc, rcvcc)
register struct socket *so;
u_long sndcc, rcvcc;
{
 
if (sbreserve(&so->so_snd, sndcc) == 0)
goto bad;
if (sbreserve(&so->so_rcv, rcvcc) == 0)
goto bad2;
if (so->so_rcv.sb_lowat == 0)
so->so_rcv.sb_lowat = 1;
if (so->so_snd.sb_lowat == 0)
so->so_snd.sb_lowat = MCLBYTES;
if (so->so_snd.sb_lowat > so->so_snd.sb_hiwat)
so->so_snd.sb_lowat = so->so_snd.sb_hiwat;
return (0);
bad2:
sbrelease(&so->so_snd);
bad:
return (ENOBUFS);
}
 
/*
* Allot mbufs to a sockbuf.
* Attempt to scale mbmax so that mbcnt doesn't become limiting
* if buffering efficiency is near the normal case.
*/
int
sbreserve(sb, cc)
struct sockbuf *sb;
u_long cc;
{
 
if (cc == 0 || cc > sb_max * MCLBYTES / (MSIZE + MCLBYTES))
return (0);
sb->sb_hiwat = cc;
sb->sb_mbmax = min(cc * 2, sb_max);
if (sb->sb_lowat > sb->sb_hiwat)
sb->sb_lowat = sb->sb_hiwat;
return (1);
}
 
/*
* Free mbufs held by a socket, and reserved mbuf space.
*/
void
sbrelease(sb)
struct sockbuf *sb;
{
 
sbflush(sb);
sb->sb_hiwat = sb->sb_mbmax = 0;
}
 
/*
* Routines to add and remove
* data from an mbuf queue.
*
* The routines sbappend() or sbappendrecord() are normally called to
* append new mbufs to a socket buffer, after checking that adequate
* space is available, comparing the function sbspace() with the amount
* of data to be added. sbappendrecord() differs from sbappend() in
* that data supplied is treated as the beginning of a new record.
* To place a sender's address, optional access rights, and data in a
* socket receive buffer, sbappendaddr() should be used. To place
* access rights and data in a socket receive buffer, sbappendrights()
* should be used. In either case, the new data begins a new record.
* Note that unlike sbappend() and sbappendrecord(), these routines check
* for the caller that there will be enough space to store the data.
* Each fails if there is not enough space, or if it cannot find mbufs
* to store additional information in.
*
* Reliable protocols may use the socket send buffer to hold data
* awaiting acknowledgement. Data is normally copied from a socket
* send buffer in a protocol with m_copy for output to a peer,
* and then removing the data from the socket buffer with sbdrop()
* or sbdroprecord() when the data is acknowledged by the peer.
*/
 
/*
* Append mbuf chain m to the last record in the
* socket buffer sb. The additional space associated
* the mbuf chain is recorded in sb. Empty mbufs are
* discarded and mbufs are compacted where possible.
*/
void
sbappend(sb, m)
struct sockbuf *sb;
struct mbuf *m;
{
register struct mbuf *n;
 
if (m == 0)
return;
if ((n = sb->sb_mb) != NULL) {
while (n->m_nextpkt)
n = n->m_nextpkt;
do {
if (n->m_flags & M_EOR) {
sbappendrecord(sb, m); /* XXXXXX!!!! */
return;
}
} while (n->m_next && (n = n->m_next));
}
sbcompress(sb, m, n);
}
 
#ifdef SOCKBUF_DEBUG
void
sbcheck(sb)
register struct sockbuf *sb;
{
register struct mbuf *m;
register int len = 0, mbcnt = 0;
 
for (m = sb->sb_mb; m; m = m->m_next) {
len += m->m_len;
mbcnt += MSIZE;
if (m->m_flags & M_EXT)
mbcnt += m->m_ext.ext_size;
if (m->m_nextpkt)
panic("sbcheck nextpkt");
}
if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) {
printf("cc %d != %d || mbcnt %d != %d\n", len, sb->sb_cc,
mbcnt, sb->sb_mbcnt);
panic("sbcheck");
}
}
#endif
 
/*
* As above, except the mbuf chain
* begins a new record.
*/
void
sbappendrecord(sb, m0)
register struct sockbuf *sb;
register struct mbuf *m0;
{
register struct mbuf *m;
 
if (m0 == 0)
return;
if ((m = sb->sb_mb) != NULL)
while (m->m_nextpkt)
m = m->m_nextpkt;
/*
* Put the first mbuf on the queue.
* Note this permits zero length records.
*/
sballoc(sb, m0);
if (m)
m->m_nextpkt = m0;
else
sb->sb_mb = m0;
m = m0->m_next;
m0->m_next = 0;
if (m && (m0->m_flags & M_EOR)) {
m0->m_flags &= ~M_EOR;
m->m_flags |= M_EOR;
}
sbcompress(sb, m, m0);
}
 
/*
* As above except that OOB data
* is inserted at the beginning of the sockbuf,
* but after any other OOB data.
*/
void
sbinsertoob(sb, m0)
register struct sockbuf *sb;
register struct mbuf *m0;
{
register struct mbuf *m;
register struct mbuf **mp;
 
if (m0 == 0)
return;
for (mp = &sb->sb_mb; (m = *mp) != NULL; mp = &((*mp)->m_nextpkt)) {
again:
switch (m->m_type) {
 
case MT_OOBDATA:
continue; /* WANT next train */
 
case MT_CONTROL:
if ((m = m->m_next) != NULL)
goto again; /* inspect THIS train further */
}
break;
}
/*
* Put the first mbuf on the queue.
* Note this permits zero length records.
*/
sballoc(sb, m0);
m0->m_nextpkt = *mp;
*mp = m0;
m = m0->m_next;
m0->m_next = 0;
if (m && (m0->m_flags & M_EOR)) {
m0->m_flags &= ~M_EOR;
m->m_flags |= M_EOR;
}
sbcompress(sb, m, m0);
}
 
/*
* Append address and data, and optionally, control (ancillary) data
* to the receive queue of a socket. If present,
* m0 must include a packet header with total length.
* Returns 0 if no space in sockbuf or insufficient mbufs.
*/
int
sbappendaddr(sb, asa, m0, control)
register struct sockbuf *sb;
struct sockaddr *asa;
struct mbuf *m0, *control;
{
register struct mbuf *m, *n;
int space = asa->sa_len;
 
if (m0 && (m0->m_flags & M_PKTHDR) == 0)
panic("sbappendaddr");
if (m0)
space += m0->m_pkthdr.len;
for (n = control; n; n = n->m_next) {
space += n->m_len;
if (n->m_next == 0) /* keep pointer to last control buf */
break;
}
if (space > sbspace(sb))
return (0);
if (asa->sa_len > MLEN)
return (0);
MGET(m, M_DONTWAIT, MT_SONAME);
if (m == 0)
return (0);
m->m_len = asa->sa_len;
bcopy((caddr_t)asa, mtod(m, caddr_t), asa->sa_len);
if (n)
n->m_next = m0; /* concatenate data to control */
else
control = m0;
m->m_next = control;
for (n = m; n; n = n->m_next)
sballoc(sb, n);
if ((n = sb->sb_mb) != NULL) {
while (n->m_nextpkt)
n = n->m_nextpkt;
n->m_nextpkt = m;
} else
sb->sb_mb = m;
return (1);
}
 
int
sbappendcontrol(sb, m0, control)
struct sockbuf *sb;
struct mbuf *m0, *control;
{
register struct mbuf *m, *n;
int space = 0;
 
if (control == 0)
panic("sbappendcontrol");
for (m = control; ; m = m->m_next) {
space += m->m_len;
if (m->m_next == 0)
break;
}
n = m; /* save pointer to last control buffer */
for (m = m0; m; m = m->m_next)
space += m->m_len;
if (space > sbspace(sb))
return (0);
n->m_next = m0; /* concatenate data to control */
for (m = control; m; m = m->m_next)
sballoc(sb, m);
if ((n = sb->sb_mb) != NULL) {
while (n->m_nextpkt)
n = n->m_nextpkt;
n->m_nextpkt = control;
} else
sb->sb_mb = control;
return (1);
}
 
/*
* Compress mbuf chain m into the socket
* buffer sb following mbuf n. If n
* is null, the buffer is presumed empty.
*/
void
sbcompress(sb, m, n)
register struct sockbuf *sb;
register struct mbuf *m, *n;
{
register int eor = 0;
register struct mbuf *o;
 
while (m) {
eor |= m->m_flags & M_EOR;
if (m->m_len == 0 &&
(eor == 0 ||
(((o = m->m_next) || (o = n)) &&
o->m_type == m->m_type))) {
m = m_free(m);
continue;
}
if (n && (n->m_flags & (M_EXT | M_EOR)) == 0 &&
(n->m_data + n->m_len + m->m_len) < &n->m_dat[MLEN] &&
n->m_type == m->m_type) {
bcopy(mtod(m, caddr_t), mtod(n, caddr_t) + n->m_len,
(unsigned)m->m_len);
n->m_len += m->m_len;
sb->sb_cc += m->m_len;
m = m_free(m);
continue;
}
if (n)
n->m_next = m;
else
sb->sb_mb = m;
sballoc(sb, m);
n = m;
m->m_flags &= ~M_EOR;
m = m->m_next;
n->m_next = 0;
}
if (eor) {
if (n)
n->m_flags |= eor;
else
#ifdef __ECOS
diag_printf("semi-panic: sbcompress\n");
#else
printf("semi-panic: sbcompress\n");
#endif
}
}
 
/*
* Free all mbufs in a sockbuf.
* Check that all resources are reclaimed.
*/
void
sbflush(sb)
register struct sockbuf *sb;
{
 
if (sb->sb_flags & SB_LOCK)
panic("sbflush");
while (sb->sb_mbcnt)
sbdrop(sb, (int)sb->sb_cc);
if (sb->sb_cc || sb->sb_mb)
panic("sbflush 2");
}
 
/*
* Drop data from (the front of) a sockbuf.
*/
void
sbdrop(sb, len)
register struct sockbuf *sb;
register int len;
{
register struct mbuf *m, *mn;
struct mbuf *next;
 
next = (m = sb->sb_mb) ? m->m_nextpkt : 0;
while (len > 0) {
if (m == 0) {
if (next == 0)
panic("sbdrop");
m = next;
next = m->m_nextpkt;
continue;
}
if (m->m_len > len) {
m->m_len -= len;
m->m_data += len;
sb->sb_cc -= len;
break;
}
len -= m->m_len;
sbfree(sb, m);
MFREE(m, mn);
m = mn;
}
while (m && m->m_len == 0) {
sbfree(sb, m);
MFREE(m, mn);
m = mn;
}
if (m) {
sb->sb_mb = m;
m->m_nextpkt = next;
} else
sb->sb_mb = next;
}
 
/*
* Drop a record off the front of a sockbuf
* and move the next record to the front.
*/
void
sbdroprecord(sb)
register struct sockbuf *sb;
{
register struct mbuf *m, *mn;
 
m = sb->sb_mb;
if (m) {
sb->sb_mb = m->m_nextpkt;
do {
sbfree(sb, m);
MFREE(m, mn);
} while ((m = mn) != NULL);
}
}
 
/*
* Create a "control" mbuf containing the specified data
* with the specified type for presentation on a socket buffer.
*/
struct mbuf *
sbcreatecontrol(p, size, type, level)
caddr_t p;
register int size;
int type, level;
{
register struct cmsghdr *cp;
struct mbuf *m;
 
if (size + sizeof(*cp) > MCLBYTES) {
#ifdef __ECOS
diag_printf("sbcreatecontrol: message too large %d\n", size);
#else
printf("sbcreatecontrol: message too large %d\n", size);
#endif
return NULL;
}
 
if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
return ((struct mbuf *) NULL);
if (size + sizeof(*cp) > MLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
return NULL;
}
}
cp = mtod(m, struct cmsghdr *);
bcopy(p, CMSG_DATA(cp), size);
size += sizeof(*cp);
m->m_len = size;
cp->cmsg_len = size;
cp->cmsg_level = level;
cp->cmsg_type = type;
return (m);
}
/uipc_syscalls.c
0,0 → 1,1272
//==========================================================================
//
// sys/kern/uipc_syscalls.c
//
//
//
//==========================================================================
//####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: uipc_syscalls.c,v 1.29 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */
 
/*
* Copyright (c) 1982, 1986, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94
*/
 
#include <sys/param.h>
#ifdef __ECOS
#include <cyg/io/file.h>
static int ecos_getsock(int fdes, struct file **fpp);
#define getsock(fdp, fdes, fpp) ecos_getsock(fdes, fpp)
#else
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/file.h>
#include <sys/buf.h>
#endif
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#ifndef __ECOS
#include <sys/signalvar.h>
#include <sys/un.h>
#endif
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
 
#ifndef __ECOS
#include <sys/mount.h>
#endif
#include <sys/syscallargs.h>
 
/*
* System call interface to the socket abstraction.
*/
extern struct fileops socketops;
 
#ifdef __ECOS
int
sys_socket(struct sys_socket_args *uap, register_t *retval)
{
#else
int
sys_socket(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_socket_args /* {
syscallarg(int) domain;
syscallarg(int) type;
syscallarg(int) protocol;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
#endif // __ECOS
struct socket *so;
struct file *fp;
int fd, error;
 
#ifdef __ECOS
if ((error = falloc(&fp, &fd)) != 0)
#else
if ((error = falloc(p, &fp, &fd)) != 0)
#endif
return (error);
fp->f_flag = FREAD|FWRITE;
fp->f_type = DTYPE_SOCKET;
fp->f_ops = &socketops;
error = socreate(SCARG(uap, domain), &so, SCARG(uap, type),
SCARG(uap, protocol));
if (error) {
#ifndef __ECOS
fdremove(fdp, fd);
#endif
ffree(fp);
} else {
#ifdef __ECOS
fp->f_data = (CYG_ADDRWORD)so;
#else
fp->f_data = (caddr_t)so;
#endif
*retval = fd;
}
return (error);
}
 
#ifdef __ECOS
int
sys_bind(struct sys_bind_args *uap, register_t *retval)
{
#else
/* ARGSUSED */
int
sys_bind(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_bind_args /* {
syscallarg(int) s;
syscallarg(struct sockaddr *) name;
syscallarg(socklen_t) namelen;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
struct mbuf *nam;
int error;
 
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
error = sockargs(&nam, (caddr_t)SCARG(uap, name), SCARG(uap, namelen),
MT_SONAME);
if (error)
return (error);
error = sobind((struct socket *)fp->f_data, nam);
m_freem(nam);
return (error);
}
 
/* ARGSUSED */
#ifdef __ECOS
int
sys_listen(struct sys_listen_args *uap, register_t *retval)
{
#else
int
sys_listen(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_listen_args /* {
syscallarg(int) s;
syscallarg(int) backlog;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
int error;
 
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
return (solisten((struct socket *)fp->f_data, SCARG(uap, backlog)));
}
 
#ifdef __ECOS
int
sys_accept(struct sys_accept_args *uap, register_t *retval)
{
#else
int
sys_accept(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_accept_args /* {
syscallarg(int) s;
syscallarg(struct sockaddr *) name;
syscallarg(socklen_t *) anamelen;
} */ *uap = v;
#endif
struct file *fp;
struct mbuf *nam;
socklen_t namelen;
int error, s, tmpfd;
register struct socket *so;
 
if (SCARG(uap, name) && (error = copyin((caddr_t)SCARG(uap, anamelen),
(caddr_t)&namelen, sizeof (namelen))))
return (error);
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
s = splsoftnet();
so = (struct socket *)fp->f_data;
if ((so->so_options & SO_ACCEPTCONN) == 0) {
splx(s);
return (EINVAL);
}
if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
splx(s);
return (EWOULDBLOCK);
}
while (so->so_qlen == 0 && so->so_error == 0) {
if (so->so_state & SS_CANTRCVMORE) {
so->so_error = ECONNABORTED;
break;
}
error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
netcon, 0);
if (error) {
splx(s);
return (error);
}
}
if (so->so_error) {
error = so->so_error;
so->so_error = 0;
splx(s);
return (error);
}
#ifdef __ECOS
if ((error = falloc(&fp, &tmpfd)) != 0) {
#else
if ((error = falloc(p, &fp, &tmpfd)) != 0) {
#endif
splx(s);
return (error);
}
*retval = tmpfd;
{ struct socket *aso = so->so_q;
if (soqremque(aso, 1) == 0)
panic("accept");
so = aso;
}
fp->f_type = DTYPE_SOCKET;
fp->f_flag = FREAD|FWRITE;
fp->f_ops = &socketops;
#ifdef __ECOS
fp->f_data = (CYG_ADDRWORD)so;
#else
fp->f_data = (caddr_t)so;
#endif
nam = m_get(M_WAIT, MT_SONAME);
(void) soaccept(so, nam);
if (SCARG(uap, name)) {
if (namelen > nam->m_len)
namelen = nam->m_len;
/* SHOULD COPY OUT A CHAIN HERE */
if ((error = copyout(mtod(nam, caddr_t),
(caddr_t)SCARG(uap, name), namelen)) == 0)
error = copyout((caddr_t)&namelen,
(caddr_t)SCARG(uap, anamelen),
sizeof (*SCARG(uap, anamelen)));
}
m_freem(nam);
splx(s);
return (error);
}
 
#ifdef __ECOS
int
sys_connect(struct sys_connect_args *uap, register_t *retval)
{
#else
/* ARGSUSED */
int
sys_connect(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_connect_args /* {
syscallarg(int) s;
syscallarg(struct sockaddr *) name;
syscallarg(socklen_t) namelen;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
register struct socket *so;
struct mbuf *nam;
int error, s;
 
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
so = (struct socket *)fp->f_data;
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING))
return (EALREADY);
error = sockargs(&nam, (caddr_t)SCARG(uap, name), SCARG(uap, namelen),
MT_SONAME);
if (error)
return (error);
error = soconnect(so, nam);
if (error)
goto bad;
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
m_freem(nam);
return (EINPROGRESS);
}
s = splsoftnet();
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
netcon, 0);
if (error)
break;
}
if (error == 0) {
error = so->so_error;
so->so_error = 0;
}
splx(s);
bad:
so->so_state &= ~SS_ISCONNECTING;
m_freem(nam);
#ifndef __ECOS
if (error == ERESTART)
error = EINTR;
#endif
return (error);
}
 
#ifndef __ECOS
int
sys_socketpair(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_socketpair_args /* {
syscallarg(int) domain;
syscallarg(int) type;
syscallarg(int) protocol;
syscallarg(int *) rsv;
} */ *uap = v;
register struct filedesc *fdp = p->p_fd;
struct file *fp1, *fp2;
struct socket *so1, *so2;
int fd, error, sv[2];
 
error = socreate(SCARG(uap, domain), &so1, SCARG(uap, type),
SCARG(uap, protocol));
if (error)
return (error);
error = socreate(SCARG(uap, domain), &so2, SCARG(uap, type),
SCARG(uap, protocol));
if (error)
goto free1;
if ((error = falloc(p, &fp1, &fd)) != 0)
goto free2;
sv[0] = fd;
fp1->f_flag = FREAD|FWRITE;
fp1->f_type = DTYPE_SOCKET;
fp1->f_ops = &socketops;
fp1->f_data = (caddr_t)so1;
if ((error = falloc(p, &fp2, &fd)) != 0)
goto free3;
fp2->f_flag = FREAD|FWRITE;
fp2->f_type = DTYPE_SOCKET;
fp2->f_ops = &socketops;
fp2->f_data = (caddr_t)so2;
sv[1] = fd;
if ((error = soconnect2(so1, so2)) != 0)
goto free4;
if (SCARG(uap, type) == SOCK_DGRAM) {
/*
* Datagram socket connection is asymmetric.
*/
if ((error = soconnect2(so2, so1)) != 0)
goto free4;
}
error = copyout((caddr_t)sv, (caddr_t)SCARG(uap, rsv),
2 * sizeof (int));
if (error == 0)
return (error);
free4:
ffree(fp2);
fdremove(fdp, sv[1]);
free3:
ffree(fp1);
fdremove(fdp, sv[0]);
free2:
(void)soclose(so2);
free1:
(void)soclose(so1);
return (error);
}
#endif
 
#ifdef __ECOS
int
sys_sendto(struct sys_sendto_args *uap, register_t *retval)
{
#else
int
sys_sendto(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_sendto_args /* {
syscallarg(int) s;
syscallarg(caddr_t) buf;
syscallarg(size_t) len;
syscallarg(int) flags;
syscallarg(struct sockaddr *) to;
syscallarg(socklen_t) tolen;
} */ *uap = v;
#endif // __ECOS
struct msghdr msg;
struct iovec aiov;
 
msg.msg_name = (caddr_t)SCARG(uap, to);
msg.msg_namelen = SCARG(uap, tolen);
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
msg.msg_control = 0;
#ifdef COMPAT_OLDSOCK
msg.msg_flags = 0;
#endif
aiov.iov_base = (char *)SCARG(uap, buf);
aiov.iov_len = SCARG(uap, len);
#ifdef __ECOS
return (sendit(SCARG(uap, s), &msg, SCARG(uap, flags), retval));
#else
return (sendit(p, SCARG(uap, s), &msg, SCARG(uap, flags), retval));
#endif
}
 
#ifndef __ECOS
int
sys_sendmsg(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_sendmsg_args /* {
syscallarg(int) s;
syscallarg(caddr_t) msg;
syscallarg(int) flags;
} */ *uap = v;
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *iov;
int error;
 
error = copyin(SCARG(uap, msg), (caddr_t)&msg, sizeof (msg));
if (error)
return (error);
if (msg.msg_iovlen <= 0 || msg.msg_iovlen > IOV_MAX)
return (EMSGSIZE);
if (msg.msg_iovlen > UIO_SMALLIOV)
MALLOC(iov, struct iovec *,
sizeof(struct iovec) * msg.msg_iovlen, M_IOV, M_WAITOK);
else
iov = aiov;
if (msg.msg_iovlen &&
(error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
(unsigned)(msg.msg_iovlen * sizeof (struct iovec)))))
goto done;
msg.msg_iov = iov;
#ifdef COMPAT_OLDSOCK
msg.msg_flags = 0;
#endif
error = sendit(p, SCARG(uap, s), &msg, SCARG(uap, flags), retval);
done:
if (iov != aiov)
FREE(iov, M_IOV);
return (error);
}
#endif
 
#ifdef __ECOS
int
sendit(int s, struct msghdr *mp, int flags, register_t *retsize)
{
#else
int
sendit(p, s, mp, flags, retsize)
register struct proc *p;
int s;
register struct msghdr *mp;
int flags;
register_t *retsize;
{
#endif // __ECOS
struct file *fp;
struct uio auio;
register struct iovec *iov;
register int i;
struct mbuf *to, *control;
int len, error;
#ifdef KTRACE
struct iovec *ktriov = NULL;
#endif
if ((error = getsock(p->p_fd, s, &fp)) != 0)
return (error);
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_WRITE;
#ifndef __ECOS
auio.uio_procp = p;
#endif
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
/* Don't allow sum > SSIZE_MAX */
if (iov->iov_len > SSIZE_MAX ||
(auio.uio_resid += iov->iov_len) > SSIZE_MAX)
return (EINVAL);
}
if (mp->msg_name) {
error = sockargs(&to, mp->msg_name, mp->msg_namelen,
MT_SONAME);
if (error)
return (error);
} else
to = 0;
if (mp->msg_control) {
if (mp->msg_controllen < sizeof(struct cmsghdr)
#ifdef COMPAT_OLDSOCK
&& mp->msg_flags != MSG_COMPAT
#endif
) {
error = EINVAL;
goto bad;
}
error = sockargs(&control, mp->msg_control,
mp->msg_controllen, MT_CONTROL);
if (error)
goto bad;
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags == MSG_COMPAT) {
register struct cmsghdr *cm;
 
M_PREPEND(control, sizeof(*cm), M_WAIT);
if (control == 0) {
error = ENOBUFS;
goto bad;
} else {
cm = mtod(control, struct cmsghdr *);
cm->cmsg_len = control->m_len;
cm->cmsg_level = SOL_SOCKET;
cm->cmsg_type = SCM_RIGHTS;
}
}
#endif
} else
control = 0;
#ifdef KTRACE
if (KTRPOINT(p, KTR_GENIO)) {
int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
 
MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
}
#endif
len = auio.uio_resid;
error = sosend((struct socket *)fp->f_data, to, &auio,
NULL, control, flags);
if (error) {
#ifdef __ECOS
if (auio.uio_resid != len &&
(error == EINTR || error == EWOULDBLOCK))
#else
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
#endif
error = 0;
#ifndef __ECOS
if (error == EPIPE)
psignal(p, SIGPIPE);
#endif
}
if (error == 0)
*retsize = len - auio.uio_resid;
#ifdef KTRACE
if (ktriov != NULL) {
if (error == 0)
ktrgenio(p->p_tracep, s, UIO_WRITE,
ktriov, *retsize, error);
FREE(ktriov, M_TEMP);
}
#endif
bad:
if (to)
m_freem(to);
return (error);
}
 
#ifdef __ECOS
int
sys_recvfrom(struct sys_recvfrom_args *uap, register_t *retval)
{
#else
int
sys_recvfrom(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_recvfrom_args /* {
syscallarg(int) s;
syscallarg(caddr_t) buf;
syscallarg(size_t) len;
syscallarg(int) flags;
syscallarg(struct sockaddr *) from;
syscallarg(socklen_t *) fromlenaddr;
} */ *uap = v;
#endif // __ECOS
struct msghdr msg;
struct iovec aiov;
int error;
 
if (SCARG(uap, fromlenaddr)) {
error = copyin((caddr_t)SCARG(uap, fromlenaddr),
(caddr_t)&msg.msg_namelen,
sizeof (msg.msg_namelen));
if (error)
return (error);
} else
msg.msg_namelen = 0;
msg.msg_name = (caddr_t)SCARG(uap, from);
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = SCARG(uap, buf);
aiov.iov_len = SCARG(uap, len);
msg.msg_control = 0;
msg.msg_flags = SCARG(uap, flags);
#ifdef __ECOS
return (recvit(SCARG(uap, s), &msg,
(caddr_t)SCARG(uap, fromlenaddr), retval));
#else
return (recvit(p, SCARG(uap, s), &msg,
(caddr_t)SCARG(uap, fromlenaddr), retval));
#endif
}
 
#ifndef __ECOS
int
sys_recvmsg(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_recvmsg_args /* {
syscallarg(int) s;
syscallarg(struct msghdr *) msg;
syscallarg(int) flags;
} */ *uap = v;
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *uiov, *iov;
register int error;
 
error = copyin((caddr_t)SCARG(uap, msg), (caddr_t)&msg,
sizeof (msg));
if (error)
return (error);
if (msg.msg_iovlen <= 0 || msg.msg_iovlen > IOV_MAX)
return (EMSGSIZE);
if (msg.msg_iovlen > UIO_SMALLIOV)
MALLOC(iov, struct iovec *,
sizeof(struct iovec) * msg.msg_iovlen, M_IOV, M_WAITOK);
else
iov = aiov;
#ifdef COMPAT_OLDSOCK
msg.msg_flags = SCARG(uap, flags) &~ MSG_COMPAT;
#else
msg.msg_flags = SCARG(uap, flags);
#endif
uiov = msg.msg_iov;
msg.msg_iov = iov;
error = copyin((caddr_t)uiov, (caddr_t)iov,
(unsigned)(msg.msg_iovlen * sizeof (struct iovec)));
if (error)
goto done;
if ((error = recvit(p, SCARG(uap, s), &msg, (caddr_t)0, retval)) == 0) {
msg.msg_iov = uiov;
error = copyout((caddr_t)&msg, (caddr_t)SCARG(uap, msg),
sizeof(msg));
}
done:
if (iov != aiov)
FREE(iov, M_IOV);
return (error);
}
#endif
 
#ifdef __ECOS
int
recvit(int s, struct msghdr *mp, caddr_t namelenp, register_t *retsize)
{
#else
int
recvit(p, s, mp, namelenp, retsize)
register struct proc *p;
int s;
register struct msghdr *mp;
caddr_t namelenp;
register_t *retsize;
{
#endif // __ECOS
struct file *fp;
struct uio auio;
register struct iovec *iov;
register int i;
size_t len;
int error;
struct mbuf *from = 0, *control = 0;
#ifdef KTRACE
struct iovec *ktriov = NULL;
#endif
 
if ((error = getsock(p->p_fd, s, &fp)) != 0)
return (error);
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_READ;
#ifndef __ECOS
auio.uio_procp = p;
#endif
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
/* Don't allow sum > SSIZE_MAX */
if (iov->iov_len > SSIZE_MAX ||
(auio.uio_resid += iov->iov_len) > SSIZE_MAX)
return (EINVAL);
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_GENIO)) {
int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
 
MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
}
#endif
len = auio.uio_resid;
error = soreceive((struct socket *)fp->f_data, &from, &auio,
NULL, mp->msg_control ? &control : NULL,
&mp->msg_flags);
if (error) {
#ifdef __ECOS
if (auio.uio_resid != len &&
(error == EINTR || error == EWOULDBLOCK))
#else
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
#endif
error = 0;
}
#ifdef KTRACE
if (ktriov != NULL) {
if (error == 0)
ktrgenio(p->p_tracep, s, UIO_READ,
ktriov, len - auio.uio_resid, error);
FREE(ktriov, M_TEMP);
}
#endif
if (error)
goto out;
*retsize = len - auio.uio_resid;
if (mp->msg_name) {
len = mp->msg_namelen;
if (len <= 0 || from == 0)
len = 0;
else {
/* save sa_len before it is destroyed by MSG_COMPAT */
if (len > from->m_len)
len = from->m_len;
/* else if len < from->m_len ??? */
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags & MSG_COMPAT)
mtod(from, struct osockaddr *)->sa_family =
mtod(from, struct sockaddr *)->sa_family;
#endif
error = copyout(mtod(from, caddr_t),
(caddr_t)mp->msg_name, (unsigned)len);
if (error)
goto out;
}
mp->msg_namelen = len;
if (namelenp &&
(error = copyout((caddr_t)&len, namelenp, sizeof (int)))) {
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags & MSG_COMPAT)
error = 0; /* old recvfrom didn't check */
else
#endif
goto out;
}
}
if (mp->msg_control) {
#ifdef COMPAT_OLDSOCK
/*
* We assume that old recvmsg calls won't receive access
* rights and other control info, esp. as control info
* is always optional and those options didn't exist in 4.3.
* If we receive rights, trim the cmsghdr; anything else
* is tossed.
*/
if (control && mp->msg_flags & MSG_COMPAT) {
if (mtod(control, struct cmsghdr *)->cmsg_level !=
SOL_SOCKET ||
mtod(control, struct cmsghdr *)->cmsg_type !=
SCM_RIGHTS) {
mp->msg_controllen = 0;
goto out;
}
control->m_len -= sizeof (struct cmsghdr);
control->m_data += sizeof (struct cmsghdr);
}
#endif
len = mp->msg_controllen;
if (len <= 0 || control == 0)
len = 0;
else {
struct mbuf *m = control;
caddr_t p = (caddr_t)mp->msg_control;
 
do {
i = m->m_len;
if (len < i) {
mp->msg_flags |= MSG_CTRUNC;
i = len;
}
error = copyout(mtod(m, caddr_t), p,
(unsigned)i);
if (m->m_next)
i = ALIGN(i);
p += i;
len -= i;
if (error != 0 || len <= 0)
break;
} while ((m = m->m_next) != NULL);
len = p - (caddr_t)mp->msg_control;
}
mp->msg_controllen = len;
}
out:
if (from)
m_freem(from);
if (control)
m_freem(control);
return (error);
}
 
/* ARGSUSED */
#ifdef __ECOS
int
sys_shutdown(struct sys_shutdown_args *uap, register_t *retval)
{
#else
int
sys_shutdown(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_shutdown_args /* {
syscallarg(int) s;
syscallarg(int) how;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
int error;
 
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
return (soshutdown((struct socket *)fp->f_data, SCARG(uap, how)));
}
 
#ifdef __ECOS
int
sys_setsockopt(struct sys_setsockopt_args *uap, register_t *retval)
{
#else
/* ARGSUSED */
int
sys_setsockopt(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_setsockopt_args /* {
syscallarg(int) s;
syscallarg(int) level;
syscallarg(int) name;
syscallarg(caddr_t) val;
syscallarg(socklen_t) valsize;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
struct mbuf *m = NULL;
int error;
 
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
if (SCARG(uap, valsize) > MCLBYTES)
return (EINVAL);
if (SCARG(uap, val)) {
m = m_get(M_WAIT, MT_SOOPTS);
if (SCARG(uap, valsize) > MLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
return (ENOBUFS);
}
}
if (m == NULL)
return (ENOBUFS);
error = copyin(SCARG(uap, val), mtod(m, caddr_t),
SCARG(uap, valsize));
if (error) {
(void) m_free(m);
return (error);
}
m->m_len = SCARG(uap, valsize);
}
return (sosetopt((struct socket *)fp->f_data, SCARG(uap, level),
SCARG(uap, name), m));
}
 
#ifdef __ECOS
int
sys_getsockopt(struct sys_getsockopt_args *uap, register_t *retval)
{
#else
/* ARGSUSED */
int
sys_getsockopt(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_getsockopt_args /* {
syscallarg(int) s;
syscallarg(int) level;
syscallarg(int) name;
syscallarg(caddr_t) val;
syscallarg(socklen_t *) avalsize;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
struct mbuf *m = NULL;
socklen_t valsize;
int error;
 
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
if (SCARG(uap, val)) {
error = copyin((caddr_t)SCARG(uap, avalsize),
(caddr_t)&valsize, sizeof (valsize));
if (error)
return (error);
} else
valsize = 0;
if ((error = sogetopt((struct socket *)fp->f_data, SCARG(uap, level),
SCARG(uap, name), &m)) == 0 && SCARG(uap, val) && valsize &&
m != NULL) {
if (valsize > m->m_len)
valsize = m->m_len;
error = copyout(mtod(m, caddr_t), SCARG(uap, val), valsize);
if (error == 0)
error = copyout((caddr_t)&valsize,
(caddr_t)SCARG(uap, avalsize), sizeof (valsize));
}
if (m != NULL)
(void) m_free(m);
return (error);
}
 
#ifndef __ECOS
int
sys_pipe(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_pipe_args /* {
syscallarg(int *) fdp;
} */ *uap = v;
int error, fds[2];
register_t rval[2];
 
if ((error = sys_opipe(p, v, rval)) == -1)
return (error);
fds[0] = rval[0];
fds[1] = rval[1];
error = copyout((caddr_t)fds, (caddr_t)SCARG(uap, fdp),
2 * sizeof (int));
if (error) {
fdrelease(p, retval[0]);
fdrelease(p, retval[1]);
}
return (error);
}
 
#ifdef OLD_PIPE
 
/* ARGSUSED */
int
sys_opipe(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct filedesc *fdp = p->p_fd;
struct file *rf, *wf;
struct socket *rso, *wso;
int fd, error;
 
if ((error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0)) != 0)
return (error);
if ((error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0)) != 0)
goto free1;
if ((error = falloc(p, &rf, &fd)) != 0)
goto free2;
retval[0] = fd;
rf->f_flag = FREAD;
rf->f_type = DTYPE_SOCKET;
rf->f_ops = &socketops;
rf->f_data = (caddr_t)rso;
if ((error = falloc(p, &wf, &fd)) != 0)
goto free3;
wf->f_flag = FWRITE;
wf->f_type = DTYPE_SOCKET;
wf->f_ops = &socketops;
wf->f_data = (caddr_t)wso;
retval[1] = fd;
if ((error = unp_connect2(wso, rso)) != 0)
goto free4;
return (0);
free4:
ffree(wf);
fdremove(fdp, retval[1]);
free3:
ffree(rf);
fdremove(fdp, retval[0]);
free2:
(void)soclose(wso);
free1:
(void)soclose(rso);
return (error);
}
#endif
#endif // __ECOS
 
/*
* Get socket name.
*/
#ifdef __ECOS
int
sys_getsockname(struct sys_getsockname_args *uap, register_t *retval)
{
#else
/* ARGSUSED */
int
sys_getsockname(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_getsockname_args /* {
syscallarg(int) fdes;
syscallarg(caddr_t) asa;
syscallarg(socklen_t *) alen;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
register struct socket *so;
struct mbuf *m;
socklen_t len;
int error;
 
if ((error = getsock(p->p_fd, SCARG(uap, fdes), &fp)) != 0)
return (error);
error = copyin((caddr_t)SCARG(uap, alen), (caddr_t)&len, sizeof (len));
if (error)
return (error);
so = (struct socket *)fp->f_data;
m = m_getclr(M_WAIT, MT_SONAME);
if (m == NULL)
return (ENOBUFS);
error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKADDR, 0, m, 0);
if (error)
goto bad;
if (len > m->m_len)
len = m->m_len;
error = copyout(mtod(m, caddr_t), (caddr_t)SCARG(uap, asa), len);
if (error == 0)
error = copyout((caddr_t)&len, (caddr_t)SCARG(uap, alen),
sizeof (len));
bad:
m_freem(m);
return (error);
}
 
/*
* Get name of peer for connected socket.
*/
/* ARGSUSED */
#ifdef __ECOS
int
sys_getpeername(struct sys_getpeername_args *uap, register_t *retval)
{
#else
int
sys_getpeername(p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
register struct sys_getpeername_args /* {
syscallarg(int) fdes;
syscallarg(caddr_t) asa;
syscallarg(socklen_t *) alen;
} */ *uap = v;
#endif // __ECOS
struct file *fp;
register struct socket *so;
struct mbuf *m;
socklen_t len;
int error;
 
if ((error = getsock(p->p_fd, SCARG(uap, fdes), &fp)) != 0)
return (error);
so = (struct socket *)fp->f_data;
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0)
return (ENOTCONN);
error = copyin((caddr_t)SCARG(uap, alen), (caddr_t)&len, sizeof (len));
if (error)
return (error);
m = m_getclr(M_WAIT, MT_SONAME);
if (m == NULL)
return (ENOBUFS);
error = (*so->so_proto->pr_usrreq)(so, PRU_PEERADDR, 0, m, 0);
if (error)
goto bad;
if (len > m->m_len)
len = m->m_len;
error = copyout(mtod(m, caddr_t), (caddr_t)SCARG(uap, asa), len);
if (error == 0)
error = copyout((caddr_t)&len, (caddr_t)SCARG(uap, alen),
sizeof (len));
bad:
m_freem(m);
return (error);
}
 
int
sockargs(mp, buf, buflen, type)
struct mbuf **mp;
caddr_t buf;
socklen_t buflen;
int type;
{
register struct sockaddr *sa;
register struct mbuf *m;
int error;
 
if (buflen > MLEN) {
#ifdef COMPAT_OLDSOCK
if (type == MT_SONAME && buflen <= 112)
buflen = MLEN; /* unix domain compat. hack */
else
#endif
return (EINVAL);
}
m = m_get(M_WAIT, type);
if (m == NULL)
return (ENOBUFS);
m->m_len = buflen;
error = copyin(buf, mtod(m, caddr_t), buflen);
if (error) {
(void) m_free(m);
return (error);
}
*mp = m;
if (type == MT_SONAME) {
sa = mtod(m, struct sockaddr *);
 
#if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN
if (sa->sa_family == 0 && sa->sa_len < AF_MAX)
sa->sa_family = sa->sa_len;
#endif
sa->sa_len = buflen;
}
return (0);
}
 
#ifdef __ECOS
static int
ecos_getsock(int fdes, struct file **fpp)
{
struct file *fp;
if (getfp(fdes, &fp))
return (EBADF);
if (fp->f_type != DTYPE_SOCKET)
return (ENOTSOCK);
*fpp = fp;
return (0);
}
#else
int
getsock(fdp, fdes, fpp)
struct filedesc *fdp;
int fdes;
struct file **fpp;
{
register struct file *fp;
 
if ((unsigned)fdes >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[fdes]) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_SOCKET)
return (ENOTSOCK);
*fpp = fp;
return (0);
}
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.