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