#include #include #include #include #ifdef linux #include #endif #include #include #ifdef vxWorks #include #include #include #include #else #include #include #include #endif #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* name) { return (BDT*)NULL; } /* --------------------------------------------------------------- */ /* open a bulk data socket to a server given the server IP address */ /* --------------------------------------------------------------- */ BDT* BdtIpOpen(char* address, int Port) { struct sockaddr_in tsin; unsigned long addr; int osoc; BDT* bdt; /* request the process variables (bulk data?) */ addr=inet_addr(address); 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->pending_delta=0; bdt->remaining_send=0; bdt->remaining_recv=0; bdt->state=BdtUnbound; /* 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,total; unsigned char* data; data=(unsigned char*)buffer; total=size; do { /* 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,remaining,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,"BdtSendData: To much data to send\n"); len=bdt->remaining_send; } else len=size; /* this function should loop until size bytes is sent */ /* send out the chunk */ if((rc=send(bdt->soc,(char*)buffer,len,0))<0) { perror("BdtSendData: Send data chunk to remote failed"); return -1; } bdt->remaining_send-=rc; 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 rc; } /* ------------------------------------------------------------------------ report if data is pending from remote clear the pending data condition ------------------------------------------------------------------------ */ int BdtPvDeltaPending(BDT* bdt) { int rc = bdt->pending_delta; bdt->pending_delta=0; return rc; } /* ------------------------------------- */ /* Read exactly size bytes from remote */ /* ------------------------------------- */ int BdtRead(int soc,void* buffer,int size) { int rc,total; unsigned char* data; data=(unsigned char*)buffer; total=size; do { /* wait for data chunk */ if((rc=recv(soc,&data[size-total],total,0))<0) { if(errno==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(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* service_name) { int len,rc,size,verb; if(bdt->state!=BdtUnbound) { fprintf(stderr,"BdtServiceConnect: can only bind to one service\n"); return -1; } bdt->state=BdtIdle; len=strlen(service_name)+1; /* send out connect message */ if(BdtSendHeader(bdt,BDT_Connect,len)<0) { fprintf(stderr,"BdtServiceConnect: send of connect header failed\n"); bdt->state=BdtUnbound; return -1; } /* send out the process variable to connect to */ if(BdtSendData(bdt,service_name,len)<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->pending_delta=0; 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; } /* ------------------------------------------------- */ /* connect to a generic service on the remote server */ /* ------------------------------------------------- */ int BdtPvConnect(BDT* bdt, char* pv_name) { char* data; int len,rc; len=strlen(pv_name)+strlen(BDT_PV_SERVICE)+2; data=(char*)malloc(len); sprintf(data,"%s %s",BDT_PV_SERVICE,pv_name); rc=BdtServiceConnect(bdt,data); free(data); return rc; }