#include "mt.h"
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <stropts.h>
#include <sys/stream.h>
#define _SUN_TPI_VERSION 2
#include <sys/tihdr.h>
#include <sys/timod.h>
#include <sys/stat.h>
#include <xti.h>
#include <fcntl.h>
#include <signal.h>
#include <assert.h>
#include <syslog.h>
#include <limits.h>
#include <ucred.h>
#include "tx.h"
#define DEFSIZE 2048
#define ROUNDUP32(X) ((X + 0x03)&~0x03)
static struct _ti_user *find_tilink(int s);
static struct _ti_user *add_tilink(int s);
static void _t_free_lookbufs(struct _ti_user *tiptr);
static unsigned int _t_setsize(t_scalar_t infosize, boolean_t option);
static int _t_cbuf_alloc(struct _ti_user *tiptr, char **retbuf);
static int _t_rbuf_alloc(struct _ti_user *tiptr, char **retbuf);
static int _t_adjust_state(int fd, int instate);
static int _t_alloc_bufs(int fd, struct _ti_user *tiptr,
struct T_info_ack *tsap);
mutex_t _ti_userlock = DEFAULTMUTEX;
struct _ti_user *
_t_checkfd(int fd, int force_sync, int api_semantics)
{
sigset_t mask;
struct _ti_user *tiptr;
int retval, timodpushed;
if (fd < 0) {
t_errno = TBADF;
return (NULL);
}
if (!force_sync) {
sig_mutex_lock(&_ti_userlock);
tiptr = find_tilink(fd);
sig_mutex_unlock(&_ti_userlock);
if (tiptr != NULL)
return (tiptr);
}
timodpushed = 0;
do {
retval = ioctl(fd, I_FIND, "timod");
} while (retval < 0 && errno == EINTR);
if (retval < 0 || (retval == 0 && _T_IS_TLI(api_semantics))) {
t_errno = TBADF;
return (NULL);
}
if (retval == 0) {
do {
retval = ioctl(fd, I_PUSH, "timod");
} while (retval < 0 && errno == EINTR);
if (retval < 0) {
t_errno = TSYSERR;
return (NULL);
}
timodpushed = 1;
}
(void) thr_sigsetmask(SIG_SETMASK, &fillset, &mask);
sig_mutex_lock(&_ti_userlock);
tiptr = _t_create(fd, NULL, api_semantics, NULL);
if (tiptr == NULL) {
int sv_errno = errno;
sig_mutex_unlock(&_ti_userlock);
(void) thr_sigsetmask(SIG_SETMASK, &mask, NULL);
if (timodpushed)
(void) ioctl(fd, I_POP, 0);
errno = sv_errno;
return (NULL);
}
sig_mutex_unlock(&_ti_userlock);
(void) thr_sigsetmask(SIG_SETMASK, &mask, NULL);
return (tiptr);
}
int
_t_aligned_copy(
struct strbuf *strbufp,
int len,
int init_offset,
char *datap,
t_scalar_t *rtn_offset)
{
*rtn_offset = ROUNDUP32(init_offset);
if ((*rtn_offset + len) > strbufp->maxlen) {
return (-1);
}
(void) memcpy(strbufp->buf + *rtn_offset, datap, (size_t)len);
return (0);
}
int
_t_register_lookevent(
struct _ti_user *tiptr,
caddr_t dptr,
int dsize,
caddr_t cptr,
int csize)
{
struct _ti_lookbufs *tlbs;
int cbuf_size, dbuf_size;
assert(MUTEX_HELD(&tiptr->ti_lock));
cbuf_size = tiptr->ti_ctlsize;
dbuf_size = tiptr->ti_rcvsize;
if ((csize > cbuf_size) || dsize > dbuf_size) {
return (-1);
}
if (tiptr->ti_lookcnt > 0) {
if (cptr && csize >= (int)sizeof (struct T_discon_ind) &&
*(t_scalar_t *)cptr == T_DISCON_IND) {
assert(tiptr->ti_servtype != T_CLTS);
if (*(t_scalar_t *)tiptr->ti_lookbufs.tl_lookcbuf ==
T_ORDREL_IND) {
_t_free_looklist_head(tiptr);
}
}
}
tlbs = &tiptr->ti_lookbufs;
if (tiptr->ti_lookcnt > 0) {
int listcount = 0;
while (tlbs->tl_next != NULL) {
listcount++;
tlbs = tlbs->tl_next;
}
assert(tiptr->ti_lookcnt == listcount);
if ((tlbs->tl_next = malloc(sizeof (struct _ti_lookbufs))) ==
NULL)
return (-1);
tlbs = tlbs->tl_next;
if ((tlbs->tl_lookcbuf = malloc(cbuf_size)) == NULL) {
free(tlbs);
return (-1);
}
if ((dsize > 0) &&
((tlbs->tl_lookdbuf = malloc(dbuf_size)) == NULL)) {
free(tlbs->tl_lookcbuf);
free(tlbs);
return (-1);
}
}
(void) memcpy(tlbs->tl_lookcbuf, cptr, csize);
if (dsize > 0)
(void) memcpy(tlbs->tl_lookdbuf, dptr, dsize);
tlbs->tl_lookdlen = dsize;
tlbs->tl_lookclen = csize;
tlbs->tl_next = NULL;
tiptr->ti_lookcnt++;
return (0);
}
int
_t_is_event(int fd, struct _ti_user *tiptr)
{
int size, retval;
assert(MUTEX_HELD(&tiptr->ti_lock));
if ((retval = ioctl(fd, I_NREAD, &size)) < 0) {
t_errno = TSYSERR;
return (-1);
}
if ((retval > 0) || (tiptr->ti_lookcnt > 0)) {
t_errno = TLOOK;
return (-1);
}
return (0);
}
int
_t_is_ok(int fd, struct _ti_user *tiptr, t_scalar_t type)
{
struct strbuf ctlbuf;
struct strbuf databuf;
union T_primitives *pptr;
int retval, cntlflag;
int size;
int didalloc, didralloc;
int flags = 0;
assert(MUTEX_HELD(&tiptr->ti_lock));
if (_t_acquire_ctlbuf(tiptr, &ctlbuf, &didalloc) < 0)
return (-1);
if (_t_acquire_databuf(tiptr, &databuf, &didralloc) < 0) {
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
return (-1);
}
cntlflag = fcntl(fd, F_GETFL, 0);
if (cntlflag & (O_NDELAY | O_NONBLOCK))
(void) fcntl(fd, F_SETFL, cntlflag & ~(O_NDELAY | O_NONBLOCK));
flags = RS_HIPRI;
while ((retval = getmsg(fd, &ctlbuf, &databuf, &flags)) < 0) {
if (errno == EINTR)
continue;
if (cntlflag & (O_NDELAY | O_NONBLOCK))
(void) fcntl(fd, F_SETFL, cntlflag);
t_errno = TSYSERR;
goto err_out;
}
if (retval > 0) {
if (cntlflag & (O_NDELAY | O_NONBLOCK))
(void) fcntl(fd, F_SETFL, cntlflag);
t_errno = TSYSERR;
errno = EIO;
goto err_out;
}
if (ctlbuf.len < (int)sizeof (t_scalar_t)) {
if (cntlflag & (O_NDELAY | O_NONBLOCK))
(void) fcntl(fd, F_SETFL, cntlflag);
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
}
if (cntlflag & (O_NDELAY | O_NONBLOCK))
(void) fcntl(fd, F_SETFL, cntlflag);
pptr = (union T_primitives *)ctlbuf.buf;
switch (pptr->type) {
case T_OK_ACK:
if ((ctlbuf.len < (int)sizeof (struct T_ok_ack)) ||
(pptr->ok_ack.CORRECT_prim != type)) {
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
}
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
if (didralloc)
free(databuf.buf);
else
tiptr->ti_rcvbuf = databuf.buf;
return (0);
case T_ERROR_ACK:
if ((ctlbuf.len < (int)sizeof (struct T_error_ack)) ||
(pptr->error_ack.ERROR_prim != type)) {
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
}
if (pptr->error_ack.TLI_error == TOUTSTATE) {
if ((retval = ioctl(fd, I_NREAD, &size)) < 0) {
t_errno = TSYSERR;
goto err_out;
}
if (retval > 0)
t_errno = TLOOK;
else
t_errno = TOUTSTATE;
} else {
t_errno = pptr->error_ack.TLI_error;
if (t_errno == TSYSERR)
errno = pptr->error_ack.UNIX_error;
}
goto err_out;
default:
t_errno = TSYSERR;
errno = EPROTO;
}
err_out:
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
if (didralloc)
free(databuf.buf);
else
tiptr->ti_rcvbuf = databuf.buf;
return (-1);
}
int
_t_do_ioctl(int fd, char *buf, int size, int cmd, int *retlenp)
{
int retval;
struct strioctl strioc;
strioc.ic_cmd = cmd;
strioc.ic_timout = -1;
strioc.ic_len = size;
strioc.ic_dp = buf;
if ((retval = ioctl(fd, I_STR, &strioc)) < 0) {
t_errno = TSYSERR;
return (-1);
}
if (retval > 0) {
t_errno = retval & 0xff;
if (t_errno == TSYSERR)
errno = (retval >> 8) & 0xff;
return (-1);
}
if (retlenp)
*retlenp = strioc.ic_len;
return (0);
}
static int
_t_alloc_bufs(int fd, struct _ti_user *tiptr, struct T_info_ack *tsap)
{
unsigned int size1, size2;
t_scalar_t optsize;
unsigned int csize, dsize, asize, osize;
char *ctlbuf, *rcvbuf;
char *lookdbuf, *lookcbuf;
csize = _t_setsize(tsap->CDATA_size, B_FALSE);
dsize = _t_setsize(tsap->DDATA_size, B_FALSE);
size1 = _T_MAX(csize, dsize);
if (size1 != 0) {
if ((rcvbuf = malloc(size1)) == NULL)
return (-1);
if ((lookdbuf = malloc(size1)) == NULL) {
free(rcvbuf);
return (-1);
}
} else {
rcvbuf = NULL;
lookdbuf = NULL;
}
asize = _t_setsize(tsap->ADDR_size, B_FALSE);
if (tsap->OPT_size >= 0)
optsize = tsap->OPT_size + TX_XTI_LEVEL_MAX_OPTBUF;
else
optsize = tsap->OPT_size;
osize = _t_setsize(optsize, B_TRUE);
size2 = (unsigned int)sizeof (union T_primitives)
+ asize + (unsigned int)sizeof (t_scalar_t) +
asize + (unsigned int)sizeof (t_scalar_t) +
osize + (unsigned int)sizeof (t_scalar_t);
if ((ctlbuf = malloc(size2)) == NULL) {
if (size1 != 0) {
free(rcvbuf);
free(lookdbuf);
}
return (-1);
}
if ((lookcbuf = malloc(size2)) == NULL) {
if (size1 != 0) {
free(rcvbuf);
free(lookdbuf);
}
free(ctlbuf);
return (-1);
}
tiptr->ti_rcvsize = size1;
tiptr->ti_rcvbuf = rcvbuf;
tiptr->ti_ctlsize = size2;
tiptr->ti_ctlbuf = ctlbuf;
tiptr->ti_lookbufs.tl_lookclen = 0;
tiptr->ti_lookbufs.tl_lookcbuf = lookcbuf;
tiptr->ti_lookbufs.tl_lookdlen = 0;
tiptr->ti_lookbufs.tl_lookdbuf = lookdbuf;
return (0);
}
static unsigned int
_t_setsize(t_scalar_t infosize, boolean_t option)
{
static size_t optinfsize;
switch (infosize) {
case T_INFINITE :
if (option) {
if (optinfsize == 0) {
size_t uc = ucred_size();
if (uc < DEFSIZE/2)
optinfsize = DEFSIZE;
else
optinfsize = ucred_size() + DEFSIZE/2;
}
return ((unsigned int)optinfsize);
}
return (DEFSIZE);
case T_INVALID :
return (0);
default:
return ((unsigned int) infosize);
}
}
static void
_t_reinit_tiptr(struct _ti_user *tiptr)
{
tiptr->ti_flags = 0;
tiptr->ti_rcvsize = 0;
tiptr->ti_rcvbuf = NULL;
tiptr->ti_ctlsize = 0;
tiptr->ti_ctlbuf = NULL;
tiptr->ti_lookbufs.tl_lookdbuf = NULL;
tiptr->ti_lookbufs.tl_lookcbuf = NULL;
tiptr->ti_lookbufs.tl_lookdlen = 0;
tiptr->ti_lookbufs.tl_lookclen = 0;
tiptr->ti_lookbufs.tl_next = NULL;
tiptr->ti_maxpsz = 0;
tiptr->ti_tsdusize = 0;
tiptr->ti_etsdusize = 0;
tiptr->ti_cdatasize = 0;
tiptr->ti_ddatasize = 0;
tiptr->ti_servtype = 0;
tiptr->ti_lookcnt = 0;
tiptr->ti_state = 0;
tiptr->ti_ocnt = 0;
tiptr->ti_prov_flag = 0;
tiptr->ti_qlen = 0;
}
#define NBUCKETS 64
static struct _ti_user *hash_bucket[NBUCKETS];
static struct _ti_user *
add_tilink(int s)
{
struct _ti_user *tiptr;
struct _ti_user *prevptr;
struct _ti_user *curptr;
int x;
struct stat stbuf;
assert(MUTEX_HELD(&_ti_userlock));
if (s < 0 || fstat(s, &stbuf) != 0)
return (NULL);
x = s % NBUCKETS;
if (hash_bucket[x] != NULL) {
for (curptr = hash_bucket[x]; curptr != NULL;
curptr = curptr->ti_next) {
if (curptr->ti_fd == s) {
if (curptr->ti_rcvbuf != NULL)
free(curptr->ti_rcvbuf);
free(curptr->ti_ctlbuf);
_t_free_lookbufs(curptr);
_t_reinit_tiptr(curptr);
curptr->ti_rdev = stbuf.st_rdev;
curptr->ti_ino = stbuf.st_ino;
return (curptr);
}
prevptr = curptr;
}
if ((tiptr = malloc(sizeof (*tiptr))) == NULL)
return (NULL);
_t_reinit_tiptr(tiptr);
prevptr->ti_next = tiptr;
tiptr->ti_prev = prevptr;
} else {
if ((tiptr = malloc(sizeof (*tiptr))) == NULL)
return (NULL);
_t_reinit_tiptr(tiptr);
hash_bucket[x] = tiptr;
tiptr->ti_prev = NULL;
}
tiptr->ti_next = NULL;
tiptr->ti_fd = s;
tiptr->ti_rdev = stbuf.st_rdev;
tiptr->ti_ino = stbuf.st_ino;
(void) mutex_init(&tiptr->ti_lock, USYNC_THREAD, NULL);
return (tiptr);
}
static struct _ti_user *
find_tilink(int s)
{
struct _ti_user *curptr;
int x;
struct stat stbuf;
assert(MUTEX_HELD(&_ti_userlock));
if (s < 0 || fstat(s, &stbuf) != 0)
return (NULL);
x = s % NBUCKETS;
for (curptr = hash_bucket[x]; curptr; curptr = curptr->ti_next) {
if (curptr->ti_fd == s) {
if (curptr->ti_rdev == stbuf.st_rdev &&
curptr->ti_ino == stbuf.st_ino)
return (curptr);
(void) _t_delete_tilink(s);
}
}
return (NULL);
}
int
_t_delete_tilink(int s)
{
struct _ti_user *curptr;
int x;
assert(MUTEX_HELD(&_ti_userlock));
if (s < 0)
return (-1);
x = s % NBUCKETS;
for (curptr = hash_bucket[x]; curptr; curptr = curptr->ti_next) {
if (curptr->ti_fd == s) {
struct _ti_user *nextptr;
struct _ti_user *prevptr;
nextptr = curptr->ti_next;
prevptr = curptr->ti_prev;
if (prevptr)
prevptr->ti_next = nextptr;
else
hash_bucket[x] = nextptr;
if (nextptr)
nextptr->ti_prev = prevptr;
if (curptr->ti_rcvbuf != NULL)
free(curptr->ti_rcvbuf);
free(curptr->ti_ctlbuf);
_t_free_lookbufs(curptr);
(void) mutex_destroy(&curptr->ti_lock);
free(curptr);
return (0);
}
}
return (-1);
}
struct _ti_user *
_t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
{
union {
struct ti_sync_req ti_req;
struct ti_sync_ack ti_ack;
union T_primitives t_prim;
char pad[128];
} ioctl_data;
void *ioctlbuf = &ioctl_data;
struct ti_sync_req *tsrp = (struct ti_sync_req *)ioctlbuf;
struct ti_sync_ack *tsap = (struct ti_sync_ack *)ioctlbuf;
struct T_capability_req *tcrp = (struct T_capability_req *)ioctlbuf;
struct T_capability_ack *tcap = (struct T_capability_ack *)ioctlbuf;
struct T_info_ack *tiap = &tcap->INFO_ack;
struct _ti_user *ntiptr;
int expected_acksize;
int retlen, rstate, sv_errno, rval;
assert(MUTEX_HELD(&_ti_userlock));
tcrp->PRIM_type = T_CAPABILITY_REQ;
tcrp->CAP_bits1 = TC1_INFO | TC1_ACCEPTOR_ID;
rval = _t_do_ioctl(fd, (char *)ioctlbuf,
(int)sizeof (struct T_capability_ack), TI_CAPABILITY, &retlen);
expected_acksize = (int)sizeof (struct T_capability_ack);
if (rval < 0) {
if (t_capreq_failed != NULL)
*t_capreq_failed = 1;
return (NULL);
}
if (retlen != expected_acksize) {
t_errno = TSYSERR;
errno = EIO;
return (NULL);
}
if ((tcap->CAP_bits1 & TC1_INFO) == 0) {
t_errno = TSYSERR;
errno = EPROTO;
return (NULL);
}
if (info != NULL) {
if (tiap->PRIM_type != T_INFO_ACK) {
t_errno = TSYSERR;
errno = EPROTO;
return (NULL);
}
info->addr = tiap->ADDR_size;
info->options = tiap->OPT_size;
info->tsdu = tiap->TSDU_size;
info->etsdu = tiap->ETSDU_size;
info->connect = tiap->CDATA_size;
info->discon = tiap->DDATA_size;
info->servtype = tiap->SERV_type;
if (_T_IS_XTI(api_semantics)) {
info->flags = 0;
if (tiap->PROVIDER_flag & (SENDZERO|OLD_SENDZERO))
info->flags |= T_SENDZERO;
}
}
ntiptr = add_tilink(fd);
if (ntiptr == NULL) {
t_errno = TSYSERR;
errno = ENOMEM;
return (NULL);
}
if (_t_alloc_bufs(fd, ntiptr, tiap) < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
t_errno = TSYSERR;
errno = sv_errno;
return (NULL);
}
ntiptr->ti_lookcnt = 0;
ntiptr->ti_flags = USED;
ntiptr->ti_state = T_UNINIT;
ntiptr->ti_ocnt = 0;
assert(tiap->TIDU_size > 0);
ntiptr->ti_maxpsz = tiap->TIDU_size;
assert(tiap->TSDU_size >= -2);
ntiptr->ti_tsdusize = tiap->TSDU_size;
assert(tiap->ETSDU_size >= -2);
ntiptr->ti_etsdusize = tiap->ETSDU_size;
assert(tiap->CDATA_size >= -2);
ntiptr->ti_cdatasize = tiap->CDATA_size;
assert(tiap->DDATA_size >= -2);
ntiptr->ti_ddatasize = tiap->DDATA_size;
ntiptr->ti_servtype = tiap->SERV_type;
ntiptr->ti_prov_flag = tiap->PROVIDER_flag;
if ((tcap->CAP_bits1 & TC1_ACCEPTOR_ID) != 0) {
ntiptr->acceptor_id = tcap->ACCEPTOR_id;
ntiptr->ti_flags |= V_ACCEPTOR_ID;
}
else
ntiptr->ti_flags &= ~V_ACCEPTOR_ID;
switch (tiap->CURRENT_state) {
case TS_UNBND:
ntiptr->ti_state = T_UNBND;
break;
case TS_IDLE:
if ((rstate = _t_adjust_state(fd, T_IDLE)) < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
errno = sv_errno;
return (NULL);
}
ntiptr->ti_state = rstate;
break;
case TS_WRES_CIND:
ntiptr->ti_state = T_INCON;
break;
case TS_WCON_CREQ:
ntiptr->ti_state = T_OUTCON;
break;
case TS_DATA_XFER:
if ((rstate = _t_adjust_state(fd, T_DATAXFER)) < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
errno = sv_errno;
return (NULL);
}
ntiptr->ti_state = rstate;
break;
case TS_WIND_ORDREL:
ntiptr->ti_state = T_OUTREL;
break;
case TS_WREQ_ORDREL:
if ((rstate = _t_adjust_state(fd, T_INREL)) < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
errno = sv_errno;
return (NULL);
}
ntiptr->ti_state = rstate;
break;
default:
t_errno = TSTATECHNG;
(void) _t_delete_tilink(fd);
return (NULL);
}
tsrp->tsr_flags = TSRF_QLEN_REQ;
rval = _t_do_ioctl(fd, ioctlbuf,
(int)sizeof (struct ti_sync_req), TI_SYNC, &retlen);
expected_acksize = (int)sizeof (struct ti_sync_ack);
if (rval < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
t_errno = TSYSERR;
errno = sv_errno;
return (NULL);
}
if (retlen < expected_acksize) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
t_errno = TSYSERR;
errno = sv_errno;
return (NULL);
}
if (_T_IS_TLI(api_semantics))
tsap->tsa_qlen = 0;
ntiptr->ti_qlen = tsap->tsa_qlen;
return (ntiptr);
}
static int
_t_adjust_state(int fd, int instate)
{
char ctlbuf[sizeof (t_scalar_t)];
char databuf[sizeof (int)];
struct strpeek arg;
int outstate, retval;
arg.ctlbuf.buf = ctlbuf;
arg.ctlbuf.maxlen = (int)sizeof (ctlbuf);
arg.ctlbuf.len = 0;
arg.databuf.buf = databuf;
arg.databuf.maxlen = (int)sizeof (databuf);
arg.databuf.len = 0;
arg.flags = 0;
if ((retval = ioctl(fd, I_PEEK, &arg)) < 0) {
t_errno = TSYSERR;
return (-1);
}
outstate = instate;
if (retval > 0) {
switch (instate) {
case T_IDLE:
if (((arg.ctlbuf.len == 4) &&
((*(int32_t *)arg.ctlbuf.buf) == T_DATA_IND)) ||
((arg.ctlbuf.len == 0) && arg.databuf.len)) {
outstate = T_DATAXFER;
}
break;
case T_DATAXFER:
if ((arg.ctlbuf.len == 4) &&
((*(int32_t *)arg.ctlbuf.buf) == T_CONN_CON))
outstate = T_OUTCON;
break;
case T_INREL:
if (((arg.ctlbuf.len == 4) &&
((*(int32_t *)arg.ctlbuf.buf) == T_DATA_IND)) ||
((arg.ctlbuf.len == 0) && arg.databuf.len)) {
outstate = T_DATAXFER;
}
break;
default:
break;
}
}
return (outstate);
}
static int
_t_cbuf_alloc(struct _ti_user *tiptr, char **retbuf)
{
unsigned size2;
assert(MUTEX_HELD(&tiptr->ti_lock));
size2 = tiptr->ti_ctlsize;
if ((*retbuf = malloc(size2)) == NULL) {
return (-1);
}
return (size2);
}
int
_t_rbuf_alloc(struct _ti_user *tiptr, char **retbuf)
{
unsigned size1;
assert(MUTEX_HELD(&tiptr->ti_lock));
size1 = tiptr->ti_rcvsize;
if ((*retbuf = malloc(size1)) == NULL) {
return (-1);
}
return (size1);
}
static void
_t_free_lookbufs(struct _ti_user *tiptr)
{
struct _ti_lookbufs *tlbs, *prev_tlbs, *head_tlbs;
assert(MUTEX_HELD(&tiptr->ti_lock) || MUTEX_HELD(&_ti_userlock));
head_tlbs = &tiptr->ti_lookbufs;
if (head_tlbs->tl_lookdbuf != NULL) {
free(head_tlbs->tl_lookdbuf);
head_tlbs->tl_lookdbuf = NULL;
}
free(head_tlbs->tl_lookcbuf);
head_tlbs->tl_lookcbuf = NULL;
tlbs = head_tlbs->tl_next;
head_tlbs->tl_next = NULL;
while (tlbs != NULL) {
if (tlbs->tl_lookdbuf != NULL)
free(tlbs->tl_lookdbuf);
free(tlbs->tl_lookcbuf);
prev_tlbs = tlbs;
tlbs = tlbs->tl_next;
free(prev_tlbs);
}
}
void
_t_free_looklist_head(struct _ti_user *tiptr)
{
struct _ti_lookbufs *tlbs, *next_tlbs;
tlbs = &tiptr->ti_lookbufs;
if (tlbs->tl_next) {
if (tlbs->tl_lookdbuf != NULL)
free(tlbs->tl_lookdbuf);
free(tlbs->tl_lookcbuf);
next_tlbs = tlbs->tl_next;
tlbs->tl_next = next_tlbs->tl_next;
tlbs->tl_lookcbuf = next_tlbs->tl_lookcbuf;
tlbs->tl_lookclen = next_tlbs->tl_lookclen;
tlbs->tl_lookdbuf = next_tlbs->tl_lookdbuf;
tlbs->tl_lookdlen = next_tlbs->tl_lookdlen;
free(next_tlbs);
tiptr->ti_lookcnt--;
assert(tiptr->ti_lookcnt > 0);
} else {
assert(tiptr->ti_lookcnt == 1);
tiptr->ti_lookcnt = 0;
}
}
void
_t_flush_lookevents(struct _ti_user *tiptr)
{
struct _ti_lookbufs *tlbs, *prev_tlbs;
assert(MUTEX_HELD(&tiptr->ti_lock));
tiptr->ti_lookcnt = 0;
tlbs = tiptr->ti_lookbufs.tl_next;
tiptr->ti_lookbufs.tl_next = NULL;
while (tlbs != NULL) {
if (tlbs->tl_lookdbuf != NULL)
free(tlbs->tl_lookdbuf);
free(tlbs->tl_lookcbuf);
prev_tlbs = tlbs;
tlbs = tlbs->tl_next;
free(prev_tlbs);
}
}
int
_t_acquire_ctlbuf(
struct _ti_user *tiptr,
struct strbuf *ctlbufp,
int *didallocp)
{
*didallocp = 0;
ctlbufp->len = 0;
if (tiptr->ti_ctlbuf) {
ctlbufp->buf = tiptr->ti_ctlbuf;
tiptr->ti_ctlbuf = NULL;
ctlbufp->maxlen = tiptr->ti_ctlsize;
} else {
if ((ctlbufp->maxlen = _t_cbuf_alloc(tiptr,
&ctlbufp->buf)) < 0) {
t_errno = TSYSERR;
return (-1);
}
*didallocp = 1;
}
return (0);
}
int
_t_acquire_databuf(
struct _ti_user *tiptr,
struct strbuf *databufp,
int *didallocp)
{
*didallocp = 0;
databufp->len = 0;
if (tiptr->ti_rcvbuf) {
assert(tiptr->ti_rcvsize != 0);
databufp->buf = tiptr->ti_rcvbuf;
tiptr->ti_rcvbuf = NULL;
databufp->maxlen = tiptr->ti_rcvsize;
} else if (tiptr->ti_rcvsize == 0) {
databufp->buf = NULL;
databufp->maxlen = 0;
} else {
if ((databufp->maxlen = _t_rbuf_alloc(tiptr,
&databufp->buf)) < 0) {
t_errno = TSYSERR;
return (-1);
}
*didallocp = 1;
}
return (0);
}
int
_t_expinline_queued(int fd, int *expedited_queuedp)
{
union {
struct ti_sync_req ti_req;
struct ti_sync_ack ti_ack;
char pad[128];
} ioctl_data;
void *ioctlbuf = &ioctl_data;
struct ti_sync_req *tsrp = (struct ti_sync_req *)ioctlbuf;
struct ti_sync_ack *tsap = (struct ti_sync_ack *)ioctlbuf;
int rval, retlen;
*expedited_queuedp = 0;
tsrp->tsr_flags = TSRF_IS_EXP_IN_RCVBUF;
do {
rval = _t_do_ioctl(fd, ioctlbuf,
(int)sizeof (struct T_info_req), TI_SYNC, &retlen);
} while (rval < 0 && errno == EINTR);
if (rval < 0)
return (-1);
if (retlen < (int)sizeof (struct ti_sync_ack)) {
t_errno = TSYSERR;
errno = EIO;
return (-1);
}
if (tsap->tsa_flags & TSAF_EXP_QUEUED)
*expedited_queuedp = 1;
return (0);
}
unsigned int
_t_bytecount_upto_intmax(const struct t_iovec *tiov, unsigned int tiovcount)
{
size_t nbytes;
int i;
nbytes = 0;
for (i = 0; i < tiovcount && nbytes < INT_MAX; i++) {
if (tiov[i].iov_len >= INT_MAX) {
nbytes = INT_MAX;
break;
}
nbytes += tiov[i].iov_len;
}
if (nbytes > INT_MAX)
nbytes = INT_MAX;
return ((unsigned int)nbytes);
}
void
_t_gather(char *dataptr, const struct t_iovec *tiov, unsigned int tiovcount)
{
char *curptr;
unsigned int cur_count;
unsigned int nbytes_remaining;
int i;
curptr = dataptr;
cur_count = 0;
nbytes_remaining = _t_bytecount_upto_intmax(tiov, tiovcount);
for (i = 0; i < tiovcount && nbytes_remaining != 0; i++) {
if (tiov[i].iov_len <= nbytes_remaining)
cur_count = (int)tiov[i].iov_len;
else
cur_count = nbytes_remaining;
(void) memcpy(curptr, tiov[i].iov_base, cur_count);
curptr += cur_count;
nbytes_remaining -= cur_count;
}
}
void
_t_scatter(struct strbuf *pdatabuf, struct t_iovec *tiov, int tiovcount)
{
char *curptr;
unsigned int nbytes_remaining;
unsigned int curlen;
int i;
assert(pdatabuf->len <= _t_bytecount_upto_intmax(tiov, tiovcount));
curptr = pdatabuf->buf;
nbytes_remaining = pdatabuf->len;
for (i = 0; i < tiovcount && nbytes_remaining != 0; i++) {
if (tiov[i].iov_len < nbytes_remaining)
curlen = (unsigned int)tiov[i].iov_len;
else
curlen = nbytes_remaining;
(void) memcpy(tiov[i].iov_base, curptr, curlen);
curptr += curlen;
nbytes_remaining -= curlen;
}
}
void
_t_adjust_iov(int bytes_sent, struct iovec *iov, int *iovcountp)
{
int i;
for (i = 0; i < *iovcountp && bytes_sent; i++) {
if (iov[i].iov_len == 0)
continue;
if (bytes_sent < iov[i].iov_len)
break;
else {
bytes_sent -= iov[i].iov_len;
iov[i].iov_len = 0;
}
}
iov[i].iov_len -= bytes_sent;
iov[i].iov_base += bytes_sent;
}
void
_t_copy_tiov_to_iov(const struct t_iovec *tiov, int tiovcount,
struct iovec *iov, int *iovcountp)
{
int i;
unsigned int nbytes_remaining;
nbytes_remaining = _t_bytecount_upto_intmax(tiov, tiovcount);
i = 0;
do {
iov[i].iov_base = tiov[i].iov_base;
if (tiov[i].iov_len > nbytes_remaining)
iov[i].iov_len = nbytes_remaining;
else
iov[i].iov_len = tiov[i].iov_len;
nbytes_remaining -= iov[i].iov_len;
i++;
} while (nbytes_remaining != 0 && i < tiovcount);
*iovcountp = i;
}
int
_t_do_postconn_sync(int fd, struct _ti_user *tiptr)
{
union {
struct T_capability_req tc_req;
struct T_capability_ack tc_ack;
} ioctl_data;
void *ioctlbuf = &ioctl_data;
int expected_acksize;
int retlen, rval;
struct T_capability_req *tc_reqp = (struct T_capability_req *)ioctlbuf;
struct T_capability_ack *tc_ackp = (struct T_capability_ack *)ioctlbuf;
struct T_info_ack *tiap;
tc_reqp->PRIM_type = T_CAPABILITY_REQ;
tc_reqp->CAP_bits1 = TC1_INFO;
rval = _t_do_ioctl(fd, (char *)ioctlbuf,
(int)sizeof (struct T_capability_ack), TI_CAPABILITY, &retlen);
expected_acksize = (int)sizeof (struct T_capability_ack);
if (rval < 0)
return (-1);
if (retlen != expected_acksize) {
t_errno = TSYSERR;
errno = EIO;
return (-1);
}
if ((tc_ackp->CAP_bits1 & TC1_INFO) == 0) {
t_errno = TSYSERR;
errno = EPROTO;
return (-1);
}
tiap = &tc_ackp->INFO_ack;
if (tiap->PRIM_type != T_INFO_ACK) {
t_errno = TSYSERR;
errno = EPROTO;
return (-1);
}
assert(tiap->TIDU_size > 0);
tiptr->ti_maxpsz = tiap->TIDU_size;
assert(tiap->TSDU_size >= T_INVALID);
tiptr->ti_tsdusize = tiap->TSDU_size;
assert(tiap->ETSDU_size >= T_INVALID);
tiptr->ti_etsdusize = tiap->ETSDU_size;
assert(tiap->CDATA_size >= T_INVALID);
tiptr->ti_cdatasize = tiap->CDATA_size;
assert(tiap->DDATA_size >= T_INVALID);
tiptr->ti_ddatasize = tiap->DDATA_size;
tiptr->ti_prov_flag = tiap->PROVIDER_flag;
return (0);
}