This is Unofficial EPICS BASE Doxygen Site
iocLogServer.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2011 UChicago Argonne LLC, 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 /* iocLogServer.c */
10 
11 /*
12  * archive logMsg() from several IOC's to a common rotating file
13  *
14  *
15  * Author: Jeffrey O. Hill
16  * Date: 080791
17  */
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <limits.h>
24 #include <time.h>
25 
26 #ifdef UNIX
27 #include <unistd.h>
28 #include <signal.h>
29 #endif
30 
31 #include "dbDefs.h"
32 #include "epicsAssert.h"
33 #include "fdmgr.h"
34 #include "envDefs.h"
35 #include "osiSock.h"
36 #include "epicsStdio.h"
37 
38 static unsigned short ioc_log_port;
39 static long ioc_log_file_limit;
40 static char ioc_log_file_name[512];
41 static char ioc_log_file_command[256];
42 
43 
44 struct iocLogClient {
45  int insock;
47  size_t nChar;
48  char recvbuf[1024];
49  char name[32];
50  char ascii_time[32];
51 };
52 
54  char outfile[256];
55  long filePos;
56  FILE *poutfile;
57  void *pfdctx;
60 };
61 
62 #define IOCLS_ERROR (-1)
63 #define IOCLS_OK 0
64 
65 static void acceptNewClient (void *pParam);
66 static void readFromClient(void *pParam);
67 static void logTime (struct iocLogClient *pclient);
68 static int getConfig(void);
69 static int openLogFile(struct ioc_log_server *pserver);
70 static void handleLogFileError(void);
71 static void envFailureNotify(const ENV_PARAM *pparam);
72 static void freeLogClient(struct iocLogClient *pclient);
73 static void writeMessagesToLog (struct iocLogClient *pclient);
74 
75 #ifdef UNIX
76 static int setupSIGHUP(struct ioc_log_server *);
77 static void sighupHandler(int);
78 static void serviceSighupRequest(void *pParam);
79 static int getDirectory(void);
80 static int sighupPipe[2];
81 #endif
82 
83 
84 
85 /*
86  *
87  * main()
88  *
89  */
90 int main(void)
91 {
92  struct sockaddr_in serverAddr; /* server's address */
93  struct timeval timeout;
94  int status;
95  struct ioc_log_server *pserver;
96 
97  osiSockIoctl_t optval;
98 
99  status = getConfig();
100  if (status<0) {
101  fprintf(stderr, "iocLogServer: EPICS environment underspecified\n");
102  fprintf(stderr, "iocLogServer: failed to initialize\n");
103  return IOCLS_ERROR;
104  }
105 
106  pserver = (struct ioc_log_server *)
107  calloc(1, sizeof *pserver);
108  if (!pserver) {
109  fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
110  return IOCLS_ERROR;
111  }
112 
113  pserver->pfdctx = (void *) fdmgr_init();
114  if (!pserver->pfdctx) {
115  fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
116  return IOCLS_ERROR;
117  }
118 
119  /*
120  * Open the socket. Use ARPA Internet address format and stream
121  * sockets. Format described in <sys/socket.h>.
122  */
123  pserver->sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
124  if (pserver->sock == INVALID_SOCKET) {
125  char sockErrBuf[64];
126  epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
127  fprintf(stderr, "iocLogServer: sock create err: %s\n", sockErrBuf);
128  free(pserver);
129  return IOCLS_ERROR;
130  }
131 
133 
134  /* Zero the sock_addr structure */
135  memset((void *)&serverAddr, 0, sizeof serverAddr);
136  serverAddr.sin_family = AF_INET;
137  serverAddr.sin_port = htons(ioc_log_port);
138 
139  /* get server's Internet address */
140  status = bind ( pserver->sock,
141  (struct sockaddr *)&serverAddr,
142  sizeof (serverAddr) );
143  if (status < 0) {
144  char sockErrBuf[64];
145  epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
146  fprintf(stderr, "iocLogServer: bind err: %s\n", sockErrBuf );
147  fprintf (stderr,
148  "iocLogServer: a server is already installed on port %u?\n",
149  (unsigned)ioc_log_port);
150  return IOCLS_ERROR;
151  }
152 
153  /* listen and accept new connections */
154  status = listen(pserver->sock, 10);
155  if (status < 0) {
156  char sockErrBuf[64];
157  epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
158  fprintf(stderr, "iocLogServer: listen err %s\n", sockErrBuf);
159  return IOCLS_ERROR;
160  }
161 
162  /*
163  * Set non blocking IO
164  * to prevent dead locks
165  */
166  optval = TRUE;
167  status = socket_ioctl(
168  pserver->sock,
169  FIONBIO,
170  &optval);
171  if (status < 0){
172  char sockErrBuf[64];
173  epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
174  fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf);
175  return IOCLS_ERROR;
176  }
177 
178 # ifdef UNIX
179  status = setupSIGHUP(pserver);
180  if (status < 0) {
181  return IOCLS_ERROR;
182  }
183 # endif
184 
185  status = openLogFile(pserver);
186  if (status < 0) {
187  fprintf(stderr,
188  "File access problems to `%s' because `%s'\n",
189  ioc_log_file_name,
190  strerror(errno));
191  return IOCLS_ERROR;
192  }
193 
194  status = fdmgr_add_callback(
195  pserver->pfdctx,
196  pserver->sock,
197  fdi_read,
198  acceptNewClient,
199  pserver);
200  if (status < 0) {
201  fprintf(stderr,
202  "iocLogServer: failed to add read callback\n");
203  return IOCLS_ERROR;
204  }
205 
206 
207  while (TRUE) {
208  timeout.tv_sec = 60; /* 1 min */
209  timeout.tv_usec = 0;
210  fdmgr_pend_event(pserver->pfdctx, &timeout);
211  fflush(pserver->poutfile);
212  }
213 }
214 
215 /*
216  * seekLatestLine (struct ioc_log_server *pserver)
217  */
218 static int seekLatestLine (struct ioc_log_server *pserver)
219 {
220  static const time_t invalidTime = (time_t) -1;
221  time_t theLatestTime = invalidTime;
222  long latestFilePos = -1;
223  int status;
224 
225  /*
226  * start at the beginning
227  */
228  rewind (pserver->poutfile);
229 
230  while (1) {
231  struct tm theDate;
232  int convertStatus;
233  char month[16];
234 
235  /*
236  * find the line in the file with the latest date
237  *
238  * this assumes ctime() produces dates of the form:
239  * DayName MonthName dayNum 24hourHourNum:minNum:secNum yearNum
240  */
241  convertStatus = fscanf (
242  pserver->poutfile, " %*s %*s %15s %d %d:%d:%d %d %*[^\n] ",
243  month, &theDate.tm_mday, &theDate.tm_hour,
244  &theDate.tm_min, &theDate.tm_sec, &theDate.tm_year);
245  if (convertStatus==6) {
246  static const char *pMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
247  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
248  static const unsigned nMonths = sizeof(pMonths)/sizeof(pMonths[0]);
249  time_t lineTime = (time_t) -1;
250  unsigned iMonth;
251 
252  for (iMonth=0; iMonth<nMonths; iMonth++) {
253  if ( strcmp (pMonths[iMonth], month)==0 ) {
254  theDate.tm_mon = iMonth;
255  break;
256  }
257  }
258  if (iMonth<nMonths) {
259  static const int tm_epoch_year = 1900;
260  if (theDate.tm_year>tm_epoch_year) {
261  theDate.tm_year -= tm_epoch_year;
262  theDate.tm_isdst = -1; /* dont know */
263  lineTime = mktime (&theDate);
264  if ( lineTime != invalidTime ) {
265  if (theLatestTime == invalidTime ||
266  difftime(lineTime, theLatestTime)>=0) {
267  latestFilePos = ftell (pserver->poutfile);
268  theLatestTime = lineTime;
269  }
270  }
271  else {
272  char date[128];
273  size_t nChar;
274  nChar = strftime (date, sizeof(date), "%a %b %d %H:%M:%S %Y\n", &theDate);
275  if (nChar>0) {
276  fprintf (stderr, "iocLogServer: strange date in log file: %s\n", date);
277  }
278  else {
279  fprintf (stderr, "iocLogServer: strange date in log file\n");
280  }
281  }
282  }
283  else {
284  fprintf (stderr, "iocLogServer: strange year in log file: %d\n", theDate.tm_year);
285  }
286  }
287  else {
288  fprintf (stderr, "iocLogServer: strange month in log file: %s\n", month);
289  }
290  }
291  else {
292  int c = fgetc (pserver->poutfile);
293 
294  /*
295  * bypass the line if it does not match the expected format
296  */
297  while ( c!=EOF && c!='\n' ) {
298  c = fgetc (pserver->poutfile);
299  }
300 
301  if (c==EOF) {
302  break;
303  }
304  }
305  }
306 
307  /*
308  * move to the proper location in the file
309  */
310  if (latestFilePos != -1) {
311  status = fseek (pserver->poutfile, latestFilePos, SEEK_SET);
312  if (status!=IOCLS_OK) {
313  fclose (pserver->poutfile);
314  pserver->poutfile = stderr;
315  return IOCLS_ERROR;
316  }
317  }
318  else {
319  status = fseek (pserver->poutfile, 0L, SEEK_END);
320  if (status!=IOCLS_OK) {
321  fclose (pserver->poutfile);
322  pserver->poutfile = stderr;
323  return IOCLS_ERROR;
324  }
325  }
326 
327  pserver->filePos = ftell (pserver->poutfile);
328 
329  if (theLatestTime==invalidTime) {
330  if (pserver->filePos!=0) {
331  fprintf (stderr, "iocLogServer: **** Warning ****\n");
332  fprintf (stderr, "iocLogServer: no recognizable dates in \"%s\"\n",
333  ioc_log_file_name);
334  fprintf (stderr, "iocLogServer: logging at end of file\n");
335  }
336  }
337 
338  return IOCLS_OK;
339 }
340 
341 
342 /*
343  * openLogFile()
344  *
345  */
346 static int openLogFile (struct ioc_log_server *pserver)
347 {
348  enum TF_RETURN ret;
349 
350  if (pserver->poutfile && pserver->poutfile != stderr){
351  fclose (pserver->poutfile);
352  pserver->poutfile = NULL;
353  }
354 
355  pserver->poutfile = fopen(ioc_log_file_name, "r+");
356  if (pserver->poutfile) {
357  fclose (pserver->poutfile);
358  pserver->poutfile = NULL;
359  ret = truncateFile (ioc_log_file_name, ioc_log_file_limit);
360  if (ret==TF_ERROR) {
361  return IOCLS_ERROR;
362  }
363  pserver->poutfile = fopen(ioc_log_file_name, "r+");
364  }
365  else {
366  pserver->poutfile = fopen(ioc_log_file_name, "w");
367  }
368 
369  if (!pserver->poutfile) {
370  pserver->poutfile = stderr;
371  return IOCLS_ERROR;
372  }
373  strcpy (pserver->outfile, ioc_log_file_name);
374  pserver->max_file_size = ioc_log_file_limit;
375 
376  return seekLatestLine (pserver);
377 }
378 
379 
380 /*
381  * handleLogFileError()
382  *
383  */
384 static void handleLogFileError(void)
385 {
386  fprintf(stderr,
387  "iocLogServer: log file access problem (errno=%s)\n",
388  strerror(errno));
389  exit(IOCLS_ERROR);
390 }
391 
392 
393 
394 /*
395  * acceptNewClient()
396  *
397  */
398 static void acceptNewClient ( void *pParam )
399 {
400  struct ioc_log_server *pserver = (struct ioc_log_server *) pParam;
401  struct iocLogClient *pclient;
402  osiSocklen_t addrSize;
403  struct sockaddr_in addr;
404  int status;
405  osiSockIoctl_t optval;
406 
407  pclient = ( struct iocLogClient * ) malloc ( sizeof ( *pclient ) );
408  if ( ! pclient ) {
409  return;
410  }
411 
412  addrSize = sizeof ( addr );
413  pclient->insock = epicsSocketAccept ( pserver->sock, (struct sockaddr *)&addr, &addrSize );
414  if ( pclient->insock==INVALID_SOCKET || addrSize < sizeof (addr) ) {
415  static unsigned acceptErrCount;
416  static int lastErrno;
417  int thisErrno;
418 
419  free ( pclient );
421  return;
422  }
423 
424  thisErrno = SOCKERRNO;
425  if ( acceptErrCount % 1000 || lastErrno != thisErrno ) {
426  fprintf ( stderr, "Accept Error %d\n", SOCKERRNO );
427  }
428  acceptErrCount++;
429  lastErrno = thisErrno;
430 
431  return;
432  }
433 
434  /*
435  * Set non blocking IO
436  * to prevent dead locks
437  */
438  optval = TRUE;
439  status = socket_ioctl(
440  pclient->insock,
441  FIONBIO,
442  &optval);
443  if(status<0){
444  char sockErrBuf[64];
445  epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
446  fprintf(stderr, "%s:%d ioctl FBIO client er %s\n",
447  __FILE__, __LINE__, sockErrBuf);
448  epicsSocketDestroy ( pclient->insock );
449  free(pclient);
450  return;
451  }
452 
453  pclient->pserver = pserver;
454  pclient->nChar = 0u;
455 
456  ipAddrToA (&addr, pclient->name, sizeof(pclient->name));
457 
458  logTime(pclient);
459 
460 #if 0
461  status = fprintf(
462  pclient->pserver->poutfile,
463  "%s %s ----- Client Connect -----\n",
464  pclient->name,
465  pclient->ascii_time);
466  if(status<0){
467  handleLogFileError();
468  }
469 #endif
470 
471  /*
472  * turn on KEEPALIVE so if the client crashes
473  * this task will find out and exit
474  */
475  {
476  long true = 1;
477 
478  status = setsockopt(
479  pclient->insock,
480  SOL_SOCKET,
481  SO_KEEPALIVE,
482  (char *)&true,
483  sizeof(true) );
484  if(status<0){
485  fprintf(stderr, "Keepalive option set failed\n");
486  }
487  }
488 
489  status = shutdown(pclient->insock, SHUT_WR);
490  if(status<0){
491  char sockErrBuf[64];
492  epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
493  fprintf (stderr, "%s:%d shutdown err %s\n", __FILE__, __LINE__,
494  sockErrBuf);
495  epicsSocketDestroy ( pclient->insock );
496  free(pclient);
497 
498  return;
499  }
500 
501  status = fdmgr_add_callback(
502  pserver->pfdctx,
503  pclient->insock,
504  fdi_read,
505  readFromClient,
506  pclient);
507  if (status<0) {
508  epicsSocketDestroy ( pclient->insock );
509  free(pclient);
510  fprintf(stderr, "%s:%d client fdmgr_add_callback() failed\n",
511  __FILE__, __LINE__);
512  return;
513  }
514 }
515 
516 
517 
518 /*
519  * readFromClient()
520  *
521  */
522 #define NITEMS 1
523 
524 static void readFromClient(void *pParam)
525 {
526  struct iocLogClient *pclient = (struct iocLogClient *)pParam;
527  int recvLength;
528  int size;
529 
530  logTime(pclient);
531 
532  size = (int) (sizeof(pclient->recvbuf) - pclient->nChar);
533  recvLength = recv(pclient->insock,
534  &pclient->recvbuf[pclient->nChar],
535  size,
536  0);
537  if (recvLength <= 0) {
538  if (recvLength<0) {
539  int errnoCpy = SOCKERRNO;
540  if (errnoCpy==SOCK_EWOULDBLOCK || errnoCpy==SOCK_EINTR) {
541  return;
542  }
543  if (errnoCpy != SOCK_ECONNRESET &&
544  errnoCpy != SOCK_ECONNABORTED &&
545  errnoCpy != SOCK_EPIPE &&
546  errnoCpy != SOCK_ETIMEDOUT
547  ) {
548  char sockErrBuf[64];
549  epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
550  fprintf(stderr,
551  "%s:%d socket=%d size=%d read error=%s\n",
552  __FILE__, __LINE__, pclient->insock,
553  size, sockErrBuf);
554  }
555  }
556  /*
557  * disconnect
558  */
559  freeLogClient (pclient);
560  return;
561  }
562 
563  pclient->nChar += (size_t) recvLength;
564 
565  writeMessagesToLog (pclient);
566 }
567 
568 /*
569  * writeMessagesToLog()
570  */
571 static void writeMessagesToLog (struct iocLogClient *pclient)
572 {
573  int status;
574  size_t lineIndex = 0;
575 
576  while (TRUE) {
577  size_t nchar;
578  size_t nTotChar;
579  size_t crIndex;
580  int ntci;
581 
582  if ( lineIndex >= pclient->nChar ) {
583  pclient->nChar = 0u;
584  break;
585  }
586 
587  /*
588  * find the first carrage return and create
589  * an entry in the log for the message associated
590  * with it. If a carrage return does not exist and
591  * the buffer isnt full then move the partial message
592  * to the front of the buffer and wait for a carrage
593  * return to arrive. If the buffer is full and there
594  * is no carrage return then force the message out and
595  * insert an artificial carrage return.
596  */
597  nchar = pclient->nChar - lineIndex;
598  for ( crIndex = lineIndex; crIndex < pclient->nChar; crIndex++ ) {
599  if ( pclient->recvbuf[crIndex] == '\n' ) {
600  break;
601  }
602  }
603  if ( crIndex < pclient->nChar ) {
604  nchar = crIndex - lineIndex;
605  }
606  else {
607  nchar = pclient->nChar - lineIndex;
608  if ( nchar < sizeof ( pclient->recvbuf ) ) {
609  if ( lineIndex != 0 ) {
610  pclient->nChar = nchar;
611  memmove ( pclient->recvbuf,
612  & pclient->recvbuf[lineIndex], nchar);
613  }
614  break;
615  }
616  }
617 
618  /*
619  * reset the file pointer if we hit the end of the file
620  */
621  nTotChar = strlen(pclient->name) +
622  strlen(pclient->ascii_time) + nchar + 3u;
623  assert (nTotChar <= INT_MAX);
624  ntci = (int) nTotChar;
625  if ( pclient->pserver->max_file_size && pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) {
626  if ( pclient->pserver->max_file_size >= pclient->pserver->filePos ) {
627  unsigned nPadChar;
628  /*
629  * this gets rid of leftover junk at the end of the file
630  */
631  nPadChar = pclient->pserver->max_file_size - pclient->pserver->filePos;
632  while (nPadChar--) {
633  status = putc ( ' ', pclient->pserver->poutfile );
634  if ( status == EOF ) {
635  handleLogFileError();
636  }
637  }
638  }
639 
640 # ifdef DEBUG
641  fprintf ( stderr,
642  "ioc log server: resetting the file pointer\n" );
643 # endif
644  fflush ( pclient->pserver->poutfile );
645  rewind ( pclient->pserver->poutfile );
646  pclient->pserver->filePos = ftell ( pclient->pserver->poutfile );
647  }
648 
649  /*
650  * NOTE: !! change format string here then must
651  * change nTotChar calc above !!
652  */
653  assert (nchar<INT_MAX);
654  status = fprintf(
655  pclient->pserver->poutfile,
656  "%s %s %.*s\n",
657  pclient->name,
658  pclient->ascii_time,
659  (int) nchar,
660  &pclient->recvbuf[lineIndex]);
661  if (status<0) {
662  handleLogFileError();
663  }
664  else {
665  if (status != ntci) {
666  fprintf(stderr, "iocLogServer: didnt calculate number of characters correctly?\n");
667  }
668  pclient->pserver->filePos += status;
669  }
670  lineIndex += nchar+1u;
671  }
672 }
673 
674 
675 /*
676  * freeLogClient ()
677  */
678 static void freeLogClient(struct iocLogClient *pclient)
679 {
680  int status;
681 
682 # ifdef DEBUG
683  if(length == 0){
684  fprintf(stderr, "iocLogServer: nil message disconnect\n");
685  }
686 # endif
687 
688  /*
689  * flush any left overs
690  */
691  if (pclient->nChar) {
692  /*
693  * this forces a flush
694  */
695  if (pclient->nChar<sizeof(pclient->recvbuf)) {
696  pclient->recvbuf[pclient->nChar] = '\n';
697  }
698  writeMessagesToLog (pclient);
699  }
700 
701  status = fdmgr_clear_callback(
702  pclient->pserver->pfdctx,
703  pclient->insock,
704  fdi_read);
705  if (status!=IOCLS_OK) {
706  fprintf(stderr, "%s:%d fdmgr_clear_callback() failed\n",
707  __FILE__, __LINE__);
708  }
709 
710  epicsSocketDestroy ( pclient->insock );
711 
712  free (pclient);
713 
714  return;
715 }
716 
717 
718 /*
719  *
720  * logTime()
721  *
722  */
723 static void logTime(struct iocLogClient *pclient)
724 {
725  time_t sec;
726  char *pcr;
727  char *pTimeString;
728 
729  sec = time (NULL);
730  pTimeString = ctime (&sec);
731  strncpy (pclient->ascii_time,
732  pTimeString,
733  sizeof (pclient->ascii_time) );
734  pclient->ascii_time[sizeof(pclient->ascii_time)-1] = '\0';
735  pcr = strchr(pclient->ascii_time, '\n');
736  if (pcr) {
737  *pcr = '\0';
738  }
739 }
740 
741 
742 /*
743  *
744  * getConfig()
745  * Get Server Configuration
746  *
747  *
748  */
749 static int getConfig(void)
750 {
751  int status;
752  char *pstring;
753  long param;
754 
755  status = envGetLongConfigParam(
757  &param);
758  if(status>=0){
759  ioc_log_port = (unsigned short) param;
760  }
761  else {
762  ioc_log_port = 7004U;
763  }
764 
765  status = envGetLongConfigParam(
767  &ioc_log_file_limit);
768  if(status>=0){
769  if (ioc_log_file_limit < 0) {
770  envFailureNotify (&EPICS_IOC_LOG_FILE_LIMIT);
771  return IOCLS_ERROR;
772  }
773  }
774  else {
775  ioc_log_file_limit = 10000;
776  }
777 
778  pstring = envGetConfigParam(
780  sizeof ioc_log_file_name,
781  ioc_log_file_name);
782  if(pstring == NULL){
783  envFailureNotify(&EPICS_IOC_LOG_FILE_NAME);
784  return IOCLS_ERROR;
785  }
786 
787  /*
788  * its ok to not specify the IOC_LOG_FILE_COMMAND
789  */
790  pstring = envGetConfigParam(
792  sizeof ioc_log_file_command,
793  ioc_log_file_command);
794  return IOCLS_OK;
795 }
796 
797 
798 
799 /*
800  *
801  * failureNotify()
802  *
803  *
804  */
805 static void envFailureNotify(const ENV_PARAM *pparam)
806 {
807  fprintf(stderr,
808  "iocLogServer: EPICS environment variable `%s' undefined\n",
809  pparam->name);
810 }
811 
812 
813 
814 #ifdef UNIX
815 static int setupSIGHUP(struct ioc_log_server *pserver)
816 {
817  int status;
818  struct sigaction sigact;
819 
820  status = getDirectory();
821  if (status<0){
822  fprintf(stderr, "iocLogServer: failed to determine log file "
823  "directory\n");
824  return IOCLS_ERROR;
825  }
826 
827  /*
828  * Set up SIGHUP handler. SIGHUP will cause the log file to be
829  * closed and re-opened, possibly with a different name.
830  */
831  sigact.sa_handler = sighupHandler;
832  sigemptyset (&sigact.sa_mask);
833  sigact.sa_flags = 0;
834  if (sigaction(SIGHUP, &sigact, NULL)){
835  fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
836  return IOCLS_ERROR;
837  }
838 
839  status = pipe(sighupPipe);
840  if(status<0){
841  fprintf(stderr,
842  "iocLogServer: failed to create pipe because `%s'\n",
843  strerror(errno));
844  return IOCLS_ERROR;
845  }
846 
847  status = fdmgr_add_callback(
848  pserver->pfdctx,
849  sighupPipe[0],
850  fdi_read,
851  serviceSighupRequest,
852  pserver);
853  if(status<0){
854  fprintf(stderr,
855  "iocLogServer: failed to add SIGHUP callback\n");
856  return IOCLS_ERROR;
857  }
858  return IOCLS_OK;
859 }
860 
861 /*
862  *
863  * sighupHandler()
864  *
865  *
866  */
867 static void sighupHandler(int signo)
868 {
869  const char msg[] = "SIGHUP\n";
870  const ssize_t bytesWritten = write(sighupPipe[1], msg, sizeof(msg));
871  if (bytesWritten != sizeof(msg)) {
872  fprintf(stderr, "iocLogServer: failed to write to SIGHUP pipe because "
873  "`%s'\n", strerror(errno));
874  }
875 }
876 
877 
878 
879 /*
880  * serviceSighupRequest()
881  *
882  */
883 static void serviceSighupRequest(void *pParam)
884 {
885  struct ioc_log_server *pserver = (struct ioc_log_server *)pParam;
886  char buff[256];
887  int status;
888 
889  /*
890  * Read and discard message from pipe.
891  */
892  if (read(sighupPipe[0], buff, sizeof buff) <= 0) {
893  fprintf(stderr, "iocLogServer: failed to read from SIGHUP pipe because "
894  "`%s'\n", strerror(errno));
895  };
896 
897  /*
898  * Determine new log file name.
899  */
900  status = getDirectory();
901  if (status<0){
902  fprintf(stderr, "iocLogServer: failed to determine new log "
903  "file name\n");
904  return;
905  }
906 
907  /*
908  * Try (re)opening the file.
909  */
910  status = openLogFile(pserver);
911  if(status<0){
912  fprintf(stderr,
913  "File access problems to `%s' because `%s'\n",
914  ioc_log_file_name,
915  strerror(errno));
916  /* Revert to old filename */
917  strcpy(ioc_log_file_name, pserver->outfile);
918  status = openLogFile(pserver);
919  if(status<0){
920  fprintf(stderr,
921  "File access problems to `%s' because `%s'\n",
922  ioc_log_file_name,
923  strerror(errno));
924  return;
925  }
926  else {
927  fprintf(stderr,
928  "iocLogServer: re-opened old log file %s\n",
929  ioc_log_file_name);
930  }
931  }
932  else {
933  fprintf(stderr,
934  "iocLogServer: opened new log file %s\n",
935  ioc_log_file_name);
936  }
937 }
938 
939 
940 
941 /*
942  *
943  * getDirectory()
944  *
945  *
946  */
947 static int getDirectory(void)
948 {
949  FILE *pipe;
950  char dir[256];
951  int i;
952 
953  if (ioc_log_file_command[0] != '\0') {
954 
955  /*
956  * Use popen() to execute command and grab output.
957  */
958  pipe = popen(ioc_log_file_command, "r");
959  if (pipe == NULL) {
960  fprintf(stderr,
961  "Problem executing `%s' because `%s'\n",
962  ioc_log_file_command,
963  strerror(errno));
964  return IOCLS_ERROR;
965  }
966  if (fgets(dir, sizeof(dir), pipe) == NULL) {
967  fprintf(stderr,
968  "Problem reading o/p from `%s' because `%s'\n",
969  ioc_log_file_command,
970  strerror(errno));
971  (void) pclose(pipe);
972  return IOCLS_ERROR;
973  }
974  (void) pclose(pipe);
975 
976  /*
977  * Terminate output at first newline and discard trailing
978  * slash character if present..
979  */
980  for (i=0; dir[i] != '\n' && dir[i] != '\0'; i++)
981  ;
982  dir[i] = '\0';
983 
984  i = strlen(dir);
985  if (i > 1 && dir[i-1] == '/') dir[i-1] = '\0';
986 
987  /*
988  * Use output as directory part of file name.
989  */
990  if (dir[0] != '\0') {
991  char *name = ioc_log_file_name;
992  char *slash = strrchr(ioc_log_file_name, '/');
993  char temp[256];
994 
995  if (slash != NULL) name = slash + 1;
996  strcpy(temp,name);
997  sprintf(ioc_log_file_name,"%s/%s",dir,temp);
998  }
999  }
1000  return IOCLS_OK;
1001 }
1002 #endif
#define SHUT_WR
Definition: osdSock.h:45
LIBCOM_API void epicsStdCall epicsSocketDestroy(SOCKET s)
Definition: osdSock.c:117
LIBCOM_API int epicsStdCall fdmgr_pend_event(fdctx *pfdctx, struct timeval *ptimeout)
Definition: fdmgr.cpp:298
#define SOCK_ECONNABORTED
Definition: osdSock.h:59
#define INVALID_SOCKET
Definition: osdSock.h:32
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
#define SEEK_END
Definition: truncateFile.c:20
char outfile[256]
Definition: iocLogServer.c:54
pvd::Status status
An EPICS-specific replacement for ANSI C&#39;s assert.
int osiSocklen_t
Definition: osdSock.h:36
char * name
Name of the parameter.
Definition: envDefs.h:42
int i
Definition: scan.c:967
TF_RETURN
Definition: epicsStdio.h:70
LIBCOM_API const ENV_PARAM EPICS_IOC_LOG_FILE_COMMAND
char recvbuf[1024]
Definition: iocLogServer.c:48
Routines to get and set EPICS environment parameters.
#define SOCK_ETIMEDOUT
Definition: osdSock.h:54
LIBCOM_API enum TF_RETURN truncateFile(const char *pFileName, unsigned long size)
Definition: truncateFile.c:27
int main(void)
Definition: iocLogServer.c:90
#define NULL
Definition: catime.c:38
Definition: fdmgr.h:33
#define IOCLS_OK
Definition: iocLogServer.c:63
Miscellaneous macro definitions.
A structure to hold a single environment parameter.
Definition: envDefs.h:41
void epicsSocketConvertErrnoToString(char *pBuf, unsigned bufSize)
#define socket_ioctl(A, B, C)
Definition: osdSock.h:34
#define SOCK_ECONNRESET
Definition: osdSock.h:53
LIBCOM_API SOCKET epicsStdCall epicsSocketCreate(int domain, int type, int protocol)
Definition: osdSock.c:71
int osiSockIoctl_t
Definition: osdSock.h:35
LIBCOM_API void epicsStdCall epicsSocketEnableAddressReuseDuringTimeWaitState(SOCKET s)
LIBCOM_API int epicsStdCall epicsSocketAccept(int sock, struct sockaddr *pAddr, osiSocklen_t *addrlen)
Definition: osdSock.c:94
char ascii_time[32]
Definition: iocLogServer.c:50
BSD and SRV5 Unix timestamp.
Definition: epicsTime.h:52
int SOCKET
Definition: osdSock.h:31
LIBCOM_API int epicsStdCall fdmgr_clear_callback(fdctx *pfdctx, SOCKET fd, enum fdi_type fdi)
Definition: fdmgr.cpp:271
void date(const char *format)
#define SOCKERRNO
Definition: osdSock.h:33
LIBCOM_API const ENV_PARAM EPICS_IOC_LOG_FILE_LIMIT
size_t nChar
Definition: iocLogServer.c:47
struct ioc_log_server * pserver
Definition: iocLogServer.c:46
LIBCOM_API int epicsStdCall fdmgr_add_callback(fdctx *pfdctx, SOCKET fd, enum fdi_type fdi, pCallBackFDMgr pFunc, void *pParam)
Definition: fdmgr.cpp:229
#define SOCK_EPIPE
Definition: osdSock.h:65
#define SOCK_EINTR
Definition: osdSock.h:64
LIBCOM_API fdctx *epicsStdCall fdmgr_init(void)
Definition: fdmgr.cpp:156
char name[32]
Definition: iocLogServer.c:49
LIBCOM_API long epicsStdCall envGetLongConfigParam(const ENV_PARAM *pParam, long *pLong)
Get value of a long configuration parameter.
Definition: envSubr.c:303
#define TRUE
Definition: dbDefs.h:27
unsigned epicsStdCall ipAddrToA(const struct sockaddr_in *paddr, char *pBuf, unsigned bufSize)
Definition: osiSock.c:92
#define stderr
Definition: epicsStdio.h:32
LIBCOM_API char *epicsStdCall envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf)
Get value of a configuration parameter.
Definition: envSubr.c:139
LIBCOM_API const ENV_PARAM EPICS_IOC_LOG_FILE_NAME
LIBCOM_API const ENV_PARAM EPICS_IOC_LOG_PORT
#define SOCK_EWOULDBLOCK
Definition: osdSock.h:51
#define IOCLS_ERROR
Definition: iocLogServer.c:62