1 /* Copyright (C) 2002 GFRN systems
2 
3    This program is free software; you can redistribute it and/or
4    modify it under the terms of the GNU General Public License as
5    published by the Free Software Foundation; either version 2 of the
6    License, or (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful, but
9    WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11    See the GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16    02111-1307, USA.
17 
18    The latest version of this program may be found at
19    http://CQiNet.sourceforge.net
20 
21    This is a generic main loop for an application that's primarily an
22    communications server.  It's primarily a just a small wrapper function
23    for the magic select() system call.
24 
25    $Log: main.c,v $
26    Revision 1.18  2012/12/09 16:21:46  wb6ymh
27    Added support for LogFileRolloverType '5' - monthly logs.
28 
29    Revision 1.17  2010/01/14 19:40:34  wb6ymh
30    Modified to call select() from SelectCall when built for profiling.
31    Hopefully tbd/tlb will spend most of its life in select so it's a
32    particulary interesting function call to get stats on.
33 
34    Revision 1.16  2009/02/08 16:42:42  wb6ymh
35    Added fflush to DumpMe to flush files before crashing.
36 
37    Revision 1.15  2008/05/14 18:10:43  wb6ymh
38    1. Changed a bunch of functions to take const char * rather than char *
39       to fix compile problems with GCC 4.2.x.
40    2. Added LogHex.
41 
42    Revision 1.14  2008/02/26 18:02:08  wb6ymh
43    Added dmalloc support.
44 
45    Revision 1.13  2007/12/27 15:59:13  wb6ymh
46    Modified ChildSigHandler to save and restore errno! The new waitpid
47    loop clobbers errno which causes ECHILD to overwrite EINTR causing
48    interrupted system calls to appear to return ECHILD causing confusion
49    and an exit!  God I hate signal and global variables!
50 
51    Revision 1.12  2007/12/22 17:27:15  wb6ymh
52    Modified ChildSigHandler to loop until waitpid finds no more children.
53    Previously we used wait and could miss children exiting if more than one
54    child exited before we ran.  Fixes event script failures.
55 
56    Revision 1.11  2007/12/01 00:54:44  wb6ymh
57    Changed a bunch of D2PRINTFs to D3PRINTFs to quiet things down at
58    debuglevel 2.
59 
60    Revision 1.10  2007/07/02 13:36:23  wb6ymh
61    Changed EOL convention back to Unix style.
62 
63    Revision 1.9  2007/07/02 13:34:32  wb6ymh
64    Added hooks for tbdQt project by Scott KI4LKF.
65 
66    Revision 1.8  2006/08/05 23:27:33  wb6ymh
67    Applied patch supplied by Satoshi Yasuda(7m3tjz/ad6gz) to correct logfile
68    filename generation for the daily log option.  Thanks Satoshi!
69 
70    Revision 1.7  2003/08/31 23:09:10  wb6ymh
71    Modified Err2String() to eliminate the use of sys_nerr.  It's not portable
72    and although it's exported by SunOS 5.9 it doesn't appear in any header
73    file.
74 
75    Revision 1.6  2003/08/16 13:37:15  wb6ymh
76    1. Added generalized handling to retrieve exit status from child processes.
77    2. Modified logging routine to allow it to be used under *nix as well as
78       Windoze.
79    3. Replaced ReadLine() kludge with GetRxData(). End of line detection and
80       processing is now the responsibility of the client.
81    4. Modified BufPrintf() to work with more flavors of snprintf correctly.
82       (The C99 standard defined the behavour differently than eariler
83       implementations.)
84    5. Replaced *nix syslog specific %m with calls to new Err2String() routine.
85 
86    Revision 1.5  2003/04/30 22:05:01  wb6ymh
87    Modified Log() to use the AppName configuration variable's value as the
88    application name when creating pid files rather than a hardcoded "tbd".
89 
90    Revision 1.4  2003/01/05 17:01:41  wb6ymh
91    1. Fixed infinite loop in ReadLine which occurs when read() fails with
92       anything other than no data available.
93    2. Modified Log() to avoid erasing the current day's log on startup for
94       LogFileRolloverType of 3 or 4.
95    3. Modified Log() to call DumpMe() when the log file grows too large
96       rather than attempting to do a graceful exit.  The assumption is that
97       the excessively large log was caused by an infinite loop and a
98       graceful exit is impossible anyway.
99 
100    Revision 1.3  2003/01/01 19:06:14  wb6ymh
101    1. Added timeout detection to ReadLine().
102    2. Modified ClientCompare to sort by SN rather than HisAdr. This is necessary
103       to allow multiple connections to the same remote host:port.
104    3. Added infinite loop detection logic to CallCurrentState().
105    4. Modified Log() to close and (possibly) reopen log when LogFileRolloverType
106       changes.
107    5. Added code to cause the application to exit if the current log file
108       exceeds 10mb in size.
109 
110    Revision 1.2  2002/12/18 00:53:36  wb6ymh
111    Added logging routines for Windows.
112 
113    Revision 1.1.1.1  2002/11/02 17:03:54  wb6ymh
114    Moved common source files to common subdirectory from src.
115 
116    Revision 1.6  2002/09/14 16:53:46  wb6ymh
117    Modified DeltaTime2String() to suppress day display unless days > 0.
118    Removed extra linefeeds in Log*() when logging directly to console.
119 
120    Revision 1.5  2002/09/02 15:32:19  wb6ymh
121    Changed the way clients are deleted from the ClientTree.  Previously the
122    mainloop kept a pointer to the next client to be processed while calling
123    the current client so the current client could deleted itself.  This caused
124    problems when the current client deleted what would have been the next client
125    to be processed (like when dirclient deleted hung clients when it tried to
126    start a new request).  Now DeleteClient() doesn't delete the current client,
127    it only flags it for deletion by the main loop when the mainloop finishes
128    with it.
129 
130    Revision 1.4  2002/08/31 23:48:33  wb6ymh
131    Added ability to wake on socket exceptions.  Added DeltaTime2String().
132    Added debug code to track the number of open sockets in OpenSockets variable.
133    Modified SigHandler to cause an immediate exit if a second SIGINT or
134    SIGTERM is received.
135 
136    Revision 1.3  2002/08/18 16:34:10  wb6ymh
137    Added DumpMe() to cause core dumps on demand for debugging.
138    Added call to DumpMe() in DeleteClient() when client being deleted isn't
139    found in AVL tree. (caused infinite loop when EchoLink server went down
140    recently).  I don't see the problem, the coredump will tell all.
141 
142    Revision 1.2  2002/08/12 17:10:47  wb6ymh
143    Made TimeNow a global.
144    Removed argument to GetTimeNow().
145 
146    Revision 1.1.1.1  2002/08/10 20:33:41  wb6ymh
147    initial import
148 
149 */
150 #include "common.h"
151 
152 #ifndef _WIN32
153    // FreeBSD, Linux, etc..
154    #include <stdio.h>
155    #include <stdlib.h>
156    #include <unistd.h>
157    #include <netinet/in.h>
158    #include <sys/socket.h>
159    #include <ctype.h>
160    #ifdef TIME_WITH_SYS_TIME
161       #include <sys/time.h>
162       #include <time.h>
163    #else
164       #ifdef HAVE_SYS_TIME_H
165          #include <sys/time.h>
166       #else
167          #include <time.h>
168       #endif
169    #endif
170    #include <signal.h>
171 #ifdef HAVE_SYS_SIGNAL_H
172    #include <sys/signal.h>
173 #endif
174    #include <string.h>
175 
176 #ifdef HAVE_LIMITS_H
177    #include <limits.h>
178 #else
179    #define  INT_MAX     0x7fffffff  /* assume 32 bit ints */
180 #endif
181 
182    #include <errno.h>
183    #include <syslog.h>
184    #include <stdarg.h>
185    #ifdef HAVE_SYS_TIMEB_H
186       #include <sys/timeb.h>
187    #endif
188    #include <sys/types.h>
189    #include <sys/wait.h>
190    #include <fcntl.h>
191 #else
192    // Windoze
193    #include <stdio.h>
194    #include <time.h>
195    #include <sys\timeb.h>
196    #include <io.h>
197    #include <winsock2.h>
198    #include <limits.h>
199    #include <stdarg.h>
200    #include <ctype.h>
201    #define  ProcessExitStatus()
202 #endif
203 
204 #include "avl.h"
205 #include "main.h"
206 #include "tbdthread_common.h"
207 
208 #ifdef USE_DMALLOC
209 #include "dmalloc.h"
210 #endif
211 
212 #define MAX_SEND_SIZE      1024
213 #define CLIENT_RX_BUF_SIZE 1024
214 
215 int SN;
216 int DebugLevel;
217 unsigned int Debug;  // Same as above but a bit map
218 int bShutdown = FALSE;
219 int bRunning = TRUE;
220 int bRefreshConfig = FALSE;
221 int bDaemon = FALSE;
222 
223 time_t BootTime;
224 struct timeval TimeNow;
225 
226 struct avl_table *ClientTree;
227 
228 ClientInfo *pClientDebug = NULL;
229 int OpenSockets = 0;
230 
231 
232 void CallCurrentState(ClientInfo *pClient);
233 
234 #ifndef _WIN32
235 // FreeBSD, Linux, etc..
236 void SigHandler(int Signal);
237 void ChildSigHandler(int Signal);
238 void ProcessExitStatus();
239 int LogFileRolloverType = 0;  // use syslog
240 
241 #define MAX_CHILDREN 16
242 // Exit status ring buffer
243 struct {
244    int ExitStatus;
245    int Pid;
246 } ChildExitStatus[MAX_CHILDREN];
247 int CurrentChildCount = 0;
248 
249 int ExitStatWr = 0;
250 int ExitStatRd = 0;
251 int bExitStatRingOvfl = 0;
252 int ExitStatRingOvfls = 0;
253 
254 struct {
255    int ExitStatus;
256    int Pid;
257    ExitCB ExitCB;
258 } ExitCallBacks[MAX_CHILDREN];
259 
260 
261 // child exit callbacks
262 
263 #else
264 BOOL SigHandler(DWORD Type);
265 int LogFileRolloverType = -1;  // no rollover, single logfile
266 #endif
267 
268 void vLog(const char *fmt,va_list args);
269 
270 #ifdef PROFILED_BUILD
271 // wrap certain system calls in a routine so the profiling code can
272 // track them.
SelectCall(int a1,fd_set * a2,fd_set * a3,fd_set * a4,struct timeval * a5)273 int SelectCall(int a1,fd_set *a2,fd_set *a3,fd_set *a4,struct timeval *a5)
274 {
275    return select(a1,a2,a3,a4,a5);
276 }
277 
278 #define select(a1,a2,a3,a4,a5) SelectCall(a1,a2,a3,a4,a5)
279 
280 #endif
281 
MainLoopInit()282 int MainLoopInit()
283 {
284 #ifndef _WIN32
285    struct sigaction SigAction;
286 
287 // Setup signal handlers using sigaction() rather than the signal() quagmire!
288 
289    SigAction.sa_handler = SigHandler;
290    SigAction.sa_flags = 0;
291    sigemptyset(&SigAction.sa_mask);
292 
293    sigaction(SIGHUP,&SigAction,NULL);
294    sigaction(SIGTERM,&SigAction,NULL);
295    sigaction(SIGINT,&SigAction,NULL);
296 
297    SigAction.sa_handler = ChildSigHandler;
298    sigaction(SIGCHLD,&SigAction,NULL);
299 #else
300    SetConsoleCtrlHandler((PHANDLER_ROUTINE) SigHandler,TRUE);                           // add to list
301 #endif
302    BootTime = time(NULL);
303    return 0;
304 }
305 
MainLoop()306 int MainLoop()
307 {
308    fd_set ReadFDset;
309    fd_set WriteFDset;
310    fd_set ExceptFDset;
311    int Selret;
312    ClientInfo *pClient;
313    ClientInfo *pClient1;
314    struct avl_traverser avl_trans;
315    struct timeval SelTO;
316    long t;
317    int ReadWaitCnt;
318    int WriteWaitCnt;
319    int ExceptWaitCnt;
320    int Err = 0;
321    long TimeOutSeconds;
322 
323    while(bRunning) {
324       if(bRefreshConfig) {
325          bRefreshConfig = FALSE;
326          if(ReadConfig()) {
327             LOG_ERROR(("parse error reloading configuration file, exiting\n"));
328             Err = ERR_CONFIG_FILE;
329             break;
330          }
331       }
332 
333       if(bShutdown) {
334          bShutdown = FALSE;
335          LOG_NORM(("Starting shut down\n"));
336          Shutdown();
337          if(!bRunning) {
338             break;
339          }
340       }
341 
342       FD_ZERO(&ReadFDset);
343       FD_ZERO(&WriteFDset);
344       FD_ZERO(&ExceptFDset);
345 
346 #ifndef _WIN32
347       SelTO.tv_sec = INT_MAX;
348 #else
349    // No infinite wait in Windoze even if no clients request a timeout
350       SelTO.tv_sec = INT_MAX - 1;
351 #endif
352 
353       SelTO.tv_usec = 0;
354       ReadWaitCnt = WriteWaitCnt = ExceptWaitCnt = 0;
355 
356       pClient = (ClientInfo *) avl_t_first(&avl_trans,ClientTree);
357       while(pClient != NULL) {
358          pClient->bReadReady =
359          pClient->bWriteReady =
360          pClient->bTimeOut =
361          pClient->bNew = FALSE;
362 
363          if(pClient->bReadWait) {
364             ReadWaitCnt++;
365             FD_SET(pClient->Socket,&ReadFDset);
366          }
367 
368          if(pClient->bWriteWait) {
369             WriteWaitCnt++;
370             FD_SET(pClient->Socket,&WriteFDset);
371          }
372 
373          if(pClient->bExceptWait) {
374             ExceptWaitCnt++;
375             FD_SET(pClient->Socket,&ExceptFDset);
376          }
377 
378          if(pClient->bTimeWait) {
379          // Set SelTO to the shortest timeout requested
380             t = SelTO.tv_sec - pClient->Timeout.tv_sec;
381             if(t == 0) {
382                t = SelTO.tv_usec - pClient->Timeout.tv_usec;
383             }
384 
385             if(t > 0) {
386                SelTO = pClient->Timeout;
387             }
388          }
389          pClient = (ClientInfo *) avl_t_next(&avl_trans);
390       }
391 
392       if(SelTO.tv_sec != INT_MAX) {
393       // Calculate delta T to shortest timeout requested
394          GetTimeNow();
395          SelTO.tv_usec -= TimeNow.tv_usec;
396          SelTO.tv_sec -= TimeNow.tv_sec;
397          if(SelTO.tv_usec < 0) {
398             SelTO.tv_usec += 1000000;
399             SelTO.tv_sec--;
400          }
401 
402          if(SelTO.tv_sec < 0) {
403          // Timeout time has already passed
404             SelTO.tv_sec = SelTO.tv_usec = 0;
405          }
406          else if(SelTO.tv_sec == 0) {
407             if(SelTO.tv_usec < 0) {
408             // Timeout time has already passed
409                SelTO.tv_usec = 0;
410             }
411          }
412 
413 #ifdef _WIN32
414          // The Windoze control-C handler can't abort the select call so
415          // don't wait for more than a second so we can exit quickly when the
416          // user hits control-C
417 
418          if(SelTO.tv_sec > 1) {
419             SelTO.tv_sec = 1;
420             SelTO.tv_usec = 0;
421          }
422 #endif
423          TimeOutSeconds = SelTO.tv_sec;
424 
425          if(SelTO.tv_sec == 0 && SelTO.tv_usec == 0) {
426             D3PRINTF(("Calling select() %d read, %d write, %d except, "
427                       "immediate timeout\n",ReadWaitCnt,WriteWaitCnt,
428                       ExceptWaitCnt));
429          }
430          else {
431             D3PRINTF(("Calling select() %d read, %d write, %d except, "
432                       "%ld sec timeout\n",ReadWaitCnt,WriteWaitCnt,
433                       ExceptWaitCnt,TimeOutSeconds));
434          }
435          Selret = select(FD_SETSIZE,&ReadFDset,&WriteFDset,&ExceptFDset,&SelTO);
436       }
437       else {
438          D3PRINTF(("Calling select() %d read, %d write, %d except, no timeout\n",
439                   ReadWaitCnt,WriteWaitCnt,ExceptWaitCnt));
440          Selret = select(FD_SETSIZE,&ReadFDset,&WriteFDset,&ExceptFDset,NULL);
441       }
442 
443       GetTimeNow();
444       ProcessExitStatus();
445 
446 #ifndef _WIN32
447       if(Selret == SOCKET_ERROR) {
448          if(errno != EINTR) {
449             LOG_ERROR(("select() failed, %s",Err2String(errno)));
450             Err = ERROR_CODE;
451             break;
452          }
453 
454          if(!bRunning) {
455             break;
456          }
457          else {
458             continue;
459          }
460       }
461 
462 #else
463       if(Selret == SOCKET_ERROR) {
464          Err = ERROR_CODE;
465          LOG_ERROR(("select() failed, %s",Err2String(Err)));
466          break;
467       }
468 #endif
469 
470       pClient1 = (ClientInfo *) avl_t_first(&avl_trans,ClientTree);
471 
472       while((pClient = pClient1) != NULL) {
473          if(pClient->bNew) {
474          // Ignore this guy, he was created since the last time we called select.
475             pClient1 = (ClientInfo *) avl_t_next(&avl_trans);
476             continue;
477          }
478 
479          if(pClient->bReadWait && FD_ISSET(pClient->Socket,&ReadFDset)) {
480             pClient->bReadReady = TRUE;
481             D3PRINTF(("client %u socket ready for read\n",pClient->SN));
482          }
483 
484          if(pClient->bWriteWait && FD_ISSET(pClient->Socket,&WriteFDset)) {
485             pClient->bWriteReady = TRUE;
486             D3PRINTF(("client %u socket ready for write\n",pClient->SN));
487          }
488 
489          if(pClient->bExceptWait && FD_ISSET(pClient->Socket,&ExceptFDset)) {
490             pClient->bException = TRUE;
491             D3PRINTF(("client %u socket has an exception\n",pClient->SN));
492          }
493 
494 
495          if(pClient->bTimeWait &&
496             (TimeNow.tv_sec > pClient->Timeout.tv_sec ||
497             (TimeNow.tv_sec == pClient->Timeout.tv_sec &&
498              TimeNow.tv_usec >= pClient->Timeout.tv_usec)) )
499          {
500                pClient->bTimeOut = TRUE;
501                D3PRINTF(("client %u timer timeout\n",pClient->SN));
502          }
503 
504          if(pClient->bReadReady || pClient->bWriteReady ||
505             pClient->bException || pClient->bTimeOut)
506          {
507             pClient->bCurrent = TRUE;
508             CallCurrentState(pClient);
509             pClient->bCurrent = FALSE;
510          }
511 
512          pClient1 = (ClientInfo *) avl_t_next(&avl_trans);
513          if(pClient->bDelete) {
514          // Delete the last client
515             DeleteClient(pClient);
516          }
517       }
518    }
519    return Err;
520 }
521 
522 
DeleteClient(ClientInfo * pClient)523 int DeleteClient(ClientInfo *pClient)
524 {
525    if(pClient->bCurrent) {
526    // This is the current client, NEVER delete the current client while
527    // transversing the tree or the client tree will be corrupted.
528    // Just flag it for deletion later.
529       pClient->bDelete = TRUE;
530    }
531    else {
532       if(pClient->Socket != INVALID_SOCKET) {
533          CLOSE(pClient->Socket);
534          OpenSockets--;
535       }
536       if(pClient->bInClientTree && avl_delete(ClientTree,pClient) == NULL) {
537       // This should never happen, log it and core dump
538          LOG_ERROR(("DeleteClient(): avl_delete() failed to client %d\n",
539                     pClient->SN));
540          DumpMe();
541       }
542       else {
543          if(pClient->Buf != NULL) {
544             free(pClient->Buf);
545          }
546 
547          if(pClient->Cleanup != NULL) {
548             pClient->Cleanup(pClient);
549          }
550 
551          free(pClient);
552       }
553    }
554 
555    return FALSE;
556 }
557 
558 
SendBuffer(ClientInfo * pClient)559 int SendBuffer(ClientInfo *pClient)
560 {
561    int SendLen;
562    int BytesSent;
563    int Ret = 0;
564 
565    SET_WAIT4_WR(pClient);
566 
567    while((SendLen = pClient->BufSize - pClient->Count) > 0) {
568       if(SendLen > MAX_SEND_SIZE) {
569          SendLen = MAX_SEND_SIZE;
570       }
571       D3PRINTF(("SendBuffer(), sending %d of %d bytes.\n",SendLen,
572                pClient->BufSize));
573 
574       BytesSent = WRITE(pClient->Socket,&pClient->Buf[pClient->Count],SendLen);
575       if(BytesSent == SOCKET_ERROR ) {
576          if(ERROR_CODE == ERROR_WOULD_BLOCK) {
577          // buffer full, try again later
578             break;
579          }
580          else {
581          // Some kind of error other than buffer full
582             LOG_WARN(("SendBuffer(), WRITE() failed, %s",
583                       Err2String(ERROR_CODE)));
584             DeleteClient(pClient);
585             break;
586          }
587       }
588       else {
589          pClient->Count += SendLen;
590       }
591    }
592 
593    if(SendLen == 0) {
594    // We're done with the guy for now, close the socket
595       D3PRINTF(("SendBuffer(), send complete.\n"));
596       pClient->State = pClient->NextState;
597       Ret = 1;
598    }
599 
600    return Ret;
601 }
602 
603 
604 // Return true if more data read or an error occurs
GetRxData(ClientInfo * p)605 int GetRxData(ClientInfo *p)
606 {
607    int BytesRead;
608    int Ret = FALSE;
609 
610    if(p->Count > 0 && p->RetCount > 0) {
611    // move remaining data down to bottom of buffer
612       memmove(p->Buf,&p->Buf[p->RetCount],p->Count+1);
613    }
614    p->RetCount = 0;
615 
616    // Read some more data
617    BytesRead = READ(p->Socket,&p->Buf[p->Count],p->BufSize-p->Count-1);
618 
619    if(BytesRead > 0) {
620       p->Count += BytesRead;
621       p->Buf[p->Count] = 0;
622       Ret = TRUE;
623    }
624    else if(BytesRead == 0) {
625    // Connection has closed
626       D2PRINTF(("GetRxData(): Client %u connection closed.\n",p->SN));
627       p->Err = ERR_CONNECTION_CLOSED;
628       Ret = TRUE;
629    }
630    else {
631       if(ERROR_CODE == ERROR_WOULD_BLOCK) {
632       // Nothing available to read now, keep waiting
633          SET_WAIT4_RD(p);
634       }
635       else {
636       // Some kind of error other than no data available
637          LOG_WARN(("GetRxData(): READ() failed client %u, %s",
638                   p->SN,Err2String(ERROR_CODE)));
639          p->Err = ERROR_CODE;
640          Ret = TRUE;
641       }
642    }
643 
644    return Ret;
645 }
646 
647 
648 // Client compares by SN
ClientCompare(const void * avl_a,const void * avl_b,void * avl_param)649 int ClientCompare(const void *avl_a,const void *avl_b,void *avl_param)
650 {
651    ClientInfo *p_a = (ClientInfo *) avl_a;
652    ClientInfo *p_b = (ClientInfo *) avl_b;
653 
654    return p_a->SN - p_b->SN;
655 }
656 
657 
CreateNewClient()658 ClientInfo *CreateNewClient()
659 {
660    ClientInfo *pClient = malloc(sizeof(ClientInfo));
661 
662    if(pClient != NULL) {
663       memset(pClient,0,sizeof(ClientInfo));
664       pClient->SN = SN++;
665       pClient->Socket = INVALID_SOCKET;
666       pClient->bNew = TRUE;
667    }
668    return pClient;
669 }
670 
671 
CallCurrentState(ClientInfo * pClient)672 void CallCurrentState(ClientInfo *pClient)
673 {
674    int bLoop = FALSE;
675    int LoopCounter = 100000;
676 
677    do {
678       if(pClient->State == NULL) {
679          LOG_ERROR(("CallCurrentState(): Client %d attempted to call "
680                     "undefined state!\n",pClient->SN));
681          DumpMe();
682       }
683       else {
684          bLoop = pClient->State(pClient);
685       }
686    } while(bLoop && --LoopCounter);
687 
688    if(LoopCounter == 0) {
689       LOG_ERROR(("CallCurrentState(): Client %d looped in state 0x%lx!\n",
690                  pClient->SN,(long) pClient->State));
691       DumpMe();
692    }
693 }
694 
695 // Set a timeout for <Timeout> milliseconds from now
SetTimeout(ClientInfo * p,int Timeout)696 void SetTimeout(ClientInfo *p,int Timeout)
697 {
698    long microseconds;
699 
700    GetTimeNow();
701    microseconds = TimeNow.tv_usec + ((Timeout % 1000) * 1000);
702 
703    p->bTimeWait = TRUE;
704    p->Timeout.tv_usec = microseconds % 1000000;
705    p->Timeout.tv_sec  = TimeNow.tv_sec + (Timeout / 1000) +
706                         microseconds / 1000000;
707 }
708 
709 
GetTimeNow()710 void GetTimeNow()
711 {
712 #ifdef HAVE_GETTIMEOFDAY
713    struct timezone tz;
714    gettimeofday(&TimeNow,&tz);
715 #else
716    #ifdef HAVE_FTIME
717       // Use ftime() and convert
718       #ifndef _WIN32
719          struct timeb FTimeNow;
720          ftime(&FTimeNow);
721       #else
722          struct _timeb FTimeNow;
723          _ftime(&FTimeNow);
724       #endif
725 
726       TimeNow.tv_sec = FTimeNow.time;
727       TimeNow.tv_usec = FTimeNow.millitm * 1000;
728    #else
729    // How the heck do you get the time of day on this OS ?
730       #error Need either ftime() or gettimeofday() !
731    #endif
732 #endif
733 }
734 
DeltaTime2String(time_t StartTime,time_t EndTime,int bShort)735 char *DeltaTime2String(time_t StartTime, time_t EndTime, int bShort)
736 {
737    static char TimeString[] = "xxxx days, xx hours and xx minutes";
738    int Days, Hours, Minutes;
739 
740    time_t Seconds = EndTime - StartTime;
741    Days = Seconds / (60 * 60 * 24);
742    Seconds %= 60 * 60 * 24;
743    Hours = Seconds / (60 * 60);
744    Seconds %= 60 * 60;
745    Minutes = Seconds / 60;
746    Seconds %= 60;
747 
748    if(bShort) {
749       if(Days > 0) {
750          snprintf(TimeString,sizeof(TimeString),"%d/%d:%02d:%02d",
751                   Days,Hours,Minutes,(int) Seconds);
752       }
753       else {
754          snprintf(TimeString,sizeof(TimeString),"%d:%02d:%02d",
755                   Hours,Minutes,(int) Seconds);
756       }
757    }
758    else if(Days > 0) {
759       snprintf(TimeString,sizeof(TimeString),"%d days, %d hours and %d minutes",
760                Days,Hours,Minutes);
761    }
762    else if(Hours > 0) {
763       snprintf(TimeString,sizeof(TimeString),"%d hours and %d minutes",Hours,
764                Minutes);
765    }
766    else {
767       snprintf(TimeString,sizeof(TimeString),"%d minutes and %d seconds",
768                Minutes,(int) Seconds);
769    }
770 
771    return TimeString;
772 }
773 
TimeT2String(time_t time)774 char *TimeT2String(time_t time)
775 {
776    struct tm *pTm;
777    static char TimeString[] = "mm/dd hh:mm";
778 
779    if(time != 0) {
780       pTm = localtime(&time);
781       if(pTm != NULL) {
782          snprintf(TimeString,sizeof(TimeString),"%2d/%02d %2d:%02d",
783                  pTm->tm_mon + 1,pTm->tm_mday,pTm->tm_hour,pTm->tm_min);
784       }
785       else {
786          return "";
787       }
788    }
789    else {
790       return "";
791    }
792    return TimeString;
793 }
794 
TimeLapse2String(time_t StartTime,int bShort)795 char *TimeLapse2String(time_t StartTime,int bShort)
796 {
797    return DeltaTime2String(StartTime,TimeNow.tv_sec,bShort);
798 }
799 
OpComplete(ClientInfo * pClient,int Err)800 void OpComplete(ClientInfo *pClient, int Err)
801 {
802    pClient->Err = Err;
803    if(pClient->Complete != NULL) {
804       pClient->Complete(pClient);
805    }
806    else {
807       DeleteClient(pClient);
808    }
809 }
810 
811 
BufPrintf(ClientInfo * p,const char * fmt,...)812 void BufPrintf(ClientInfo *p,const char *fmt,...)
813 {
814    va_list args;
815    int   Wrote;
816 
817    va_start(args,fmt);
818 
819    if(p->Count < p->BufSize) {
820       Wrote = vsnprintf(&p->Buf[p->Count],p->BufSize-p->Count-1,fmt,args);
821       if(Wrote > 0 && p->Count + Wrote < p->BufSize) {
822          p->Count += Wrote;
823       }
824       p->Buf[p->Count] = 0;
825    }
826    va_end(args);
827 }
828 
829 
830 #ifndef _WIN32
LogNorm(const char * fmt,...)831 void LogNorm(const char *fmt, ...)
832 {
833    va_list args;
834    va_start(args,fmt);
835 
836    if(LogFileRolloverType == 0) {
837       if(bDaemon) {
838          vsyslog(LOG_INFO,fmt,args);
839       }
840       else {
841          vprintf(fmt,args);
842       }
843    }
844    else {
845       vLog(fmt,args);
846    }
847    va_end(args);
848 }
849 
LogWarn(const char * fmt,...)850 void LogWarn(const char *fmt, ...)
851 {
852    va_list args;
853    va_start(args,fmt);
854 
855    if(LogFileRolloverType == 0) {
856       if(bDaemon) {
857          vsyslog(LOG_WARNING,fmt,args);
858       }
859       else {
860          vprintf(fmt,args);
861       }
862    }
863    else {
864       vLog(fmt,args);
865    }
866    va_end(args);
867 }
868 
LogErr(const char * fmt,...)869 void LogErr(const char *fmt, ...)
870 {
871    va_list args;
872    va_start(args,fmt);
873 
874    if(LogFileRolloverType == 0) {
875       if(bDaemon) {
876          vsyslog(LOG_ERR,fmt,args);
877       }
878       else {
879          vprintf(fmt,args);
880       }
881    }
882    else {
883       vLog(fmt,args);
884    }
885    va_end(args);
886 }
887 
888 #endif
889 
LogHex(void * AdrIn,int Len)890 void LogHex(void *AdrIn,int Len)
891 {
892    char Line[80];
893    char *cp;
894    unsigned char *Adr = (unsigned char *) AdrIn;
895    int i = 0;
896    int j;
897 
898    while(i < Len) {
899       cp = Line;
900       for(j = 0; j < 16; j++) {
901          if((i + j) == Len) {
902             break;
903          }
904          sprintf(cp,"%02x ",Adr[i+j]);
905          cp += 3;
906       }
907 
908       *cp++ = ' ';
909       for(j = 0; j < 16; j++) {
910          if((i + j) == Len) {
911             break;
912          }
913          if(isprint(Adr[i+j])) {
914             *cp++ = Adr[i+j];
915          }
916          else {
917             *cp++ = '.';
918          }
919       }
920       *cp = 0;
921       LOG_ERROR(("%s\n",Line));
922       i += 16;
923    }
924 }
925 
926 FILE *LogFp = NULL;
927 
928 static char *LogNames[] = {
929    "sun.log",
930    "mon.log",
931    "tue.log",
932    "wed.log",
933    "thr.log",
934    "fri.log",
935    "sat.log"
936 };
937 
938 static char *MonthNames[] = {
939    "Jan",
940    "Feb",
941    "Mar",
942    "Apr",
943    "May",
944    "Jun",
945    "Jul",
946    "Aug",
947    "Sep",
948    "Oct",
949    "Nov",
950    "Dec"
951 };
952 
953 /*
954 We have a bunch of possible logfile formats selected by the configuration
955 file variable LogFileRolloverType.
956    0 - logging disabled.
957    1 - Never: single logfile, filename = tbd.log
958    2 - Never: new logfile opened everyday, filename = tbdmmddyy.log
959    3 - Daily: new logfile opened everyday, filename = tbd.log, tbd.bak
960    4 - Weekly: new logfile opened everyday, filename = mon.log, tues.log, ...
961 */
vLog(const char * fmt,va_list args)962 void vLog(const char *fmt,va_list args)
963 {
964    int WriteLen;
965    char  Temp[1024];
966    char  Temp1[1024];
967    time_t ltime;
968    struct tm *tm;
969    static int LastLogDay = -1;
970    static int LastLogType;
971 
972    time(&ltime);
973    tm = localtime(&ltime);
974 
975    if(LogFileRolloverType != 0 &&
976          (LogFp == NULL ||
977           tm->tm_mday != LastLogDay ||
978           LastLogType != LogFileRolloverType))
979    {
980       LastLogType = LogFileRolloverType;
981 
982       if(LogFp != NULL) {
983          fclose(LogFp);
984          LogFp = NULL;
985       }
986 
987    // Assume the name will be tbd.log
988       snprintf(Temp,sizeof(Temp),"%s.log",AppName == NULL ? "tbd" : AppName);
989 
990       switch(LogFileRolloverType) {
991          case 2:  // daily log
992             snprintf(Temp,sizeof(Temp),"%s%02d%02d%02d.log",AppName,
993                      tm->tm_mon+1,tm->tm_mday,tm->tm_year % 100);
994             break;
995 
996          case 3:  // daily log
997             if(LastLogDay != -1 && tm->tm_mday != LastLogDay) {
998             // A new day is born, rename the old log to .bak and open a new log
999                snprintf(Temp1,sizeof(Temp1),"%s.bak",AppName);
1000                unlink(Temp1);
1001                rename(Temp,Temp1);
1002             }
1003             break;
1004 
1005          case 4:  // weekly
1006             if(LastLogDay != -1 && tm->tm_mday != LastLogDay) {
1007             // delete last weeks log
1008                unlink(LogNames[tm->tm_wday]);
1009             }
1010             strcpy(Temp,LogNames[tm->tm_wday]);
1011             break;
1012 
1013 			case 5:	// Monthly
1014             snprintf(Temp,sizeof(Temp),"%s%02d%02d.log",AppName,
1015                      tm->tm_mon+1,tm->tm_year % 100);
1016 				break;
1017       }
1018 
1019       LastLogDay = tm->tm_mday;
1020       LogFp = FOPEN(Temp,"a");
1021    }
1022 
1023    WriteLen = vsnprintf(Temp,sizeof(Temp),fmt,args);
1024 
1025 /* KI4LKF: same function for either Win GUI or X11 Gui,
1026    but just in case */
1027 #ifdef _WIN32
1028    #ifdef _WIN32GUI
1029    UpdateGui(GUIUPDATETYPE_LOG,Temp,WriteLen);
1030    #endif
1031 #else
1032    #ifdef _X11GUI
1033    UpdateGui(GUIUPDATETYPE_LOG,Temp,WriteLen);
1034    #endif
1035 #endif
1036 
1037    if(!bDaemon && isatty(fileno(stdin))) {
1038       printf("%d:%02d:%02d %s",tm->tm_hour,tm->tm_min,tm->tm_sec,Temp);
1039    }
1040 
1041    if(LogFp != NULL) {
1042       if(LogFileRolloverType == 1 || LogFileRolloverType == -1) {
1043       // one big file log date & time
1044          fprintf(LogFp,"%s %d %d:%02d:%02d %s",MonthNames[tm->tm_mon],
1045                  tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,Temp);
1046       }
1047       else {
1048       // Not one big file, just log time
1049          fprintf(LogFp,"%d:%02d:%02d %s",tm->tm_hour,tm->tm_min,
1050                  tm->tm_sec,Temp);
1051       }
1052       fflush(LogFp);
1053 
1054       if(ftell(LogFp) > 10000000) {
1055       // shit, get out of dodge
1056          fprintf(LogFp,"LOG FILE OVERFLOW, shutting down !\n");
1057          fclose(LogFp);
1058          LogFp = NULL;
1059          if(isatty(fileno(stdin))) {
1060             printf("LOG FILE OVERFLOW, shutting down !\n");
1061          }
1062          DumpMe();
1063       }
1064    }
1065 }
1066 
Log(char * fmt,...)1067 void Log(char *fmt, ...)
1068 {
1069    va_list args;
1070    va_start(args,fmt);
1071 
1072    vLog(fmt,args);
1073    va_end(args);
1074 }
1075 
CloseLog()1076 void CloseLog()
1077 {
1078    if(LogFp != NULL) {
1079       fclose(LogFp);
1080       LogFp = NULL;
1081    }
1082 }
1083 
1084 
1085 /* Force a code dump */
DumpMe()1086 void DumpMe()
1087 {
1088    char *cp = NULL;
1089 
1090    fflush(NULL);
1091    *cp = 0;
1092 }
1093 
Err2String(int err)1094 char *Err2String(int err)
1095 {
1096    static char Ret[80];
1097    char *ErrMsg = strerror(err);
1098 
1099    if(ErrMsg != NULL) {
1100       snprintf(Ret,sizeof(Ret),"%s (%d)\n",strerror(err),err);
1101    }
1102    else {
1103       snprintf(Ret,sizeof(Ret),"err=%d\n",err);
1104    }
1105    return Ret;
1106 }
1107 
1108 #ifndef _WIN32
SigHandler(int Signal)1109 void SigHandler(int Signal)
1110 {
1111    switch(Signal) {
1112       case SIGHUP:
1113          LOG_NORM(("Received SIGHUP, reloading conf file\n"));
1114          bRefreshConfig = TRUE;
1115          break;
1116 
1117       case SIGINT:
1118          LOG_NORM(("Received SIGINT, shutting down\n"));
1119          break;
1120 
1121       case SIGTERM:
1122          LOG_NORM(("Received SIGTERM, shutting down\n"));
1123          break;
1124    }
1125 
1126    if(Signal == SIGINT || Signal == SIGTERM) {
1127       if(!bShutdown) {
1128       // Try an orderly shutdown
1129          bShutdown = TRUE;
1130       }
1131       else {
1132       // Already tried an orderly shutdown, really exit now.
1133          bRunning = FALSE;
1134       }
1135    }
1136 }
1137 
1138 int SigChilds = 0;
1139 int ChildExits = 0;
ChildSigHandler(int Signal)1140 void ChildSigHandler(int Signal)
1141 {
1142    int Status;
1143    int Pid;
1144 // NB: save errno, our waitpid loop is going to clobber it!
1145    int ErrnoSave = errno;
1146 
1147    SigChilds++;
1148 
1149    for(; ; ) {
1150    // Read the exit status
1151       if((Pid = waitpid(-1,&Status,WNOHANG)) == -1 || Pid == 0) {
1152          break;
1153       }
1154 
1155       ChildExits++;
1156       if(((ExitStatWr + 1) % MAX_CHILDREN) == ExitStatRd) {
1157       // status buffer overflow
1158          bExitStatRingOvfl = TRUE;
1159          ExitStatRingOvfls++;
1160       }
1161       else {
1162          ChildExitStatus[ExitStatWr].ExitStatus = Status;
1163          ChildExitStatus[ExitStatWr].Pid = Pid;
1164          ExitStatWr = (ExitStatWr + 1) % MAX_CHILDREN;
1165       }
1166    }
1167    errno = ErrnoSave;
1168 }
1169 
ProcessExitStatus()1170 void ProcessExitStatus()
1171 {
1172    int Status;
1173    int Pid;
1174    int i;
1175    ExitCB ExitCB;
1176 
1177    while(ExitStatRd != ExitStatWr) {
1178       Status = ChildExitStatus[ExitStatRd].ExitStatus;
1179       Pid = ChildExitStatus[ExitStatRd].Pid;
1180       ExitStatRd = (ExitStatRd + 1) % MAX_CHILDREN;
1181       for(i = 0; i < MAX_CHILDREN; i++) {
1182          if(ExitCallBacks[i].Pid == Pid && ExitCallBacks[i].ExitCB != NULL) {
1183             ExitCB = ExitCallBacks[i].ExitCB;
1184             ExitCallBacks[i].ExitCB = NULL;
1185             CurrentChildCount--;
1186             ExitCB(Pid,Status);
1187             break;
1188          }
1189       }
1190       if(i == MAX_CHILDREN) {
1191          LOG_ERROR(("Warning: Callback for PID %d not found\n",Pid));
1192       }
1193    }
1194 
1195    if(bExitStatRingOvfl) {
1196       bExitStatRingOvfl = FALSE;
1197       LOG_ERROR(("Error: The exit status ring buffer has overflowed %d times\n",
1198                  ExitStatRingOvfls));
1199    }
1200 }
1201 
1202 int CallBacksRegistered = 0;
RegisterExitCB(int Pid,ExitCB ExitCB)1203 int RegisterExitCB(int Pid,ExitCB ExitCB)
1204 {
1205    int i;
1206 
1207    CallBacksRegistered++;
1208 
1209    for(i = 0; i < MAX_CHILDREN; i++) {
1210       if(ExitCallBacks[i].ExitCB == NULL) {
1211          ExitCallBacks[i].Pid = Pid;
1212          ExitCallBacks[i].ExitCB = ExitCB;
1213          CurrentChildCount++;
1214          break;
1215       }
1216    }
1217 
1218    if(i == MAX_CHILDREN) {
1219       LOG_ERROR(("Error: RegisterExitCB() - ExitCallBacks array overflow\n"));
1220    }
1221 
1222    return i != MAX_CHILDREN;
1223 }
1224 
1225 //
FOPEN(const char * fmt,const char * mode)1226 FILE *FOPEN(const char *fmt,const char *mode)
1227 {
1228    FILE *fp;
1229 
1230    if((fp = fopen(fmt,mode)) != NULL) {
1231    // Set close on exec flag
1232       fcntl(fileno(fp),F_SETFD,FD_CLOEXEC);
1233    }
1234    return fp;
1235 }
1236 #else // Windoze
1237 
SigHandler(DWORD Type)1238 BOOL SigHandler(DWORD Type)
1239 {
1240    if(Type == CTRL_C_EVENT) {
1241    // Handle the CTRL+C signal.
1242       LOG_NORM(("Received SIGINT, shutting down\n"));
1243       if(!bShutdown) {
1244       // Try an orderly shutdown
1245          bShutdown = TRUE;
1246       }
1247       else {
1248       // Already tried an orderly shutdown, really exit now.
1249          return FALSE;
1250       }
1251       return TRUE;
1252    }
1253    return FALSE;
1254 }
1255 
1256 #endif
1257 
1258 
1259