Files
pcas/src/rsrv/cast_server.c
T
Andrew Johnson c9bc70bd6a Added iocBuild, iocPause and iocRun commands.
These are mainly for redundant IOCs, but may be useful elsewhere.
iocBuild prepares the IOC and freezes it just before it goes live.
A subsequent iocRun kicks it into life, or restarts it when paused.
iocPause freezes all scan tasks and disconnects the server.
2008-07-09 21:31:56 +00:00

316 lines
9.3 KiB
C

/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Jeffrey O. Hill
* hill@luke.lanl.gov
* (505) 665 1831
* Date: 5-88
*
* Improvements
* ------------
* .01
* Dont send channel found message unless there is memory, a task slot,
* and a TCP socket available. Send a diagnostic instead.
* Or ... make the timeout shorter? This is only a problem if
* they persist in trying to make a connection after getting no
* response.
*
* Notes:
* ------
* .01
* Replies to broadcasts are not returned over
* an existing TCP connection to avoid a TCP
* pend which could lock up the cast server.
*/
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "osiSock.h"
#include "epicsMutex.h"
#include "dbDefs.h"
#include "errlog.h"
#include "taskwd.h"
#include "epicsTime.h"
#include "envDefs.h"
#include "freeList.h"
#define epicsExportSharedSymbols
#include "server.h"
#include "rsrv.h"
#define TIMEOUT 60.0 /* sec */
/*
* clean_addrq
*/
LOCAL void clean_addrq()
{
struct channel_in_use * pciu;
struct channel_in_use * pnextciu;
epicsTimeStamp current;
double delay;
double maxdelay = 0;
unsigned ndelete=0;
double timeout = TIMEOUT;
int s;
epicsTimeGetCurrent ( &current );
epicsMutexMustLock ( prsrv_cast_client->chanListLock );
pnextciu = (struct channel_in_use *)
prsrv_cast_client->chanList.node.next;
while( (pciu = pnextciu) ) {
pnextciu = (struct channel_in_use *)pciu->node.next;
delay = epicsTimeDiffInSeconds(&current,&pciu->time_at_creation);
if (delay > timeout) {
ellDelete(&prsrv_cast_client->chanList, &pciu->node);
LOCK_CLIENTQ;
s = bucketRemoveItemUnsignedId (
pCaBucket,
&pciu->sid);
if(s){
errMessage (s, "Bad id at close");
}
else {
rsrvChannelCount--;
}
UNLOCK_CLIENTQ;
if ( ! s ) {
freeListFree(rsrvChanFreeList, pciu);
ndelete++;
}
if(delay>maxdelay) maxdelay = delay;
}
}
epicsMutexUnlock ( prsrv_cast_client->chanListLock );
# ifdef DEBUG
if(ndelete){
epicsPrintf ("CAS: %d CA channels have expired after %f sec\n",
ndelete, maxdelay);
}
# endif
}
/*
* CAST_SERVER
*
* service UDP messages
*
*/
void cast_server(void *pParm)
{
struct sockaddr_in sin;
int status;
int count=0;
struct sockaddr_in new_recv_addr;
osiSocklen_t recv_addr_size;
unsigned short port;
osiSockIoctl_t nchars;
if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) {
port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT,
(unsigned short) CA_SERVER_PORT );
}
else {
port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT,
(unsigned short) CA_SERVER_PORT );
}
recv_addr_size = sizeof(new_recv_addr);
if( IOC_cast_sock!=0 && IOC_cast_sock!=INVALID_SOCKET ) {
epicsSocketDestroy ( IOC_cast_sock );
}
/*
* Open the socket.
* Use ARPA Internet address format and datagram socket.
*/
if ( ( IOC_cast_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) {
epicsPrintf ("CAS: cast socket creation error\n");
epicsThreadSuspendSelf ();
}
/*
* some concern that vxWorks will run out of mBuf's
* if this change is made
*
* joh 11-10-98
*/
#if 0
{
/*
*
* this allows for faster connects by queuing
* additional incomming UDP search frames
*
* this allocates a 32k buffer
* (uses a power of two)
*/
int size = 1u<<15u;
status = setsockopt (IOC_cast_sock, SOL_SOCKET,
SO_RCVBUF, (char *)&size, sizeof(size));
if (status<0) {
epicsPrintf ("CAS: unable to set cast socket size\n");
}
}
#endif
epicsSocketEnableAddressUseForDatagramFanout ( IOC_cast_sock );
/* Zero the sock_addr structure */
memset((char *)&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(port);
/* get server's Internet address */
if( bind(IOC_cast_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
epicsPrintf ("CAS: UDP server port bind error was \"%s\"\n", sockErrBuf );
epicsSocketDestroy ( IOC_cast_sock );
epicsThreadSuspendSelf ();
}
/*
* setup new client structure but reuse old structure if
* possible
*
*/
while ( TRUE ) {
prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP );
if ( prsrv_cast_client ) {
break;
}
epicsThreadSleep(300.0);
}
casAttachThreadToClient ( prsrv_cast_client );
/*
* add placeholder for the first version message should it be needed
*/
rsrv_version_reply ( prsrv_cast_client );
epicsEventSignal(casudp_startStopEvent);
while (TRUE) {
status = recvfrom (
IOC_cast_sock,
prsrv_cast_client->recv.buf,
prsrv_cast_client->recv.maxstk,
0,
(struct sockaddr *)&new_recv_addr,
&recv_addr_size);
if (status<0) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
epicsPrintf ("CAS: UDP recv error (errno=%s)\n",
sockErrBuf);
epicsThreadSleep(1.0);
}
else if (casudp_ctl == ctlRun) {
prsrv_cast_client->recv.cnt = (unsigned) status;
prsrv_cast_client->recv.stk = 0ul;
epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv);
prsrv_cast_client->minor_version_number = 0;
prsrv_cast_client->seqNoOfReq = 0;
/*
* If we are talking to a new client flush to the old one
* in case we are holding UDP messages waiting to
* see if the next message is for this same client.
*/
if (prsrv_cast_client->send.stk>sizeof(caHdr)) {
status = memcmp( (void *)&prsrv_cast_client->addr, (void *)&new_recv_addr, recv_addr_size);
if(status){
/*
* if the address is different
*/
cas_send_dg_msg(prsrv_cast_client);
prsrv_cast_client->addr = new_recv_addr;
}
}
else {
prsrv_cast_client->addr = new_recv_addr;
}
if (CASDEBUG>1) {
char buf[40];
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
errlogPrintf ("CAS: cast server msg of %d bytes from addr %s\n",
prsrv_cast_client->recv.cnt, buf);
}
if (CASDEBUG>2)
count = ellCount (&prsrv_cast_client->chanList);
status = camessage ( prsrv_cast_client );
if(status == RSRV_OK){
if(prsrv_cast_client->recv.cnt !=
prsrv_cast_client->recv.stk){
char buf[40];
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n",
prsrv_cast_client->recv.cnt-prsrv_cast_client->recv.stk, buf);
}
}
else {
char buf[40];
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf);
}
if (CASDEBUG>2) {
if ( ellCount (&prsrv_cast_client->chanList) ) {
errlogPrintf ("CAS: Fnd %d name matches (%d tot)\n",
ellCount(&prsrv_cast_client->chanList)-count,
ellCount(&prsrv_cast_client->chanList));
}
}
}
/*
* allow messages to batch up if more are comming
*/
nchars = 0; /* supress purify warning */
status = socket_ioctl(IOC_cast_sock, FIONREAD, &nchars);
if (status<0) {
errlogPrintf ("CA cast server: Unable to fetch N characters pending\n");
cas_send_dg_msg (prsrv_cast_client);
clean_addrq ();
}
else if (nchars == 0) {
cas_send_dg_msg (prsrv_cast_client);
clean_addrq ();
}
}
}