Files
epics-base/src/bdt/bdt.c
1995-07-27 14:23:02 +00:00

702 lines
14 KiB
C

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#ifdef linux
#include <sys/resource.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#ifdef vxWorks
#include <vxWorks.h>
#include <in.h>
#include <inetLib.h>
#include <taskLib.h>
#include <ioLib.h>
#include <selectLib.h>
#else
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#endif
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#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);
}