In some cases the license-identification header was missing, so I added that as well. Replaced the remaining headers that specifically identified "Versions 3.13.7 and higher". Makefiles and the build system were deliberately excluded.
302 lines
7.6 KiB
C++
302 lines
7.6 KiB
C++
/*************************************************************************\
|
|
* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
|
|
* National Laboratory.
|
|
* Copyright (c) 2002 The Regents of the University of California, as
|
|
* Operator of Los Alamos National Laboratory.
|
|
* SPDX-License-Identifier: EPICS
|
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "osiSock.h"
|
|
#include "osiWireFormat.h"
|
|
#include "epicsThread.h"
|
|
#include "epicsSignal.h"
|
|
#include "epicsUnitTest.h"
|
|
#include "testMain.h"
|
|
|
|
union address {
|
|
struct sockaddr_in ia;
|
|
struct sockaddr sa;
|
|
};
|
|
|
|
class circuit {
|
|
public:
|
|
circuit ( SOCKET );
|
|
void recvTest ();
|
|
void shutdown ();
|
|
void close ();
|
|
bool recvWakeupDetected () const;
|
|
bool sendWakeupDetected () const;
|
|
virtual const char * pName () = 0;
|
|
protected:
|
|
SOCKET sock;
|
|
epicsThreadId id;
|
|
bool recvWakeup;
|
|
bool sendWakeup;
|
|
protected:
|
|
virtual ~circuit() {}
|
|
};
|
|
|
|
class serverCircuit : public circuit {
|
|
public:
|
|
serverCircuit ( SOCKET );
|
|
private:
|
|
const char * pName ();
|
|
};
|
|
|
|
class clientCircuit : public circuit {
|
|
public:
|
|
clientCircuit ( const address & );
|
|
private:
|
|
const char * pName ();
|
|
};
|
|
|
|
class server {
|
|
public:
|
|
server ( const address & );
|
|
void start ();
|
|
void daemon ();
|
|
void stop ();
|
|
address addr () const;
|
|
protected:
|
|
address srvaddr;
|
|
SOCKET sock;
|
|
epicsThreadId id;
|
|
bool exit;
|
|
};
|
|
|
|
circuit::circuit ( SOCKET sockIn ) :
|
|
sock ( sockIn ),
|
|
id ( 0 ),
|
|
recvWakeup ( false ),
|
|
sendWakeup ( false )
|
|
{
|
|
testOk ( this->sock != INVALID_SOCKET, "Socket valid" );
|
|
}
|
|
|
|
bool circuit::recvWakeupDetected () const
|
|
{
|
|
return this->recvWakeup;
|
|
}
|
|
|
|
bool circuit::sendWakeupDetected () const
|
|
{
|
|
return this->sendWakeup;
|
|
}
|
|
|
|
void circuit::shutdown ()
|
|
{
|
|
int status = ::shutdown ( this->sock, SHUT_RDWR );
|
|
testOk ( status == 0, "Shutdown() returned Ok" );
|
|
}
|
|
|
|
void circuit::close ()
|
|
{
|
|
epicsSocketDestroy ( this->sock );
|
|
}
|
|
|
|
void circuit::recvTest ()
|
|
{
|
|
char buf [1];
|
|
while ( true ) {
|
|
int status = recv ( this->sock,
|
|
buf, (int) sizeof ( buf ), 0 );
|
|
if ( status == 0 ) {
|
|
testDiag ( "%s was disconnected", this->pName () );
|
|
this->recvWakeup = true;
|
|
break;
|
|
}
|
|
else if ( status > 0 ) {
|
|
testDiag ( "%s received %i characters", this->pName (), status );
|
|
}
|
|
else {
|
|
char sockErrBuf[64];
|
|
epicsSocketConvertErrnoToString (
|
|
sockErrBuf, sizeof ( sockErrBuf ) );
|
|
testDiag ( "%s socket recv() error was \"%s\"",
|
|
this->pName (), sockErrBuf );
|
|
this->recvWakeup = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" void socketRecvTest ( void * pParm )
|
|
{
|
|
circuit * pCir = reinterpret_cast < circuit * > ( pParm );
|
|
pCir->recvTest ();
|
|
}
|
|
|
|
clientCircuit::clientCircuit ( const address & addrIn ) :
|
|
circuit ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) )
|
|
{
|
|
address tmpAddr = addrIn;
|
|
int status = ::connect (
|
|
this->sock, & tmpAddr.sa, sizeof ( tmpAddr ) );
|
|
testOk ( status == 0, "Client end connected" );
|
|
|
|
circuit * pCir = this;
|
|
this->id = epicsThreadCreate (
|
|
"client circuit", epicsThreadPriorityMedium,
|
|
epicsThreadGetStackSize(epicsThreadStackMedium),
|
|
socketRecvTest, pCir );
|
|
testOk ( this->id != 0, "Client thread created" );
|
|
}
|
|
|
|
|
|
const char * clientCircuit::pName ()
|
|
{
|
|
return "client circuit";
|
|
}
|
|
|
|
extern "C" void serverDaemon ( void * pParam ) {
|
|
server * pSrv = reinterpret_cast < server * > ( pParam );
|
|
pSrv->daemon ();
|
|
}
|
|
|
|
server::server ( const address & addrIn ) :
|
|
srvaddr ( addrIn ),
|
|
sock ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ),
|
|
id ( 0 ), exit ( false )
|
|
{
|
|
testOk ( this->sock != INVALID_SOCKET, "Server socket valid" );
|
|
|
|
// setup server side
|
|
osiSocklen_t slen = sizeof ( this->srvaddr );
|
|
int status = bind ( this->sock, & this->srvaddr.sa, slen );
|
|
if ( status ) {
|
|
testDiag ( "bind to server socket failed, status = %d", status );
|
|
}
|
|
if ( getsockname(this->sock, & this->srvaddr.sa, & slen) != 0 ) {
|
|
testAbort ( "Failed to read socket address" );
|
|
}
|
|
status = listen ( this->sock, 10 );
|
|
testOk ( status == 0, "Server socket listening" );
|
|
}
|
|
|
|
void server::start ()
|
|
{
|
|
this->id = epicsThreadCreate (
|
|
"server daemon", epicsThreadPriorityMedium,
|
|
epicsThreadGetStackSize(epicsThreadStackMedium),
|
|
serverDaemon, this );
|
|
testOk ( this->id != 0, "Server thread created" );
|
|
}
|
|
|
|
void server::daemon ()
|
|
{
|
|
while ( ! this->exit ) {
|
|
// accept client side
|
|
address addr;
|
|
osiSocklen_t addressSize = sizeof ( addr );
|
|
SOCKET ns = accept ( this->sock,
|
|
& addr.sa, & addressSize );
|
|
if ( this->exit )
|
|
break;
|
|
testOk ( ns != INVALID_SOCKET, "Accepted socket valid" );
|
|
circuit * pCir = new serverCircuit ( ns );
|
|
testOk ( pCir != 0, "Server circuit created" );
|
|
}
|
|
}
|
|
|
|
void server::stop ()
|
|
{
|
|
this->exit = true;
|
|
epicsSocketDestroy ( this->sock );
|
|
}
|
|
|
|
address server::addr () const
|
|
{
|
|
return this->srvaddr;
|
|
}
|
|
|
|
serverCircuit::serverCircuit ( SOCKET sockIn ) :
|
|
circuit ( sockIn )
|
|
{
|
|
circuit * pCir = this;
|
|
epicsThreadId threadId = epicsThreadCreate (
|
|
"server circuit", epicsThreadPriorityMedium,
|
|
epicsThreadGetStackSize(epicsThreadStackMedium),
|
|
socketRecvTest, pCir );
|
|
testOk ( threadId != 0, "Server circuit thread created" );
|
|
}
|
|
|
|
const char * serverCircuit::pName ()
|
|
{
|
|
return "server circuit";
|
|
}
|
|
|
|
static const char *mechName(int mech)
|
|
{
|
|
static const struct {
|
|
int mech;
|
|
const char *name;
|
|
} mechs[] = {
|
|
{-1, "Unknown shutdown mechanism" },
|
|
{esscimqi_socketCloseRequired, "esscimqi_socketCloseRequired" },
|
|
{esscimqi_socketBothShutdownRequired, "esscimqi_socketBothShutdownRequired" },
|
|
{esscimqi_socketSigAlarmRequired, "esscimqi_socketSigAlarmRequired" }
|
|
};
|
|
|
|
for (unsigned i=0; i < (sizeof(mechs) / sizeof(mechs[0])); ++i) {
|
|
if (mech == mechs[i].mech)
|
|
return mechs[i].name;
|
|
}
|
|
return "Unknown shutdown mechanism value";
|
|
}
|
|
|
|
MAIN(blockingSockTest)
|
|
{
|
|
testPlan(13);
|
|
osiSockAttach();
|
|
|
|
address addr;
|
|
memset ( (char *) & addr, 0, sizeof ( addr ) );
|
|
addr.ia.sin_family = AF_INET;
|
|
addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK );
|
|
addr.ia.sin_port = 0;
|
|
|
|
server srv ( addr );
|
|
srv.start ();
|
|
addr = srv.addr ();
|
|
clientCircuit client ( addr );
|
|
|
|
epicsThreadSleep ( 1.0 );
|
|
testOk ( ! client.recvWakeupDetected (), "Client is asleep" );
|
|
|
|
testDiag("Trying Shutdown mechanism");
|
|
client.shutdown ();
|
|
epicsThreadSleep ( 1.0 );
|
|
int mech = -1;
|
|
if ( client.recvWakeupDetected () ) {
|
|
mech = esscimqi_socketBothShutdownRequired;
|
|
testDiag("Shutdown succeeded");
|
|
}
|
|
else {
|
|
testDiag("Trying Close mechanism");
|
|
client.close ();
|
|
epicsThreadSleep ( 1.0 );
|
|
if ( client.recvWakeupDetected () ) {
|
|
mech = esscimqi_socketCloseRequired;
|
|
testDiag("Close succeeded");
|
|
}
|
|
}
|
|
testDiag("This OS behaves like \"%s\".", mechName(mech));
|
|
|
|
int query = epicsSocketSystemCallInterruptMechanismQuery ();
|
|
if (! testOk(mech == query, "Declared mechanism works") )
|
|
testDiag("epicsSocketSystemCallInterruptMechanismQuery returned \"%s\"",
|
|
mechName(query));
|
|
|
|
srv.stop ();
|
|
epicsThreadSleep ( 1.0 );
|
|
|
|
osiSockRelease();
|
|
return testDone();
|
|
}
|
|
|