Files
epics-base/src/dbtools/BSlib.c
Janet B. Anderson 636c7ba56c Added SGI to ifdef
1995-11-27 15:47:06 +00:00

987 lines
20 KiB
C

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include "BSlib.h"
#ifdef linux
#include <sys/time.h>
#include <sys/resource.h>
#endif
#ifdef vxWorks
#include <vxWorks.h>
#include <in.h>
#include <ioctl.h>
#include <inetLib.h>
#include <taskLib.h>
#include <ioLib.h>
#include <sockLib.h>
#include <selectLib.h>
#else
#include <sys/sockio.h>
#include <sys/time.h>
#include <netdb.h>
#include <fcntl.h>
#endif
#ifdef SOLARIS
#define BSD_COMP
#include <sys/ioctl.h>
#endif
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <net/if.h>
static long BSgetBroadcastAddr(int soc, struct sockaddr* sin);
/* ---------------------------------------------------------------------- */
/* 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)
{
#ifdef SOLARIS
while(waitpid(-1,(int*)NULL,WNOHANG)>0);
#else
while(wait3((int *)NULL,WNOHANG,(struct rusage *)NULL)>0);
#endif
#if defined linux || defined SOLARIS
signal(SIGCHLD,get_child); /* for reaping children */
#endif
}
/* ------------------------------- */
/* Clear the signals for a process */
/* ------------------------------- */
int BSserverClearSignals()
{
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 BSmakeServer(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 */
#if defined linux || defined SOLARIS || defined SGI
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 */
/* --------------------------------------------------------------- */
/* open a bulk data socket to a server given the server IP address */
/* --------------------------------------------------------------- */
BS* BSipOpen(char* address, int Port)
{
struct hostent* pHostent;
unsigned long addr;
BSDATA info;
struct sockaddr_in* tsin;
tsin=(struct sockaddr_in*)&(info.sin);
#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;
memcpy((char*)&addr,pHostent->h_addr,sizeof(addr));
}
#endif
tsin->sin_port=htons(Port);
tsin->sin_family=AF_INET;
memcpy((char*)&(tsin->sin_addr),(char*)&addr,sizeof(addr));
return BSipOpenData(&info);
}
BS* BSipOpenData(BSDATA* info)
{
struct sockaddr_in tsin;
int osoc;
BS* bdt;
tsin.sin_port=0;
tsin.sin_family=AF_INET;
tsin.sin_addr.s_addr=htonl(INADDR_ANY);
if((osoc=socket(AF_INET,SOCK_STREAM,BS_TCP))<0)
{
perror("BSipOpen: create socket failed");
return (BS*)NULL;
}
if((bind(osoc,(struct sockaddr*)&tsin,sizeof(tsin)))<0)
{
perror("BSipOpen: local address bind failed");
return (BS*)NULL;
}
if(connect(osoc,(struct sockaddr*)&(info->sin),sizeof(info->sin))<0)
{
perror("BSipOpen: connect failed");
close(osoc);
return (BS*)NULL;
}
bdt=(BS*)malloc(sizeof(BS));
bdt->soc=osoc;
bdt->remaining_send=0;
bdt->remaining_recv=0;
bdt->state=BSidle;
#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 BSwrite(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(FD_SETSIZE,NULL,&fds,NULL,&to) != 1)
{
printf("BSwrite: timeout waiting to write data\n");
return -1;
}
/* send block of data */
if((rc=send(soc,(char*)&data[size-total],total,0))<0)
{
if(errno == EINTR)
rc = 0;
else
perror("BSwrite: Send to remote failed");
}
else
total-=rc;
}
while(rc>0 && total>0);
return (rc<=0)?-1:0;
}
/* --------------------------------------- */
/* send a message header down a BS socket */
/* --------------------------------------- */
int BSsendHeader(BS* bdt,unsigned short verb,int size)
{
BSmsgHead buf;
if(bdt->state!=BSidle)
{
fprintf(stderr,"BSsendHeader: Interface not idle\n");
bdt->state=BSbad;
return -1;
}
buf.verb=htons(verb);
buf.size=htonl((unsigned long)size);
if(BSwrite(bdt->soc,&buf.verb, sizeof(buf.verb))<0)
{
fprintf(stderr,"BSsendHeader: write to remote failed");
return -1;
}
if(BSwrite(bdt->soc,&buf.size, sizeof(buf.size))<0)
{
fprintf(stderr,"BSsendHeader: write to remote failed");
return -1;
}
/* don't wait for response if data must go out */
if(size)
{
bdt->remaining_send=size;
bdt->state=BSsData;
}
return 0;
}
/* ------------------------------------------- */
/* send a message data chunk down a BS socket */
/* ------------------------------------------- */
int BSsendData(BS* bdt,void* buffer,int size)
{
int len;
int remaining;
int rc;
if(bdt->state!=BSsData)
{
fprintf(stderr,"BSsendData: Interface not in send data mode\n");
bdt->state=BSbad;
return -1;
}
remaining=bdt->remaining_send-size;
if(remaining<0)
{
fprintf(stderr,"WARNING -- BSsendData: To much data to send\n");
len=bdt->remaining_send;
}
else
len=size;
if (BSwrite(bdt->soc, buffer, len) < 0)
return -1;
bdt->remaining_send-=len;
if(bdt->remaining_send<0)
{
fprintf(stderr,"BSsendData: To much data Sent\n");
bdt->remaining_send=0;
}
if(bdt->remaining_send==0)
bdt->state=BSidle;
return len;
}
int BSflushOutput(BS* bdt)
{
#ifdef vxWorks
ioctl(bdt->soc, FIOWFLUSH, 0);
#endif
return 0;
}
/* ------------------------------------- */
/* Read exactly size bytes from remote */
/* ------------------------------------- */
int BSread(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("BSread: timeout waiting for data\n");
return(-1);
}
#endif
if((rc=recv(soc,(char*)&data[size-total],total,0))<0)
{
if(errno==EINTR)
{
printf("BSread: EINTR");
rc=0;
}
else
{
perror("BSread: 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 BSreceiveHeader(BS* bdt,int* verb,int* size)
{
BSmsgHead buf;
/* can only receive header when in the idle state */
if (bdt->state == BSeof)
return -1;
if(bdt->state != BSidle)
{
fprintf(stderr,"BSreceiveHeader: Interface not idle\n");
bdt->state=BSbad;
return -1;
}
if(BSread(bdt->soc,&buf.verb,sizeof(buf.verb))<0)
{
fprintf(stderr,"BSreceiveHeader: Read failed\n");
return -1;
}
if(BSread(bdt->soc,&buf.size,sizeof(buf.size))<0)
{
fprintf(stderr,"BSreceiveHeader: Read failed\n");
return -1;
}
/* copy message data to user */
*verb=ntohs(buf.verb);
*size=ntohl(buf.size);
if(*size)
bdt->state=BSrData;
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 BSreceiveData(BS* bdt,void* buffer,int size)
{
int rc;
/* can only receive data when in the receive data state */
switch(bdt->state)
{
case BSrData: break;
case BSidle: return 0;
default:
fprintf(stderr,"BSreceiveData: bad receive state\n");
bdt->state=BSbad;
break;
}
if (bdt->remaining_recv < size)
size = bdt->remaining_recv;
if(BSread(bdt->soc,buffer,size)<0)
{
fprintf(stderr,"BSreceiveData: Read failed\n");
bdt->state = BSeof;
return -1;
}
bdt->remaining_recv-=size;
if(bdt->remaining_recv<0)
{
fprintf(stderr,"BSreceiveData: To much data received\n");
bdt->remaining_recv=0;
}
if(bdt->remaining_recv==0)
bdt->state=BSidle;
return size;
}
/* -------------------- */
/* close the connection */
/* -------------------- */
int BSclose(BS* bdt)
{
int verb,size,done;
/* send a close message out */
if(BSsendHeader(bdt,BS_Close,0)<0)
{
fprintf(stderr,"BSclose: Cannot send close message\n");
return -1;
}
done=0;
do
{
/* check response */
if(BSreceiveHeader(bdt,&verb,&size)<0)
{
fprintf(stderr,"BSclose: Close message response error\n");
return -1;
}
switch(verb)
{
case BS_Ok:
done=1;
break;
case BS_Error:
fprintf(stderr,"BSclose: Close rejected\n");
return -1;
default: break;
}
}
while(done==0);
BSfreeBS(bdt);
return 0;
}
/* --------------------------------------- */
/* make a listener socket for UDP - simple */
/* --------------------------------------- */
int BSopenListenerUDP(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,BS_UDP))<0)
{
perror("BSopenListenerUDP: open socket failed");
return -1;
}
if((bind(nsoc,(struct sockaddr*)&tsin,sizeof(tsin)))<0)
{
perror("BSopenListenerUDP: local bind failed");
close(nsoc);
return -1;
}
return nsoc;
}
/* --------------------------------------- */
/* make a listener socket for TCP - simple */
/* --------------------------------------- */
int BSopenListenerTCP(int Port)
{
int nsoc;
struct sockaddr_in tsin;
memset ((void *)&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,BS_TCP))<0)
{
perror("BSopenListenerTCP: open socket failed");
return -1;
}
if((bind(nsoc,(struct sockaddr*)&tsin,sizeof(tsin)))<0)
{
perror("BSopenListenerTCP: local bind failed");
close(nsoc);
return -1;
}
listen(nsoc,5);
return nsoc;
}
/* ------------------------------- */
/* make BS from a socket - simple */
/* ------------------------------- */
BS* BSmakeBS(int soc)
{
BS* bdt;
bdt=(BS*)malloc(sizeof(BS));
bdt->soc=soc;
bdt->remaining_send=0;
bdt->remaining_recv=0;
bdt->state=BSidle;
return bdt;
}
/* --------------------------- */
/* free a BS and close socket */
/* --------------------------- */
int BSfreeBS(BS* bdt)
{
close(bdt->soc);
free(bdt);
return 0;
}
/*-----------------------------------------------------------------------*/
/*
Next three function adjust the fields of the BSDATA structure.
*/
/*-----------------------------------------------------------------------*/
int BSgetAddressPort(BSDATA* info, char* ip_addr, int* dest_port)
{
char* name;
struct sockaddr_in* sin = (struct sockaddr_in*)&(info->sin);
*dest_port=sin->sin_port;
name=inet_ntoa(sin->sin_addr);
strcpy(ip_addr,name);
return 0;
}
int BSsetAddressPort(BSDATA* info, char* ip_addr, int dest_port)
{
BSsetPort(info, dest_port);
return BSsetAddress(info, ip_addr);
}
int BSsetPort(BSDATA* info, int dest_port)
{
struct sockaddr_in* sin = (struct sockaddr_in*)&(info->sin);
sin->sin_family=AF_INET;
sin->sin_port=htons(dest_port);
return 0;
}
int BSsetAddress(BSDATA* info, char* ip_addr)
{
struct hostent *pHostent;
struct sockaddr_in* sin = (struct sockaddr_in*)&(info->sin);
unsigned long addr;
#ifndef vxWorks
/* Deal with the name -vs- IP number issue. */
if (isdigit(ip_addr[0]))
{
#endif
if((addr=inet_addr(ip_addr))==-1) return -1;
#ifndef vxWorks
}
else
{
if((pHostent=gethostbyname(ip_addr))==NULL) return -1;
memcpy((char*)&addr,pHostent->h_addr,sizeof(addr));
}
#endif
sin->sin_family=AF_INET;
memcpy((char*)&(sin->sin_addr),(char*)&addr,sizeof(addr));
return 0;
}
/* ----------------------------------------------------------------- */
/*
All this function does is send a broadcast message and return the
addressing info to the caller of a responder to the broadcast.
arguments:
soc - broadcast socket to use for sending data
trys - number of times to send if no response
o_info - outgoing BSDATA, address/port info
i_info - incoming BSDATA, address/port where response came from
omsg/osize - outgoing message and size
imsg/isize - incoming message and buffer size
Returns the number of bytes read.
*/
/* ----------------------------------------------------------------- */
int BSbroadcastTrans(int soc,int trys,BSDATA* o_info,BSDATA* i_info,
void* omsg,int osize,void* imsg,int isize)
{
int i;
int rc=0;
for(i=0;rc==0 && i<trys;i++)
{
/* send out the message */
rc=BSwriteUDP(soc,o_info,omsg,osize);
if(rc<0) return rc;
/* wait for a response */
rc=BSreadUDP(soc,i_info,1,imsg,isize);
}
return rc;
}
int BSbroadcast(int soc,BSDATA* o_info, void* omsg,int osize)
{
int rc;
/* send out the message */
rc=BSwriteUDP(soc,o_info,omsg,osize);
return rc;
}
/* ----------------------------------------------------------------------- */
/*
This function waits for a message and sends out an ACK when it gets one.
addressing info to the caller of a responder to the broadcast.
Lots of arguments:
soc - The socket to send the message down.
info - The socket information telling where the data read came from
(returned to the user).
tout - Timeout for read in seconds. 0=no wait, -1=wait forever.
buf - Buffer to populate with read data.
size - Size of the buf.
returns the length the read message, 0 is timeout, -1 is error
*/
/* ----------------------------------------------------------------------- */
int BSreadUDP(int soc,BSDATA* info,BS_ULONG tout,void* buf,int size)
{
int mlen,rc;
fd_set fds;
struct timeval to;
int error=0;
do
{
FD_ZERO(&fds);
FD_SET(soc, &fds);
if(tout==-1)
rc=select(FD_SETSIZE,&fds,NULL,NULL,NULL);
else
{
to.tv_sec=tout;
to.tv_usec=0;
rc=select(FD_SETSIZE,&fds,NULL,NULL,&to);
}
switch(rc)
{
case -1: /* bad */
switch(errno)
{
case EINTR: break;
default: error=-1; break;
}
break;
case 0: /* timeout */
break;
default: /* data ready */
break;
}
}
while(rc<0 && error==0);
error=0;
if(rc>0)
{
error=0;
do
{
info->len=sizeof(info->sin);
mlen=recvfrom(soc,(char*)buf,size,0, &info->sin,&info->len);
if(mlen<0)
{
switch(errno)
{
case EINTR: break;
default: error=-1; break;
}
}
}
while(mlen<0 && error==0);
if(mlen<0)
rc=-1;
else
rc=mlen;
}
return rc;
}
/*-----------------------------------------------------------------------*/
/*
Write a chuck of data to a UDP socket at an internet address
*/
/*-----------------------------------------------------------------------*/
int BSwriteDataUDP(int soc,int dest_port,char* ip_addr,void* buf,int size)
{
BSDATA data;
BSsetAddress(&data,ip_addr);
BSsetPort(&data,dest_port);
return BSwriteUDP(soc,&data,buf,size);
}
/*-----------------------------------------------------------------------*/
/*
Write a chunk of data to a UDP socket using BSDATA.
Arguments:
*/
/*-----------------------------------------------------------------------*/
int BSwriteUDP(int soc,BSDATA* info,void* obuf,int osize)
{
int mlen;
mlen=sendto(soc,(char*)obuf,osize,0,&info->sin,sizeof(info->sin));
return mlen;
}
/*-----------------------------------------------------------------------*/
/*
Write/Read a chuck of data to a UDP socket using BSDATA.
Arguments:
soc - socket to send message down and read from
info - address/port to send message to
obuf/osize - outgoing message and size of it.
ibuf/isize - incoming message and size of the buffer.
Returns the number of bytes read, -1 for error.
*/
/*-----------------------------------------------------------------------*/
int BStransUDP(int soc,BSDATA* info,void* obuf,int osize,void* ibuf,int isize)
{
int done,i,mlen,flen;
struct sockaddr fromsin;
fd_set fds;
struct timeval tout;
done=0;
mlen=0;
for(i=0;i<BS_RETRY_COUNT && done==0;i++)
{
mlen=sendto(soc,(char*)obuf,osize,0,&(info->sin),sizeof(info->sin));
if(mlen<0)
{
printf("send failed\n");
return -1;
}
tout.tv_sec=0;
tout.tv_usec=200000;
FD_ZERO(&fds);
FD_SET(soc,&fds);
switch(select(FD_SETSIZE,&fds,(fd_set*)0,(fd_set*)0,&tout))
{
case 0: /* timeout */ break;
case -1: /* error */
printf("select failed\n");
return -1;
default: /* data ready */
flen=sizeof(fromsin);
mlen=recvfrom(soc,(char*)ibuf,isize,0,&fromsin,&flen);
if(mlen<0) return -1;
done=1;
break;
}
}
if(i>=BS_RETRY_COUNT) return 0;
return mlen;
}
/*-----------------------------------------------------------------------*/
/*
Open a broadcast socket and set port to a default.
*/
/*-----------------------------------------------------------------------*/
int BSipBroadcastOpen(BSDATA* info, int default_dest_port)
{
struct sockaddr_in* sin;
int soc;
sin=(struct sockaddr_in*)&(info->sin);
if( (soc=BSgetBroadcastSocket(0,sin)) <0) return -1;
sin->sin_port=htons(default_dest_port);
return soc;
}
/*-----------------------------------------------------------------------*/
/*
BSgetBroadcastSocket() - return a broadcast socket for a port, return
a sockaddr also.
*/
/*-----------------------------------------------------------------------*/
int BSgetBroadcastSocket(int port, struct sockaddr_in* sin)
{
int on=1;
int soc;
BS bs;
sin->sin_port=htons(port);
sin->sin_family=AF_INET;
sin->sin_addr.s_addr=htonl(INADDR_ANY);
if( (soc=socket(AF_INET,SOCK_DGRAM,BS_UDP)) < 0 )
{
perror("socket create failed");
return -1;
}
setsockopt(soc,SOL_SOCKET,SO_BROADCAST,(char*)&on,sizeof(on));
if( bind(soc,(struct sockaddr*)sin,sizeof(struct sockaddr_in)) < 0 )
{
perror("socket bind failed");
close(soc);
return -1;
}
if( BSgetBroadcastAddr(soc,(struct sockaddr*)sin) < 0 )
return -1;
return soc;
}
/*-----------------------------------------------------------------------*/
/*
BSgetBroadcastAddr() - Determine the broadcast address, this is
directly from the Sun Network Programmer's guide.
*/
/*-----------------------------------------------------------------------*/
static long BSgetBroadcastAddr(int soc, struct sockaddr* sin)
{
struct ifconf ifc;
struct ifreq* ifr;
struct ifreq* save;
char buf[BUFSIZ];
int tot,i;
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if(ioctl(soc,SIOCGIFCONF,(int)&ifc) < 0)
{ perror("ioctl SIOCGIFCONF failed"); return -1; }
ifr = ifc.ifc_req;
tot = ifc.ifc_len/sizeof(struct ifreq);
save=(struct ifreq*)NULL;
i=0;
do
{
if(ifr[i].ifr_addr.sa_family==AF_INET)
{
if(ioctl(soc,SIOCGIFFLAGS,(int)&ifr[i])<0)
{ perror("ioctl SIOCGIFFLAGS failed"); return -1; }
if( (ifr[i].ifr_flags&IFF_UP) &&
!(ifr[i].ifr_flags&IFF_LOOPBACK) &&
(ifr[i].ifr_flags&IFF_BROADCAST))
{ save=&ifr[i]; }
}
} while( !save && ++i<tot );
if(save)
{
if(ioctl(soc,SIOCGIFBRDADDR,(int)save)<0)
{ perror("ioctl SIOCGIFBRDADDR failed"); return -1; }
memcpy((char*)sin,(char*)&save->ifr_broadaddr,
sizeof(save->ifr_broadaddr));
}
else
return -1;
return 0;
}