_utility.c revision 2
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright (c) 1988, 2011, Oracle and/or its affiliates. All rights reserved. 2N/A/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 2N/A/* All Rights Reserved */ 2N/A * The following used to be in tiuser.h, but was causing too much namespace 2N/A * Checkfd - checks validity of file descriptor 2N/A * Not found or a forced sync is required. 2N/A * check if this is a valid TLI/XTI descriptor. 2N/A * not a stream or a TLI endpoint with no timod 2N/A * XXX Note: If it is a XTI call, we push "timod" and 2N/A * try to convert it into a transport endpoint later. 2N/A * We do not do it for TLI and "retain" the old buggy 2N/A * behavior because ypbind and a lot of other deamons seem 2N/A * to use a buggy logic test of the form 2N/A * "(t_getstate(0) != -1 || t_errno != TBADF)" to see if 2N/A * they we ever invoked with request on stdin and drop into 2N/A * untested code. This test is in code generated by rpcgen 2N/A * which is why it is replicated test in many daemons too. 2N/A * We will need to fix that test too with "IsaTLIendpoint" 2N/A * test if we ever fix this for TLI 2N/A * "timod" not already on stream, then push it 2N/A * Assumes (correctly) that I_PUSH is 2N/A * Try to (re)constitute the info at user level from state 2N/A * in the kernel. This could be information that lost due 2N/A * to an exec or being instantiated at a new descriptor due 2N/A * to , open(), dup2() etc. 2N/A * _t_create() requires that all signals be blocked. 2N/A * Note that sig_mutex_lock() only defers signals, it does not 2N/A * block them, so interruptible syscalls could still get EINTR. 2N/A * restore to stream before timod pushed. It may 2N/A * not have been a network transport stream. 2N/A * copy data to output buffer making sure the output buffer is 32 bit 2N/A * aligned, even though the input buffer may not be. 2N/A * Aligned copy will overflow buffer 2N/A * append data and control info in look buffer (list in the MT case) 2N/A * The only thing that can be in look buffer is a T_DISCON_IND, 2N/A * T_ORDREL_IND or a T_UDERROR_IND. 2N/A * It also enforces priority of T_DISCONDs over any T_ORDREL_IND 2N/A * already in the buffer. It assumes no T_ORDREL_IND is appended 2N/A * when there is already something on the looklist (error case) and 2N/A * that a T_ORDREL_IND if present will always be the first on the 2N/A * This also assumes ti_lock is held via sig_mutex_lock(), 2N/A * so signals are deferred here. 2N/A /* can't fit - return error */ 2N/A return (-
1);
/* error */ 2N/A * Enforce priority of T_DISCON_IND over T_ORDREL_IND 2N/A * Note: Since there can be only at most one T_ORDREL_IND 2N/A * queued (more than one is error case), and we look for it 2N/A * on each append of T_DISCON_IND, it can only be at the 2N/A * head of the list if it is there. 2N/A /* LINTED pointer cast */ 2N/A /* appending discon ind */ 2N/A /* LINTED pointer cast */ 2N/A * Blow away T_ORDREL_IND 2N/A * Allocate and append a new lookbuf to the 2N/A * existing list. (Should only happen in MT case) 2N/A * signals are deferred, calls to malloc() are safe. 2N/A return (-
1);
/* error */ 2N/A * Allocate the buffers. The sizes derived from the 2N/A * sizes of other related buffers. See _t_alloc_bufs() 2N/A /* giving up - free other memory chunks */ 2N/A return (-
1);
/* error */ 2N/A /* giving up - free other memory chunks */ 2N/A return (-
1);
/* error */ 2N/A return (0);
/* ok return */ 2N/A * Is there something that needs attention? 2N/A * Assumes tiptr->ti_lock held and this threads signals blocked 2N/A * assumes tiptr->ti_lock held in MT case 2N/A * Temporarily convert a non blocking endpoint to a 2N/A * blocking one and restore status later 2N/A /* did I get entire message */ 2N/A * is ctl part large enough to determine type? 2N/A /* LINTED pointer cast */ 2N/A * if error is out of state and there is something 2N/A * on read queue, then indicate to user that 2N/A * there is something that needs attention 2N/A /* fallthru to err_out: */ 2N/A * alloc scratch buffers and look buffers 2N/A /* compensate for XTI level options */ 2N/A * We compute the largest buffer size needed for this provider by 2N/A * adding the components. [ An extra sizeof (t_scalar_t) is added to 2N/A * take care of rounding off for alignment) for each buffer ] 2N/A * The goal here is compute the size of largest possible buffer that 2N/A * might be needed to hold a TPI message for the transport provider 2N/A * Note: T_ADDR_ACK contains potentially two address buffers. 2N/A /* first addr buffer plus alignment */ 2N/A /* second addr buffer plus ailignment */ 2N/A /* option buffer plus alignment */ 2N/A * Note: The head of the lookbuffers list (and associated buffers) 2N/A * is allocated here on initialization. 2N/A * More allocated on demand. 2N/A * set sizes of buffers 2N/A * Note: This routine is designed for a "reinitialization" 2N/A * Following fields are not modified here and preserved. 2N/A * The above fields have to be separately initialized if this 2N/A * is used for a fresh initialization. 2N/A * Link manipulation routines. 2N/A * NBUCKETS hash buckets are used to give fast 2N/A * access. The number is derived the file descriptor softlimit 2N/A * Allocates a new link and returns a pointer to it. 2N/A * Assumes that the caller is holding _ti_userlock via sig_mutex_lock(), 2N/A * so signals are deferred here. 2N/A * Walk along the bucket looking for 2N/A * duplicate entry or the end. 2N/A * This can happen when the user has close(2)'ed 2N/A * a descriptor and then been allocated it again 2N/A * We will re-use the existing _ti_user struct 2N/A * in this case rather than using the one 2N/A * we allocated above. If there are buffers 2N/A * associated with the existing _ti_user 2N/A * struct, they may not be the correct size, 2N/A * so we can not use it. We free them 2N/A * here and re-allocate a new ones 2N/A * Allocate and link in a new one. 2N/A * First initialize fields common with reinitialization and 2N/A * then other fields too 2N/A * Find a link by descriptor 2N/A * Assumes that the caller is holding _ti_userlock. 2N/A * Walk along the bucket looking for the descriptor. 2N/A * Assumes that the caller is holding _ti_userlock. 2N/A * Also assumes that all signals are blocked. 2N/A * Walk along the bucket looking for 2N/A * free resource associated with the curptr 2N/A * Allocate a TLI state structure and synch it with the kernel 2N/A * *tiptr is returned 2N/A * Assumes that the caller is holding the _ti_userlock and has blocked signals. 2N/A * This function may fail the first time it is called with given transport if it 2N/A * doesn't support T_CAPABILITY_REQ TPI message. 2N/A * Aligned data buffer for ioctl. 2N/A /* preferred location first local variable */ 2N/A /* see note below */ 2N/A * Note: We use "ioctlbuf" allocated on stack above with 2N/A * room to grow since (struct ti_sync_ack) can grow in size 2N/A * on future kernels. (We do not use malloc'd "ti_ctlbuf" as that 2N/A * part of instance structure which may not exist yet) 2N/A * Its preferred declaration location is first local variable in this 2N/A * procedure as bugs causing overruns will be detectable on 2N/A * platforms where procedure calling conventions place return 2N/A * address on stack (such as x86) instead of causing silent 2N/A * memory corruption. 2N/A * Use ioctl required for sync'ing state with kernel. 2N/A * We use two ioctls. TI_CAPABILITY is used to get TPI information and 2N/A * TI_SYNC is used to synchronise state with timod. Statically linked 2N/A * TLI applications will no longer work on older releases where there 2N/A * are no TI_SYNC and TI_CAPABILITY. 2N/A * Request info about transport. 2N/A * Assumes that TC1_INFO should always be implemented. 2N/A * For TI_CAPABILITY size argument to ioctl specifies maximum buffer 2N/A * TI_CAPABILITY may fail when transport provider doesn't 2N/A * support T_CAPABILITY_REQ message type. In this case file 2N/A * descriptor may be unusable (when transport provider sent 2N/A * M_ERROR in response to T_CAPABILITY_REQ). This should only 2N/A * happen once during system lifetime for given transport 2N/A * provider since timod will emulate TI_CAPABILITY after it 2N/A * detected the failure. 2N/A * XTI ONLY - TLI "struct t_info" does not 2N/A * Some day there MAY be a NEW bit in T_info_ack 2N/A * PROVIDER_flag namespace exposed by TPI header 2N/A * role played by T_ORDRELDATA in info->flags namespace 2N/A * When that bit exists, we can add a test to see if 2N/A * it is set and set T_ORDRELDATA. 2N/A * Note: Currently only mOSI ("minimal OSI") provider 2N/A * is specified to use T_ORDRELDATA so probability of 2N/A * needing it is minimal. 2N/A * if first time or no instance (after fork/exec, dup etc, 2N/A * then create initialize data structure 2N/A * and allocate buffers 2N/A * Allocate buffers for the new descriptor 2N/A /* Fill instance structure */ 2N/A * Restore state from kernel (caveat some heuristics) 2N/A * Sync information with timod. 2N/A * This is a "less than" check as "struct ti_sync_ack" returned by 2N/A * TI_SYNC can grow in size in future kernels. If/when a statically 2N/A * linked application is run on a future kernel, it should not fail. 2N/A char databuf[
sizeof (
int)];
/* size unimportant - anything > 0 */ 2N/A * Peek at message on stream head (if any) 2N/A * and see if it is data 2N/A * If peek shows something at stream head, then 2N/A * Adjust "outstate" based on some heuristics. 2N/A * The following heuristic is to handle data 2N/A * ahead of T_DISCON_IND indications that might 2N/A * be at the stream head waiting to be 2N/A * read (T_DATA_IND or M_DATA) 2N/A /* LINTED pointer cast */ 2N/A * The following heuristic is to handle 2N/A * the case where the connection is established 2N/A * and in data transfer state at the provider 2N/A * but the T_CONN_CON has not yet been read 2N/A * from the stream head. 2N/A /* LINTED pointer cast */ 2N/A * The following heuristic is to handle data 2N/A * ahead of T_ORDREL_IND indications that might 2N/A * be at the stream head waiting to be 2N/A * read (T_DATA_IND or M_DATA) 2N/A /* LINTED pointer cast */ 2N/A * Assumes caller has blocked signals at least in this thread (for safe 2N/A * Assumes caller has blocked signals at least in this thread (for safe 2N/A * Free lookbuffer structures and associated resources 2N/A * Assumes ti_lock held for MT case. 2N/A * The structure lock should be held or the global list 2N/A * manipulation lock. The assumption is that nothing 2N/A * else can access the descriptor since global list manipulation 2N/A * lock is held so it is OK to manipulate fields without the 2N/A * Free only the buffers in the first lookbuf 2N/A * Free the node and the buffers in the rest of the 2N/A * Free lookbuffer event list head. 2N/A * Consume current lookbuffer event 2N/A * Assumes ti_lock held for MT case. 2N/A * Note: The head of this list is part of the instance 2N/A * structure so the code is a little unorthodox. 2N/A * Free the control and data buffers 2N/A * Replace with next lookbuf event contents 2N/A * Decrement the flag - should never get to zero. 2N/A * No more look buffer events - just clear the flag 2N/A * and leave the buffers alone 2N/A * Discard lookbuffer events. 2N/A * Assumes ti_lock held for MT case. 2N/A * Leave the first nodes buffers alone (i.e. allocated) 2N/A * but reset the flag. 2N/A * Blow away the rest of the list 2N/A * This routine checks if the receive. buffer in the instance structure 2N/A * is available (non-null). If it is, the buffer is acquired and marked busy 2N/A * (null). If it is busy (possible in MT programs), it allocates a new 2N/A * buffer and sets a flag indicating new memory was allocated and the caller 2N/A * tiptr->ti_ctlbuf is in use 2N/A * allocate new buffer and free after use. 2N/A * This routine checks if the receive buffer in the instance structure 2N/A * is available (non-null). If it is, the buffer is acquired and marked busy 2N/A * (null). If it is busy (possible in MT programs), it allocates a new 2N/A * buffer and sets a flag indicating new memory was allocated and the caller 2N/A * Note: The receive buffer pointer can also be null if the transport 2N/A * just when it is "busy". In that case, ti_rcvsize will be 0 and that is 2N/A * used to instantiate the databuf which points to a null buffer of 2N/A * length 0 which is the right thing to do for that case. 2N/A * tiptr->ti_rcvbuf is in use 2N/A * allocate new buffer and free after use. 2N/A * This routine requests timod to look for any expedited data 2N/A * queued in the "receive buffers" in the kernel. Used for XTI 2N/A * t_look() semantics for transports that send expedited data 2N/A * data inline (e.g TCP). 2N/A * Returns -1 for failure 2N/A * Returns 0 for success 2N/A * On a successful return, the location pointed by "expedited_queuedp" 2N/A * 0 if no expedited data is found queued in "receive buffers" 2N/A * 1 if expedited data is found queued in "receive buffers" 2N/A /* preferred location first local variable */ 2N/A /* see note in _t_create above */ 2N/A /* request info on rq expinds */ 2N/A * This is a "less than" check as "struct ti_sync_ack" returned by 2N/A * TI_SYNC can grow in size in future kernels. If/when a statically 2N/A * linked application is run on a future kernel, it should not fail. 2N/A * like t_sndv(), t_rcvv() etc..follow below. 2N/A * _t_bytecount_upto_intmax() : 2N/A * Sum of the lengths of the individual buffers in 2N/A * the t_iovec array. If the sum exceeds INT_MAX 2N/A * it is truncated to INT_MAX. 2N/A * Gather the data in the t_iovec buffers, into a single linear buffer 2N/A * starting at dataptr. Caller must have allocated sufficient space 2N/A * starting at dataptr. The total amount of data that is gathered is 2N/A * limited to INT_MAX. Any remaining data in the t_iovec buffers is 2N/A * Scatter the data from the single linear buffer at pdatabuf->buf into 2N/A * the t_iovec buffers. 2N/A * There cannot be any uncopied data leftover in pdatabuf 2N/A * at the conclusion of this function. (asserted below) 2N/A * Adjust the iovec array, for subsequent use. Examine each element in the 2N/A * iovec array,and zero out the iov_len if the buffer was sent fully. 2N/A * otherwise the buffer was only partially sent, so adjust both iov_len and 2N/A * Copy the t_iovec array to the iovec array while taking care to see 2N/A * that the sum of the buffer lengths in the result is not more than 2N/A * INT_MAX. This function requires that T_IOV_MAX is no larger than 2N/A * IOV_MAX. Otherwise the resulting array is not a suitable input to 2N/A * writev(). If the sum of the lengths in t_iovec is zero, so is the 2N/A * Routine called after connection establishment on transports where 2N/A * connection establishment changes certain transport attributes such as 2N/A * This T_CAPABILITY_REQ should not fail, even if it is unsupported 2N/A * by the transport provider. timod will emulate it in that case. 2N/A * T_capability TPI messages are extensible and can grow in future. 2N/A * However timod will take care of returning no more information 2N/A * than what was requested, and truncating the "extended" 2N/A * information towards the end of the T_capability_ack, if necessary. 2N/A * The T_info_ack part of the T_capability_ack is guaranteed to be 2N/A * present only if the corresponding TC1_INFO bit is set 2N/A * Note: Sync with latest information returned in "struct T_info_ack 2N/A * but we deliberately not sync the state here as user level state 2N/A * construction here is not required, only update of attributes which 2N/A * may have changed because of negotations during connection