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(<ime);
973 tm = localtime(<ime);
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