uipc_socket.c
来自「eCos操作系统源码」· C语言 代码 · 共 1,522 行 · 第 1/3 页
C
1,522 行
//==========================================================================//// src/sys/kern/uipc_socket.c////==========================================================================//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD, // FreeBSD or other sources, and are covered by the appropriate// copyright disclaimers included herein.//// Portions created by Red Hat are// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================/* * 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 * $FreeBSD: src/sys/kern/uipc_socket.c,v 1.68.2.16 2001/06/14 20:46:06 ume Exp $ */#include <sys/param.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/domain.h>#include <sys/malloc.h>#include <sys/protosw.h>#include <sys/socket.h>#include <sys/socketvar.h>#include <cyg/io/file.h>#ifdef INETstatic int do_setopt_accept_filter(struct socket *so, struct sockopt *sopt);#endif /* INET */#if 0 // FILTstatic void filt_sordetach(struct knote *kn);static int filt_soread(struct knote *kn, long hint);static void filt_sowdetach(struct knote *kn);static int filt_sowrite(struct knote *kn, long hint);static int filt_solisten(struct knote *kn, long hint);static struct filterops solisten_filtops = { 1, NULL, filt_sordetach, filt_solisten };static struct filterops soread_filtops = { 1, NULL, filt_sordetach, filt_soread };static struct filterops sowrite_filtops = { 1, NULL, filt_sowdetach, filt_sowrite };#endifstruct vm_zone *socket_zone;so_gen_t so_gencnt; /* generation count for sockets */static int somaxconn = SOMAXCONN;/* * 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. *//* * Get a socket structure from our zone, and initialize it. * 'waitok' has been implemented for eCos, with [currently] some * rather fixed strategy - it will retry some number of times (10) * after at most 2 minutes. This seems sufficient for sockets which * are tied up in the TCP close process. */struct socket *soalloc(int waitok){ struct socket *so = NULL; int maxtries = waitok ? 10 : 1; while (maxtries-- > 0) { so = zalloci(socket_zone); if (so) { /* XXX race condition for reentrant kernel */ bzero(so, sizeof *so); so->so_gencnt = ++so_gencnt; so->so_zone = socket_zone; TAILQ_INIT(&so->so_aiojobq); return so; } if (waitok) { diag_printf("DEBUG: Out of sockets - waiting\n"); tsleep(socket_zone, PVM|PCATCH, "soalloc", 120*100); diag_printf("DEBUG: ... retry sockets\n"); } } return so;}intsocreate(dom, aso, type, proto, p) int dom; struct socket **aso; register int type; int proto; struct proc *p;{ 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_usrreqs->pru_attach == 0) return (EPROTONOSUPPORT); if (prp->pr_type != type) return (EPROTOTYPE); so = soalloc(p != 0); if (so == 0) { return (ENOBUFS); } TAILQ_INIT(&so->so_incomp); TAILQ_INIT(&so->so_comp); so->so_type = type; so->so_proto = prp; error = (*prp->pr_usrreqs->pru_attach)(so, proto, p); if (error) { so->so_state |= SS_NOFDREF; sofree(so); return (error); } *aso = so; return (0);}intsobind(so, nam, p) struct socket *so; struct sockaddr *nam; struct proc *p;{ int s = splnet(); int error; error = (*so->so_proto->pr_usrreqs->pru_bind)(so, nam, p); splx(s); return (error);}voidsodealloc(so) struct socket *so;{ so->so_gencnt = ++so_gencnt;#ifdef INET if (so->so_accf != NULL) { if (so->so_accf->so_accept_filter != NULL && so->so_accf->so_accept_filter->accf_destroy != NULL) { so->so_accf->so_accept_filter->accf_destroy(so); } if (so->so_accf->so_accept_filter_str != NULL) FREE(so->so_accf->so_accept_filter_str, M_ACCF); FREE(so->so_accf, M_ACCF); }#endif /* INET */ zfreei(so->so_zone, so); wakeup(so->so_zone);}intsolisten(so, backlog, p) register struct socket *so; int backlog; struct proc *p;{ int s, error; s = splnet(); error = (*so->so_proto->pr_usrreqs->pru_listen)(so, p); if (error) { splx(s); return (error); } if (TAILQ_EMPTY(&so->so_comp)) so->so_options |= SO_ACCEPTCONN; if (backlog < 0 || backlog > somaxconn) backlog = somaxconn; so->so_qlimit = backlog; splx(s); return (0);}voidsofree(so) register struct socket *so;{ struct socket *head = so->so_head; if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) return; if (head != NULL) { if (so->so_state & SS_INCOMP) { TAILQ_REMOVE(&head->so_incomp, so, so_list); head->so_incqlen--; } else if (so->so_state & SS_COMP) { /* * 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. */ return; } else { panic("sofree: not queued"); } head->so_qlen--; so->so_state &= ~SS_INCOMP; so->so_head = NULL; } sbrelease(&so->so_snd, so); sorflush(so); sodealloc(so);}/* * Close a socket on last file table reference removal. * Initiate disconnect if connected. * Free socket when disconnect complete. */intsoclose(so) register struct socket *so;{ int s = splnet(); /* conservative */ int error = 0; if (so->so_options & SO_ACCEPTCONN) { struct socket *sp, *sonext; sp = TAILQ_FIRST(&so->so_incomp); for (; sp != NULL; sp = sonext) { sonext = TAILQ_NEXT(sp, so_list); (void) soabort(sp); } for (sp = TAILQ_FIRST(&so->so_comp); sp != NULL; sp = sonext) { sonext = TAILQ_NEXT(sp, so_list); /* Dequeue from so_comp since sofree() won't do it */ TAILQ_REMOVE(&so->so_comp, sp, so_list); so->so_qlen--; sp->so_state &= ~SS_COMP; sp->so_head = NULL; (void) soabort(sp); } } 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, "soclos", so->so_linger * hz); if (error) break; } } }drop: if (so->so_pcb) { int error2 = (*so->so_proto->pr_usrreqs->pru_detach)(so); 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 splnet... */intsoabort(so) struct socket *so;{ int error; error = (*so->so_proto->pr_usrreqs->pru_abort)(so); if (error) { sofree(so); return error; } return (0);}intsoaccept(so, nam) register struct socket *so; struct sockaddr **nam;{ int s = splnet(); int error; if ((so->so_state & SS_NOFDREF) == 0) panic("soaccept: !NOFDREF"); so->so_state &= ~SS_NOFDREF; error = (*so->so_proto->pr_usrreqs->pru_accept)(so, nam); splx(s); return (error);}intsoconnect(so, nam, p) register struct socket *so; struct sockaddr *nam; struct proc *p;{ int s; int error; if (so->so_options & SO_ACCEPTCONN) return (EOPNOTSUPP); s = splnet(); /* * 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_usrreqs->pru_connect)(so, nam, p); splx(s); return (error);}intsoconnect2(so1, so2) register struct socket *so1; struct socket *so2;{ int s = splnet(); int error; error = (*so1->so_proto->pr_usrreqs->pru_connect2)(so1, so2); splx(s); return (error);}intsodisconnect(so) register struct socket *so;{ int s = splnet(); 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_usrreqs->pru_disconnect)(so);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. */intsosend(so, addr, uio, top, control, flags, p) register struct socket *so; struct sockaddr *addr; struct uio *uio; struct mbuf *top; struct mbuf *control; int flags; struct proc *p;{ struct mbuf **mp; register struct mbuf *m; register long space, len, 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. * 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. * * Also check to make sure that MSG_EOR isn't used on SOCK_STREAM * type sockets since that's an error. */ 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); if (control) clen = control->m_len;#define snderr(errno) { error = errno; splx(s); goto release; }restart: error = sblock(&so->so_snd, SBLOCKWAIT(flags)); if (error) goto out; do { s = splnet(); if (so->so_state & SS_CANTSENDMORE) snderr(EPIPE); if (so->so_error) { error = so->so_error; so->so_error = 0; splx(s); goto release; } if ((so->so_state & SS_ISCONNECTED) == 0) { /* * `sendto' and `sendmsg' is allowed on a connection- * based socket if it supports implied connect. * Return ENOTCONN if not connected and no address is * supplied. */ if ((so->so_proto->pr_flags & PR_CONNREQUIRED) && (so->so_proto->pr_flags & PR_IMPLOPCL) == 0) { if ((so->so_state & SS_ISCONFIRMING) == 0 && !(resid == 0 && clen != 0))
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?