2021-10-11 22:19:34 -03:00

1240 lines
27 KiB
C

static char sccsid[] = "@(#)99 1.32.1.28 src/bos/kernel/uipc/usrreq.c, sysuipc, bos41J, 9525E_all 6/21/95 13:07:03";
/*
* COMPONENT_NAME: SYSUIPC
*
* FUNCTIONS:
* uipc_init
* uipc_usrreq
* unp_abort
* unp_attach
* unp_bind
* unp_connect
* unp_connect2
* unp_detach
* unp_discard
* unp_disconnect
* unp_dispose
* unp_drain
* unp_drop
* unp_externalize
* unp_internalize
* unp_mark
* unp_scan
* unp_shutdown
* unp_usrclosed
*
*
* ORIGINS: 26,27,85
*
*
* (C) COPYRIGHT International Business Machines Corp. 1988,1993
* All Rights Reserved
* Licensed Materials - Property of IBM
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
/*
*
* (c) Copyright 1991, OPEN SOFTWARE FOUNDATION, INC.
* ALL RIGHTS RESERVED
*
*/
/*
* OSF/1 1.2
*/
/* uipc_usrreq.c 2.1 16:10:44 4/20/90 SecureWare, Inc. */
/*
* Copyright (C) 1988,1989 Encore Computer Corporation. All Rights Reserved
*
* Property of Encore Computer Corporation.
* This software is made available solely pursuant to the terms of
* a software license agreement which governs its use. Unauthorized
* duplication, distribution or sale are strictly prohibited.
*
*/
/*
* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Base: uipc_usrreq.c 7.13 (Berkeley) 10/19/89
* Merged: uipc_usrreq.c 7.20 (Berkeley) 6/28/90
* Merged: uipc_usrreq.c 7.26 (Berkeley) 6/3/91
*/
#include "net/net_globals.h"
#if _AIX_FULLOSF
#include <sys/secdefines.h>
#endif
#include "sys/param.h"
#include "sys/user.h"
#include "sys/vnode.h"
#include "sys/file.h"
#include "sys/stat.h"
#include "sys/time.h"
#include "sys/mbuf.h"
#include "sys/socket.h"
#include "sys/socketvar.h"
#include "sys/domain.h"
#include "sys/protosw.h"
#include "sys/unpcb.h"
#include "sys/un.h"
#include "net/net_malloc.h"
#ifdef _AIX
#include "sys/var.h"
#include "sys/vnode.h"
#include "sys/vattr.h"
#include "sys/low.h"
#include <sys/fs_locks.h>
#endif /* _AIX */
#if SEC_ARCH
#include <sys/security.h>
#include <sys/secpolicy.h>
#include <sys/ioctl.h>
extern caddr_t findrights();
#endif
#ifdef _AIX
#include <sys/errno.h>
#include <sys/intr.h>
#include <sys/uio.h>
#endif /* _AIX */
LOCK_ASSERTL_DECL
/*
* Unix communications domain.
*
* TODO:
* SEQPACKET, RDM
* rethink name space problems
* need a proper out-of-band
*/
struct unpcb unb; /* global list used by netstat */
CONST struct sockaddr sun_noname = { sizeof(sun_noname), AF_UNIX };
ino_t unp_vno; /* prototype for fake vnode numbers */
#if NETSYNC_LOCK
simple_lock_data_t global_unpconn_lock;
simple_lock_data_t unp_misc_lock;
#endif
simple_lock_data_t socklock_free_lock;
struct socklocks * free_socklocks;
simple_lock_data_t free_sockets_lock;
struct socket * free_sockets;
void
uipc_init()
{
unb.unp_queue.next = unb.unp_queue.prev = &unb.unp_queue;
UNPCONN_LOCKINIT();
UNPMISC_LOCKINIT();
/* Initialize socklocks free list and lock. */
free_socklocks = 0;
lock_alloc(&socklock_free_lock, LOCK_ALLOC_PIN, UNPMISC_LOCK_FAMILY,
4);
simple_lock_init(&socklock_free_lock);
/* Initialize socket free list and lock. */
free_sockets = 0;
lock_alloc(&free_sockets_lock, LOCK_ALLOC_PIN, UNPMISC_LOCK_FAMILY, 4);
simple_lock_init(&free_sockets_lock);
}
/*ARGSUSED*/
uipc_usrreq(so, req, m, nam, control)
struct socket *so;
int req;
struct mbuf *m, *nam, *control;
{
struct unpcb *unp = sotounpcb(so);
register struct socket *so2;
register int error = 0;
if (req == PRU_CONTROL) {
#if SEC_ARCH
/* XXX m == scalar, nam == int * */
if ((int) m == SIOCGPEERPRIV) {
if (unp->unp_conn == (struct unpcb *) 0)
return ENOTCONN;
so2 = unp->unp_conn->unp_socket;
/* Don't lock so2 for this */
*(int *) nam = (so2->so_state & SS_PRIV) != 0;
return 0;
}
#endif
return (EOPNOTSUPP);
}
if (req != PRU_SEND && control && control->m_len) {
error = EOPNOTSUPP;
goto release;
}
if (unp == 0 && req != PRU_ATTACH) {
error = EINVAL;
goto release;
}
LOCK_ASSERT("uipc_usrreq", SOCKET_ISLOCKED(so));
switch (req) {
case PRU_ATTACH:
if (unp) {
error = EISCONN;
break;
}
error = unp_attach(so);
break;
case PRU_DETACH:
unp_detach(unp);
break;
case PRU_BIND:
SOCKET_UNLOCK(so); /* namei may be interrupted */
/* if no nam, then came in a backdoor (xtiso); feign success */
error = nam ? unp_bind(unp, nam) : 0;
SOCKET_LOCK(so);
break;
case PRU_LISTEN:
if (unp->unp_vnode == 0)
error = EINVAL;
#ifdef _AIX
if (so->so_type == SOCK_DGRAM)
error = EOPNOTSUPP;
#endif /* _AIX */
break;
case PRU_CONNECT:
error = unp_connect(so, nam);
break;
case PRU_CONNECT2:
error = unp_connect2(so, (struct socket *)nam);
break;
case PRU_DISCONNECT:
unp_disconnect(unp);
break;
case PRU_ACCEPT:
/*
* Pass back name of connected socket,
* if it was bound and we are still connected
* (our peer may have closed already!).
*/
if (unp->unp_conn && unp->unp_conn->unp_addr) {
nam->m_len = unp->unp_conn->unp_addr->m_len;
bcopy(mtod(unp->unp_conn->unp_addr, caddr_t),
mtod(nam, caddr_t), (unsigned)nam->m_len);
} else {
nam->m_len = sizeof(sun_noname);
*(mtod(nam, struct sockaddr *)) = sun_noname;
}
break;
case PRU_SHUTDOWN:
socantsendmore(so);
unp_shutdown(unp);
break;
case PRU_RCVD:
switch (so->so_type) {
case SOCK_DGRAM:
panic("uipc 1");
/*NOTREACHED*/
case SOCK_STREAM:
#define rcv (&so->so_rcv)
#define snd (&so2->so_snd)
if (unp->unp_conn == 0)
break;
so2 = unp->unp_conn->unp_socket;
LOCK_ASSERT("uipc_usrreq PRU_RCVD STREAM so2notso", (so->so_lock == so2->so_lock));
SOCKBUF_LOCK(rcv);
SOCKBUF_LOCK(snd);
/*
* Adjust backpressure on sender
* and wakeup any waiting to write.
*/
snd->sb_mbmax += unp->unp_mbcnt - rcv->sb_mbcnt;
unp->unp_mbcnt = rcv->sb_mbcnt;
snd->sb_hiwat += unp->unp_cc - rcv->sb_cc;
unp->unp_cc = rcv->sb_cc;
#ifdef _AIX_FULLOSF
if (so->so_special & SP_PIPE) { /* Posix/AES */
struct timeval now;
microtime(&now);
unp->unp_atime = now.tv_sec;
}
#endif /* _AIX_FULLOSF */
if (snd->sb_flags & SB_NOTIFY) {
SOCKBUF_UNLOCK(snd);
sowwakeup(so2);
} else
SOCKBUF_UNLOCK(snd);
SOCKBUF_UNLOCK(rcv);
#undef snd
#undef rcv
break;
default:
panic("uipc 2");
}
break;
case PRU_SEND:
if (control && (error = unp_internalize(control)))
break;
switch (so->so_type) {
case SOCK_DGRAM: {
struct sockaddr *from;
if (nam) {
if (unp->unp_conn) {
error = EISCONN;
break;
}
error = unp_connect(so, nam);
LOCK_ASSERT("uipc_usrreq PRU_SEND DGRAM so", SOCKET_ISLOCKED(so));
if (error)
break;
} else {
if (unp->unp_conn == 0) {
error = ENOTCONN;
break;
}
}
so2 = unp->unp_conn->unp_socket;
LOCK_ASSERT("uipc_usrreq PRU_SEND DGRAM so2notso", (so2->so_lock == so->so_lock));
SOCKBUF_LOCK(&so->so_snd);
SOCKBUF_LOCK(&so2->so_rcv);
if (unp->unp_addr)
from = mtod(unp->unp_addr, struct sockaddr *);
else
from = (struct sockaddr *)&sun_noname;
if (sbappendaddr(&so2->so_rcv, from, m, control)) {
if (so2->so_rcv.sb_flags & SB_NOTIFY) {
SOCKBUF_UNLOCK(&so2->so_rcv);
sorwakeup(so2);
} else
SOCKBUF_UNLOCK(&so2->so_rcv);
m = 0;
control = 0;
} else {
SOCKBUF_UNLOCK(&so2->so_rcv);
error = ENOBUFS;
}
SOCKBUF_UNLOCK(&so->so_snd);
if (nam)
unp_disconnect(unp);
break;
}
case SOCK_STREAM:
#define rcv (&so2->so_rcv)
#define snd (&so->so_snd)
if (so->so_state & SS_CANTSENDMORE) {
error = EPIPE;
break;
}
if (unp->unp_conn == 0)
panic("uipc 3");
so2 = unp->unp_conn->unp_socket;
LOCK_ASSERT("uipc_usrreq PRU_SEND STREAM so2notso", (so2->so_lock == so->so_lock));
SOCKBUF_LOCK(snd);
SOCKBUF_LOCK(rcv);
/*
* Send to paired receive port, and then reduce
* send buffer hiwater marks to maintain backpressure.
* Wake up readers.
*/
if (control) {
if (sbappendcontrol(rcv, m, control))
control = 0;
else {
unp_dispose(control);
error = ENOBUFS;
}
} else
sbappend(rcv, m);
if (error == 0) {
snd->sb_mbmax -=
rcv->sb_mbcnt - unp->unp_conn->unp_mbcnt;
unp->unp_conn->unp_mbcnt = rcv->sb_mbcnt;
snd->sb_hiwat -=
rcv->sb_cc - unp->unp_conn->unp_cc;
unp->unp_conn->unp_cc = rcv->sb_cc;
#ifdef _AIX_FULLOSF
if (so->so_special & SP_PIPE) { /* Posix/AES */
struct timeval now;
microtime(&now);
unp->unp_ctime =
unp->unp_mtime = now.tv_sec;
}
#endif /* _AIX_FULLOSF */
if (rcv->sb_flags & SB_NOTIFY) {
SOCKBUF_UNLOCK(rcv);
sorwakeup(so2);
} else
SOCKBUF_UNLOCK(rcv);
SOCKBUF_UNLOCK(snd);
m = 0;
}
#undef snd
#undef rcv
break;
default:
panic("uipc 4");
}
break;
case PRU_ABORT:
unp_drop(unp, ECONNABORTED);
break;
case PRU_SENSE:
if (so->so_type == SOCK_STREAM && unp->unp_conn != 0) {
so2 = unp->unp_conn->unp_socket;
#ifdef _AIX_FULLOSF
/* Pipe stat's must behave per Posix/AES */
if (so->so_special & SP_PIPE) {
struct socket *rso, *wso;
struct unpcb *runp, *wunp;
/* Make socket pair appear as one entity */
if (so->so_special & SP_WATOMIC) { /* write */
wso = so; rso = so2;
wunp = unp; runp = unp->unp_conn;
} else { /* read */
wso = so2; rso = so;
wunp = unp->unp_conn; runp = unp;
}
((struct stat *) m)->st_atime = runp->unp_atime;
((struct stat *) m)->st_mtime = wunp->unp_mtime;
((struct stat *) m)->st_ctime = wunp->unp_ctime;
((struct stat *) m)->st_blksize =
wso->so_snd.sb_hiwat+rso->so_rcv.sb_cc;
((struct stat *) m)->st_size =
rso->so_rcv.sb_cc;
if (unp->unp_vno == 0)
unp->unp_vno = unp->unp_conn->unp_vno;
/* Else traditional socket behavior */
} else
#endif
{
((struct stat *) m)->st_blksize =
so->so_snd.sb_hiwat+so2->so_rcv.sb_cc;
((struct stat *) m)->st_size =
so2->so_rcv.sb_cc;
}
} else
((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
#ifdef _AIX_FULLOSF
((struct stat *) m)->st_dev = NODEV;
#else /* _AIX_FULLOSF */
((struct stat *) m)->st_dev = NODEVICE;
#endif /* _AIX_FULLOSF */
if (unp->unp_vno == 0) {
UNPMISC_LOCK();
unp->unp_vno = unp_vno++;
UNPMISC_UNLOCK();
}
((struct stat *) m)->st_ino = unp->unp_vno;
return (0);
case PRU_RCVOOB:
return (EOPNOTSUPP);
case PRU_SENDOOB:
error = EOPNOTSUPP;
break;
case PRU_SOCKADDR:
if (unp->unp_addr) {
nam->m_len = unp->unp_addr->m_len;
bcopy(mtod(unp->unp_addr, caddr_t),
mtod(nam, caddr_t), (unsigned)nam->m_len);
} else
nam->m_len = 0;
break;
case PRU_PEERADDR:
if (unp->unp_conn && unp->unp_conn->unp_addr) {
nam->m_len = unp->unp_conn->unp_addr->m_len;
bcopy(mtod(unp->unp_conn->unp_addr, caddr_t),
mtod(nam, caddr_t), (unsigned)nam->m_len);
} else if (unp->unp_conn) {
nam->m_len = sizeof(sun_noname);
*(mtod(nam, struct sockaddr *)) = sun_noname;
} else
nam->m_len = 0;
break;
case PRU_SLOWTIMO:
break;
default:
panic("piusrreq");
}
release:
if (control)
m_freem(control);
if (m)
m_freem(m);
return (error);
}
/*
* Both send and receive buffers are allocated PIPSIZ bytes of buffering
* for stream sockets, although the total for sender and receiver is
* actually only PIPSIZ.
* Datagram sockets really use the sendspace as the maximum datagram size,
* and don't really want to reserve the sendspace. Their recvspace should
* be large enough for at least one max-size datagram plus address.
*/
#define PIPSIZ 4096
u_long unpst_sendspace = PIPSIZ;
u_long unpst_recvspace = PIPSIZ;
u_long unpdg_sendspace = 2*1024; /* really max datagram size */
u_long unpdg_recvspace = 4*1024;
int unp_rights=0; /* file descriptors in flight */
unp_attach(so)
struct socket *so;
{
register struct unpcb *unp;
int error;
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
switch (so->so_type) {
case SOCK_STREAM:
error = soreserve(so, unpst_sendspace, unpst_recvspace);
break;
case SOCK_DGRAM:
error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
break;
}
if (error)
return (error);
}
NET_MALLOC(unp, struct unpcb *, sizeof *unp, M_PCB, M_NOWAIT);
if (unp == NULL)
return (ENOBUFS);
bzero((caddr_t)unp, sizeof *unp);
so->so_pcb = (caddr_t)unp;
unp->unp_socket = so;
UNPCONN_LOCK();
insque(&unp->unp_queue, &unb.unp_queue);
UNPCONN_UNLOCK();
return (0);
}
void
unp_detach(unp)
register struct unpcb *unp;
{
struct socket *unp_save = unp->unp_socket;
SOCKET_UNLOCK(unp->unp_socket);
bsdlog_unreg(unp->unp_socket);
if (unp->unp_vnode) {
/*
* Respect the lock heiarchy...
*/
VN_LOCK(unp->unp_vnode);
unp->unp_vnode->v_socket = 0;
VN_UNLOCK(unp->unp_vnode);
#ifdef _AIX_FULLOSF
vrele(unp->unp_vnode);
#else /* _AIX_FULLOSF */
VNOP_RELE(unp->unp_vnode);
#endif /* _AIX_FULLOSF */
unp->unp_vnode = 0;
}
SOCKET_LOCK(unp->unp_socket);
if (unp->unp_conn)
unp_disconnect(unp);
while (unp->unp_refs)
unp_drop(unp->unp_refs, ECONNRESET);
soisdisconnected(unp->unp_socket);
UNPCONN_LOCK();
remque(&unp->unp_queue);
UNPCONN_UNLOCK();
unp->unp_socket->so_pcb = 0;
m_freem(unp->unp_addr);
NET_FREE(unp, M_PCB);
UNPMISC_LOCK();
if (unp_rights) {
UNPMISC_UNLOCK();
sorflush(unp_save);
}
else
UNPMISC_UNLOCK();
}
unp_bind(unp, nam)
struct unpcb *unp;
struct mbuf *nam;
{
struct sockaddr_un *soun;
struct vnode *vp;
#ifdef _AIX_FULLOSF
register struct nameidata *ndp = &u.u_nd;
#endif /* _AIX_FULLOSF */
struct vattr vattr;
int error;
if (nam == NULL)
return (EDESTADDRREQ);
soun = mtod(nam, struct sockaddr_un *);
#ifdef _AIX_FULLOSF
ndp->ni_dirp = soun->sun_path;
#endif /* _AIX_FULLOSF */
if (unp->unp_vnode != NULL)
return (EINVAL);
#ifdef _AIX
#ifdef notdef /* 152207 */
if (soun->sun_family != AF_UNIX)
return (EAFNOSUPPORT);
#endif
#endif
if (nam->m_len == MLEN) {
if (*(mtod(nam, caddr_t) + nam->m_len - 1) != 0)
return (EINVAL);
} else if (nam->m_len < 3) {
return (EINVAL);
} else
*(mtod(nam, caddr_t) + nam->m_len) = 0;
/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
#ifdef _AIX_FULLOSF
#if MACH
ndp->ni_nameiop = CREATE | FOLLOW | WANTPARENT;
#else
ndp->ni_nameiop = CREATE | FOLLOW | LOCKPARENT;
#endif
ndp->ni_segflg = UIO_SYSSPACE;
#if SEC_BASE
audstub_unp_bind();
#endif
if (error = namei(ndp))
return (error);
vp = ndp->ni_vp;
if (vp != NULL) {
#if MACH
VOP_ABORTOP(ndp, error);
vrele(ndp->ni_dvp);
#else
VOP_ABORTOP(ndp);
if (ndp->ni_dvp == vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
#endif
vrele(vp);
return (EADDRINUSE);
}
#if MACH
vattr_null(&vattr);
vattr.va_type = VSOCK;
vattr.va_mode = 0777;
/*
* The creation of the socket must be "atomic," so the create
* operation needs additional information for the VSOCK case.
*/
vattr.va_socket = (char *) unp->unp_socket;
VOP_CREATE(ndp, &vattr, error);
if (error)
return (error);
vp = ndp->ni_vp;
if (vp->v_socket != unp->unp_socket) /* filesystem sanity check */
panic("unp_bind");
unp->unp_vnode = vp;
unp->unp_addr = m_copym(nam, 0, (int)M_COPYALL, M_DONTWAIT);
#else
VATTR_NULL(&vattr);
vattr.va_type = VSOCK;
vattr.va_mode = 0777;
if (error = VOP_CREATE(ndp, &vattr))
return (error);
vp = ndp->ni_vp;
vp->v_socket = unp->unp_socket;
unp->unp_vnode = vp;
unp->unp_addr = m_copym(nam, 0, (int)M_COPYALL, M_DONTWAIT);
VOP_UNLOCK(vp);
#endif
#else /* _AIX_FULLOSF */
bzero(&vattr, sizeof(struct vattr));
vattr.va_type = VSOCK;
vattr.va_mode = 0777;
error = vn_create(soun->sun_path, UIO_SYSSPACE, &vattr, FEXCL, 0, &vp);
if (error) {
if (error == EEXIST)
return (EADDRINUSE);
return (error);
}
vp->v_socket = unp->unp_socket;
unp->unp_vnode = vp;
/* Don't forget the null. */
nam->m_len++;
unp->unp_addr = m_copy(nam, 0, (int) M_COPYALL);
bsdlog_reg(unp->unp_socket);
#endif /* _AIX_FULLOSF */
return (0);
}
#ifndef _AIX_FULLOSF
unp_connect(so, nam)
struct socket *so;
struct mbuf *nam;
{
register struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *);
struct vnode *vp;
register struct socket *so2, *so3;
struct unpcb *unp2;
int error;
#if NETSYNC_LOCK
int so2notso;
#endif
struct ucred *crp;
LOCK_ASSERT("unp_connect", SOCKET_ISLOCKED(so));
if (soun->sun_family != AF_UNIX)
return (EAFNOSUPPORT);
if (nam->m_data + nam->m_len == &nam->m_dat[MLEN]) { /* XXX */
if (*(mtod(nam, caddr_t) + nam->m_len - 1) != 0)
return (EMSGSIZE);
} else
*(mtod(nam, caddr_t) + nam->m_len) = 0;
SOCKET_UNLOCK(so);
crp = crref();
error = lookupname(soun->sun_path, UIO_SYSSPACE, 0,
(struct vnode **)0, &vp, crp);
if (error == 0) {
if ( (error = VNOP_ACCESS(vp, W_ACC, ACC_SELF, crp)) !=0)
VNOP_RELE(vp);
}
crfree(crp);
if (error) {
SOCKET_LOCK(so);
return (error);
}
/*
* Lock vp until both sockets are locked to serialize with
* unp_disconnect...
*/
VN_LOCK(vp);
if (vp->v_type != VSOCK) {
error = ENOTSOCK;
VN_UNLOCK(vp);
goto bad2;
}
so2 = vp->v_socket;
if (so2 == 0 || !so2->so_pcb) {
error = ECONNREFUSED;
VN_UNLOCK(vp);
goto bad2;
}
if (so->so_type != so2->so_type) {
error = EPROTOTYPE;
VN_UNLOCK(vp);
goto bad2;
}
SOCKET_LOCK2(so, so2);
VN_UNLOCK(vp);
#if NETSYNC_LOCK
so2notso = (so2->so_lock != so->so_lock);
#endif
if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
if ((so2->so_options & SO_ACCEPTCONN) == 0) {
error = ECONNREFUSED;
#if NETSYNC_LOCK
if (so2notso)
SOCKET_UNLOCK(so2);
#endif
goto bad;
}
#if SEC_ARCH
bcopy(so->so_tag, so2->so_tag, SEC_NUM_TAGS * sizeof(tag_t));
#endif
unp2 = sotounpcb(so2); /* Get server name before unlock */
if (unp2->unp_addr)
nam = m_copym(unp2->unp_addr, 0, (int)M_COPYALL, M_DONTWAIT);
else
nam = 0;
if ((so3 = sonewconn(so2, 0)) == 0) {
error = ECONNREFUSED;
if (nam) m_freem(nam);
#if NETSYNC_LOCK
/*
* sonewconn() unlocks so2. But if
* so and so2 shared the same lock...
*/
if (!so2notso)
goto bad2;
#endif
goto bad;
}
#if NETSYNC_LOCK
/*
* Note that so3 is brand new and has no relation
* to so. But sonewconn unlocked the old so2, which
* may have shared a lock with so (or have been so
* itself!). We thus may need to relock so.
*/
if (!so2notso)
SOCKET_LOCK(so);
#endif
sotounpcb(so3)->unp_addr = nam;
so2 = so3;
}
error = unp_connect2(so, so2);
if (error)
SOCKET_UNLOCK(so2);
goto bad;
bad2:
SOCKET_LOCK(so);
bad:
VNOP_RELE(vp);
return (error);
}
#endif /* _AIX_FULLOSF */
unp_connect2(so, so2)
register struct socket *so;
register struct socket *so2;
{
register struct unpcb *unp = sotounpcb(so);
register struct unpcb *unp2;
register int error;
LOCK_ASSERT("unp_connect2 so", SOCKET_ISLOCKED(so));
LOCK_ASSERT("unp_connect2 so2", SOCKET_ISLOCKED(so2));
if (so2->so_type != so->so_type)
return (EPROTOTYPE);
#if NETSYNC_LOCK
if (error=solockpair(so, so2))
return(error);
#endif
unp2 = sotounpcb(so2);
unp->unp_conn = unp2;
switch (so->so_type) {
case SOCK_DGRAM:
UNPCONN_LOCK();
unp->unp_nextref = unp2->unp_refs;
unp2->unp_refs = unp;
UNPCONN_UNLOCK();
soisconnected(so);
break;
case SOCK_STREAM:
unp2->unp_conn = unp;
soisconnected(so);
soisconnected(so2);
#ifdef _AIX_FULLOSF
if (so->so_special & SP_PIPE) { /* Posix/AES */
struct timeval now;
microtime(&now);
unp->unp_ctime = unp->unp_atime =
unp->unp_mtime = now.tv_sec;
unp2->unp_ctime = unp2->unp_atime =
unp2->unp_mtime = now.tv_sec;
}
#endif /* _AIX_FULLOSF */
break;
default:
panic("unp_connect2");
}
return (0);
}
void
unp_disconnect(unp)
struct unpcb *unp;
{
register struct unpcb *unp2 = unp->unp_conn;
if (unp2 == 0)
return;
#if NETSYNC_LOCK
/*
* unp->unp_socket is locked on entry... be sure unp2's is also.
*/
LOCK_ASSERT("unp_disconnect", SOCKET_ISLOCKED(unp->unp_socket));
if (unp->unp_socket->so_lock != unp2->unp_socket->so_lock)
SOCKET_LOCK(unp2->unp_socket);
#endif
unp->unp_conn = 0;
switch (unp->unp_socket->so_type) {
case SOCK_DGRAM:
UNPCONN_LOCK();
if (unp2->unp_refs == unp)
unp2->unp_refs = unp->unp_nextref;
else {
unp2 = unp2->unp_refs;
for (;;) {
if (unp2 == 0)
panic("unp_disconnect");
if (unp2->unp_nextref == unp)
break;
unp2 = unp2->unp_nextref;
}
unp2->unp_nextref = unp->unp_nextref;
}
unp->unp_nextref = 0;
UNPCONN_UNLOCK();
unp->unp_socket->so_state &= ~SS_ISCONNECTED;
break;
case SOCK_STREAM:
soisdisconnected(unp->unp_socket);
unp2->unp_conn = 0;
soisdisconnected(unp2->unp_socket);
break;
}
#if NETSYNC_LOCK
if (unp->unp_socket->so_lock != unp2->unp_socket->so_lock)
SOCKET_UNLOCK(unp2->unp_socket);
#endif
}
#ifdef notdef
void
unp_abort(unp)
struct unpcb *unp;
{
unp_detach(unp);
}
#endif
/*ARGSUSED*/
void
unp_shutdown(unp)
struct unpcb *unp;
{
struct socket *so;
if (unp->unp_socket->so_type == SOCK_STREAM && unp->unp_conn &&
(so = unp->unp_conn->unp_socket)) {
LOCK_ASSERT("unp_shutdown", SOCKET_ISLOCKED(so));
socantrcvmore(so);
}
}
void
unp_drop(unp, errno)
struct unpcb *unp;
int errno;
{
struct socket *so = unp->unp_socket;
LOCK_ASSERT("unp_drop", SOCKET_ISLOCKED(so));
so->so_error = errno;
unp_disconnect(unp);
if (so->so_head) {
UNPCONN_LOCK();
remque(&unp->unp_queue);
UNPCONN_UNLOCK();
so->so_pcb = (caddr_t) 0;
m_freem(unp->unp_addr);
NET_FREE(unp, M_PCB);
sofree(so);
}
}
#ifdef notdef
void
unp_drain()
{
}
#endif
unp_externalize(rights)
struct mbuf *rights;
{
register int i;
register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
register struct file **rp = (struct file **)(cm + 1);
register struct file *fp;
int newfds = (cm->cmsg_len - sizeof(*cm)) / sizeof (int);
int f, last;
last = 0;
for (i = 0; i < newfds; i++) {
if (ufdalloc(last,&f)) {
/* clean up */
for (i = 0; i < newfds; i++) {
fp = *rp;
FP_LOCK(fp);
ASSERT(fp->f_count > 0);
if ((fp)->f_count > 1) {
(fp)->f_count--;
FP_UNLOCK(fp);
} else {
FP_UNLOCK(fp);
(void) closef(fp);
}
*rp++ = 0;
}
return (EMSGSIZE);
}
fp = *rp;
u.u_ofile(f) = fp;
FP_LOCK(fp);
fp->f_msgcount--;
FP_UNLOCK(fp);
UNPMISC_LOCK();
unp_rights--;
UNPMISC_UNLOCK();
*(int *)rp++ = f;
last = f;
}
return (0);
}
unp_internalize(control)
struct mbuf *control;
{
register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
register struct file **rp;
struct file *fp;
register int i, fd, error = 0;
int oldfds;
int rc;
if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
cm->cmsg_len != control->m_len) {
return (EINVAL);
}
oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
rp = (struct file **)(cm + 1);
UNPMISC_LOCK();
for (i = 0; i < oldfds; i++) {
rc = *(int *)rp++;
if (rc < 0 || rc > u.u_maxofile || !u.u_ufd[rc].fp) {
UNPMISC_UNLOCK();
return (EBADF);
}
}
rp = (struct file **)(cm + 1);
for (i = 0; i < oldfds; i++) {
if (rc = getf(*(int *)rp,&fp)) {
UNPMISC_UNLOCK();
return (rc);
}
FP_LOCK(fp);
fp->f_count++;
fp->f_msgcount++;
FP_UNLOCK(fp);
/* Decrement the file descriptor count */
ufdrele(*(int *)rp);
*rp++ = fp;
unp_rights++;
}
UNPMISC_UNLOCK();
return (0);
}
int unp_defer;
extern struct domain unixdomain;
struct extra_ref {
struct file *fp;
struct extra_ref *fp_next;
};
void
unp_dispose(m)
struct mbuf *m;
{
if (m)
unp_scan(m, unp_discard);
}
void
unp_scan(m0, op)
register struct mbuf *m0;
void (*op)(struct file *);
{
register struct mbuf *m;
register struct file **rp;
register struct cmsghdr *cm;
register int i;
int qfds;
while (m0) {
for (m = m0; m; m = m->m_next)
if (m->m_type == MT_CONTROL &&
m->m_len >= sizeof(*cm)) {
cm = mtod(m, struct cmsghdr *);
if (cm->cmsg_level != SOL_SOCKET ||
cm->cmsg_type != SCM_RIGHTS)
continue;
qfds = (cm->cmsg_len - sizeof *cm)
/ sizeof (struct file *);
rp = (struct file **)(cm + 1);
for (i = 0; i < qfds; i++)
(*op)(*rp++);
}
m0 = m0->m_act;
}
}
void
unp_mark(fp)
struct file *fp;
{
FP_LOCK(fp);
if (fp->f_flag & GCFMARK) {
FP_UNLOCK(fp);
return;
}
unp_defer++;
fp->f_flag |= (GCFMARK|GCFDEFER);
FP_UNLOCK(fp);
}
void
unp_discard(fp)
struct file *fp;
{
FP_LOCK(fp);
fp->f_msgcount--;
UNPMISC_LOCK();
unp_rights--;
UNPMISC_UNLOCK();
ASSERT(fp->f_count > 0);
if ((fp)->f_count > 1) {
(fp)->f_count--;
FP_UNLOCK(fp);
} else {
FP_UNLOCK(fp);
(void) closef(fp);
}
}
/* Slow timeout routine for UNIX domain sockets.
*
* The only thing we currently do here is free struct socklocks
* structures hanging off free_socklocks.
*
* First shot at this algorithm:
* Grab socklock_free_lock.
* while (free_socklocks) {
* save free_socklocks.freelist
* free free_socklocks
* set free_socklocks to saved free_socklocks.freelist
* }
* Unlock socklock_free_lock.
*
* If this doesn't work, try aging the entries on the list by using a
* free field in struct socklocks as a counter.
*/
void
uipc_slowtimo(void)
{
register struct socklocks *cur, *next;
register struct socket *curso, *nextso;
/* Lock socklock_free_lock. */
simple_lock(&socklock_free_lock);
cur = free_socklocks;
free_socklocks = 0;
simple_unlock(&socklock_free_lock);
/* Free items on the list */
for (; cur; cur = next) {
next = cur->freelist;
LOCK_FREE(&cur->sock_lock);
NET_FREE(cur, M_SOCKET);
}
/* Go through free_sockets */
simple_lock(&free_sockets_lock);
curso = free_sockets;
free_sockets = 0;
simple_unlock(&free_sockets_lock);
/* Free any sockets on the list whose so->so_spare <= 0. */
for (; curso; curso = nextso) {
nextso = curso->so_tpcb;
if (curso->so_spare <= 0) {
/* Free socklock structure, then socket. */
struct socklocks *lp = curso->so_lock;
if (lp) {
int cnt = --lp->refcnt;
curso->so_lock = 0;
curso->so_rcv.sb_lock = curso->so_snd.sb_lock =
0;
if (cnt <= 0) {
LOCK_FREE(&lp->sock_lock);
NET_FREE(lp, M_SOCKET);
}
}
if ((curso->so_rcv.sb_wakeone != EVENT_NULL) ||
(curso->so_snd.sb_wakeone != EVENT_NULL))
panic("sounlock - freeing socket with sleepers");
NET_FREE(curso, M_SOCKET);
} else {
/* Put it back on the free list. */
simple_lock(&free_sockets_lock);
curso->so_tpcb = (caddr_t) free_sockets;
free_sockets = curso;
simple_unlock(&free_sockets_lock);
}
}
}