From 1e2d258c6645e44c52d03705a057977bc248b42c Mon Sep 17 00:00:00 2001 From: John Winans Date: Thu, 27 Jul 1995 14:23:02 +0000 Subject: [PATCH] first release --- src/bdt/Makefile | 18 ++ src/bdt/Makefile.Unix | 10 + src/bdt/Makefile.Vx | 21 ++ src/bdt/bdt.c | 701 ++++++++++++++++++++++++++++++++++++++++++ src/bdt/bdtServ.c | 392 +++++++++++++++++++++++ src/bdt/bdtServName.c | 129 ++++++++ src/bdt/bdtServPv.c | 377 +++++++++++++++++++++++ 7 files changed, 1648 insertions(+) create mode 100644 src/bdt/Makefile create mode 100644 src/bdt/Makefile.Unix create mode 100644 src/bdt/Makefile.Vx create mode 100644 src/bdt/bdt.c create mode 100644 src/bdt/bdtServ.c create mode 100644 src/bdt/bdtServName.c create mode 100644 src/bdt/bdtServPv.c diff --git a/src/bdt/Makefile b/src/bdt/Makefile new file mode 100644 index 000000000..4c4cbe2bb --- /dev/null +++ b/src/bdt/Makefile @@ -0,0 +1,18 @@ +# +# $Id$ +# +# Base Lowest Level Directroy Makefile +# by Janet Anderson +# +# $Log$ +# Revision 1.1 1994/09/07 19:26:22 jba +# New file +# +# + +EPICS=../../.. + +include $(EPICS)/config/CONFIG_BASE + +include $(EPICS)/config/RULES_ARCHS + diff --git a/src/bdt/Makefile.Unix b/src/bdt/Makefile.Unix new file mode 100644 index 000000000..17fc18423 --- /dev/null +++ b/src/bdt/Makefile.Unix @@ -0,0 +1,10 @@ +EPICS = ../../../.. +include Target.include +include $(EPICS)/config/CONFIG_BASE + + +LIBOBJS += bdt.o + +LIBNAME = libBdt.a + +include $(EPICS)/config/RULES.Unix diff --git a/src/bdt/Makefile.Vx b/src/bdt/Makefile.Vx new file mode 100644 index 000000000..4257793d1 --- /dev/null +++ b/src/bdt/Makefile.Vx @@ -0,0 +1,21 @@ +EPICS = ../../../.. +include Target.include +include $(EPICS)/config/CONFIG_BASE + +SRCS.c += ../bdt.c +SRCS.c += ../bdtServ.c +SRCS.c += ../bdtServName.c +SRCS.c += ../bdtServPv.c + +OBJS += bdt.o +OBJS += bdtServ.o +OBJS += bdtServName.o +OBJS += bdtServPv.o + +PROD = bdt + +include $(EPICS)/config/RULES.Vx + +$(PROD): $(OBJS) + $(RM) $@ + $(LINK.c) $@ $(OBJS) $(LDLIBS) diff --git a/src/bdt/bdt.c b/src/bdt/bdt.c new file mode 100644 index 000000000..b53366f31 --- /dev/null +++ b/src/bdt/bdt.c @@ -0,0 +1,701 @@ +#include +#include +#include +#include +#ifdef linux +#include +#endif +#include +#include + +#ifdef vxWorks +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include "bdt.h" + +/* ---------------------------------------------------------------------- */ +/* server mode functions */ + +#ifndef vxWorks /* server mode functions */ +static char* filename=(char*)NULL; + +/* ----------------------------- */ +/* signal catcher for the server */ +/* ----------------------------- */ +static void catch_sig(int sig) +{ + fprintf(stderr,"\nbdt server exiting\n"); + unlink(filename); + exit(0); +} + +/* -------------------------------- */ +/* child reaper for the server mode */ +/* -------------------------------- */ +static void get_child(int sig) +{ + while(wait3((int *)NULL,WNOHANG,(struct rusage *)NULL)>0); +#ifdef linux + signal(SIGCHLD,get_child); /* for reaping children */ +#endif +} + +/* ------------------------------- */ +/* Clear the signals for a process */ +/* ------------------------------- */ +int BdtServerClearSignals() +{ + signal(SIGCHLD,SIG_DFL); + signal(SIGHUP,SIG_DFL); + signal(SIGINT,SIG_DFL); + signal(SIGTERM,SIG_DFL); + signal(SIGQUIT,SIG_DFL); + return 0; +} + +/* ----------------------------------------------------- */ +/* Make a unix process into a generic background process */ +/* ----------------------------------------------------- */ +int BdtMakeServer(char** argv) +{ + FILE* fd; + + if(filename) return -1; + + /* set up signal handling for the server */ + signal(SIGCHLD,get_child); /* for reaping children */ + signal(SIGHUP,catch_sig); + signal(SIGINT,catch_sig); + signal(SIGTERM,catch_sig); + signal(SIGQUIT,catch_sig); + + /* disconnect from parent */ + switch(fork()) + { + case -1: /* error */ + perror("Cannot fork"); + return -1; + case 0: /* child */ +#ifdef linux + setpgrp(); +#else + setpgrp(0,0); +#endif + setsid(); + break; + default: /* parent goes away */ + exit(0); + } + + /* save process ID */ + filename=(char*)malloc(strlen(argv[0])+10); + sprintf(filename,"%s.%d",argv[0],getpid()); + fd=fopen(filename,"w"); + fprintf(fd,"%d",getpid()); + fprintf(stderr,"\npv server pid: %d\n",getpid()); + fclose(fd); + + return 0; +} +#endif /* server mode functions */ + +/* ------------------------------------------ */ +/* unimplemented channel access open function */ +/* ------------------------------------------ */ +BDT* BdtPvOpen(char *IocName, char* PvName) +{ + BDT *bdt; + + if((bdt = BdtIpOpen(IocName, BDT_TCP_PORT)) == NULL) + { + fprintf(stderr,"open of address %s failed\n", IocName); + return(NULL); + } + + if(BdtServiceConnect(bdt, BDT_SERVICE_PV, PvName) < 0) + { + fprintf(stderr,"connect to PV %s failed\n", PvName); + BdtClose(bdt); + return(NULL); + } + return(bdt); +} + +/* --------------------------------------------------------------- */ +/* open a bulk data socket to a server given the server IP address */ +/* --------------------------------------------------------------- */ +BDT* BdtIpOpen(char* address, int Port) +{ + struct hostent *pHostent; + struct sockaddr_in tsin; + unsigned long addr; + int osoc; + BDT *bdt; + +#ifndef vxWorks + /* Deal with the name -vs- IP number issue. */ + if (isdigit(address[0])) +#endif + addr=inet_addr(address); +#ifndef vxWorks + else + { + if ((pHostent = gethostbyname (address)) == NULL) + return(NULL); + bcopy (pHostent->h_addr, (char *) &addr, sizeof(addr)); + printf("Converting name >%s< to IP number %08.8X\n", address, addr); + } +#endif + + tsin.sin_port=0; + tsin.sin_family=AF_INET; + tsin.sin_addr.s_addr=htonl(INADDR_ANY); + + if((osoc=socket(AF_INET,SOCK_STREAM,BDT_TCP))<0) + { + perror("BdtIpOpen: create socket failed"); + return (BDT*)NULL; + } + + if((bind(osoc,(struct sockaddr*)&tsin,sizeof(tsin)))<0) + { + perror("BdtIpOpen: local address bind failed"); + return (BDT*)NULL; + } + + tsin.sin_port=htons(Port); + memcpy((char*)&tsin.sin_addr,(char*)&addr,sizeof(addr)); + + if(connect(osoc,(struct sockaddr*)&tsin,sizeof(tsin))<0) + { + perror("BdtIpOpen: connect failed"); + close(osoc); + return (BDT*)NULL; + } + + bdt=(BDT*)malloc(sizeof(BDT)); + bdt->soc=osoc; + bdt->remaining_send=0; + bdt->remaining_recv=0; + bdt->state=BdtUnbound; + +#ifndef vxWorks + { + int j; + j = fcntl(bdt->soc, F_GETFL, 0); + fcntl(bdt->soc, F_SETFL, j|O_NDELAY); + } +#endif + /* now connected to the bulk data socket on the IOC */ + return bdt; +} + +/* -------------------------------------- */ +/* write size bytes from buffer to socket */ +/* -------------------------------------- */ +int BdtWrite(int soc,void* buffer,int size) +{ + int rc; + int total; + unsigned char* data; + fd_set fds; + struct timeval to; + + data=(unsigned char*)buffer; + total=size; + + to.tv_sec = 5; + to.tv_usec = 0; + + do + { + FD_ZERO(&fds); + FD_SET(soc, &fds); + if (select(soc+1, NULL, &fds, NULL, &to) != 1) + { + printf("BdtWrite: timeout waiting to write data\n"); + return(-1); + } + /* send block of data */ + if((rc=send(soc,&data[size-total],total,0))<0) + { + if(errno == EINTR) + rc = 0; + else + perror("BdtWrite: Send to remote failed"); + } + else + total-=rc; + } + while(rc>0 && total>0); + + return (rc<=0)?-1:0; +} + +/* --------------------------------------- */ +/* send a message header down a BDT socket */ +/* --------------------------------------- */ +int BdtSendHeader(BDT* bdt,unsigned short verb,int size) +{ + BdtMsgHead buf; + + if(bdt->state!=BdtIdle) + { + fprintf(stderr,"BdtSendHeader: Interface not idle\n"); + bdt->state=BdtBad; + return -1; + } + + buf.verb=htons(verb); + buf.size=htonl((unsigned long)size); + + if(BdtWrite(bdt->soc,&buf.verb, sizeof(buf.verb))<0) + { + fprintf(stderr,"BdtSendHeader: write to remote failed"); + return -1; + } + if(BdtWrite(bdt->soc,&buf.size, sizeof(buf.size))<0) + { + fprintf(stderr,"BdtSendHeader: write to remote failed"); + return -1; + } + + /* don't wait for response if data must go out */ + if(size) + { + bdt->remaining_send=size; + bdt->state=BdtSData; + } + + return 0; +} + +/* ------------------------------------------- */ +/* send a message data chunk down a BDT socket */ +/* ------------------------------------------- */ +int BdtSendData(BDT* bdt,void* buffer,int size) +{ + int len; + int remaining; + int rc; + + if(bdt->state!=BdtSData) + { + fprintf(stderr,"BdtSendData: Interface not in send data mode\n"); + bdt->state=BdtBad; + return -1; + } + + remaining=bdt->remaining_send-size; + + if(remaining<0) + { + fprintf(stderr,"WARNING -- BdtSendData: To much data to send\n"); + len=bdt->remaining_send; + } + else + len=size; + + if (BdtWrite(bdt->soc, buffer, len) < 0) + return -1; + + bdt->remaining_send-=len; + + if(bdt->remaining_send<0) + { + fprintf(stderr,"BdtSendData: To much data Sent\n"); + bdt->remaining_send=0; + } + + if(bdt->remaining_send==0) + bdt->state=BdtIdle; + + return len; +} + +int BdtFlushOutput(BDT* bdt) +{ +#ifdef vxWorks + ioctl(bdt->soc, FIOWFLUSH, 0); +#endif +} + +/* ------------------------------------- */ +/* Read exactly size bytes from remote */ +/* ------------------------------------- */ +int BdtRead(int soc,void* buffer,int size) +{ + int rc,total; + unsigned char* data; + fd_set fds; + struct timeval to; + + to.tv_sec = 5; + to.tv_usec = 0; + + data=(unsigned char*)buffer; + total=size; + + do + { +#if 1 + /* wait for data chunk */ + FD_ZERO(&fds); + FD_SET(soc, &fds); + if (select(soc+1, &fds, NULL, NULL, &to) != 1) + { + printf("BdtRead: timeout waiting for data\n"); + return(-1); + } +#endif + if((rc=recv(soc,&data[size-total],total,0))<0) + { + if(errno==EINTR) + { + printf("BdtRead: EINTR"); + rc=0; + } + else + { + perror("BdtRead: Receive data chunk failed"); + } + } + else + total-=rc; + } + while(rc>0 && total>0); + + return (rc<=0)?-1:0; +} + +/* ------------------------------------- */ +/* wait for a message header from remote */ +/* ------------------------------------- */ +int BdtReceiveHeader(BDT* bdt,int* verb,int* size) +{ + BdtMsgHead buf; + + /* can only receive header when in the idle state */ + if (bdt->state == BdtEof) + return -1; + + if(bdt->state != BdtIdle) + { + fprintf(stderr,"BdtReceiveHeader: Interface not idle\n"); + bdt->state=BdtBad; + return -1; + } + + if(BdtRead(bdt->soc,&buf.verb,sizeof(buf.verb))<0) + { + fprintf(stderr,"BdtReceiveHeader: Read failed\n"); + return -1; + } + if(BdtRead(bdt->soc,&buf.size,sizeof(buf.size))<0) + { + fprintf(stderr,"BdtReceiveHeader: Read failed\n"); + return -1; + } + + /* copy message data to user */ + *verb=ntohs(buf.verb); + *size=ntohl(buf.size); + + if(*size) + bdt->state=BdtRData; + + bdt->remaining_recv=*size; + + return 0; +} + +/* ------------------------------------------------------------------------ + Wait for a chunk of data from remote. + User can continually call this with a maximum size until it return 0. + ------------------------------------------------------------------------ */ +int BdtReceiveData(BDT* bdt,void* buffer,int size) +{ + int rc; + + /* can only receive data when in the receive data state */ + switch(bdt->state) + { + case BdtRData: break; + case BdtIdle: return 0; + default: + fprintf(stderr,"BdtReceiveData: bad receive state\n"); + bdt->state=BdtBad; + break; + } + + if (bdt->remaining_recv < size) + size = bdt->remaining_recv; + + if(BdtRead(bdt->soc,buffer,size)<0) + { + fprintf(stderr,"BdtReceiveData: Read failed\n"); + bdt->state = BdtEof; + return -1; + } + + bdt->remaining_recv-=size; + + if(bdt->remaining_recv<0) + { + fprintf(stderr,"BdtReceiveData: To much data received\n"); + bdt->remaining_recv=0; + } + + if(bdt->remaining_recv==0) + bdt->state=BdtIdle; + + return size; +} + +/* ------------------------------------------------------ */ +/* connect to a process variable, useful if raw open used */ +/* ------------------------------------------------------ */ +int BdtServiceConnect(BDT* bdt, char* Name, char *Args) +{ + int len; + int rc; + int size; + int verb; + unsigned char NameLen; + unsigned char ArgLen; + + if(bdt->state!=BdtUnbound) + { + fprintf(stderr,"BdtServiceConnect: can only bind to one service\n"); + return -1; + } + bdt->state=BdtIdle; + NameLen = strlen(Name)+1; + if (Args != NULL) + ArgLen = strlen(Args)+1; + else + ArgLen = 0; + + /* send out connect message */ + if(BdtSendHeader(bdt, BDT_Connect, NameLen+ArgLen) < 0) + { + fprintf(stderr,"BdtServiceConnect: send of connect header failed\n"); + bdt->state=BdtUnbound; + return -1; + } + + NameLen--; + ArgLen--; + /* send out the process variable to connect to */ + if((BdtSendData(bdt, &NameLen, 1) < 0) || (BdtSendData(bdt, Name, NameLen) < 0)) + + { + fprintf(stderr,"BdtServiceConnect: send of connect body failed\n"); + bdt->state=BdtUnbound; + return -1; + } + if (ArgLen > 0) + { + if ((BdtSendData(bdt, &ArgLen, 1) < 0) || (BdtSendData(bdt, Args, ArgLen) < 0)) + { + fprintf(stderr,"BdtServiceConnect: send of connect body failed\n"); + bdt->state=BdtUnbound; + return -1; + } + } + rc=0; + + /* wait for response from connect to process variable */ + if(BdtReceiveHeader(bdt,&verb,&size)<0) + { + fprintf(stderr,"BdtServiceConnect: receive reponse failed\n"); + bdt->state=BdtUnbound; + return -1; + } + + /* check response */ + switch(verb) + { + case BDT_Ok: + rc=0; + break; + case BDT_Error: + fprintf(stderr,"BdtServiceConnect: connection rejected\n"); + bdt->state=BdtUnbound; + rc=-1; + break; + default: + fprintf(stderr,"BdtServiceConnect: unknown response from remote\n"); + bdt->state=BdtUnbound; + rc=-1; + break; + } + + return rc; +} + +/* -------------------- */ +/* close the connection */ +/* -------------------- */ +int BdtClose(BDT* bdt) +{ + int verb,size,done; + + /* send a close message out */ + if(BdtSendHeader(bdt,BDT_Close,0)<0) + { + fprintf(stderr,"BdtClose: Cannot send close message\n"); + return -1; + } + + done=0; + + do + { + /* check response */ + if(BdtReceiveHeader(bdt,&verb,&size)<0) + { + fprintf(stderr,"BdtClose: Close message response error\n"); + return -1; + } + + switch(verb) + { + case BDT_Ok: + done=1; + break; + case BDT_Error: + fprintf(stderr,"BdtClose: Close rejected\n"); + return -1; + break; + default: break; + } + } + while(done==0); + + bdt->state=BdtUnbound; + free(bdt); + return 0; +} + +/* --------------------------------------- */ +/* make a listener socket for UDP - simple */ +/* --------------------------------------- */ +int BdtOpenListenerUDP(int Port) +{ + int nsoc; + struct sockaddr_in tsin; + + tsin.sin_port=htons(Port); + tsin.sin_family=AF_INET; + tsin.sin_addr.s_addr=htonl(INADDR_ANY); + + if((nsoc=socket(AF_INET,SOCK_DGRAM,BDT_UDP))<0) + { + perror("BdtOpenListenerUDP: open socket failed"); + return -1; + } + + if((bind(nsoc,(struct sockaddr*)&tsin,sizeof(tsin)))<0) + { + perror("BdtOpenListenerUDP: local bind failed"); + close(nsoc); + return -1; + } + + return nsoc; +} + +/* --------------------------------------- */ +/* make a listener socket for TCP - simple */ +/* --------------------------------------- */ +int BdtOpenListenerTCP(int Port) +{ + int nsoc; + struct sockaddr_in tsin; + + memset (&tsin, 0, sizeof(struct sockaddr_in)); + tsin.sin_port=htons(Port); + tsin.sin_family=htons(AF_INET); + tsin.sin_addr.s_addr=htonl(INADDR_ANY); + + if((nsoc=socket(AF_INET,SOCK_STREAM,BDT_TCP))<0) + { + perror("BdtOpenListenerTCP: open socket failed"); + return -1; + } + + if((bind(nsoc,(struct sockaddr*)&tsin,sizeof(tsin)))<0) + { + perror("BdtOpenListenerTCP: local bind failed"); + close(nsoc); + return -1; + } + + listen(nsoc,5); + return nsoc; +} + +/* ------------------------------- */ +/* make BDT from a socket - simple */ +/* ------------------------------- */ +BDT* BdtMakeBDT(int soc) +{ + BDT* bdt; + bdt=(BDT*)malloc(sizeof(BDT)); + bdt->soc=soc; + bdt->remaining_send=0; + bdt->remaining_recv=0; + bdt->state=BdtIdle; + return bdt; +} + +/* --------------------------- */ +/* free a BDT and close socket */ +/* --------------------------- */ +int BdtFreeBDT(BDT* bdt) +{ + close(bdt->soc); + free(bdt); + return 0; +} + +int BdtPvPutArray(BDT *bdt, short DbrType, void *Buf, unsigned long NumElements, + unsigned long ElementSize) +{ + int Verb; + int Size; + unsigned long BufSize; + + BufSize = NumElements * ElementSize; + if (BdtSendHeader(bdt, BDT_Put, 6 + BufSize) != 0) + return(-1); + if (BdtSendData(bdt, &DbrType, 2) < 0) + return(-1); + if (BdtSendData(bdt, &NumElements, 4) < 0) + return(-1); + if (BdtSendData(bdt, Buf, BufSize) < 0) + return(-1); + if (BdtReceiveHeader(bdt, &Verb, &Size) != 0) + return(-1); + if (Verb != BDT_Ok) + return(-1); + + return(0); +} diff --git a/src/bdt/bdtServ.c b/src/bdt/bdtServ.c new file mode 100644 index 000000000..4a2072092 --- /dev/null +++ b/src/bdt/bdtServ.c @@ -0,0 +1,392 @@ +/* +* +* Author: John Winans +* Date: 95-05-22 +* +* $Log$ +*/ + +/***************************************************************************** +* +* IOC listener task blocks on accept calls waiting for binders. +* If a bind arrives, a receiver task is spawned. +* +* IOC receiver task blocks on read calls waiting for transactions. +* When a transaction arrives it is serviced. +* At the end of a transaction service, a response is sent back. +* After the response is sent, a chack is made to see if a delta transmission +* was blocked by the transaction's use of the socket... if so, it is sent. +* +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bdt.h" + +#define BDT_TASK_PRIO 200 +#define BDT_TASK_OPTIONS VX_FP_TASK +#define BDT_TASK_STACK 5000 +#define STATIC static + +/* used for debugging */ +STATIC char *BdtNames[] = { + "BDT_Ok", + "BDT_Connect", + "BDT_Error", + "BDT_Get", + "BDT_Put", + "BDT_Close", + "BDT_Monitor", + "BDT_Value", + "BDT_Delta", + "BDT_Add", + "BDT_Delete", + "BDT_Ping" +}; + +STATIC int HexDump(char *ReadBuffer, int rbytes); + +/***************************************************************************** +* +* A debugging routine that hex-dumps a message to the console. +* +*****************************************************************************/ +void BDT_DumpMessage(BDT *Bdt) +{ + char Buf[16*4]; + int RecvLen; + + while(Bdt->remaining_recv) + { + RecvLen = (Bdt->remaining_recv > sizeof(Buf)) ? sizeof(Buf): Bdt->remaining_recv; + if (BdtReceiveData(Bdt, Buf, RecvLen) != RecvLen) + return; /* Got EOF, (EOM handled by the while() */ + + HexDump(Buf, RecvLen); + } +} +/***************************************************************************** +* +* Throw away a message. +* +****************************************************************************/ +void BDT_DiscardMessage(BDT *Bdt) +{ + char Buf[16*4]; + int RecvLen; + + while(Bdt->remaining_recv) + { + RecvLen = (Bdt->remaining_recv > sizeof(Buf)) ? sizeof(Buf): Bdt->remaining_recv; + if (BdtReceiveData(Bdt, Buf, RecvLen) != RecvLen) + return; /* Got EOF, (EOM handled by the while() */ + } +} + +/***************************************************************************** +* +* Process a single Connect message. And return a response. +* +******************************************************************************/ +STATIC int BDT_ProcessConnect(BDT *Bdt) +{ + SYM_TYPE Type; + unsigned char length; + char Buf[50]; + char HandlerName[70]; + + if (Bdt->remaining_recv > sizeof(Buf)) + { + printf("BDT_ProcessConnect Connect Message too long %d\n", Bdt->remaining_recv); + BDT_DiscardMessage(Bdt); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + if (Bdt->remaining_recv < 2) + { + printf("BDT_ProcessConnect Connect Message w/missing service name\n"); + BDT_DiscardMessage(Bdt); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + BdtReceiveData(Bdt, &length, 1); + if (length > sizeof(Buf)) + { + printf("BDT_ProcessConnect Connect Message service name too long %d\n", length); + BDT_DiscardMessage(Bdt); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + BdtReceiveData(Bdt, Buf, length); + Buf[length] = '\0'; + + sprintf(HandlerName, "_BDT_ServiceHandler_%s", Buf); + printf("BDT_ProcessConnect NAME service (%s)\n", HandlerName); + + /*Bdt->pHandlers = (BdthandlerFunc *)(&BDT_NameServicehandlers);*/ + if (symFindByName(sysSymTbl, HandlerName, (char **)&(Bdt->pHandlers), &Type) != OK) + { + printf("BDT_ProcessConnect Connect to unknown service (%s)\n", Buf); + BdtSendHeader(Bdt, BDT_Error, 0); + } + else + { + Bdt->Name = (char *)malloc(strlen(Buf)+1); + strcpy(Bdt->Name, Buf); + if (Bdt->pHandlers[BDT_Connect] != NULL) + return((*(Bdt->pHandlers[BDT_Connect]))(Bdt)); + else + { + BDT_DiscardMessage(Bdt); + BdtSendHeader(Bdt, BDT_Ok, 0); + } + } + return(0); +} +/***************************************************************************** +* +* Process a single message. And return a response. +* +******************************************************************************/ +STATIC int BDT_ProcMessage(BDT *Bdt, unsigned short Command) +{ + int RecvLen; + + if (Command > BDT_LAST_VERB) + { + printf("BDT: %s Invalid command %d, length = %d\n", Bdt->Name, Command, Bdt->remaining_recv); + BDT_DumpMessage(Bdt); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + + if (Bdt->pHandlers == NULL) + { + if (Command == BDT_Connect) + BDT_ProcessConnect(Bdt); + else + { + printf("BDT_ProcMessage: %s got %s before connect\n", Bdt->Name, BdtNames[Command]); + BDT_DiscardMessage(Bdt); + BdtSendHeader(Bdt, BDT_Error, 0); + } + return(0); + } + if (Bdt->pHandlers[Command] == NULL) + { + printf("BDT_ProcMessage: service %s got %s... invalid\n", Bdt->Name, BdtNames[Command]); + } + return((*(Bdt->pHandlers[Command]))(Bdt)); +} + +/***************************************************************************** +* +* Wait on a socket read for a message. When one arrives, read the header, +* decode it, and call the message handler routine to process and respond to it. +* +******************************************************************************/ +STATIC void BDT_ReceiverTask(int Sock) +{ + int Verb; + int Size; + BDT Bdt; + int MonitorLockTimeout = (BDT_PING_INTERVAL*sysClkRateGet())/2; + static char *NoBdtName = "(No Name)"; + fd_set FdSet; + struct timeval TimeVal; + int PollStatus; + int SocketState; + + Bdt.soc = Sock; + Bdt.pMonitor = NULL; + Bdt.WriteLock = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + Bdt.MonitorLock = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + Bdt.state = BdtIdle; + Bdt.pHandlers = NULL; + Bdt.Name = NoBdtName; + + printf("BDT_ReceiverTask(%d) started\n", Sock); + + TimeVal.tv_sec = BDT_CONENCTION_TIMEOUT; + TimeVal.tv_usec = 0; + FD_ZERO(&FdSet); + FD_SET(Bdt.soc, &FdSet); + + SocketState = 0; + while (((PollStatus = select(FD_SETSIZE, &FdSet, NULL, NULL, &TimeVal)) > 0) && (BdtReceiveHeader(&Bdt, &Verb, &Size) == 0)) + { + semTake(Bdt.WriteLock, WAIT_FOREVER); + + SocketState = BDT_ProcMessage(&Bdt, Verb); + + if (SocketState != 0) + break; + +#if 0 + if (semTake(Bdt.MonitorLock, MonitorLockTimeout) == OK) + { + /* Check for delta flag and send if so */ + + /* Change this to run thru a delta-message linked list */ + if (Bdt.pMonitor != NULL) + { + /* Send delta notifier */ + } + semGive(Bdt.WriteLock); /* Order important for BDT_SendDelta */ + semGive(Bdt.MonitorLock); + } + else + { + printf("BDT_ReceiverTask timeout on monitor semaphore. Monitors are stuck!\n"); + semGive(Bdt.WriteLock); + } +#else + semGive(Bdt.WriteLock); +#endif + BdtFlushOutput(&Bdt); + + FD_ZERO(&FdSet); + FD_SET(Bdt.soc, &FdSet); + } + if (SocketState == 0) + { + if (PollStatus == 0) + printf("BDT_ReceiverTask(%d) exiting on client timeout\n", Sock); + else + printf("BDT_ReceiverTask(%d) exiting on I/O error talking to Client\n", Sock); + } + else + printf("BDT_ReceiverTask(%d) received close from client\n", Sock); + + /* Free up resources */ + if (Bdt.Name != NoBdtName) + free(Bdt.Name); + + close(Sock); + semDelete(Bdt.WriteLock); + semDelete(Bdt.MonitorLock); + return; +} + +/***************************************************************************** +* +******************************************************************************/ +#if 0 +int BDT_SendDelta(int Socket, char *Message) +{ + semTake (DeltaFlagLock, WAIT_FOREVER); + if (if (semTake(SocketWriteLock, no wait) == failed) + { + /* Reader task is busy... Post message for future transmission */ + Bdt.pending_delta = 1; + } + else + { + write(Message); /* This COULD block */ + semGive(SocketWriteLock); + } + semGive(DeltaFlagLock); + return (0); +} +#endif + +/***************************************************************************** +* +* This task listens on a port for new connections. When one is made, it +* spawns a task to manage it. +* +******************************************************************************/ +void BDT_ListenerTask(int Port) +{ + /* Open a socket to listen on */ + struct sockaddr_in ListenerAddr; + struct sockaddr_in ClientAddr; + int ListenerSock; + int ClientSock; + int ClientAddrLen; + int SockAddrSize = sizeof(struct sockaddr_in); + + if (Port == 0) + Port = BDT_TCP_PORT; + + printf("BDT_Listener(%d) started\n", Port); + + if ((ListenerSock = BdtOpenListenerTCP(Port)) < 0) + { + printf("BDT_ListenerTask(%d) can't start listener\n", Port); + return; + } + + while (1) + { + ClientAddrLen = sizeof(ClientAddr); + if((ClientSock = accept(ListenerSock, (struct sockaddr*)&ClientAddr, &ClientAddrLen)) < 0) + { + if(errno!=EINTR) + { + printf("BDT_ListenerTask(%d) accept() failed\n", Port); + } + } + else + { + /* Spawn a task to handle the new connection */ + printf("Accepted a connection\n"); + taskSpawn("BDT", BDT_TASK_PRIO, BDT_TASK_OPTIONS, BDT_TASK_STACK, (FUNCPTR)BDT_ReceiverTask, ClientSock, 2,3,4,5,6,7,8,9,0); + } + } + /* Never reached */ +} + +/***************************************************************************** +* +* A handy routine to assist in debugging. +* +******************************************************************************/ +STATIC int HexDump(char *ReadBuffer, int rbytes) +{ + int c = 0; + int i = 0; + int firsttime; + char ascii[20]; /* To hold printable portion of string */ + + if (!rbytes) + return(0); + + firsttime = 1; + while(c < rbytes) + { + if ((c % 16) == 0) + { + if (!firsttime) + { + ascii[i] = '\0'; + printf(" *%s*\n", ascii); + } + firsttime=0; + i = 0; + } + printf(" %02.2X", ReadBuffer[c] & 0xff); + ascii[i] = ReadBuffer[c]; + if (!isprint(ascii[i])) + ascii[i] = '.'; + ++i; + ++c; + } + while (c%16) + { + fputs(" ", stdout); + ++c; + } + ascii[i] = '\0'; + printf(" *%s*\n", ascii); + return(0); +} diff --git a/src/bdt/bdtServName.c b/src/bdt/bdtServName.c new file mode 100644 index 000000000..4b2d60f57 --- /dev/null +++ b/src/bdt/bdtServName.c @@ -0,0 +1,129 @@ +/***************************************************************************** +* +* Author: John Winans +* Date: 95-06-05 +* +* $Id$ +* +* $Log$ +* +*****************************************************************************/ + +#include +#include +#include + +/***************************************************************************** +* +* IOC listener task blocks on accept calls waiting for binders. +* If a bind arrives, a receiver task is spawned. +* +* IOC receiver task blocks on read calls waiting for transactions. +* When a transaction arrives it is serviced. +* At the end of a transaction service, a response is sent back. +* After the response is sent, a chack is made to see if a delta transmission +* was blocked by the transaction's use of the socket... if so, it is sent. +* +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bdt.h" + +#define STATIC static + +/***************************************************************************** +* +*****************************************************************************/ +STATIC int BDT_NameServiceOk(BDT *Bdt) +{ + printf("BDT_NameServiceOk \n"); + return(0); +} +/***************************************************************************** +* +*****************************************************************************/ +STATIC int BDT_NameServiceConnect(BDT *Bdt) +{ + printf("BDT_NameServiceConnect \n"); + BdtSendHeader(Bdt, BDT_Ok, 0); + return(0); +} +STATIC int BDT_NameServiceError(BDT *Bdt) +{ + printf("BDT_NameServiceError \n"); + return(0); +} +STATIC int BDT_NameServiceGet(BDT *Bdt) +{ + printf("BDT_NameServiceGet \n"); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +STATIC int BDT_NameServicePut(BDT *Bdt) +{ + printf("BDT_NameServicePut \n"); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +STATIC int BDT_NameServiceClose(BDT *Bdt) +{ + printf("BDT_NameServiceClose \n"); + BdtSendHeader(Bdt, BDT_Ok, 0); + return(0); +} +STATIC int BDT_NameServiceMonitor(BDT *Bdt) +{ + printf("BDT_NameServiceMonitor \n"); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +STATIC int BDT_NameServiceValue(BDT *Bdt) +{ + printf("BDT_NameServiceValue \n"); + return(0); +} +STATIC int BDT_NameServiceDelta(BDT *Bdt) +{ + printf("BDT_NameServiceDelta \n"); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +STATIC int BDT_NameServiceAdd(BDT *Bdt) +{ + printf("BDT_NameServiceAdd \n"); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +STATIC int BDT_NameServiceDelete(BDT *Bdt) +{ + printf("BDT_NameServiceDelete \n"); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +STATIC int BDT_NameServicePing(BDT *Bdt) +{ + printf("BDT_NameServicePing \n"); + BdtSendHeader(Bdt, BDT_Ok, 0); + return(0); +} + +BdtHandlers BDT_ServiceHandler_name = +{ + BDT_NameServiceOk, + BDT_NameServiceConnect, + BDT_NameServiceError, + BDT_NameServiceGet, + BDT_NameServicePut, + BDT_NameServiceClose, + BDT_NameServiceMonitor, + BDT_NameServiceValue, + BDT_NameServiceDelta, + BDT_NameServiceAdd, + BDT_NameServiceDelete, + BDT_NameServicePing +}; diff --git a/src/bdt/bdtServPv.c b/src/bdt/bdtServPv.c new file mode 100644 index 000000000..47ce03b9a --- /dev/null +++ b/src/bdt/bdtServPv.c @@ -0,0 +1,377 @@ +/***************************************************************************** +* +* Author: John Winans +* Date: 95-06-05 +* +* $Id$ +* +* $Log$ +* +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bdt.h" + +#define STATIC static +#define MESSAGE_PREFIX "BDT PV server:" + +/***************************************************************************** +* +* These conversion finctions take care of one of the most insane parts +* of dealing with database access... having two different interfaces that +* have the same named enumerators in two seperate header files... that +* therefore can not be both included in the same file. +* +* This is so bad, I wanted to vomit when typing it in. +* +******************************************************************************/ +STATIC int DbrOld2New(int Old) +{ + switch (Old) + { + case 0: return(DBR_STRING); + case 1: return(DBR_SHORT); + case 2: return(DBR_FLOAT); + case 3: return(DBR_ENUM); + case 4: return(DBR_CHAR); + case 5: return(DBR_LONG); + case 6: return(DBR_DOUBLE); + default: + return(-1); + } +} + +STATIC int DbrNew2Old(int New) +{ + switch (New) + { + case DBR_STRING: return(0); + case DBR_CHAR: return(4); + case DBR_UCHAR: return(4); + case DBR_SHORT: return(1); + case DBR_USHORT: return(1); + case DBR_LONG: return(5); + case DBR_ULONG: return(5); + case DBR_FLOAT: return(2); + case DBR_DOUBLE: return(6); + case DBR_ENUM: return(3); + default: + return(-1); + } +} +/***************************************************************************** +* +* Handle the receipt of an OK message. +* +* The OK message is received as a confirmation of the last operation. It is +* not normally responded to. +* +*****************************************************************************/ +STATIC int BDT_ServiceOk(BDT *Bdt) +{ + printf("%s got a Ok message\n", MESSAGE_PREFIX); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Connect message. +* +* The Connect is received when a new connection is first made. +* Any arguments left in the message body have not yet been read. +* +*****************************************************************************/ +STATIC int BDT_ServiceConnect(BDT *Bdt) +{ + unsigned char Length; + char Buf[100]; + struct dbAddr *pDbAddr; + + Buf[0] = '\0'; + if (Bdt->remaining_recv > 0) + { + BdtReceiveData(Bdt, &Length, 1); + if (Length <= sizeof(Buf)) + { + BdtReceiveData(Bdt, Buf, Length); + Buf[Length] = '\0'; + } + else + { + printf("%s Connect message argument list too long\n", MESSAGE_PREFIX); + BDT_DiscardMessage(Bdt); + return(-1); + } + + } +#ifdef DEBUG_VERBOSE + printf("%s got Connect >%s<\n", MESSAGE_PREFIX, Buf); +#endif + /* Find the PV in the database */ + Bdt->pService = malloc(sizeof(struct dbAddr)); + pDbAddr = (struct dbAddr *)(Bdt->pService); + if (dbNameToAddr(Buf, pDbAddr)) + { + BdtSendHeader(Bdt, BDT_Error, 0); + free(Bdt->pService); + } + else + { + if (pDbAddr->dbr_field_type != pDbAddr->field_type) + { + BdtSendHeader(Bdt, BDT_Error, 0); + free(Bdt->pService); + } + else + BdtSendHeader(Bdt, BDT_Ok, 0); + } + return(0); +} +/***************************************************************************** +* +* Handle the receipt of an Error message. +* +*****************************************************************************/ +STATIC int BDT_ServiceError(BDT *Bdt) +{ + printf("%s got a Error message\n", MESSAGE_PREFIX); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Get message. +* +* The response to a Get message is either an Error or a Value: +* +* Value message body format: +* SHORT EPICS data type enumerator (in old format) +* LONG Number of elements +* CHAR[] Value image +* +* +*****************************************************************************/ +STATIC int BDT_ServiceGet(BDT *Bdt) +{ + void *Buf; + struct dbAddr *pDbAddr = (struct dbAddr *)(Bdt->pService); + long NumElements; + long Size; + long l; + short OldType; + int stat; + +#ifdef DEBUG_VERBOSE + printf("%s got a Get message\n", MESSAGE_PREFIX); + + printf("field type=%d, field size=%d, elements=%d\n", pDbAddr->field_type, pDbAddr->field_size, pDbAddr->no_elements); +#endif + + OldType = DbrNew2Old(pDbAddr->field_type); + if (OldType < 0) + { + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + /* Allocate a buffer to hold the response message data */ + Buf = malloc(pDbAddr->field_size * pDbAddr->no_elements); + if (Buf == NULL) + { + printf("Can't allocate %d-byte buffer for get request to %s\n", + pDbAddr->field_size * pDbAddr->no_elements, + pDbAddr->precord->name); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + /* Get the response message data */ + NumElements = pDbAddr->no_elements; + if (stat=dbGetField(pDbAddr, pDbAddr->field_type, Buf, 0, &NumElements, NULL)) + { + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } +#if 0 + /* Test hack to transfer HUGE buffers */ + NumElements = pDbAddr->no_elements; +#endif + /* Send the response message */ + Size = NumElements * pDbAddr->field_size; + BdtSendHeader(Bdt, BDT_Value, Size + sizeof(long) + sizeof(short)); + BdtSendData(Bdt, &OldType, sizeof(short)); + BdtSendData(Bdt, &NumElements, sizeof(long)); + if (Size) + BdtSendData(Bdt, Buf, Size); + free(Buf); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Put message. +* +* Put message body format: +* SHORT EPICS data type enumerator +* LONG Number of elements +* CHAR[] Value image +* +*****************************************************************************/ +STATIC int BDT_ServicePut(BDT *Bdt) +{ + long Size; + void *Buf; + short DbrType; + long NumElements; + struct dbAddr *pDbAddr = (struct dbAddr *)(Bdt->pService); + +#ifdef DEBUG_VERBOSE + printf("%s got a Put message\n", MESSAGE_PREFIX); +#endif + if (BdtGetResidualRead(Bdt) < 6) + { + BDT_DiscardMessage(Bdt); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + if (BdtGetResidualRead(Bdt) == 6) + { /* Do data contents, just toss it */ + BDT_DiscardMessage(Bdt); + BdtSendHeader(Bdt, BDT_Ok, 0); + return(0); + } + Buf = malloc(BdtGetResidualRead(Bdt) - 6); + if (Buf == NULL) + { + printf("Can't allocate %d-byte buffer for put request to %s\n", + BdtGetResidualRead(Bdt), pDbAddr->precord->name); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); + } + BdtReceiveData(Bdt, &DbrType, 2); + BdtReceiveData(Bdt, &NumElements, 4); + +#ifdef DEBUG_VERBOSE + printf("record field type=%d, field size=%d, elements=%d\n", pDbAddr->field_type, pDbAddr->field_size, pDbAddr->no_elements); + printf("message field type=%d, field size=%d, elements=%d total %d\n", DbrType, pDbAddr->field_type ,NumElements, BdtGetResidualRead(Bdt)); +#endif + + BdtReceiveData(Bdt, Buf, BdtGetResidualRead(Bdt)); + DbrType = DbrOld2New(DbrType); + + if (DbrType < 0) + BdtSendHeader(Bdt, BDT_Error, 0); + else if (dbPutField(pDbAddr, DbrType, Buf, NumElements)) + BdtSendHeader(Bdt, BDT_Error, 0); + else + BdtSendHeader(Bdt, BDT_Ok, 0); + + free(Buf); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Close message. +* +*****************************************************************************/ +STATIC int BDT_ServiceClose(BDT *Bdt) +{ + printf("%s got a Close message\n", MESSAGE_PREFIX); + free(Bdt->pService); + BdtSendHeader(Bdt, BDT_Ok, 0); + return(1); +} +/***************************************************************************** +* +* Handle the receipt of a Monitor message. +* +* Not Supported. +* +*****************************************************************************/ +STATIC int BDT_ServiceMonitor(BDT *Bdt) +{ + printf("%s got a Monitor message\n", MESSAGE_PREFIX); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Value message. +* +* Not Supported. +* +*****************************************************************************/ +STATIC int BDT_ServiceValue(BDT *Bdt) +{ + printf("%s got a Value message\n", MESSAGE_PREFIX); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Delta message. +* +* Not Supported. +* +*****************************************************************************/ +STATIC int BDT_ServiceDelta(BDT *Bdt) +{ + printf("%s got a Delta message\n", MESSAGE_PREFIX); + BdtSendHeader(Bdt, BDT_Ok, 0); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of an Add message. +* +* Not Supported. +* +*****************************************************************************/ +STATIC int BDT_ServiceAdd(BDT *Bdt) +{ + printf("%s got a Add message\n", MESSAGE_PREFIX); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Delete message. +* +* Not Supported. +* +*****************************************************************************/ +STATIC int BDT_ServiceDelete(BDT *Bdt) +{ + printf("%s got a Delete message\n", MESSAGE_PREFIX); + BdtSendHeader(Bdt, BDT_Error, 0); + return(0); +} +/***************************************************************************** +* +* Handle the receipt of a Ping message. +* +*****************************************************************************/ +STATIC int BDT_ServicePing(BDT *Bdt) +{ + printf("%s got a Ping message\n", MESSAGE_PREFIX); + BdtSendHeader(Bdt, BDT_Ok, 0); + return(0); +} + +BdtHandlers BDT_ServiceHandler_pv = +{ + BDT_ServiceOk, + BDT_ServiceConnect, + BDT_ServiceError, + BDT_ServiceGet, + BDT_ServicePut, + BDT_ServiceClose, + BDT_ServiceMonitor, + BDT_ServiceValue, + BDT_ServiceDelta, + BDT_ServiceAdd, + BDT_ServiceDelete, + BDT_ServicePing +};