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
49 int
_tx_rcvv(int fd,struct t_iovec * tiov,unsigned int tiovcount,int * flags,int api_semantics)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
399 err_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