502 lines
9.1 KiB
C
502 lines
9.1 KiB
C
/*************************************************************************\
|
|
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
|
* National Laboratory.
|
|
* Copyright (c) 2002 The Regents of the University of California, as
|
|
* Operator of Los Alamos National Laboratory.
|
|
* EPICS BASE Versions 3.13.7
|
|
* and higher are distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
/* $Id$ */
|
|
/*
|
|
*
|
|
* Author: Jeffrey O. Hill
|
|
* Date: 080791
|
|
*
|
|
*/
|
|
|
|
#include <vxWorks.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <stdio.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>
|
|
|
|
#define epicsExportSharedSymbols
|
|
#include "epicsAssert.h"
|
|
#include "errlog.h"
|
|
#include "envDefs.h"
|
|
#include "task_params.h"
|
|
#include "bsdSocketResource.h"
|
|
|
|
#ifndef LOCAL
|
|
#define LOCAL static
|
|
#endif /* LOCAL */
|
|
|
|
/*
|
|
* for use by the vxWorks shell
|
|
*/
|
|
int iocLogDisable = 0;
|
|
|
|
void iocLogMessage(const char *message);
|
|
|
|
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(const 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;
|
|
}
|
|
|
|
status = getConfig();
|
|
if(status<0){
|
|
printf ("iocLogClient: logging disabled\n");
|
|
iocLogDisable = 1;
|
|
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==ERROR) {
|
|
printf("Unable to start log server connection watch dog\n");
|
|
}
|
|
errlogAddListener(iocLogMessage);
|
|
|
|
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];
|
|
|
|
ipAddrToA (&addr, name, sizeof(name));
|
|
|
|
printf(
|
|
"iocLogClient: unable to connect to %s because \"%s\"\n",
|
|
name,
|
|
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) {
|
|
fclose(iocLogFile);
|
|
iocLogFile = NULL;
|
|
iocLogFD = ERROR;
|
|
}
|
|
else if (iocLogFD!=ERROR) {
|
|
close(iocLogFD);
|
|
iocLogFD = ERROR;
|
|
}
|
|
|
|
/*
|
|
* export the new connection
|
|
*/
|
|
iocLogFD = sock;
|
|
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(const ENV_PARAM *pparam)
|
|
{
|
|
printf(
|
|
"IocLogClient: EPICS environment variable \"%s\" undefined\n",
|
|
pparam->name);
|
|
}
|
|
|
|
|
|
void iocLogMessage(const char *message)
|
|
{
|
|
int status;
|
|
int semStatus;
|
|
|
|
if (iocLogDisable || !message || *message==0) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check for init
|
|
*/
|
|
if (!iocLogMutex) {
|
|
status = iocLogInit();
|
|
if (status) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* mutex on
|
|
*/
|
|
semStatus = semTake(iocLogMutex, WAIT_FOREVER);
|
|
assert(semStatus==OK);
|
|
|
|
if (iocLogFile) {
|
|
status = fprintf(iocLogFile, "%s", message);
|
|
if (status>0) {
|
|
status = fflush(iocLogFile);
|
|
}
|
|
|
|
if (status<0) {
|
|
fclose(iocLogFile);
|
|
iocLogFile = NULL;
|
|
iocLogFD = ERROR;
|
|
semStatus = semGive(iocLogSignal);
|
|
printf("iocLogClient: lost contact with the log server\n");
|
|
assert(semStatus==OK);
|
|
}
|
|
}
|
|
else {
|
|
status = EOF;
|
|
}
|
|
|
|
/*
|
|
* mutex off
|
|
*/
|
|
semStatus = semGive(iocLogMutex);
|
|
assert(semStatus==OK);
|
|
|
|
return;
|
|
}
|
|
|