1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#pragma ident	"%Z%%M%	%I%	%E% SMI"
29
30/*
31 * t_rcv.c and t_rcvv.c are very similar and contain common code.
32 * Any changes to either of them should be reviewed to see whether they
33 * are applicable to the other file.
34 */
35#include "mt.h"
36#include <stdlib.h>
37#include <errno.h>
38#include <unistd.h>
39#include <stropts.h>
40#include <sys/stream.h>
41#define	_SUN_TPI_VERSION 2
42#include <sys/tihdr.h>
43#include <sys/timod.h>
44#include <xti.h>
45#include <syslog.h>
46#include <assert.h>
47#include "tx.h"
48
49int
50_tx_rcvv(int fd, struct t_iovec *tiov, unsigned int tiovcount,  int *flags,
51    int api_semantics)
52{
53	struct strbuf ctlbuf, databuf;
54	int retval, flg = 0;
55	int msglen;
56	union T_primitives *pptr;
57	struct _ti_user *tiptr;
58	int sv_errno;
59	int didalloc;
60	unsigned int nbytes;
61	char *dataptr;
62
63	if ((tiptr = _t_checkfd(fd, 0, api_semantics)) == NULL)
64		return (-1);
65	sig_mutex_lock(&tiptr->ti_lock);
66
67	if (tiptr->ti_servtype == T_CLTS) {
68		t_errno = TNOTSUPPORT;
69		sig_mutex_unlock(&tiptr->ti_lock);
70		return (-1);
71	}
72
73	assert(api_semantics == TX_XTI_XNS5_API);
74
75	if (tiovcount == 0 || tiovcount > T_IOV_MAX) {
76		t_errno = TBADDATA;
77		sig_mutex_unlock(&tiptr->ti_lock);
78		return (-1);
79	}
80
81	if (!(tiptr->ti_state == T_DATAXFER ||
82		tiptr->ti_state == T_OUTREL)) {
83		t_errno = TOUTSTATE;
84		sig_mutex_unlock(&tiptr->ti_lock);
85		return (-1);
86	}
87
88	/*
89	 * Check in lookbuf for stuff
90	 */
91	if (tiptr->ti_lookcnt > 0) {
92		/*
93		 * Implied preference rules give priority to
94		 * T_DISCON_IND over T_ORDREL_IND. Also certain errors like
95		 * data received after T_ORDREL_IND or a duplicate T_ORDREL_IND
96		 * after a T_ORDRELING have priority over TLOOK.
97		 * This manifests in following code behavior.
98		 *
99		 * (1)  If something in lookbuf then check
100		 *	the stream head also. This may result
101		 *	in retuning a TLOOK error but only if there are
102		 *	  - message at stream head but look buffer
103		 *	    has a T_DISCON_IND event.
104		 *	  - no messages are on the stream head
105		 *
106		 * (2)  If there are messages on the stream head and
107		 *	all of them are T_ORDREL_IND(i.e. no message in
108		 *	look buffer is T_DISCON_IND), there
109		 *	could be data on stream head to be picked up and
110		 *	we work on the stream head and not return TLOOK.
111		 *	We remove the event on the stream head and queue it.
112		 *
113		 */
114		do {
115			retval = ioctl(fd, I_NREAD, &msglen);
116		} while (retval < 0 && errno == EINTR);
117
118		if (retval < 0) {
119			sv_errno = errno;
120			t_errno = TSYSERR;
121			sig_mutex_unlock(&tiptr->ti_lock);
122			errno = sv_errno;
123			return (-1);
124		}
125
126		if (retval > 0) {
127			/*
128			 * If any T_DISCON_IND event in look buffer
129			 * list then return TLOOK. Else continue
130			 * processing as what could be on the stream
131			 * head might be a possible T_DISCON_IND (which
132			 * would have priority over the T_ORDREL_INDs
133			 * on the look buffer.)
134			 */
135			struct _ti_lookbufs *tlbs;
136
137			tlbs = &tiptr->ti_lookbufs;
138			do {
139				/* LINTED pointer cast */
140				if (*((t_scalar_t *)tlbs->tl_lookcbuf)
141				    == T_DISCON_IND) {
142					t_errno = TLOOK;
143					sig_mutex_unlock(&tiptr->ti_lock);
144					return (-1);
145				}
146			} while ((tlbs = tlbs->tl_next) != NULL);
147
148		} else {	/* retval == 0 */
149			/*
150			 * Nothing on stream head so whatever in
151			 * look buffer has nothing that might override
152			 * it.
153			 */
154			t_errno = TLOOK;
155			sig_mutex_unlock(&tiptr->ti_lock);
156			return (-1);
157		}
158	}
159
160	/*
161	 * Acquire ctlbuf for use in sending/receiving control part
162	 * of the message.
163	 */
164	if (_t_acquire_ctlbuf(tiptr, &ctlbuf, &didalloc) < 0) {
165		sv_errno = errno;
166		sig_mutex_unlock(&tiptr->ti_lock);
167		errno = sv_errno;
168		return (-1);
169	}
170
171	nbytes = _t_bytecount_upto_intmax(tiov, tiovcount);
172	dataptr = NULL;
173	if (nbytes != 0 && ((dataptr = malloc(nbytes)) == NULL)) {
174		sv_errno = errno;
175		t_errno = TSYSERR;
176		if (didalloc)
177			free(ctlbuf.buf);
178		else
179			tiptr->ti_ctlbuf = ctlbuf.buf;
180		sig_mutex_unlock(&tiptr->ti_lock);
181		errno = sv_errno;
182		return (-1);
183	}
184
185	databuf.maxlen = (int)nbytes;
186	databuf.len = 0;
187	databuf.buf = dataptr;
188
189	*flags = 0;
190
191	/*
192	 * This is a call that may block indefinitely so we drop the
193	 * lock and allow signals in MT case here and reacquire it.
194	 * Error case should roll back state changes done above
195	 * (happens to be no state change here)
196	 */
197	sig_mutex_unlock(&tiptr->ti_lock);
198	if ((retval = getmsg(fd, &ctlbuf, &databuf, &flg)) < 0) {
199		if (errno == EAGAIN)
200			t_errno = TNODATA;
201		else
202			t_errno = TSYSERR;
203		sv_errno = errno;
204		sig_mutex_lock(&tiptr->ti_lock);
205		errno = sv_errno;
206		goto err_out;
207	}
208	sig_mutex_lock(&tiptr->ti_lock);
209
210	assert((retval & MORECTL) == 0); /* MORECTL should not be on */
211
212	if (databuf.len == -1) databuf.len = 0;
213
214	if (ctlbuf.len > 0) {
215		if (ctlbuf.len < (int)sizeof (t_scalar_t)) {
216			t_errno = TSYSERR;
217			errno = EPROTO;
218			goto err_out;
219		}
220
221		/* LINTED pointer cast */
222		pptr = (union T_primitives *)ctlbuf.buf;
223
224		switch (pptr->type) {
225
226		case T_EXDATA_IND:
227			*flags |= T_EXPEDITED;
228			if (retval > 0)
229				tiptr->ti_flags |= EXPEDITED;
230			/* FALLTHROUGH */
231		case T_DATA_IND:
232			/*
233			 * Uses the fact T_DATA_IND and T_EXDATA_IND
234			 * are same in size
235			 */
236			if ((ctlbuf.len < (int)sizeof (struct T_data_ind)) ||
237			    (tiptr->ti_lookcnt > 0)) {
238				/*
239				 * ti_lookcnt > 0 implies data
240				 * received after T_DISCON_IND or
241				 * T_ORDREL_IND hence error
242				 */
243				t_errno = TSYSERR;
244				errno = EPROTO;
245				goto err_out;
246			}
247
248			if ((pptr->data_ind.MORE_flag) || retval)
249				*flags |= T_MORE;
250			if ((pptr->data_ind.MORE_flag) && retval)
251				tiptr->ti_flags |= MORE;
252			/*
253			 * No real state change on T_RCV event (noop)
254			 *
255			 * We invoke the macro only for error logging
256			 * part of its capabilities when in a bad state.
257			 */
258			_T_TX_NEXTSTATE(T_RCV, tiptr,
259					"t_rcvv: invalid state event T_RCV");
260			if (didalloc)
261				free(ctlbuf.buf);
262			else
263				tiptr->ti_ctlbuf = ctlbuf.buf;
264			_t_scatter(&databuf, tiov, tiovcount);
265			if (dataptr != NULL)
266				free(dataptr);
267			sig_mutex_unlock(&tiptr->ti_lock);
268			return (databuf.len);
269
270		case T_ORDREL_IND:
271			if (tiptr->ti_lookcnt > 0) {
272				/*
273				 * ti_lookcnt > 0 implies T_ORDREL_IND
274				 * received after T_DISCON_IND or
275				 * another T_ORDREL_IND hence error.
276				 */
277				t_errno = TSYSERR;
278				errno = EPROTO;
279				goto err_out;
280			}
281			/* FALLTHROUGH */
282		case T_DISCON_IND:
283			/*
284			 * Post event (T_ORDREL_IND/T_DISCON_IND) to
285			 * the lookbuffer list.
286			 */
287
288			if (_t_register_lookevent(tiptr, databuf.buf,
289					databuf.len,
290					ctlbuf.buf, ctlbuf.len) < 0) {
291				t_errno = TSYSERR;
292				errno = ENOMEM;
293				goto err_out;
294			}
295			/*
296			 * We know that T_DISCON_IND is stored in
297			 * last look buffer. If there is more data
298			 * that follows, we try to append it to
299			 * the same look buffer
300			 */
301			if (retval & MOREDATA) {
302				ctlbuf.maxlen = 0; /* XXX why ? */
303				ctlbuf.len = 0;
304
305				/*
306				 * XXX Will break (-ve maxlen) for
307				 * transport provider with unbounded
308				 * T_DISCON_IND data part (-1).
309				 */
310				databuf.maxlen =
311					tiptr->ti_rcvsize - databuf.len;
312
313				databuf.len = 0;
314				databuf.buf =
315					tiptr->ti_lookbufs.tl_lookdbuf +
316					tiptr->ti_lookbufs.tl_lookdlen;
317				*flags = 0;
318
319				/*
320				 * Since MOREDATA was set, we assume
321				 * that this getmsg will not block
322				 * indefinitely
323				 */
324				do {
325					retval = getmsg(fd, &ctlbuf,
326							&databuf, &flg);
327				} while (retval < 0 && errno == EINTR);
328
329				if (retval < 0) {
330					t_errno = TSYSERR;
331					goto err_out;
332				}
333				if (databuf.len == -1) databuf.len = 0;
334				if (retval > 0) {
335					/* MORECTL should not be on */
336					assert((retval & MORECTL) == 0);
337					/*
338					 * XXX - Why ?
339					 * No support for unbounded data
340					 * on T_DISCON_IND ?
341					 */
342					t_errno = TSYSERR;
343					errno = EPROTO;
344					goto err_out;
345				}
346				tiptr->ti_lookbufs.tl_lookdlen +=
347					databuf.len;
348			}
349
350			t_errno = TLOOK;
351			goto err_out;
352
353		default:
354			break;
355		}
356
357		t_errno = TSYSERR;
358		errno = EPROTO;
359		goto err_out;
360
361	} else {		/* else for "if (ctlbuf.len > 0)" */
362		if (!retval && (tiptr->ti_flags & MORE)) {
363			*flags |= T_MORE;
364			tiptr->ti_flags &= ~MORE;
365		}
366		if (retval & MOREDATA)
367			*flags |= T_MORE;
368
369		/*
370		 * If inside an ETSDU, set expedited flag and turn
371		 * of internal version when reach end of "ETIDU".
372		 */
373		if (tiptr->ti_flags & EXPEDITED) {
374			*flags |= T_EXPEDITED;
375			if (!retval)
376				tiptr->ti_flags &= ~EXPEDITED;
377		}
378
379		/*
380		 * No real state change on T_RCV events (It is a NOOP)
381		 *
382		 * We invoke the macro only for error logging
383		 * part of its capabilities when in a bad state.
384		 */
385		_T_TX_NEXTSTATE(T_RCV, tiptr,
386			"t_rcvv: state invalid T_RCV event");
387		if (didalloc)
388			free(ctlbuf.buf);
389		else
390			tiptr->ti_ctlbuf = ctlbuf.buf;
391		_t_scatter(&databuf, tiov, tiovcount);
392		if (dataptr != NULL)
393			free(dataptr);
394		sig_mutex_unlock(&tiptr->ti_lock);
395		return (databuf.len);
396	}
397	/* NOTREACHED */
398
399err_out:
400	sv_errno = errno;
401	if (didalloc)
402		free(ctlbuf.buf);
403	else
404		tiptr->ti_ctlbuf = ctlbuf.buf;
405	if (dataptr != NULL)
406		free(dataptr);
407	sig_mutex_unlock(&tiptr->ti_lock);
408
409	errno = sv_errno;
410	return (-1);
411}
412