/* * * (c) Copyright 1989 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989 HEWLETT-PACKARD COMPANY * (c) Copyright 1989 DIGITAL EQUIPMENT CORPORATION * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this * file for any purpose is hereby granted without fee, provided that * the above copyright notices and this notice appears in all source * code copies, and that none of the names of Open Software * Foundation, Inc., Hewlett-Packard Company, or Digital Equipment * Corporation be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Neither Open Software Foundation, Inc., Hewlett- * Packard Company, nor Digital Equipment Corporation makes any * representations about the suitability of this software for any * purpose. * */ /* */ /* ** ** NAME: ** ** dgsoc.c ** ** FACILITY: ** ** Remote Procedure Call (RPC) ** ** ABSTRACT: ** ** DG socket manipulation routines. ** ** */ #include #include #include /* ========================================================================= */ INTERNAL void sock_free _DCE_PROTOTYPE_(( rpc_dg_sock_pool_elt_p_t * /*sp_elt*/ )); INTERNAL void use_protseq _DCE_PROTOTYPE_(( boolean32 /*is_server*/, rpc_protseq_id_t /*pseq_id*/, rpc_addr_p_t /*rpc_addr*/, unsigned_char_p_t /*endpoint*/, rpc_dg_sock_pool_elt_p_t * /*sock_pool_elt*/, unsigned32 * /*st*/ )); /* ========================================================================= */ /* * S O C K _ F R E E * * This routine is called to *really* cleanup and free a pool elt. */ INTERNAL void sock_free #ifdef _DCE_PROTO_ ( rpc_dg_sock_pool_elt_p_t *sp ) #else (sp) rpc_dg_sock_pool_elt_p_t *sp; #endif { rpc_socket_error_t serr; rpc_dg_sock_pool_elt_p_t eltp, *peltp; boolean32 is_private_socket = (*sp)->is_private; RPC_DG_SOCK_POOL_LOCK_ASSERT(0); /* * Close the socket desc. */ serr = RPC_SOCKET_CLOSE((*sp)->sock); if (RPC_SOCKET_IS_ERR(serr)) { RPC_DBG_GPRINTF(( "(sock_free) Error closing socket %d, error=%d\n", (*sp)->sock, RPC_SOCKET_ETOI(serr))); } /* * Decide whether this socket will be on the private or shared list, * and then remove it from the pool. */ peltp = (is_private_socket) ? &rpc_g_dg_sock_pool.private_sockets : &rpc_g_dg_sock_pool.shared_sockets; if (*sp == *peltp) { *peltp = (*sp)->next; } else { for (eltp = *peltp; eltp != NULL; eltp = eltp->next) { if (eltp->next == *sp) { eltp->next = (*sp)->next; break; } } } /* * For private sockets, free the private packets associated with * the socket. */ if (is_private_socket) { if ((*sp)->xqe != NULL) RPC_MEM_FREE((*sp)->xqe, RPC_C_MEM_DG_PKT_POOL_ELT); if ((*sp)->rqe != NULL) RPC_MEM_FREE((*sp)->rqe, RPC_C_MEM_DG_PKT_POOL_ELT); if ((*sp)->rqe_list_len > 0 && (*sp)->rqe_list != NULL) rpc__dg_pkt_free_rqe((*sp)->rqe_list, (rpc_dg_call_p_t)(*sp)->ccall); } rpc_g_dg_sock_pool.num_entries--; /* * Free up the elt's memory. */ RPC_MEM_FREE(*sp, RPC_C_MEM_DG_SOCK_POOL_ELT); *sp = NULL; } /* * U S E _ P R O T S E Q * * Common, internal socket pool allocation routine. Find/create a suitable * socket pool entry and add it to the listener's database. */ INTERNAL void use_protseq #ifdef _DCE_PROTO_ ( boolean32 is_server, rpc_protseq_id_t pseq_id, rpc_addr_p_t rpc_addr, unsigned_char_p_t endpoint, rpc_dg_sock_pool_elt_p_t *sock_pool_elt, unsigned32 *st ) #else (is_server, pseq_id, rpc_addr, endpoint, sock_pool_elt, st) boolean32 is_server; rpc_protseq_id_t pseq_id; rpc_addr_p_t rpc_addr; unsigned_char_p_t endpoint; rpc_dg_sock_pool_elt_p_t *sock_pool_elt; unsigned32 *st; #endif { boolean32 sock_open = false; boolean32 creating_private_socket = false; rpc_socket_error_t serr; rpc_socket_t socket_desc; rpc_dg_sock_pool_elt_p_t eltp; unsigned32 sndbuf, rcvbuf; unsigned32 priv_sock_count = 0; unsigned32 desired_sndbuf, desired_rcvbuf; *st = rpc_s_ok; RPC_DG_SOCK_POOL_LOCK(0); /* * For clients, first see if there's already an open socket of the * correct type and if so, return a ref to it. Note: servers always * create new entries. */ if (! is_server) { unsigned32 max_priv_socks = RPC_C_DG_SOCK_MAX_PRIV_SOCKS; #ifdef DEBUG if (RPC_DBG (rpc_es_dbg_dg_max_psock, 1)) { max_priv_socks = (unsigned32)(rpc_g_dbg_switches[(int)rpc_es_dbg_dg_max_psock]) - 1; } #endif /* * First, look in the list of private sockets. If we find one * that is only referenced by the socket pool, use it. While * looking, keep a count of how many private sockets exist for * this protseq. Since there's a limit on the number of private * sockets we'll create, we'll use this count later (if we have * to create a socket pool entry) to decide whether to create * a private or shared socket. */ for (eltp = rpc_g_dg_sock_pool.private_sockets; eltp != NULL; eltp = eltp->next) { if (eltp->pseq_id == pseq_id) { priv_sock_count++; if (! eltp->is_disabled && eltp->refcnt == 1) { eltp->refcnt++; /* for the ref we're returning */ *sock_pool_elt = eltp; RPC_DG_SOCK_POOL_UNLOCK(0); RPC_DBG_PRINTF(rpc_e_dbg_dg_sockets, 3, ( "(use_protseq) using private socket\n")); return; } } } /* * Didn't find a private socket. If we haven't already created * the maximum number of private sockets for this protseq, then * we'll create another one. */ if (priv_sock_count < max_priv_socks) { creating_private_socket = true; } else { /* * Else, we've maxed out the private sockets. See if we * have a shared socket for this protseq. If so, return * it. If not, drop through and create a shared socket. */ for (eltp = rpc_g_dg_sock_pool.shared_sockets; eltp != NULL; eltp = eltp->next) { if (! eltp->is_server && eltp->pseq_id == pseq_id && ! eltp->is_disabled) { eltp->refcnt++; /* for the ref we're returning */ *sock_pool_elt = eltp; RPC_DG_SOCK_POOL_UNLOCK(0); RPC_DBG_PRINTF(rpc_e_dbg_dg_sockets, 3, ( "(use_protseq) using shared socket\n")); return; } } } } RPC_DBG_PRINTF(rpc_e_dbg_dg_sockets, 3, ( "(use_protseq) allocating a %s socket\n", creating_private_socket ? "private" : "shared")); /* * Allocate a new pool entry and initialize it. */ RPC_MEM_ALLOC(eltp, rpc_dg_sock_pool_elt_p_t, sizeof(rpc_dg_sock_pool_elt_t), RPC_C_MEM_DG_SOCK_POOL_ELT, RPC_C_MEM_NOWAIT); /* * Create a network descriptor for this RPC Protocol Sequence. */ serr = rpc__socket_open(pseq_id, NULL, &socket_desc); if (RPC_SOCKET_IS_ERR(serr)) { RPC_DBG_GPRINTF(("(use_protseq) Can't create socket, error=%d\n", RPC_SOCKET_ETOI(serr))); *st = rpc_s_cant_create_sock; goto CLEANUP; } sock_open = true; /* * Bind the socket (Network descriptor) to the RPC address. */ rpc_addr->rpc_protseq_id = pseq_id; serr = rpc__socket_bind(socket_desc, rpc_addr); if (RPC_SOCKET_IS_ERR(serr)) { RPC_DBG_GPRINTF(("(use_protseq) Can't bind socket, error=%d\n", RPC_SOCKET_ETOI(serr))); *st = rpc_s_cant_bind_sock; goto CLEANUP; } /* * Request a change in the amount of system buffering provided * to the socket. */ if (creating_private_socket) { desired_sndbuf = RPC_C_DG_MAX_WINDOW_SIZE_BYTES; desired_rcvbuf = RPC_C_DG_MAX_WINDOW_SIZE_BYTES; } else { desired_sndbuf = RPC_C_DG_SOCK_DESIRED_SNDBUF; desired_rcvbuf = RPC_C_DG_SOCK_DESIRED_RCVBUF; } serr = rpc__socket_set_bufs(socket_desc, desired_sndbuf, desired_rcvbuf, &sndbuf, &rcvbuf); if (RPC_SOCKET_IS_ERR(serr)) { RPC_DBG_GPRINTF(( "(use_protseq) WARNING: Can't set socket bufs, error=%d\n", RPC_SOCKET_ETOI(serr))); } RPC_DBG_PRINTF(rpc_e_dbg_general, 3, ( "(use_protseq) desired_sndbuf %u, desired_rcvbuf %u\n", desired_sndbuf, desired_rcvbuf)); RPC_DBG_PRINTF(rpc_e_dbg_general, 3, ( "(use_protseq) actual sndbuf %lu, actual rcvbuf %lu\n", sndbuf, rcvbuf)); /* * For shared sockets, set the socket to do non-blocking IO. */ if (! creating_private_socket) { rpc__socket_set_nbio(socket_desc); } /* * Set the socket to close itself on exec. */ rpc__socket_set_close_on_exec(socket_desc); /* * For private sockets, allocate an xqe/rqe pair. These packets * allow calls with small ins/outs to avoid going through the packet * pool reservation processing. */ if (creating_private_socket) { rpc_dg_pkt_pool_elt_p_t pkt; RPC_MEM_ALLOC(pkt, rpc_dg_pkt_pool_elt_p_t, sizeof(rpc_dg_pkt_pool_elt_t), RPC_C_MEM_DG_PKT_POOL_ELT, RPC_C_MEM_NOWAIT); eltp->xqe = &pkt->u.xqe.xqe; eltp->xqe->body = &pkt->u.xqe.pkt; eltp->xqe->next = NULL; eltp->xqe->more_data = NULL; eltp->xqe->frag_len = 0; eltp->xqe->flags = 0; eltp->xqe->body_len = 0; eltp->xqe->serial_num = 0; eltp->xqe->in_cwindow = false; RPC_MEM_ALLOC(pkt, rpc_dg_pkt_pool_elt_p_t, sizeof(rpc_dg_pkt_pool_elt_t), RPC_C_MEM_DG_PKT_POOL_ELT, RPC_C_MEM_NOWAIT); eltp->rqe = &pkt->u.rqe.rqe; eltp->rqe->pkt_real = &pkt->u.rqe.pkt; eltp->rqe->pkt = (rpc_dg_raw_pkt_p_t) RPC_DG_ALIGN_8(eltp->rqe->pkt_real); eltp->rqe->next = NULL; eltp->rqe->more_data = NULL; eltp->rqe->frag_len = 0; eltp->rqe->hdrp = NULL; pkt->u.rqe.sock_ref = eltp; eltp->rqe_available = true; } else { eltp->xqe = NULL; eltp->rqe = NULL; eltp->rqe_available = false; } /* * Initialize all fields in the socket pool entry. */ eltp->sock = socket_desc; eltp->rcvbuf = rcvbuf; eltp->sndbuf = sndbuf; eltp->brd_addrs = NULL; eltp->ccall = NULL; eltp->rqe_list = NULL; eltp->rqe_list_len = 0; eltp->error_cnt = 0; eltp->pseq_id = pseq_id; eltp->refcnt = 2; /* 1 for table ref + 1 for returned ref */ eltp->is_server = is_server; eltp->is_disabled = 0; eltp->is_private = creating_private_socket; /* * Add the new elt to the appropriate list within the socket pool * before we unlock it (so others will see it); note we still have * our ref to it. After this point, call sock_free() to clean things * up. */ if (creating_private_socket) { eltp->next = rpc_g_dg_sock_pool.private_sockets; rpc_g_dg_sock_pool.private_sockets = eltp; } else { eltp->next = rpc_g_dg_sock_pool.shared_sockets; rpc_g_dg_sock_pool.shared_sockets = eltp; } rpc_g_dg_sock_pool.num_entries++; /* * Probably best to unlock the pool before calling into the * listener abstraction. */ RPC_DG_SOCK_POOL_UNLOCK(0); /* * For shared sockets, tell the network listener to start listening * on this socket (add a reference because we're giving one to it). * If this fails, just blow away the new elt. Note that private * sockets do not require the presence of the listener thread. */ if (! creating_private_socket) { rpc__dg_network_sock_reference(eltp); rpc__network_add_desc(socket_desc, is_server, (endpoint == NULL), pseq_id, (pointer_t) eltp, st); if (*st != rpc_s_ok) { RPC_DG_SOCK_POOL_LOCK(0); sock_free(&eltp); RPC_DG_SOCK_POOL_UNLOCK(0); } } *sock_pool_elt = eltp; return; CLEANUP: /* * A failure happened; clean things up, returning the previously * setup status. */ if (sock_open) serr = RPC_SOCKET_CLOSE(socket_desc); RPC_MEM_FREE(eltp, RPC_C_MEM_DG_SOCK_POOL_ELT); RPC_DG_SOCK_POOL_UNLOCK(0); return; } /* * R P C _ _ D G _ N E T W O R K _ U S E _ P R O T S E Q _ S V * * Server side entry point into socket pool allocation routine. */ PRIVATE void rpc__dg_network_use_protseq_sv #ifdef _DCE_PROTO_ ( rpc_protseq_id_t pseq_id, unsigned32 max_calls ATTRIBUTE_UNUSED, rpc_addr_p_t rpc_addr, unsigned_char_p_t endpoint, unsigned32 *st ) #else (pseq_id, max_calls, rpc_addr, endpoint, st) rpc_protseq_id_t pseq_id; unsigned32 max_calls; rpc_addr_p_t rpc_addr; unsigned_char_p_t endpoint; unsigned32 *st; #endif { rpc_dg_sock_pool_elt_p_t sp_elt; use_protseq(true, pseq_id, rpc_addr, endpoint, &sp_elt, st); if (*st == rpc_s_ok) rpc__dg_network_sock_release(&sp_elt); } /* * R P C _ _ D G _ N E T W O R K _ U S E _ P R O T S E Q _ C L * * Client side entry point into socket pool allocation routine. * Create a NULL address for the bind, and call the internal * use_protseq routine. */ PRIVATE void rpc__dg_network_use_protseq_cl #ifdef _DCE_PROTO_ ( rpc_protseq_id_t pseq_id, rpc_dg_sock_pool_elt_p_t *sp ) #else (pseq_id, sp) rpc_protseq_id_t pseq_id; rpc_dg_sock_pool_elt_p_t *sp; #endif { rpc_addr_il_t addr; unsigned32 st; *sp = NULL; /* * Specify a null address to bind to. We do this so * the socket gets an endpoint assigned right away. */ addr.len = sizeof addr.sa; memset((char *) &addr.sa, 0, addr.len); addr.sa.family = rpc_g_protseq_id[pseq_id].naf_id; use_protseq(false, pseq_id, (rpc_addr_p_t) &addr, NULL, sp, &st); } /* * R P C _ _ D G _ N E T W O R K _ D I S A B L E _ D E S C * * This routine is responsible for removing a socket from the pool of * sockets being monitored by the listener thread. It is called from * the macro which updates the error count associated with a socket pool * entry, when that count reaches the maximum allowed. * Note: the caller's socket pool reference remains intact. */ PRIVATE void rpc__dg_network_disable_desc #ifdef _DCE_PROTO_ ( rpc_dg_sock_pool_elt_p_t sp ) #else (sp) rpc_dg_sock_pool_elt_p_t sp; #endif { unsigned32 st; boolean is_private; RPC_DG_SOCK_POOL_LOCK(0); is_private = sp->is_private; /* * Make certain it's not already disabled. */ if (sp->is_disabled) { RPC_DG_SOCK_POOL_UNLOCK(0); return; } RPC_DBG_GPRINTF(("(rpc__dg_network_disable_desc) Disabing socket, error=%d\n", sp->sock)); sp->is_disabled = true; RPC_DG_SOCK_POOL_UNLOCK(0); /* * For shared sockets, remove the sock from the listener's database * and release the reference that we previously gave it. Note: * remove_desc() doesn't return until the listener has completely * ceased using the socket/reference (which is why we can safely * release its reference). */ if (is_private == false) { rpc__network_remove_desc(sp->sock, &st); rpc__dg_network_sock_release(&sp); } } /* * R P C _ _ D G _ N E T W O R K _ I N I T * * This routine is called as part of the rpc__init startup * initializations. It creates the mutex that protects the * socket pool. */ PRIVATE void rpc__dg_network_init(void) { RPC_MUTEX_INIT(rpc_g_dg_sock_pool.sp_mutex); } /* * R P C _ _ D G _ N E T W O R K _ F O R K _ H A N D L E R * * This routine handles socket pool related fork processing. */ PRIVATE void rpc__dg_network_fork_handler #ifdef _DCE_PROTO_ ( rpc_fork_stage_id_t stage ) #else (stage) rpc_fork_stage_id_t stage; #endif { rpc_dg_sock_pool_elt_p_t eltp, neltp; rpc_socket_error_t serr; switch ((int)stage) { case RPC_C_PREFORK: break; case RPC_C_POSTFORK_PARENT: break; case RPC_C_POSTFORK_CHILD: /* * In the child of a fork, scan the socket pool * for in_use socket descriptors. * * Note that some of the code below would normally * require the socket pool lock. However, after * the fork we are no longer running in a threaded * environment. */ for (eltp = rpc_g_dg_sock_pool.private_sockets; eltp != NULL; eltp = neltp) { serr = RPC_SOCKET_CLOSE(eltp->sock); neltp = eltp->next; RPC_MEM_FREE(eltp, RPC_C_MEM_DG_SOCK_POOL_ELT); } rpc_g_dg_sock_pool.private_sockets = NULL; for (eltp = rpc_g_dg_sock_pool.shared_sockets; eltp != NULL; eltp = neltp) { serr = RPC_SOCKET_CLOSE(eltp->sock); neltp = eltp->next; RPC_MEM_FREE(eltp, RPC_C_MEM_DG_SOCK_POOL_ELT); } rpc_g_dg_sock_pool.shared_sockets = NULL; rpc_g_dg_sock_pool.num_entries = 0; break; } } /* * R P C _ _ D G _ N E T W O R K _ S O C K _ R E F E R E N C E * * Increment the reference count associated with an entry in the socket pool. */ PRIVATE void rpc__dg_network_sock_reference(sp) rpc_dg_sock_pool_elt_p_t sp; { RPC_DG_SOCK_POOL_LOCK(0); sp->refcnt++; RPC_DG_SOCK_POOL_UNLOCK(0); } /* * R P C _ _ D G _ N E T W O R K _ S O C K _ R E L E A S E * * Decrement the reference count associated with an entry in the socket pool. * If the count falls to 1, only the internal pool's ref remains; if the socket * has been marked disabled, free the entry. */ PRIVATE void rpc__dg_network_sock_release #ifdef _DCE_PROTO_ ( rpc_dg_sock_pool_elt_p_t *sp ) #else (sp) rpc_dg_sock_pool_elt_p_t *sp; #endif { RPC_DG_SOCK_POOL_LOCK(0); if (--((*sp)->refcnt) == 1 && (*sp)->is_disabled) sock_free(sp); else (*sp)->ccall = NULL; RPC_DG_SOCK_POOL_UNLOCK(0); *sp = NULL; }