This is Unofficial EPICS BASE Doxygen Site
logClient.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 /*
10  * Author: Jeffrey O. Hill
11  * Date: 080791
12  */
13 
14 /*
15  * ANSI C
16  */
17 #include <stdlib.h>
18 #include <stddef.h>
19 #include <string.h>
20 #include <stdio.h>
21 
22 #define EPICS_PRIVATE_API
23 #include "dbDefs.h"
24 #include "epicsEvent.h"
25 #include "iocLog.h"
26 #include "epicsMutex.h"
27 #include "epicsThread.h"
28 #include "epicsTime.h"
29 #include "osiSock.h"
30 #include "epicsAssert.h"
31 #include "epicsExit.h"
32 #include "epicsSignal.h"
33 #include "epicsExport.h"
34 
35 #include "logClient.h"
36 
39 
40 typedef struct {
41  char msgBuf[0x4000];
42  struct sockaddr_in addr;
43  char name[64];
49  unsigned connectCount;
50  unsigned nextMsgIndex;
51  unsigned backlog;
52  unsigned connected;
53  unsigned shutdown;
54  unsigned shutdownConfirm;
56 } logClient;
57 
58 static const double LOG_RESTART_DELAY = 5.0; /* sec */
59 static const double LOG_SERVER_SHUTDOWN_TIMEOUT = 30.0; /* sec */
60 
61 /*
62  * If set using iocLogPrefix() this string is prepended to all log messages:
63  */
64 static char* logClientPrefix = NULL;
65 
66 /*
67  * logClientClose ()
68  */
69 static void logClientClose ( logClient *pClient )
70 {
71  if (logClientDebug) {
72  fprintf (stderr, "log client: lingering for connection close...");
73  fflush (stderr);
74  }
75 
76  /*
77  * mutex on
78  */
79  epicsMutexMustLock (pClient->mutex);
80 
81  /*
82  * close any preexisting connection to the log server
83  */
84  if ( pClient->sock != INVALID_SOCKET ) {
85  epicsSocketDestroy ( pClient->sock );
86  pClient->sock = INVALID_SOCKET;
87  }
88 
89  pClient->connected = 0u;
90 
91  /*
92  * mutex off
93  */
94  epicsMutexUnlock (pClient->mutex);
95 
96  if (logClientDebug)
97  fprintf (stderr, "done\n");
98 }
99 
100 /*
101  * logClientDestroy
102  */
103 static void logClientDestroy (logClientId id)
104 {
106  logClient *pClient = (logClient *) id;
107  epicsTimeStamp begin, current;
108  double diff;
109 
110  /* command log client thread to shutdown - taking mutex here */
111  /* forces cache flush on SMP machines */
112  epicsMutexMustLock ( pClient->mutex );
113  pClient->shutdown = 1u;
114  epicsMutexUnlock ( pClient->mutex );
115  epicsEventSignal ( pClient->shutdownNotify );
116 
117  /* unblock log client thread blocking in send() or connect() */
118  interruptInfo =
120  switch ( interruptInfo ) {
122  logClientClose ( pClient );
123  break;
125  shutdown ( pClient->sock, SHUT_WR );
126  break;
129  break;
130  default:
131  break;
132  };
133 
134  /* wait for confirmation that the thread exited - taking */
135  /* mutex here forces cache update on SMP machines */
136  epicsTimeGetCurrent ( & begin );
137  epicsMutexMustLock ( pClient->mutex );
138  do {
139  epicsMutexUnlock ( pClient->mutex );
141  pClient->stateChangeNotify,
142  LOG_SERVER_SHUTDOWN_TIMEOUT / 10.0 );
143  epicsTimeGetCurrent ( & current );
144  diff = epicsTimeDiffInSeconds ( & current, & begin );
145  epicsMutexMustLock ( pClient->mutex );
146  }
147  while ( ! pClient->shutdownConfirm && diff < LOG_SERVER_SHUTDOWN_TIMEOUT );
148  epicsMutexUnlock ( pClient->mutex );
149 
150  if ( ! pClient->shutdownConfirm ) {
151  fprintf ( stderr, "log client shutdown: timed out stopping reconnect\n"
152  " thread for '%s' after %.1f seconds - cleanup aborted\n",
153  pClient->name, LOG_SERVER_SHUTDOWN_TIMEOUT );
154  return;
155  }
156 
157  logClientClose ( pClient );
158 
159  epicsMutexDestroy ( pClient->mutex );
161  epicsEventDestroy ( pClient->shutdownNotify );
162 
163  free ( pClient );
164 }
165 
166 /*
167  * This method requires the pClient->mutex be owned already.
168  */
169 static void sendMessageChunk(logClient * pClient, const char * message) {
170  unsigned strSize;
171 
172  strSize = strlen ( message );
173  while ( strSize ) {
174  unsigned msgBufBytesLeft =
175  sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex;
176 
177  if ( msgBufBytesLeft < strSize && pClient->nextMsgIndex != 0u && pClient->connected)
178  {
179  /* buffer is full, thus flush it */
180  logClientFlush ( pClient );
181  msgBufBytesLeft = sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex;
182  }
183  if ( msgBufBytesLeft == 0u ) {
184  fprintf ( stderr, "log client: messages to \"%s\" are lost\n",
185  pClient->name );
186  break;
187  }
188  if ( msgBufBytesLeft > strSize) msgBufBytesLeft = strSize;
189  memcpy ( & pClient->msgBuf[pClient->nextMsgIndex],
190  message, msgBufBytesLeft );
191  pClient->nextMsgIndex += msgBufBytesLeft;
192  strSize -= msgBufBytesLeft;
193  message += msgBufBytesLeft;
194  }
195 }
196 
197 /*
198  * logClientSend ()
199  */
200 void epicsStdCall logClientSend ( logClientId id, const char * message )
201 {
202  logClient * pClient = ( logClient * ) id;
203 
204  if ( ! pClient || ! message ) {
205  return;
206  }
207 
208  epicsMutexMustLock ( pClient->mutex );
209 
210  if (logClientPrefix) {
211  sendMessageChunk(pClient, logClientPrefix);
212  }
213  sendMessageChunk(pClient, message);
214 
215  epicsMutexUnlock (pClient->mutex);
216 }
217 
218 
219 void epicsStdCall logClientFlush ( logClientId id )
220 {
221  unsigned nSent;
222  int status = 0;
223 
224  logClient * pClient = ( logClient * ) id;
225 
226  if ( ! pClient || ! pClient->connected ) {
227  return;
228  }
229 
230  epicsMutexMustLock ( pClient->mutex );
231 
232  nSent = pClient->backlog;
233  while ( nSent < pClient->nextMsgIndex && pClient->connected ) {
234  status = send ( pClient->sock, pClient->msgBuf + nSent,
235  pClient->nextMsgIndex - nSent, 0 );
236  if ( status < 0 ) break;
237  nSent += status;
238  }
239 
240  if ( pClient->backlog > 0 && status >= 0 ) {
241  /* On Linux send 0 bytes can detect EPIPE */
242  /* NOOP on Windows, fails on vxWorks */
243  errno = 0;
244  status = send ( pClient->sock, NULL, 0, 0 );
245  if (!(errno == SOCK_ECONNRESET || errno == SOCK_EPIPE)) status = 0;
246  }
247 
248  if ( status < 0 ) {
249  if ( ! pClient->shutdown ) {
250  char sockErrBuf[128];
251  epicsSocketConvertErrnoToString(sockErrBuf, sizeof(sockErrBuf));
252  fprintf(stderr, "log client: lost contact with log server at '%s'\n"
253  " because \"%s\"\n", pClient->name, sockErrBuf);
254  }
255  pClient->backlog = 0;
256  logClientClose ( pClient );
257  }
258  else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) {
259  int backlog = epicsSocketUnsentCount ( pClient->sock );
260  if (backlog >= 0) {
261  pClient->backlog = backlog;
262  nSent -= backlog;
263  }
264  pClient->nextMsgIndex -= nSent;
265  if ( nSent > 0 && pClient->nextMsgIndex > 0 ) {
266  memmove ( pClient->msgBuf, & pClient->msgBuf[nSent],
267  pClient->nextMsgIndex );
268  }
269  }
270  epicsMutexUnlock ( pClient->mutex );
271 }
272 
273 /*
274  * logClientMakeSock ()
275  */
276 static void logClientMakeSock (logClient *pClient)
277 {
278  if (logClientDebug) {
279  fprintf (stderr, "log client: creating socket...");
280  fflush (stderr);
281  }
282 
283  epicsMutexMustLock (pClient->mutex);
284 
285  /*
286  * allocate a socket
287  */
288  pClient->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, 0 );
289  if ( pClient->sock == INVALID_SOCKET ) {
290  char sockErrBuf[128];
292  sockErrBuf, sizeof ( sockErrBuf ) );
293  fprintf ( stderr, "log client: no socket error %s\n",
294  sockErrBuf );
295  }
296 
297  epicsMutexUnlock (pClient->mutex);
298 
299  if (logClientDebug)
300  fprintf (stderr, "done\n");
301 }
302 
303 /*
304  * logClientConnect()
305  */
306 static void logClientConnect (logClient *pClient)
307 {
308  osiSockIoctl_t optval;
309  int errnoCpy;
310  int status;
311 
312  if ( pClient->sock == INVALID_SOCKET ) {
313  logClientMakeSock ( pClient );
314  if ( pClient->sock == INVALID_SOCKET ) {
315  return;
316  }
317  }
318 
319  while ( 1 ) {
320  status = connect (pClient->sock,
321  (struct sockaddr *)&pClient->addr, sizeof(pClient->addr));
322  if ( status >= 0 ) {
323  break;
324  }
325  else {
326  errnoCpy = SOCKERRNO;
327  if ( errnoCpy == SOCK_EINTR ) {
328  continue;
329  }
330  else if (
331  errnoCpy==SOCK_EINPROGRESS ||
332  errnoCpy==SOCK_EWOULDBLOCK ) {
333  return;
334  }
335  else if ( errnoCpy==SOCK_EALREADY ) {
336  break;
337  }
338  else {
339  if ( pClient->connFailStatus != errnoCpy && ! pClient->shutdown ) {
340  char sockErrBuf[128];
342  sockErrBuf, sizeof ( sockErrBuf ) );
343  fprintf (stderr,
344  "log client: failed to connect to server '%s'"
345  " because '%s'\n",
346  pClient->name, sockErrBuf);
347  pClient->connFailStatus = errnoCpy;
348  }
349  logClientClose ( pClient );
350  return;
351  }
352  }
353  }
354 
355  epicsMutexMustLock (pClient->mutex);
356 
357  pClient->connected = 1u;
358  pClient->connFailStatus = 0;
359 
360  /*
361  * discover that the connection has expired
362  * (after a long delay)
363  */
364  optval = TRUE;
365  status = setsockopt (pClient->sock, SOL_SOCKET, SO_KEEPALIVE,
366  (char *)&optval, sizeof(optval));
367  if (status<0) {
368  char sockErrBuf[128];
370  sockErrBuf, sizeof ( sockErrBuf ) );
371  fprintf (stderr, "log client: unable to enable SO_KEEPALIVE\n"
372  " because '%s'\n", sockErrBuf);
373  }
374 
375  /*
376  * we don't need full-duplex (clients only write), so we shutdown
377  * the read end of our socket
378  */
379  status = shutdown (pClient->sock, SHUT_RD);
380  if (status < 0) {
381  char sockErrBuf[128];
383  sockErrBuf, sizeof ( sockErrBuf ) );
384  fprintf (stderr, "%s:%d shutdown(sock,SHUT_RD) error was '%s'\n",
385  __FILE__, __LINE__, sockErrBuf);
386  /* not fatal (although it shouldn't happen) */
387  }
388 
389  /*
390  * set how long we will wait for the TCP state machine
391  * to clean up when we issue a close(). This
392  * guarantees that messages are serialized when we
393  * switch connections.
394  */
395  {
396  struct linger lingerval;
397 
398  lingerval.l_onoff = TRUE;
399  lingerval.l_linger = 60*5;
400  status = setsockopt (pClient->sock, SOL_SOCKET, SO_LINGER,
401  (char *) &lingerval, sizeof(lingerval));
402  if (status<0) {
403  char sockErrBuf[128];
405  sockErrBuf, sizeof ( sockErrBuf ) );
406  fprintf(stderr, "log client: unable to set SO_LINGER\n"
407  " because '%s'\n", sockErrBuf);
408  }
409  }
410 
411  pClient->connectCount++;
412 
413  epicsMutexUnlock ( pClient->mutex );
414 
415  epicsEventSignal ( pClient->stateChangeNotify );
416 
417  fprintf(stderr, "log client: connected to log server at '%s'\n",
418  pClient->name);
419 }
420 
421 /*
422  * logClientRestart ()
423  */
424 static void logClientRestart ( logClientId id )
425 {
426  logClient *pClient = (logClient *)id;
427 
428  /* SMP safe state inspection */
429  epicsMutexMustLock ( pClient->mutex );
430  while ( ! pClient->shutdown ) {
431  unsigned isConn;
432 
433  isConn = pClient->connected;
434 
435  epicsMutexUnlock ( pClient->mutex );
436 
437  if ( ! isConn ) logClientConnect ( pClient );
438  logClientFlush ( pClient );
439 
441 
442  epicsMutexMustLock ( pClient->mutex );
443  }
444  epicsMutexUnlock ( pClient->mutex );
445 
446  pClient->shutdownConfirm = 1u;
447  epicsEventSignal ( pClient->stateChangeNotify );
448 }
449 
450 /*
451  * logClientCreate()
452  */
454  struct in_addr server_addr, unsigned short server_port)
455 {
456  logClient *pClient;
457 
458  pClient = calloc (1, sizeof (*pClient));
459  if (pClient==NULL) {
460  return NULL;
461  }
462 
463  pClient->addr.sin_family = AF_INET;
464  pClient->addr.sin_addr = server_addr;
465  pClient->addr.sin_port = htons(server_port);
466  ipAddrToDottedIP (&pClient->addr, pClient->name, sizeof(pClient->name));
467 
468  pClient->mutex = epicsMutexCreate ();
469  if ( ! pClient->mutex ) {
470  free ( pClient );
471  return NULL;
472  }
473 
474  pClient->sock = INVALID_SOCKET;
475  pClient->connected = 0u;
476  pClient->connFailStatus = 0;
477  pClient->shutdown = 0;
478  pClient->shutdownConfirm = 0;
479 
480  epicsAtExit (logClientDestroy, (void*) pClient);
481 
483  if ( ! pClient->stateChangeNotify ) {
484  epicsMutexDestroy ( pClient->mutex );
485  free ( pClient );
486  return NULL;
487  }
488 
490  if ( ! pClient->shutdownNotify ) {
491  epicsMutexDestroy ( pClient->mutex );
493  free ( pClient );
494  return NULL;
495  }
496 
498  "logRestart", epicsThreadPriorityLow,
500  logClientRestart, pClient );
501  if ( pClient->restartThreadId == NULL ) {
502  epicsMutexDestroy ( pClient->mutex );
504  epicsEventDestroy ( pClient->shutdownNotify );
505  free (pClient);
506  fprintf(stderr, "log client: unable to start reconnection thread\n");
507  return NULL;
508  }
509 
510  return (void *) pClient;
511 }
512 
513 /*
514  * logClientShow ()
515  */
516 void epicsStdCall logClientShow (logClientId id, unsigned level)
517 {
518  logClient *pClient = (logClient *) id;
519 
520  if ( pClient->connected ) {
521  printf ("log client: connected to log server at '%s'\n", pClient->name);
522  }
523  else {
524  printf ("log client: disconnected from log server at '%s'\n",
525  pClient->name);
526  }
527 
528  if (logClientPrefix) {
529  printf ("log client: prefix is \"%s\"\n", logClientPrefix);
530  }
531 
532  if (level>0) {
533  printf ("log client: sock %s, connect cycles = %u\n",
534  pClient->sock==INVALID_SOCKET?"INVALID":"OK",
535  pClient->connectCount);
536  }
537  if (level>1) {
538  printf ("log client: %u bytes in buffer\n", pClient->nextMsgIndex);
539  if (pClient->nextMsgIndex)
540  printf("-------------------------\n"
541  "%.*s-------------------------\n",
542  (int)(pClient->nextMsgIndex), pClient->msgBuf);
543  }
544 }
545 
546 /*
547  * iocLogPrefix()
548  */
549 void epicsStdCall iocLogPrefix(const char * prefix)
550 {
551 
552  /* If we have already established a log prefix, don't let the user change
553  * it. The iocLogPrefix command is expected to be run from the IOC startup
554  * script during initialization; the prefix can't be changed once it has
555  * been set.
556  */
557 
558  if (logClientPrefix) {
559  printf ("iocLogPrefix: The prefix was already set to \"%s\" "
560  "and can't be changed.\n", logClientPrefix);
561  return;
562  }
563 
564  if (prefix) {
565  unsigned prefixLen = strlen(prefix);
566  if (prefixLen > 0) {
567  char * localCopy = malloc(prefixLen+1);
568  strcpy(localCopy, prefix);
569  logClientPrefix = localCopy;
570  }
571  }
572 }
#define SHUT_WR
Definition: osdSock.h:45
LIBCOM_API void epicsStdCall epicsSocketDestroy(SOCKET s)
Definition: osdSock.c:117
#define INVALID_SOCKET
Definition: osdSock.h:32
#define SHUT_RD
Definition: osdSock.h:42
void epicsStdCall epicsMutexDestroy(epicsMutexId pmutexNode)
Destroy an epicsMutex semaphore.
Definition: epicsMutex.cpp:127
pvd::Status status
An EPICS-specific replacement for ANSI C&#39;s assert.
epicsExportAddress(int, logClientDebug)
unsigned connectCount
Definition: logClient.c:49
#define SOCK_EINPROGRESS
Definition: osdSock.h:60
int epicsSocketUnsentCount(SOCKET sock)
LIBCOM_API void epicsStdCall epicsSignalRaiseSigAlarm(struct epicsThreadOSD *)
Definition: osdSignal.cpp:19
struct sockaddr_in addr
Definition: logClient.c:42
#define printf
Definition: epicsStdio.h:41
enum epicsSocketSystemCallInterruptMechanismQueryInfo epicsSocketSystemCallInterruptMechanismQuery()
void epicsStdCall logClientSend(logClientId id, const char *message)
Definition: logClient.c:200
char msgBuf[0x4000]
Definition: logClient.c:41
#define NULL
Definition: catime.c:38
void * logClientId
Definition: logClient.h:30
epicsEventId stateChangeNotify
Definition: logClient.c:47
LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut)
Wait an the event or until the specified timeout period is over.
Definition: osdEvent.c:117
void epicsStdCall logClientFlush(logClientId id)
Definition: logClient.c:219
epicsTime begin
Definition: caConnTest.cpp:22
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
Miscellaneous macro definitions.
epicsSocketSystemCallInterruptMechanismQueryInfo
Definition: osiSock.h:47
LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize(epicsThreadStackSizeClass size)
Definition: osdThread.c:466
epicsThreadId epicsStdCall epicsThreadCreate(const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr, void *parm)
Definition: epicsThread.cpp:33
void epicsSocketConvertErrnoToString(char *pBuf, unsigned bufSize)
epicsMutexId mutex
Definition: logClient.c:44
#define SOCK_ECONNRESET
Definition: osdSock.h:53
LIBCOM_API SOCKET epicsStdCall epicsSocketCreate(int domain, int type, int protocol)
Definition: osdSock.c:71
#define epicsEventSignal(ID)
A synonym for epicsEventTrigger().
Definition: epicsEvent.h:172
int osiSockIoctl_t
Definition: osdSock.h:35
unsigned backlog
Definition: logClient.c:51
APIs for the epicsMutex mutual exclusion semaphore.
epicsEventId shutdownNotify
Definition: logClient.c:48
int epicsStdCall epicsTimeGetCurrent(epicsTimeStamp *pDest)
Get current time into *pDest.
SOCKET sock
Definition: logClient.c:45
#define epicsThreadPriorityLow
Definition: epicsThread.h:75
unsigned nextMsgIndex
Definition: logClient.c:50
Extended replacement for the Posix exit and atexit routines.
int SOCKET
Definition: osdSock.h:31
LIBCOM_API void epicsEventDestroy(epicsEventId id)
Destroy an epicsEvent and any resources it holds.
Definition: osdEvent.c:70
#define SOCK_EALREADY
Definition: osdSock.h:62
int logClientDebug
Definition: logClient.c:37
int connFailStatus
Definition: logClient.c:55
APIs for the epicsEvent binary semaphore.
#define SOCKERRNO
Definition: osdSock.h:33
char name[64]
Definition: logClient.c:43
void epicsStdCall iocLogPrefix(const char *prefix)
Definition: logClient.c:549
#define SOCK_EPIPE
Definition: osdSock.h:65
EPICS time stamp, for use from C code.
Definition: epicsTime.h:33
#define SOCK_EINTR
Definition: osdSock.h:64
#define TRUE
Definition: dbDefs.h:27
#define LOG_RESTART_DELAY
Definition: task_params.h:98
#define epicsMutexCreate()
Create an epicsMutex semaphore for use from C code.
Definition: epicsMutex.h:168
if(yy_init)
Definition: scan.c:972
LIBCOM_API double epicsStdCall epicsTimeDiffInSeconds(const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
Time difference between left and right in seconds.
Definition: epicsTime.cpp:1048
#define stderr
Definition: epicsStdio.h:32
OS-independent routines for ignoring Posix signals.
EPICS time-stamps (epicsTimeStamp), epicsTime C++ class and C functions for handling wall-clock times...
#define epicsAtExit(F, A)
Convenience macro to register a function and context value to be run when the process exits...
Definition: epicsExit.h:70
unsigned shutdown
Definition: logClient.c:53
logClientId epicsStdCall logClientCreate(struct in_addr server_addr, unsigned short server_port)
Definition: logClient.c:453
C++ and C descriptions for a thread.
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
unsigned shutdownConfirm
Definition: logClient.c:54
void epicsStdCall logClientShow(logClientId id, unsigned level)
Definition: logClient.c:516
LIBCOM_API epicsEventId epicsEventCreate(epicsEventInitialState initialState)
Create an epicsEvent for use from C code, or return NULL.
Definition: osdEvent.c:47
epicsThreadId restartThreadId
Definition: logClient.c:46
#define SOCK_EWOULDBLOCK
Definition: osdSock.h:51
unsigned epicsStdCall ipAddrToDottedIP(const struct sockaddr_in *paddr, char *pBuf, unsigned bufSize)
Definition: osiSock.c:144
unsigned connected
Definition: logClient.c:52
Exporting IOC objects.