#include "mt.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <stropts.h>
#include <sys/stream.h>
#define _SUN_TPI_VERSION 2
#include <sys/tihdr.h>
#include <sys/timod.h>
#include <xti.h>
#include <syslog.h>
#include <assert.h>
#include "tx.h"
int
_tx_rcvv(int fd, struct t_iovec *tiov, unsigned int tiovcount, int *flags,
int api_semantics)
{
struct strbuf ctlbuf, databuf;
int retval, flg = 0;
int msglen;
union T_primitives *pptr;
struct _ti_user *tiptr;
int sv_errno;
int didalloc;
unsigned int nbytes;
char *dataptr;
if ((tiptr = _t_checkfd(fd, 0, api_semantics)) == NULL)
return (-1);
sig_mutex_lock(&tiptr->ti_lock);
if (tiptr->ti_servtype == T_CLTS) {
t_errno = TNOTSUPPORT;
sig_mutex_unlock(&tiptr->ti_lock);
return (-1);
}
assert(api_semantics == TX_XTI_XNS5_API);
if (tiovcount == 0 || tiovcount > T_IOV_MAX) {
t_errno = TBADDATA;
sig_mutex_unlock(&tiptr->ti_lock);
return (-1);
}
if (!(tiptr->ti_state == T_DATAXFER ||
tiptr->ti_state == T_OUTREL)) {
t_errno = TOUTSTATE;
sig_mutex_unlock(&tiptr->ti_lock);
return (-1);
}
if (tiptr->ti_lookcnt > 0) {
do {
retval = ioctl(fd, I_NREAD, &msglen);
} while (retval < 0 && errno == EINTR);
if (retval < 0) {
sv_errno = errno;
t_errno = TSYSERR;
sig_mutex_unlock(&tiptr->ti_lock);
errno = sv_errno;
return (-1);
}
if (retval > 0) {
struct _ti_lookbufs *tlbs;
tlbs = &tiptr->ti_lookbufs;
do {
if (*((t_scalar_t *)tlbs->tl_lookcbuf)
== T_DISCON_IND) {
t_errno = TLOOK;
sig_mutex_unlock(&tiptr->ti_lock);
return (-1);
}
} while ((tlbs = tlbs->tl_next) != NULL);
} else {
t_errno = TLOOK;
sig_mutex_unlock(&tiptr->ti_lock);
return (-1);
}
}
if (_t_acquire_ctlbuf(tiptr, &ctlbuf, &didalloc) < 0) {
sv_errno = errno;
sig_mutex_unlock(&tiptr->ti_lock);
errno = sv_errno;
return (-1);
}
nbytes = _t_bytecount_upto_intmax(tiov, tiovcount);
dataptr = NULL;
if (nbytes != 0 && ((dataptr = malloc(nbytes)) == NULL)) {
sv_errno = errno;
t_errno = TSYSERR;
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
sig_mutex_unlock(&tiptr->ti_lock);
errno = sv_errno;
return (-1);
}
databuf.maxlen = (int)nbytes;
databuf.len = 0;
databuf.buf = dataptr;
*flags = 0;
sig_mutex_unlock(&tiptr->ti_lock);
if ((retval = getmsg(fd, &ctlbuf, &databuf, &flg)) < 0) {
if (errno == EAGAIN)
t_errno = TNODATA;
else
t_errno = TSYSERR;
sv_errno = errno;
sig_mutex_lock(&tiptr->ti_lock);
errno = sv_errno;
goto err_out;
}
sig_mutex_lock(&tiptr->ti_lock);
assert((retval & MORECTL) == 0);
if (databuf.len == -1) databuf.len = 0;
if (ctlbuf.len > 0) {
if (ctlbuf.len < (int)sizeof (t_scalar_t)) {
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
}
pptr = (union T_primitives *)ctlbuf.buf;
switch (pptr->type) {
case T_EXDATA_IND:
*flags |= T_EXPEDITED;
if (retval > 0)
tiptr->ti_flags |= EXPEDITED;
case T_DATA_IND:
if ((ctlbuf.len < (int)sizeof (struct T_data_ind)) ||
(tiptr->ti_lookcnt > 0)) {
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
}
if ((pptr->data_ind.MORE_flag) || retval)
*flags |= T_MORE;
if ((pptr->data_ind.MORE_flag) && retval)
tiptr->ti_flags |= MORE;
_T_TX_NEXTSTATE(T_RCV, tiptr,
"t_rcvv: invalid state event T_RCV");
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
_t_scatter(&databuf, tiov, tiovcount);
if (dataptr != NULL)
free(dataptr);
sig_mutex_unlock(&tiptr->ti_lock);
return (databuf.len);
case T_ORDREL_IND:
if (tiptr->ti_lookcnt > 0) {
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
}
case T_DISCON_IND:
if (_t_register_lookevent(tiptr, databuf.buf,
databuf.len,
ctlbuf.buf, ctlbuf.len) < 0) {
t_errno = TSYSERR;
errno = ENOMEM;
goto err_out;
}
if (retval & MOREDATA) {
ctlbuf.maxlen = 0;
ctlbuf.len = 0;
databuf.maxlen =
tiptr->ti_rcvsize - databuf.len;
databuf.len = 0;
databuf.buf =
tiptr->ti_lookbufs.tl_lookdbuf +
tiptr->ti_lookbufs.tl_lookdlen;
*flags = 0;
do {
retval = getmsg(fd, &ctlbuf,
&databuf, &flg);
} while (retval < 0 && errno == EINTR);
if (retval < 0) {
t_errno = TSYSERR;
goto err_out;
}
if (databuf.len == -1) databuf.len = 0;
if (retval > 0) {
assert((retval & MORECTL) == 0);
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
}
tiptr->ti_lookbufs.tl_lookdlen +=
databuf.len;
}
t_errno = TLOOK;
goto err_out;
default:
break;
}
t_errno = TSYSERR;
errno = EPROTO;
goto err_out;
} else {
if (!retval && (tiptr->ti_flags & MORE)) {
*flags |= T_MORE;
tiptr->ti_flags &= ~MORE;
}
if (retval & MOREDATA)
*flags |= T_MORE;
if (tiptr->ti_flags & EXPEDITED) {
*flags |= T_EXPEDITED;
if (!retval)
tiptr->ti_flags &= ~EXPEDITED;
}
_T_TX_NEXTSTATE(T_RCV, tiptr,
"t_rcvv: state invalid T_RCV event");
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
_t_scatter(&databuf, tiov, tiovcount);
if (dataptr != NULL)
free(dataptr);
sig_mutex_unlock(&tiptr->ti_lock);
return (databuf.len);
}
err_out:
sv_errno = errno;
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
if (dataptr != NULL)
free(dataptr);
sig_mutex_unlock(&tiptr->ti_lock);
errno = sv_errno;
return (-1);
}