1 // vim: noet sw=4 ts=4:
3 ==================================================================================
4 Copyright (c) 2020 Nokia
5 Copyright (c) 2020 AT&T Intellectual Property.
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 ==================================================================================
22 ****************************************************************************
25 * Abstract: This module contains various send functions:
26 * SIsendt -- send tcp with queuing if would block
27 * SIsendt_nq - send tcp without queuing if blocking
30 * Author: E. Scott Daniels
31 * Mod: 22 Feb 2002 - To better process queued data
32 * 14 Feb 2020 - To fix index bug if fd < 0.
34 *****************************************************************************
37 #include "sisetup.h" // get setup stuff
38 #include "sitransport.h"
41 Send a message on what is assumed to be a tcp connection. If the session
42 would block, then SI_ERR_BLOCKED is returned. Else, SI_OK or SI_ERROR
43 is returned to indicate state. Errno should be set to reflect error state:
44 EBADFD - error from system; fd was closed
45 EBUSY - system would block the send call
46 EINVAL - fd was not valid or did not reference an open session
48 //extern int SIsendt_nq( struct ginfo_blk *gptr, int fd, char *ubuf, int ulen ) {
49 extern int SIsendt( struct ginfo_blk *gptr, int fd, char *ubuf, int ulen ) {
50 int status = SI_ERROR; // assume we fail
51 fd_set writefds; // local write fdset to check blockage
52 fd_set execpfds; // exception fdset to check errors
53 struct tp_blk *tpptr; // pointer at the tp_blk for the session
54 struct ioq_blk *qptr; // pointer at i/o queue block
55 struct timeval time; // delay time parameter for select call
56 int sidx = 0; // send index
59 gptr->sierr = SI_ERR_SESSID;
62 return SI_ERROR; // bad form trying to use this fd
65 if( fd < MAX_FDS ) { // straight from map if possible
66 tpptr = gptr->tp_map[fd];
68 for( tpptr = gptr->tplist; tpptr != NULL && tpptr->fd != fd; tpptr = tpptr->next ); // find the block if out of map's range
72 tpptr->sent++; // investigate: this may over count
74 FD_ZERO( &writefds ); // clear for select call
75 FD_SET( fd, &writefds ); // set to see if this one was writable
76 FD_ZERO( &execpfds ); // clear and set execptions fdset
77 FD_SET( fd, &execpfds );
79 time.tv_sec = 0; // set both to 0 if we just want a poll, else we block at max this amount
80 time.tv_usec = 1; // small pause on check to help drain things
82 if( select( fd + 1, NULL, &writefds, &execpfds, &time ) > 0 ) { // would block if <= 0
83 gptr->sierr = SI_ERR_TP;
84 if( FD_ISSET( fd, &execpfds ) ) { // error?
86 SIterm( gptr, tpptr ); // clean up our portion of the session
87 return SI_ERROR; // and bail from this sinking ship
90 while( ulen > 0 ) { // once we start, we must ensure that it all goes out
91 status = SEND( tpptr->fd, ubuf+sidx, (unsigned int) ulen, 0 );
97 if( errno != EINTR || errno != EAGAIN ) {
106 status = SI_ERR_BLOCKED;
109 errno = EBADFD; // fd in a bad state (probably losed)
116 This routine will send a datagram to the TCP session partner
117 that is connected via the FD number that is passed in.
118 If the send would cause the process to block, the send is
119 queued on the tp_blk for the session and is sent later as
120 a function of the SIwait process. If the buffer must be
121 queued, a copy of the buffer is created such that the
122 user program may free, or reuse, the buffer upon return.
124 Parms:i gptr - The pointer to the global info structure (context)
125 fd - File descriptor (session number)
126 ubuf - User buffer to send.
127 ulen - Lenght of the user buffer.
129 Returns: SI_OK if sent, SI_QUEUED if queued for later, SI_ERROR if error.
132 extern int new_SIsendt( struct ginfo_blk *gptr, int fd, char *ubuf, int ulen ) {
133 int status = SI_OK; // status of processing
134 fd_set writefds; // local write fdset to check blockage
135 fd_set execpfds; // exception fdset to check errors
136 struct tp_blk *tpptr; // pointer at the tp_blk for the session
137 struct ioq_blk *qptr; // pointer at i/o queue block
138 struct timeval time; // delay time parameter for select call
140 gptr->sierr = SI_ERR_HANDLE;
142 //if( gptr->magicnum == MAGICNUM ) { // ensure cookie is good -- we need to be too performant for this
143 //{ // mmmm oatmeal, my favorite
144 gptr->sierr = SI_ERR_SESSID;
146 if( fd < MAX_FDS ) { // straight from map if possible
147 tpptr = gptr->tp_map[fd];
149 for( tpptr = gptr->tplist; tpptr != NULL && tpptr->fd != fd; tpptr = tpptr->next ); // find the block if out of map's range
152 if( tpptr != NULL ) {
155 FD_ZERO( &writefds ); // clear for select call
156 FD_SET( fd, &writefds ); // set to see if this one was writable
157 FD_ZERO( &execpfds ); // clear and set execptions fdset
158 FD_SET( fd, &execpfds );
160 time.tv_sec = 0; // set both to 0 if we just want a poll, else we block at max this amount
161 time.tv_usec = 1; // small pause on check to help drain things
163 if( select( fd + 1, NULL, &writefds, &execpfds, &time ) > 0 ) { // see if it would block
164 gptr->sierr = SI_ERR_TP;
165 if( FD_ISSET( fd, &execpfds ) ) { // error?
166 SIterm( gptr, tpptr ); // clean up our portion of the session
167 return SI_ERROR; // and bail from this sinking ship
169 if( tpptr->squeue ) {
170 SIsend( gptr, tpptr ); // something queued; send off queue and queue this
172 return SEND( tpptr->fd, ubuf, (unsigned int) ulen, 0 ); // done after send
177 gptr->sierr = SI_ERR_NOMEM;
180 if( (qptr = SInew( IOQ_BLK )) != NULL ) { // alloc a queue block
181 if( tpptr->sqtail == NULL ) { // if nothing on the queue
182 tpptr->squeue = qptr; // simple add to the tp blk q
183 tpptr->sqtail = qptr;
184 } else { // else - add at end of the q
185 tpptr->sqtail->next = qptr;
186 tpptr->sqtail = qptr;
187 qptr->next = NULL; // new block is the last one now
188 } // end add block at end of queue
190 qptr->dlen = ulen; // copy info to queue block
191 qptr->data = (char *) malloc( ulen ); // get buffer
192 memcpy( qptr->data, (const char*) ubuf, ulen );
194 gptr->sierr = SI_QUEUED; // indicate queued to caller
195 status = SI_QUEUED; // for return
197 } // end if tpptr was not found
198 //} // ginfo pointer was corrupted