331 lines
12 KiB
C
331 lines
12 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2011 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
/* iosocket_wait.c
|
|
*
|
|
* return a listening socket -- create a new socket for the connection and set it to current
|
|
* set it to current
|
|
* set $KEY to "CONNECT"
|
|
* return a connected socket -- set it to current
|
|
* set $KEY to "READ"
|
|
* timeout -- set $Test to 1
|
|
*/
|
|
#include "mdef.h"
|
|
#include <errno.h>
|
|
#include "gtm_socket.h"
|
|
#include "gtm_inet.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_string.h"
|
|
#include "io_params.h"
|
|
#include "gt_timer.h"
|
|
#include "io.h"
|
|
#include "iotimer.h"
|
|
#include "iotcp_select.h"
|
|
#include "iotcproutine.h"
|
|
#include "iotcpdef.h"
|
|
#include "iosocketdef.h"
|
|
#include "min_max.h"
|
|
#include "outofband.h"
|
|
#include "rtnhdr.h"
|
|
#include "stack_frame.h"
|
|
#include "mv_stent.h"
|
|
|
|
#define CONNECTED "CONNECT"
|
|
#define READ "READ"
|
|
|
|
GBLREF tcp_library_struct tcp_routines;
|
|
GBLREF volatile int4 outofband;
|
|
GBLREF int4 gtm_max_sockets;
|
|
GBLREF int socketus_interruptus;
|
|
GBLREF boolean_t dollar_zininterrupt;
|
|
GBLREF stack_frame *frame_pointer;
|
|
GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn;
|
|
GBLREF mv_stent *mv_chain;
|
|
|
|
error_def(ERR_SOCKACPT);
|
|
error_def(ERR_SOCKWAIT);
|
|
error_def(ERR_TEXT);
|
|
error_def(ERR_SOCKMAX);
|
|
error_def(ERR_ZINTRECURSEIO);
|
|
error_def(ERR_STACKCRIT);
|
|
error_def(ERR_STACKOFLOW);
|
|
|
|
boolean_t iosocket_wait(io_desc *iod, int4 timepar)
|
|
{
|
|
struct timeval utimeout;
|
|
ABS_TIME cur_time, end_time;
|
|
struct sockaddr_in peer; /* socket address + port */
|
|
fd_set tcp_fd;
|
|
d_socket_struct *dsocketptr;
|
|
socket_struct *socketptr, *newsocketptr;
|
|
socket_interrupt *sockintr;
|
|
char *errptr;
|
|
int4 errlen, ii, msec_timeout;
|
|
int rv, max_fd, len;
|
|
GTM_SOCKLEN_TYPE size;
|
|
boolean_t zint_restart;
|
|
mv_stent *mv_zintdev;
|
|
int retry_num;
|
|
|
|
/* check for validity */
|
|
assert(iod->type == gtmsocket);
|
|
dsocketptr = (d_socket_struct *)iod->dev_sp;
|
|
sockintr = &dsocketptr->sock_save_state;
|
|
|
|
/* Check for restart */
|
|
if (!dsocketptr->mupintr)
|
|
/* Simple path, no worries*/
|
|
zint_restart = FALSE;
|
|
else
|
|
{ /* We have a pending wait restart of some sort - check we aren't recursing on this device */
|
|
if (sockwhich_invalid == sockintr->who_saved)
|
|
GTMASSERT; /* Interrupt should never have an invalid save state */
|
|
if (dollar_zininterrupt)
|
|
rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO);
|
|
if (sockwhich_wait != sockintr->who_saved)
|
|
GTMASSERT; /* ZINTRECURSEIO should have caught */
|
|
DBGSOCK((stdout, "socwait: *#*#*#*#*#*#*# Restarted interrupted wait\n"));
|
|
mv_zintdev = io_find_mvstent(iod, FALSE);
|
|
if (mv_zintdev)
|
|
{
|
|
if (sockintr->end_time_valid)
|
|
/* Restore end_time for timeout */
|
|
end_time = sockintr->end_time;
|
|
|
|
/* Done with this mv_stent. Pop it off if we can, else mark it inactive. */
|
|
if (mv_chain == mv_zintdev)
|
|
POP_MV_STENT(); /* pop if top of stack */
|
|
else
|
|
{ /* else mark it unused */
|
|
mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE;
|
|
mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL;
|
|
}
|
|
zint_restart = TRUE;
|
|
DBGSOCK((stdout, "socwait: mv_stent found - endtime: %d/%d\n", end_time.at_sec, end_time.at_usec));
|
|
} else
|
|
DBGSOCK((stdout, "socwait: no mv_stent found !!\n"));
|
|
dsocketptr->mupintr = FALSE;
|
|
sockintr->who_saved = sockwhich_invalid;
|
|
}
|
|
|
|
/* check for events */
|
|
max_fd = 0;
|
|
FD_ZERO(&tcp_fd);
|
|
for (ii = 0; ii < dsocketptr->n_socket; ii++)
|
|
{
|
|
socketptr = dsocketptr->socket[ii];
|
|
if ((socket_listening == socketptr->state) || (socket_connected == socketptr->state))
|
|
{
|
|
FD_SET(socketptr->sd, &tcp_fd);
|
|
max_fd = MAX(max_fd, socketptr->sd);
|
|
}
|
|
}
|
|
utimeout.tv_sec = timepar;
|
|
utimeout.tv_usec = 0;
|
|
msec_timeout = timeout2msec(timepar);
|
|
sys_get_curr_time(&cur_time);
|
|
if (!zint_restart || !sockintr->end_time_valid)
|
|
add_int_to_abs_time(&cur_time, msec_timeout, &end_time);
|
|
else
|
|
{ /* end_time taken from restart data. Compute what msec_timeout should be so timeout timer
|
|
gets set correctly below.
|
|
*/
|
|
DBGSOCK((stdout, "socwait: Taking timeout end time from wait restart data\n"));
|
|
cur_time = sub_abs_time(&end_time, &cur_time);
|
|
if (0 > cur_time.at_sec)
|
|
{
|
|
msec_timeout = -1;
|
|
utimeout.tv_sec = 0;
|
|
utimeout.tv_usec = 0;
|
|
} else
|
|
{
|
|
msec_timeout = (int4)(cur_time.at_sec * 1000 + cur_time.at_usec / 1000);
|
|
utimeout.tv_sec = cur_time.at_sec;
|
|
utimeout.tv_usec = (gtm_tv_usec_t)cur_time.at_usec;
|
|
}
|
|
}
|
|
sockintr->end_time_valid = FALSE;
|
|
|
|
for ( ; ; )
|
|
{
|
|
rv = select(max_fd + 1, (void *)&tcp_fd, (void *)0, (void *)0,
|
|
(timepar == NO_M_TIMEOUT ? (struct timeval *)0 : &utimeout));
|
|
if (0 > rv && EINTR == errno)
|
|
{
|
|
if (0 != outofband)
|
|
{
|
|
DBGSOCK((stdout, "socwait: outofband interrupt received (%d) -- "
|
|
"queueing mv_stent for wait intr\n", outofband));
|
|
PUSH_MV_STENT(MVST_ZINTDEV);
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod;
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE;
|
|
sockintr->who_saved = sockwhich_wait;
|
|
sockintr->end_time = end_time;
|
|
sockintr->end_time_valid = TRUE;
|
|
dsocketptr->mupintr = TRUE;
|
|
socketus_interruptus++;
|
|
DBGSOCK((stdout, "socwait: mv_stent queued - endtime: %d/%d interrupts: %d\n",
|
|
end_time.at_sec, end_time.at_usec, socketus_interruptus));
|
|
outofband_action(FALSE);
|
|
GTMASSERT; /* Should *never* return from outofband_action */
|
|
return FALSE; /* For the compiler.. */
|
|
}
|
|
sys_get_curr_time(&cur_time);
|
|
cur_time = sub_abs_time(&end_time, &cur_time);
|
|
if (0 > cur_time.at_sec)
|
|
{
|
|
rv = 0; /* time out */
|
|
break;
|
|
}
|
|
utimeout.tv_sec = cur_time.at_sec;
|
|
utimeout.tv_usec = (gtm_tv_usec_t)cur_time.at_usec;
|
|
} else
|
|
break; /* either other error or done */
|
|
}
|
|
if (rv == 0)
|
|
{
|
|
dsocketptr->dollar_key[0] = '\0';
|
|
return FALSE;
|
|
} else if (rv < 0)
|
|
{
|
|
errptr = (char *)STRERROR(errno);
|
|
errlen = STRLEN(errptr);
|
|
rts_error(VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr);
|
|
return FALSE;
|
|
}
|
|
/* find out which socket is ready */
|
|
for (ii = 0; ii < dsocketptr->n_socket; ii++)
|
|
{
|
|
socketptr = dsocketptr->socket[ii];
|
|
if (0 != FD_ISSET(socketptr->sd, &tcp_fd))
|
|
break;
|
|
}
|
|
assert(ii < dsocketptr->n_socket);
|
|
if (socket_listening == socketptr->state)
|
|
{
|
|
if (gtm_max_sockets <= dsocketptr->n_socket)
|
|
{
|
|
rts_error(VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets);
|
|
return FALSE;
|
|
}
|
|
size = SIZEOF(struct sockaddr_in);
|
|
rv = tcp_routines.aa_accept(socketptr->sd, &peer, &size);
|
|
if (-1 == rv)
|
|
{
|
|
#ifdef __hpux
|
|
/*ENOBUFS in HP-UX is either because of a memory problem or when we have received a RST just
|
|
after a SYN before an accept call. Normally this is not fatal and is just a transient state. Hence
|
|
exiting just after a single error of this kind should not be done. So retry in case of HP-UX and ENOBUFS error.*/
|
|
if (ENOBUFS == errno)
|
|
{
|
|
/*In case of succeeding with select in first go, accept will still get 5ms time difference*/
|
|
retry_num = 0;
|
|
while (HPUX_MAX_RETRIES > retry_num)
|
|
{
|
|
SHORT_SLEEP(5);
|
|
for ( ; HPUX_MAX_RETRIES > retry_num; retry_num++)
|
|
{
|
|
utimeout.tv_sec = 0;
|
|
utimeout.tv_usec = HPUX_SEL_TIMEOUT;
|
|
FD_ZERO(&tcp_fd);
|
|
FD_SET(socketptr->sd, &tcp_fd);
|
|
rv = select(max_fd + 1, (void *)&tcp_fd, (void *)0, (void *)0,
|
|
&utimeout);
|
|
if (0 < rv)
|
|
break;
|
|
if (0 > rv && (EINTR == errno))
|
|
{
|
|
if (0 != outofband)
|
|
{
|
|
DBGSOCK((stdout, "socwait: outofband interrupt received (%d) -- "
|
|
"queueing mv_stent for wait intr\n", outofband));
|
|
PUSH_MV_STENT(MVST_ZINTDEV);
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod;
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE;
|
|
sockintr->who_saved = sockwhich_wait;
|
|
sockintr->end_time = end_time;
|
|
sockintr->end_time_valid = TRUE;
|
|
dsocketptr->mupintr = TRUE;
|
|
socketus_interruptus++;
|
|
DBGSOCK((stdout, "socwait: mv_stent queued - endtime: %d/%d interrupts:"
|
|
" %d\n", end_time.at_sec, end_time.at_usec,
|
|
socketus_interruptus));
|
|
outofband_action(FALSE);
|
|
GTMASSERT; /* Should *never* return from outofband_action */
|
|
return FALSE; /* For the compiler.. */
|
|
}
|
|
}
|
|
else
|
|
SHORT_SLEEP(5);
|
|
}
|
|
if (0 > rv)
|
|
{
|
|
errptr = (char *)STRERROR(errno);
|
|
errlen = STRLEN(errptr);
|
|
rts_error(VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr);
|
|
return FALSE;
|
|
}
|
|
rv = tcp_routines.aa_accept(socketptr->sd, &peer, &size);
|
|
if ((-1 == rv) && (ENOBUFS == errno))
|
|
retry_num++;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
if (-1 == rv)
|
|
#endif
|
|
{
|
|
errptr = (char *)STRERROR(errno);
|
|
errlen = STRLEN(errptr);
|
|
rts_error(VARLSTCNT(6) ERR_SOCKACPT, 0, ERR_TEXT, 2, errlen, errptr);
|
|
return FALSE;
|
|
}
|
|
}
|
|
/* got the connection, create a new socket in the device socket list */
|
|
newsocketptr = (socket_struct *)malloc(SIZEOF(socket_struct));
|
|
*newsocketptr = *socketptr;
|
|
newsocketptr->sd = rv;
|
|
memcpy(&newsocketptr->remote.sin, &peer, SIZEOF(struct sockaddr_in));
|
|
SPRINTF(newsocketptr->remote.saddr_ip, "%s", tcp_routines.aa_inet_ntoa(peer.sin_addr));
|
|
newsocketptr->remote.port = GTM_NTOHS(peer.sin_port);
|
|
newsocketptr->state = socket_connected;
|
|
newsocketptr->passive = FALSE;
|
|
iosocket_delimiter_copy(socketptr, newsocketptr);
|
|
newsocketptr->buffer = (char *)malloc(socketptr->buffer_size);
|
|
newsocketptr->buffer_size = socketptr->buffer_size;
|
|
newsocketptr->buffered_length = socketptr->buffered_offset = 0;
|
|
newsocketptr->first_read = newsocketptr->first_write = TRUE;
|
|
/* put the new-born socket to the list and create a handle for it */
|
|
iosocket_handle(newsocketptr->handle, &newsocketptr->handle_len, TRUE, dsocketptr);
|
|
dsocketptr->socket[dsocketptr->n_socket++] = newsocketptr;
|
|
dsocketptr->current_socket = dsocketptr->n_socket - 1;
|
|
len = SIZEOF(CONNECTED) - 1;
|
|
memcpy(&dsocketptr->dollar_key[0], CONNECTED, len);
|
|
dsocketptr->dollar_key[len++] = '|';
|
|
memcpy(&dsocketptr->dollar_key[len], newsocketptr->handle, newsocketptr->handle_len);
|
|
len += newsocketptr->handle_len;
|
|
dsocketptr->dollar_key[len++] = '|';
|
|
strcpy(&dsocketptr->dollar_key[len], newsocketptr->remote.saddr_ip);
|
|
} else
|
|
{
|
|
assert(socket_connected == socketptr->state);
|
|
dsocketptr->current_socket = ii;
|
|
len = SIZEOF(READ) - 1;
|
|
memcpy(&dsocketptr->dollar_key[0], READ, len);
|
|
dsocketptr->dollar_key[len++] = '|';
|
|
memcpy(&dsocketptr->dollar_key[len], socketptr->handle, socketptr->handle_len);
|
|
len += socketptr->handle_len;
|
|
dsocketptr->dollar_key[len++] = '|';
|
|
strcpy(&dsocketptr->dollar_key[len], socketptr->remote.saddr_ip);
|
|
}
|
|
return TRUE;
|
|
}
|