uipc_socket.c

来自「eCos操作系统源码」· C语言 代码 · 共 1,522 行 · 第 1/3 页

C
1,522
字号
					snderr(ENOTCONN);			} else if (addr == 0)			    snderr(so->so_proto->pr_flags & PR_CONNREQUIRED ?				   ENOTCONN : 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);				if (m == NULL) {					error = ENOBUFS;					goto release;				}				mlen = MHLEN;				m->m_pkthdr.len = 0;				m->m_pkthdr.rcvif = (struct ifnet *)0;			} else {				MGET(m, M_WAIT, MT_DATA);				if (m == NULL) {					error = ENOBUFS;					goto release;				}				mlen = MLEN;			}			if (resid >= MINCLSIZE) {				MCLGET(m, M_WAIT);				if ((m->m_flags & M_EXT) == 0)					goto nopages;				mlen = MCLBYTES;				len = min(min(mlen, resid), space);			} else {nopages:				len = min(min(mlen, resid), space);				/*				 * For datagram protocols, leave room				 * for protocol headers in first mbuf.				 */				if (atomic && top == 0 && len < mlen)					MH_ALIGN(m, len);			}			space -= 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 = splnet();				/* XXX */		    /*		     * XXX all the SS_CANTSENDMORE checks previously		     * done could be out of date.  We could have recieved		     * a reset packet in an interrupt or maybe we slept		     * while doing page faults in uiomove() etc. We could		     * probably recheck again inside the splnet() protection		     * here, but there are probably other places that this		     * also happens.  We must rethink this.		     */		    error = (*so->so_proto->pr_usrreqs->pru_send)(so,			(flags & MSG_OOB) ? PRUS_OOB :			/*			 * If the user set MSG_EOF, the protocol			 * understands this flag and nothing left to			 * send then use PRU_SEND_EOF instead of PRU_SEND.			 */			((flags & MSG_EOF) &&			 (so->so_proto->pr_flags & PR_IMPLOPCL) &&			 (resid <= 0)) ?				PRUS_EOF :			/* If there is more to send set PRUS_MORETOCOME */			(resid > 0 && space > 0) ? PRUS_MORETOCOME : 0,			top, addr, control, p);		    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. */intsoreceive(so, psa, uio, mp0, controlp, flagsp)	register struct socket *so;	struct sockaddr **psa;	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;	int orig_resid = uio->uio_resid;	mp = mp0;	if (psa)		*psa = 0;	if (controlp)		*controlp = 0;	if (flagsp)		flags = *flagsp &~ MSG_EOR;	else		flags = 0;	if (flags & MSG_OOB) {		m = m_get(M_WAIT, MT_DATA);		if (m == NULL) {			return (ENOBUFS);                }		error = (*pr->pr_usrreqs->pru_rcvoob)(so, m, flags & MSG_PEEK);		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_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)) {		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:	nextrecord = m->m_nextpkt;	if (pr->pr_flags & PR_ADDR) {		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;		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;	if (!(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR))		return (EINVAL);	if (how != SHUT_WR)		sorflush(so);	if (how != SHUT_RD)		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, so);}#ifdef INETstatic intdo_setopt_accept_filter(so, sopt)	struct	socket *so;	struct	sockopt *sopt;{	struct accept_filter_arg	*afap = NULL;	struct accept_filter	*afp;	struct so_accf	*af = so->so_accf;	int	error = 0;	/* do not set/remove accept filters on non listen sockets */	if ((so->so_options & SO_ACCEPTCONN) == 0) {		error = EINVAL;		goto out;	}	/* removing the filter */	if (sopt == NULL) {		if (af != NULL) {			if (af->so_accept_filter != NULL && 				af->so_accept_filter->accf_destroy != NULL) {				af->so_accept_filter->accf_destroy(so);			}			if (af->so_accept_filter_str != NULL) {				FREE(af->so_accept_filter_str, M_ACCF);			}			FREE(af, M_ACCF);			so->so_accf = NULL;		}		so->so_options &= ~SO_ACCEPTFILTER;		return (0);	}	/* adding a filter */	/* must remove previous filter first */	if (af != NULL) {		error = EINVAL;		goto out;	}	/* don't put large objects on the kernel stack */	MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, M_WAITOK);

⌨️ 快捷键说明

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