523 lines
9.4 KiB
C
523 lines
9.4 KiB
C
/* $Id$ */
|
||
/*
|
||
*
|
||
* Author: Jeffrey O. Hill
|
||
* Date: 080791
|
||
*
|
||
* Experimental Physics and Industrial Control System (EPICS)
|
||
*
|
||
* Copyright 1991, the Regents of the University of California,
|
||
* and the University of Chicago Board of Governors.
|
||
*
|
||
* This software was produced under U.S. Government contracts:
|
||
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
|
||
* and (W-31-109-ENG-38) at Argonne National Laboratory.
|
||
*
|
||
* Initial development by:
|
||
* The Controls and Automation Group (AT-8)
|
||
* Ground Test Accelerator
|
||
* Accelerator Technology Division
|
||
* Los Alamos National Laboratory
|
||
*
|
||
* Co-developed with
|
||
* The Controls and Computing Group
|
||
* Accelerator Systems Division
|
||
* Advanced Photon Source
|
||
* Argonne National Laboratory
|
||
*
|
||
* NOTES:
|
||
*
|
||
* Modification Log:
|
||
* -----------------
|
||
* .00 joh 080791 Created
|
||
* .01 joh 081591 Added epics env config
|
||
* .02 joh 011995 Allow stdio also
|
||
* $Log$
|
||
*/
|
||
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include <errno.h>
|
||
#include <assert.h>
|
||
|
||
#include <socket.h>
|
||
#include <in.h>
|
||
|
||
#include <ioLib.h>
|
||
#include <taskLib.h>
|
||
#include <logLib.h>
|
||
#include <inetLib.h>
|
||
#include <sockLib.h>
|
||
#include <sysLib.h>
|
||
#include <semLib.h>
|
||
#include <rebootLib.h>
|
||
|
||
#include <epicsPrint.h>
|
||
#include <envDefs.h>
|
||
#include <task_params.h>
|
||
|
||
/*
|
||
* for use by the vxWorks shell
|
||
*/
|
||
int iocLogDisable = 0;
|
||
|
||
LOCAL FILE *iocLogFile = NULL;
|
||
LOCAL int iocLogFD = ERROR;
|
||
LOCAL unsigned iocLogTries = 0U;
|
||
LOCAL unsigned iocLogConnectCount = 0U;
|
||
|
||
LOCAL long ioc_log_port;
|
||
LOCAL struct in_addr ioc_log_addr;
|
||
|
||
int iocLogInit(void);
|
||
LOCAL int getConfig(void);
|
||
LOCAL void failureNotify(ENV_PARAM *pparam);
|
||
LOCAL void logClientShutdown(void);
|
||
LOCAL void logRestart(void);
|
||
LOCAL int iocLogAttach(void);
|
||
LOCAL void logClientRollLocalPort(void);
|
||
|
||
LOCAL SEM_ID iocLogMutex; /* protects stdio */
|
||
LOCAL SEM_ID iocLogSignal; /* reattach to log server */
|
||
|
||
#define EPICS_IOC_LOG_CLIENT_CONNECT_TMO 5 /* sec */
|
||
|
||
|
||
/*
|
||
* iocLogInit()
|
||
*/
|
||
int iocLogInit(void)
|
||
{
|
||
int status;
|
||
int attachStatus;
|
||
int options;
|
||
|
||
if(iocLogDisable){
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* dont init twice
|
||
*/
|
||
if (iocLogMutex) {
|
||
return OK;
|
||
}
|
||
|
||
options = SEM_Q_PRIORITY|SEM_DELETE_SAFE|SEM_INVERSION_SAFE;
|
||
iocLogMutex = semMCreate(options);
|
||
if(!iocLogMutex){
|
||
return ERROR;
|
||
}
|
||
|
||
iocLogSignal = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
|
||
if(!iocLogSignal){
|
||
return ERROR;
|
||
}
|
||
|
||
attachStatus = iocLogAttach();
|
||
|
||
status = rebootHookAdd((FUNCPTR)logClientShutdown);
|
||
if (status<0) {
|
||
printf("Unable to add log server reboot hook\n");
|
||
}
|
||
|
||
status = taskSpawn(
|
||
LOG_RESTART_NAME,
|
||
LOG_RESTART_PRI,
|
||
LOG_RESTART_OPT,
|
||
LOG_RESTART_STACK,
|
||
(FUNCPTR)logRestart,
|
||
0,0,0,0,0,0,0,0,0,0);
|
||
if (status<0) {
|
||
printf("Unable to start log server connection watch dog\n");
|
||
}
|
||
|
||
return attachStatus;
|
||
}
|
||
|
||
|
||
/*
|
||
* iocLogAttach()
|
||
*/
|
||
LOCAL int iocLogAttach(void)
|
||
{
|
||
|
||
int sock;
|
||
struct sockaddr_in addr;
|
||
int status;
|
||
int optval;
|
||
struct timeval tval;
|
||
FILE *fp;
|
||
|
||
status = getConfig();
|
||
if(status<0){
|
||
printf (
|
||
"iocLogClient: EPICS environment under specified\n");
|
||
printf ("iocLogClient: failed to initialize\n");
|
||
return ERROR;
|
||
}
|
||
|
||
/* allocate a socket */
|
||
sock = socket(AF_INET, /* domain */
|
||
SOCK_STREAM, /* type */
|
||
0); /* deflt proto */
|
||
if (sock < 0){
|
||
printf ("iocLogClient: no socket error %s\n",
|
||
strerror(errno));
|
||
return ERROR;
|
||
}
|
||
|
||
/* set socket domain */
|
||
addr.sin_family = AF_INET;
|
||
|
||
/* set the port */
|
||
addr.sin_port = htons(ioc_log_port);
|
||
|
||
/* set the addr */
|
||
addr.sin_addr.s_addr = ioc_log_addr.s_addr;
|
||
|
||
/* connect */
|
||
#ifdef vxWorks
|
||
tval.tv_sec = EPICS_IOC_LOG_CLIENT_CONNECT_TMO;
|
||
tval.tv_usec = 0;
|
||
status = connectWithTimeout(
|
||
sock,
|
||
(struct sockaddr *)&addr,
|
||
sizeof(addr),
|
||
&tval);
|
||
#else
|
||
status = connect(
|
||
sock,
|
||
(struct sockaddr *)&addr,
|
||
sizeof(addr));
|
||
#endif
|
||
if (status < 0) {
|
||
/*
|
||
* only print a message if it is the first try and
|
||
* we havent got a valid connection already
|
||
*/
|
||
if (iocLogTries==0U && iocLogFD==ERROR) {
|
||
char name[INET_ADDR_LEN];
|
||
|
||
inet_ntoa_b(addr.sin_addr, name);
|
||
printf(
|
||
"iocLogClient: unable to connect to %s port %d because \"%s\"\n",
|
||
name,
|
||
addr.sin_port,
|
||
strerror(errno));
|
||
}
|
||
iocLogTries++;
|
||
close(sock);
|
||
return ERROR;
|
||
}
|
||
|
||
iocLogTries=0U;
|
||
iocLogConnectCount++;
|
||
|
||
/*
|
||
* discover that the connection has expired
|
||
* (after a long delay)
|
||
*/
|
||
optval = TRUE;
|
||
status = setsockopt( sock,
|
||
SOL_SOCKET,
|
||
SO_KEEPALIVE,
|
||
(char *) &optval,
|
||
sizeof(optval));
|
||
if(status<0){
|
||
printf ("iocLogClient: %s\n", strerror(errno));
|
||
close(sock);
|
||
return ERROR;
|
||
}
|
||
|
||
/*
|
||
* set how long we will wait for the TCP state machine
|
||
* to clean up when we issue a close(). This
|
||
* guarantees that messages are serialized when we
|
||
* switch connections.
|
||
*/
|
||
{
|
||
struct linger lingerval;
|
||
|
||
lingerval.l_onoff = TRUE;
|
||
lingerval.l_linger = 60*5;
|
||
status = setsockopt( sock,
|
||
SOL_SOCKET,
|
||
SO_LINGER,
|
||
(char *) &lingerval,
|
||
sizeof(lingerval));
|
||
if(status<0){
|
||
printf ("iocLogClient: %s\n", strerror(errno));
|
||
close(sock);
|
||
return ERROR;
|
||
}
|
||
}
|
||
|
||
fp = fdopen (sock, "a");
|
||
|
||
/*
|
||
* mutex on
|
||
*/
|
||
status = semTake(iocLogMutex, WAIT_FOREVER);
|
||
assert(status==OK);
|
||
|
||
/*
|
||
* close any preexisting connection to the log server
|
||
*/
|
||
if (iocLogFile) {
|
||
logFdDelete(iocLogFD);
|
||
fclose(iocLogFile);
|
||
iocLogFile = NULL;
|
||
iocLogFD = ERROR;
|
||
}
|
||
else if (iocLogFD!=ERROR) {
|
||
logFdDelete(iocLogFD);
|
||
close(iocLogFD);
|
||
iocLogFD = ERROR;
|
||
}
|
||
|
||
/*
|
||
* export the new connection
|
||
*/
|
||
iocLogFD = sock;
|
||
logFdAdd (iocLogFD);
|
||
iocLogFile = fp;
|
||
|
||
/*
|
||
* mutex off
|
||
*/
|
||
status = semGive(iocLogMutex);
|
||
assert(status==OK);
|
||
|
||
return OK;
|
||
}
|
||
|
||
|
||
/*
|
||
* logRestart()
|
||
*/
|
||
LOCAL void logRestart(void)
|
||
{
|
||
int status;
|
||
int reattach;
|
||
int delay = LOG_RESTART_DELAY;
|
||
|
||
|
||
/*
|
||
* roll the local port forward so that we dont collide
|
||
* with the first port assigned when we reboot
|
||
*/
|
||
logClientRollLocalPort();
|
||
|
||
while (1) {
|
||
semTake(iocLogSignal, delay);
|
||
|
||
/*
|
||
* mutex on
|
||
*/
|
||
status = semTake(iocLogMutex, WAIT_FOREVER);
|
||
assert(status==OK);
|
||
|
||
if (iocLogFile==NULL) {
|
||
reattach = TRUE;
|
||
}
|
||
else {
|
||
reattach = ferror(iocLogFile);
|
||
}
|
||
|
||
/*
|
||
* mutex off
|
||
*/
|
||
status = semGive(iocLogMutex);
|
||
assert(status==OK);
|
||
|
||
if (reattach==FALSE) {
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
* restart log server
|
||
*/
|
||
iocLogConnectCount = 0U;
|
||
logClientRollLocalPort();
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* logClientRollLocalPort()
|
||
*/
|
||
LOCAL void logClientRollLocalPort(void)
|
||
{
|
||
int status;
|
||
|
||
/*
|
||
* roll the local port forward so that we dont collide
|
||
* with it when we reboot
|
||
*/
|
||
while (iocLogConnectCount<10U) {
|
||
/*
|
||
* switch to a new log server connection
|
||
*/
|
||
status = iocLogAttach();
|
||
if (status==OK) {
|
||
/*
|
||
* only print a message after the first connect
|
||
*/
|
||
if (iocLogConnectCount==1U) {
|
||
printf(
|
||
"iocLogClient: reconnected to the log server\n");
|
||
}
|
||
}
|
||
else {
|
||
/*
|
||
* if we cant connect then we will roll
|
||
* the port later when we can
|
||
* (we must not spin on connect fail)
|
||
*/
|
||
if (errno!=ETIMEDOUT) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* logClientShutdown()
|
||
*/
|
||
LOCAL void logClientShutdown(void)
|
||
{
|
||
if (iocLogFD!=ERROR) {
|
||
/*
|
||
* unfortunately this does not currently work because WRS
|
||
* runs the reboot hooks in the order that
|
||
* they are installed (and the network is already shutdown
|
||
* by the time we get here)
|
||
*/
|
||
#if 0
|
||
/*
|
||
* this aborts the connection because we
|
||
* have specified a nill linger interval
|
||
*/
|
||
printf("log client: lingering for connection close...");
|
||
close(iocLogFD);
|
||
printf("done\n");
|
||
#endif
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
*
|
||
* getConfig()
|
||
* Get Server Configuration
|
||
*
|
||
*
|
||
*/
|
||
LOCAL int getConfig(void)
|
||
{
|
||
long status;
|
||
|
||
status = envGetLongConfigParam(
|
||
&EPICS_IOC_LOG_PORT,
|
||
&ioc_log_port);
|
||
if(status<0){
|
||
failureNotify(&EPICS_IOC_LOG_PORT);
|
||
return ERROR;
|
||
}
|
||
|
||
status = envGetInetAddrConfigParam(
|
||
&EPICS_IOC_LOG_INET,
|
||
&ioc_log_addr);
|
||
if(status<0){
|
||
failureNotify(&EPICS_IOC_LOG_INET);
|
||
return ERROR;
|
||
}
|
||
|
||
return OK;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* failureNotify()
|
||
*/
|
||
LOCAL void failureNotify(ENV_PARAM *pparam)
|
||
{
|
||
printf(
|
||
"IocLogClient: EPICS environment variable \"%s\" undefined\n",
|
||
pparam->name);
|
||
}
|
||
|
||
|
||
/*
|
||
* iocLogPrintf()
|
||
*/
|
||
int iocLogPrintf(const char *pFormat, ...)
|
||
{
|
||
va_list pvar;
|
||
|
||
va_start (pvar, pFormat);
|
||
|
||
return iocLogVPrintf (pFormat, pvar);
|
||
}
|
||
|
||
|
||
/*
|
||
* iocLogVPrintf()
|
||
*/
|
||
int iocLogVPrintf(const char *pFormat, va_list pvar)
|
||
{
|
||
int status;
|
||
int semStatus;
|
||
|
||
if (!pFormat || iocLogDisable) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Check for init
|
||
*/
|
||
if (!iocLogMutex) {
|
||
status = iocLogInit();
|
||
if (status) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* mutex on
|
||
*/
|
||
semStatus = semTake(iocLogMutex, WAIT_FOREVER);
|
||
assert(semStatus==OK);
|
||
|
||
if (iocLogFile) {
|
||
status = vfprintf(iocLogFile, pFormat, pvar);
|
||
if (status>0) {
|
||
status = fflush(iocLogFile);
|
||
}
|
||
|
||
if (status<0) {
|
||
logFdDelete(iocLogFD);
|
||
fclose(iocLogFile);
|
||
iocLogFile = NULL;
|
||
iocLogFD = ERROR;
|
||
semStatus = semGive(iocLogSignal);
|
||
assert(semStatus==OK);
|
||
}
|
||
}
|
||
else {
|
||
status = EOF;
|
||
}
|
||
|
||
/*
|
||
* mutex off
|
||
*/
|
||
semStatus = semGive(iocLogMutex);
|
||
assert(semStatus==OK);
|
||
|
||
return status;
|
||
}
|
||
|