#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_rcv(int fd, char *buf, unsigned nbytes, 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;
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);
}
if (_T_IS_XTI(api_semantics)) {
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);
}
databuf.maxlen = nbytes;
databuf.len = 0;
databuf.buf = buf;
*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_rcv: invalid state event T_RCV");
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
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_rcv: state invalid T_RCV event");
if (didalloc)
free(ctlbuf.buf);
else
tiptr->ti_ctlbuf = ctlbuf.buf;
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;
sig_mutex_unlock(&tiptr->ti_lock);
errno = sv_errno;
return (-1);
}