uipc_socket.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,219 行 · 第 1/2 页

C
1,219
字号
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_usrreqs->pru_rcvd)(so, 0);restart:	error = sblock(&so->so_rcv, SBLOCKWAIT(flags));	if (error)		return (error);	s = splnet();	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, or	 *   2. MSG_WAITALL is set, and it is possible to do the entire	 *	receive operation at once if we block (resid <= hiwat).	 *   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)) {		KASSERT(m != 0 || !so->so_rcv.sb_cc, ("receive 1"));		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)			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:	if (uio->uio_procp)		uio->uio_procp->p_stats->p_ru.ru_msgrcv++;	nextrecord = m->m_nextpkt;	if (pr->pr_flags & PR_ADDR) {		KASSERT(m->m_type == MT_SONAME, ("receive 1a"));		orig_resid = 0;		if (psa)			*psa = dup_sockaddr(mtod(m, struct sockaddr *),					    mp0 == 0);		if (flags & MSG_PEEK) {			m = m->m_next;		} else {			sbfree(&so->so_rcv, m);			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;	}	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;		else		    KASSERT(m->m_type == MT_DATA || m->m_type == MT_HEADER,			("receive 3"));		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) {			splx(s);			error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio);			s = splnet();			if (error)				goto release;		} 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);			}			m = so->so_rcv.sb_mb;			if (m)				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_usrreqs->pru_rcvd)(so, flags);	}	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 (flagsp)		*flagsp |= flags;release:	sbunlock(&so->so_rcv);	splx(s);	return (error);}intsoshutdown(so, how)	register struct socket *so;	register int how;{	register struct protosw *pr = so->so_proto;	how++;	if (how & FREAD)		sorflush(so);	if (how & FWRITE)		return ((*pr->pr_usrreqs->pru_shutdown)(so));	return (0);}voidsorflush(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);}/* * Perhaps this routine, and sooptcopyout(), below, ought to come in * an additional variant to handle the case where the option value needs * to be some kind of integer, but not a specific size. * In addition to their use here, these functions are also called by the * protocol-level pr_ctloutput() routines. */intsooptcopyin(sopt, buf, len, minlen)	struct	sockopt *sopt;	void	*buf;	size_t	len;	size_t	minlen;{	size_t	valsize;	/*	 * If the user gives us more than we wanted, we ignore it,	 * but if we don't get the minimum length the caller	 * wants, we return EINVAL.  On success, sopt->sopt_valsize	 * is set to however much we actually retrieved.	 */	if ((valsize = sopt->sopt_valsize) < minlen)		return EINVAL;	if (valsize > len)		sopt->sopt_valsize = valsize = len;	if (sopt->sopt_p != 0)		return (copyin(sopt->sopt_val, buf, valsize));	bcopy(sopt->sopt_val, buf, valsize);	return 0;}intsosetopt(so, sopt)	struct socket *so;	struct sockopt *sopt;{	int	error, optval;	struct	linger l;	struct	timeval tv;	short	val;	error = 0;	if (sopt->sopt_level != SOL_SOCKET) {		if (so->so_proto && so->so_proto->pr_ctloutput)			return ((*so->so_proto->pr_ctloutput)				  (so, sopt));		error = ENOPROTOOPT;	} else {		switch (sopt->sopt_name) {		case SO_LINGER:			error = sooptcopyin(sopt, &l, sizeof l, sizeof l);			if (error)				goto bad;			so->so_linger = l.l_linger;			if (l.l_onoff)				so->so_options |= SO_LINGER;			else				so->so_options &= ~SO_LINGER;			break;		case SO_DEBUG:		case SO_KEEPALIVE:		case SO_DONTROUTE:		case SO_USELOOPBACK:		case SO_BROADCAST:		case SO_REUSEADDR:		case SO_REUSEPORT:		case SO_OOBINLINE:		case SO_TIMESTAMP:			error = sooptcopyin(sopt, &optval, sizeof optval,					    sizeof optval);			if (error)				goto bad;			if (optval)				so->so_options |= sopt->sopt_name;			else				so->so_options &= ~sopt->sopt_name;			break;		case SO_SNDBUF:		case SO_RCVBUF:		case SO_SNDLOWAT:		case SO_RCVLOWAT:			error = sooptcopyin(sopt, &optval, sizeof optval,					    sizeof optval);			if (error)				goto bad;			/*			 * Values < 1 make no sense for any of these			 * options, so disallow them.			 */			if (optval < 1) {				error = EINVAL;				goto bad;			}			switch (sopt->sopt_name) {			case SO_SNDBUF:			case SO_RCVBUF:				if (sbreserve(sopt->sopt_name == SO_SNDBUF ?					      &so->so_snd : &so->so_rcv,					      (u_long) optval) == 0) {					error = ENOBUFS;					goto bad;				}				break;			/*			 * Make sure the low-water is never greater than			 * the high-water.			 */			case SO_SNDLOWAT:				so->so_snd.sb_lowat =				    (optval > so->so_snd.sb_hiwat) ?				    so->so_snd.sb_hiwat : optval;				break;			case SO_RCVLOWAT:				so->so_rcv.sb_lowat =				    (optval > so->so_rcv.sb_hiwat) ?				    so->so_rcv.sb_hiwat : optval;				break;			}			break;		case SO_SNDTIMEO:		case SO_RCVTIMEO:			error = sooptcopyin(sopt, &tv, sizeof tv,					    sizeof tv);			if (error)				goto bad;			if (tv.tv_sec > SHRT_MAX / hz - hz) {				error = EDOM;				goto bad;			}			val = tv.tv_sec * hz + tv.tv_usec / tick;			switch (sopt->sopt_name) {			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)				  (so, sopt));		}	}bad:	return (error);}/* Helper routine for getsockopt */intsooptcopyout(sopt, buf, len)	struct	sockopt *sopt;	void	*buf;	size_t	len;{	int	error;	size_t	valsize;	error = 0;	/*	 * Documented get behavior is that we always return a value,	 * possibly truncated to fit in the user's buffer.	 * Traditional behavior is that we always tell the user	 * precisely how much we copied, rather than something useful	 * like the total amount we had available for her.	 * Note that this interface is not idempotent; the entire answer must	 * generated ahead of time.	 */	valsize = min(len, sopt->sopt_valsize);	sopt->sopt_valsize = valsize;	if (sopt->sopt_val != 0) {		if (sopt->sopt_p != 0)			error = copyout(buf, sopt->sopt_val, valsize);		else			bcopy(buf, sopt->sopt_val, valsize);	}	return error;}intsogetopt(so, sopt)	struct socket *so;	struct sockopt *sopt;{	int	error, optval;	struct	linger l;	struct	timeval tv;	error = 0;	if (sopt->sopt_level != SOL_SOCKET) {		if (so->so_proto && so->so_proto->pr_ctloutput) {			return ((*so->so_proto->pr_ctloutput)				  (so, sopt));		} else			return (ENOPROTOOPT);	} else {		switch (sopt->sopt_name) {		case SO_LINGER:			l.l_onoff = so->so_options & SO_LINGER;			l.l_linger = so->so_linger;			error = sooptcopyout(sopt, &l, sizeof l);			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:		case SO_TIMESTAMP:			optval = so->so_options & sopt->sopt_name;integer:			error = sooptcopyout(sopt, &optval, sizeof optval);			break;		case SO_TYPE:			optval = so->so_type;			goto integer;		case SO_ERROR:			optval = so->so_error;			so->so_error = 0;			goto integer;		case SO_SNDBUF:			optval = so->so_snd.sb_hiwat;			goto integer;		case SO_RCVBUF:			optval = so->so_rcv.sb_hiwat;			goto integer;		case SO_SNDLOWAT:			optval = so->so_snd.sb_lowat;			goto integer;		case SO_RCVLOWAT:			optval = so->so_rcv.sb_lowat;			goto integer;		case SO_SNDTIMEO:		case SO_RCVTIMEO:			optval = (sopt->sopt_name == SO_SNDTIMEO ?				  so->so_snd.sb_timeo : so->so_rcv.sb_timeo);			tv.tv_sec = optval / hz;			tv.tv_usec = (optval % hz) * tick;			error = sooptcopyout(sopt, &tv, sizeof tv);			break;					default:			error = ENOPROTOOPT;			break;		}		return (error);	}}voidsohasoutofband(so)	register struct socket *so;{	if (so->so_sigio != NULL)		pgsigio(so->so_sigio, SIGURG, 0);	selwakeup(&so->so_rcv.sb_sel);}intsopoll(struct socket *so, int events, struct ucred *cred, struct proc *p){	int revents = 0;	int s = splnet();	if (events & (POLLIN | POLLRDNORM))		if (soreadable(so))			revents |= events & (POLLIN | POLLRDNORM);	if (events & (POLLOUT | POLLWRNORM))		if (sowriteable(so))			revents |= events & (POLLOUT | POLLWRNORM);	if (events & (POLLPRI | POLLRDBAND))		if (so->so_oobmark || (so->so_state & SS_RCVATMARK))			revents |= events & (POLLPRI | POLLRDBAND);	if (revents == 0) {		if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {			selrecord(p, &so->so_rcv.sb_sel);			so->so_rcv.sb_flags |= SB_SEL;		}		if (events & (POLLOUT | POLLWRNORM)) {			selrecord(p, &so->so_snd.sb_sel);			so->so_snd.sb_flags |= SB_SEL;		}	}	splx(s);	return (revents);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?