1 /*
2 **  The implementation of the innfeed EndPoint object class.
3 **
4 **  Written by James Brister <brister@vix.com>
5 **
6 **  The EndPoint class is what gives the illusion (sort of, kind of) of
7 **  threading.  Basically it controls a select loop and a set of EndPoint
8 **  objects.  Each EndPoint has a file descriptor it is interested in.  The
9 **  users of the EndPoint tell the EndPoints to notify them when a read or
10 **  write has been completed (or simple if the file descriptor is read or
11 **  write ready).
12 */
13 
14 #include "portable/system.h"
15 
16 #include "innfeed.h"
17 #include "portable/socket.h"
18 
19 #include <assert.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <sys/stat.h>
24 #include <sys/uio.h>
25 #include <syslog.h>
26 
27 #ifdef HAVE_LIMITS_H
28 #    include <limits.h>
29 #endif
30 
31 #ifdef HAVE_SYS_SELECT_H
32 #    include <sys/select.h>
33 #endif
34 
35 #ifdef HAVE_SYS_TIME_H
36 #    include <sys/time.h>
37 #endif
38 #include <time.h>
39 
40 #include "inn/innconf.h"
41 #include "inn/libinn.h"
42 #include "inn/messages.h"
43 
44 #include "buffer.h"
45 #include "configfile.h"
46 #include "endpoint.h"
47 #include "host.h"
48 
49 static const char *const timer_name[] = {"idle",   "blstats", "stsfile",
50                                          "newart", "readart", "prepart",
51                                          "read",   "write",   "cb"};
52 
53 #if !defined(NSIG)
54 #    define NSIG 32
55 #endif
56 
57 
58 /* This is the structure that is the EndPoint */
59 struct endpoint_s {
60     /* fields for managing multiple reads into the inBuffer. */
61     Buffer *inBuffer;         /* list of buffers to read into */
62     unsigned int inBufferIdx; /* where is list we're at. */
63     size_t inIndex;           /* where in current read we're at */
64     size_t inMinLen;          /* minimum amount to read */
65     size_t inAmtRead;         /* amount read so far */
66     EndpRWCB inCbk;           /* callback for when read complete */
67     void *inClientData;       /* callback data */
68 
69     /* fields for managing multiple writes from the outBuffer */
70     Buffer *outBuffer;         /* list of buffers to write */
71     unsigned int outBufferIdx; /* index into buffer list to start write */
72     size_t outIndex;           /* where in current buffer we write from */
73     size_t outSize;            /* total of all the buffers */
74     size_t outAmtWritten;      /* amount written so far */
75     EndpRWCB outProgressCbk;   /* callback when done */
76     EndpRWCB outDoneCbk;       /* callback when done */
77     void *outClientData;       /* callback data */
78 
79     EndpWorkCbk workCbk; /* callback to run if no I/O to do */
80     void *workData;      /* data for callback */
81 
82     int myFd;    /* the file descriptor we're handling */
83     int myErrno; /* the errno when I/O fails */
84 
85     double selectHits; /* indicates how often it's ready */
86 };
87 
88 
89 /* A private structure. These hold the information on the timer callbacks. */
90 typedef struct timerqelem_s {
91     TimeoutId id;              /* the id we gave out */
92     time_t when;               /* The time the timer should go off */
93     EndpTCB func;              /* the function to call */
94     void *data;                /* the client callback data */
95     struct timerqelem_s *next; /* next in the queue */
96 } * TimerElem, TimerElemStruct;
97 
98 static EndPoint mainEndPoint;
99 static bool mainEpIsReg = false;
100 unsigned int stdioFdMax = MAX_STDIO_FD;
101 time_t PrivateTime;
102 
103 typedef void (*sigfn)(int);
104 static sigfn *sigHandlers;
105 
106 static volatile sig_atomic_t *sigFlags;
107 
108 
109 /* private functions */
110 static IoStatus doRead(EndPoint endp);
111 static IoStatus doWrite(EndPoint endp);
112 static IoStatus doExcept(EndPoint endp);
113 static void pipeHandler(int s);
114 static void signalHandler(int s);
115 static int hitCompare(const void *v1, const void *v2);
116 static void reorderPriorityList(void);
117 static TimerElem newTimerElem(TimeoutId i, time_t w, EndpTCB f, void *d);
118 static TimeoutId timerElemAdd(time_t when, EndpTCB func, void *data);
119 static struct timeval *getTimeout(struct timeval *tout);
120 static void doTimeout(void);
121 static void handleSignals(void);
122 
123 static void endpointCleanup(void);
124 
125 
126 /* Private data */
127 static size_t maxEndPoints;
128 
129 static EndPoint *endPoints;    /* endpoints indexed on fd */
130 static EndPoint *priorityList; /* endpoints indexed on priority */
131 
132 static int absHighestFd = 0; /* never goes down */
133 static int highestFd = -1;
134 static unsigned int endPointCount = 0;
135 static unsigned int priorityCount = 0;
136 
137 static fd_set rdSet;
138 static fd_set wrSet;
139 static fd_set exSet;
140 
141 static int keepSelecting;
142 
143 static TimerElem timeoutQueue;
144 static TimerElem timeoutPool;
145 static TimeoutId nextId;
146 static int timeoutQueueLength;
147 
148 
149 /* Create a new EndPoint and hook it to the give file descriptor. All
150    fields are initialized to appropriate values.  On the first time this
151    function is called the global data structs that manages lists of
152    endpoints are intialized. */
153 static bool inited = false;
154 
155 EndPoint
newEndPoint(int fd)156 newEndPoint(int fd)
157 {
158     EndPoint ep;
159 
160     if (!inited) {
161         inited = true;
162         atexit(endpointCleanup);
163     }
164 
165     if (fd < 0)
166         return NULL;
167 
168     /* try to dup the fd to a larger number to leave lower values free for
169        broken stdio implementations. */
170     if (stdioFdMax > 0 && ((unsigned int) fd) <= stdioFdMax) {
171         int newfd = fcntl(fd, F_DUPFD, stdioFdMax + 1);
172         if (newfd >= 0) {
173             d_printf(1, "Dupped fd %d to %d\n", fd, newfd);
174             if (close(fd) != 0)
175                 syswarn("ME oserr close (%d)", fd);
176         } else {
177             d_printf(1, "Couldn't dup fd %d to above %d\n", fd, stdioFdMax);
178             newfd = fd;
179         }
180 
181         fd = newfd;
182     }
183 
184     if ((unsigned int) fd >= maxEndPoints) {
185         unsigned int i = maxEndPoints;
186 
187         maxEndPoints =
188             (((fd + 256) / 256) * 256); /* round up to nearest 256 */
189         if (endPoints == NULL) {
190             endPoints = xmalloc(sizeof(EndPoint) * maxEndPoints);
191             priorityList = xmalloc(sizeof(EndPoint) * maxEndPoints);
192         } else {
193             endPoints = xrealloc(endPoints, sizeof(EndPoint) * maxEndPoints);
194             priorityList =
195                 xrealloc(priorityList, sizeof(EndPoint) * maxEndPoints);
196         }
197 
198         for (; i < maxEndPoints; i++)
199             endPoints[i] = priorityList[i] = NULL;
200     }
201 
202     ASSERT(endPoints[fd] == NULL);
203 
204     if (fd > absHighestFd) {
205 
206 #if defined(FD_SETSIZE)
207         if ((unsigned int) fd >= FD_SETSIZE) {
208             warn("ME fd (%d) looks too big (%d -- FD_SETSIZE)", fd,
209                  FD_SETSIZE);
210             return NULL;
211         }
212 #else
213         if (fd > (sizeof(fd_set) * CHAR_BIT)) {
214             warn("ME fd (%d) looks too big (%d -- sizeof (fd_set) * CHAR_BIT)",
215                  fd, (sizeof(fd_set) * CHAR_BIT));
216             return NULL;
217         }
218 #endif
219 
220         absHighestFd = fd;
221     }
222 
223     ep = xcalloc(1, sizeof(struct endpoint_s));
224 
225     ep->inBuffer = NULL;
226     ep->inBufferIdx = 0;
227     ep->inIndex = 0;
228     ep->inMinLen = 0;
229     ep->inAmtRead = 0;
230     ep->inCbk = NULL;
231     ep->inClientData = NULL;
232 
233     ep->outBuffer = 0;
234     ep->outBufferIdx = 0;
235     ep->outIndex = 0;
236     ep->outSize = 0;
237     ep->outProgressCbk = NULL;
238     ep->outDoneCbk = NULL;
239     ep->outClientData = NULL;
240     ep->outAmtWritten = 0;
241 
242     ep->workCbk = NULL;
243     ep->workData = NULL;
244 
245     ep->myFd = fd;
246     ep->myErrno = 0;
247 
248     ep->selectHits = 0.0;
249 
250     endPoints[fd] = ep;
251     priorityList[priorityCount++] = ep;
252     endPointCount++;
253 
254     highestFd = (fd > highestFd ? fd : highestFd);
255 
256     return ep;
257 }
258 
259 
260 /* Delete the given endpoint. The files descriptor is closed and the two
261    Buffer arrays are released. */
262 
263 void
delEndPoint(EndPoint ep)264 delEndPoint(EndPoint ep)
265 {
266     unsigned int idx;
267 
268     if (ep == NULL)
269         return;
270 
271     ASSERT(endPoints[ep->myFd] == ep);
272 
273     if (mainEndPoint == ep)
274         mainEndPoint = NULL;
275 
276     if (ep->inBuffer != NULL)
277         freeBufferArray(ep->inBuffer);
278 
279     if (ep->outBuffer != NULL)
280         freeBufferArray(ep->outBuffer);
281 
282     close(ep->myFd);
283 
284     /* remove from selectable bits */
285     FD_CLR(ep->myFd, &rdSet);
286     FD_CLR(ep->myFd, &wrSet);
287     FD_CLR(ep->myFd, &exSet);
288 
289     /* Adjust the global arrays to account for deleted endpoint. */
290     endPoints[ep->myFd] = NULL;
291     if (ep->myFd == highestFd)
292         while (highestFd >= 0 && endPoints[highestFd] == NULL)
293             highestFd--;
294 
295     for (idx = 0; idx < priorityCount; idx++)
296         if (priorityList[idx] == ep)
297             break;
298 
299     ASSERT(idx < priorityCount);     /* i.e. was found */
300     ASSERT(priorityList[idx] == ep); /* redundant */
301 
302     /* this hole will removed in the reorder routine */
303     priorityList[idx] = NULL;
304 
305     endPointCount--;
306 
307     free(ep);
308 }
309 
310 int
endPointFd(EndPoint endp)311 endPointFd(EndPoint endp)
312 {
313     ASSERT(endp != NULL);
314 
315     return endp->myFd;
316 }
317 
318 
319 /* Request a read to be done next time there's data. The endpoint ENDP
320  * is what will do the read. BUFFERS is the array of Buffers the data
321  * should go into. FUNC is the callback function to call when the read
322  * is complete. CLIENTDATA is the client data to pass back into the
323  * callback function. MINLEN is the minimum amount of data to
324  * read. If MINLEN is 0 then then BUFFERS must be filled, otherwise at
325  * least MINLEN bytes must be read.
326  *
327  * BUFFERS can be NULL, in which case no read is actually done, but the
328  * callback function will be called still. This is useful for
329  * listening sockets.
330  *
331  * Returns 0 if the read couldn't be prepared (for example if a read
332  * is already outstanding).
333  */
334 
335 int
prepareRead(EndPoint endp,Buffer * buffers,EndpRWCB func,void * clientData,int minlen)336 prepareRead(EndPoint endp, Buffer *buffers, EndpRWCB func, void *clientData,
337             int minlen)
338 {
339     int bufferSizeTotal = 0;
340     int idx;
341 
342     ASSERT(endp != NULL);
343 
344     if (endp->inBuffer != NULL || FD_ISSET(endp->myFd, &rdSet))
345         return 0; /* something already there */
346 
347     for (idx = 0; buffers != NULL && buffers[idx] != NULL; idx++) {
348         size_t bs = bufferSize(buffers[idx]);
349         size_t bds = bufferDataSize(buffers[idx]);
350 
351         bufferSizeTotal += (bs - bds);
352     }
353 
354     endp->inBuffer = buffers;
355     endp->inBufferIdx = 0;
356     endp->inIndex = 0;
357     endp->inMinLen = (minlen > 0 ? minlen : bufferSizeTotal);
358     endp->inCbk = func;
359     endp->inAmtRead = 0;
360     endp->inClientData = clientData;
361 
362     FD_SET(endp->myFd, &rdSet);
363     if (InputFile == NULL)
364         FD_SET(endp->myFd, &exSet);
365 
366     return 1;
367 }
368 
369 
370 /* Request a write to be done at a later point. ENDP is the EndPoint to
371  * do the write. BUFFERS is the array of Buffers to write from. FUNC is
372  * the function to call when the write is complete. CLIENTDATA is some
373  * data to hand back to the callback function.
374  *
375  * If BUFFERS is NULL, then no write will actually by done, but the
376  * callback function will still be called. This is useful for
377  * connecting sockets.
378  *
379  * Returns 0 if the write couldn't be prepared (like if a write is
380  * still in process.
381  */
382 int
prepareWrite(EndPoint endp,Buffer * buffers,EndpRWCB progress,EndpRWCB done,void * clientData)383 prepareWrite(EndPoint endp, Buffer *buffers, EndpRWCB progress, EndpRWCB done,
384              void *clientData)
385 {
386     int bufferSizeTotal = 0;
387     int idx;
388 
389     ASSERT(endp != NULL);
390 
391     if (endp->outBuffer != NULL || FD_ISSET(endp->myFd, &wrSet))
392         return 0; /* something already there */
393 
394     for (idx = 0; buffers != NULL && buffers[idx] != NULL; idx++)
395         bufferSizeTotal += bufferDataSize(buffers[idx]);
396 
397     endp->outBuffer = buffers;
398     endp->outBufferIdx = 0;
399     endp->outIndex = 0;
400     endp->outProgressCbk = progress;
401     endp->outDoneCbk = done;
402     endp->outClientData = clientData;
403     endp->outSize = bufferSizeTotal;
404     endp->outAmtWritten = 0;
405 
406     FD_SET(endp->myFd, &wrSet);
407     FD_SET(endp->myFd, &exSet);
408 
409     return 1;
410 }
411 
412 
413 /* queue up a new timeout request. to go off at a specific time. */
414 TimeoutId
prepareWake(EndpTCB func,time_t timeToWake,void * clientData)415 prepareWake(EndpTCB func, time_t timeToWake, void *clientData)
416 {
417     TimeoutId id;
418     time_t now = theTime();
419 
420     if (now > timeToWake)
421         return 0;
422 
423     id = timerElemAdd(timeToWake, func, clientData);
424 
425 #if defined(INNFEED_DEBUG)
426     d_printf(1, "Preparing wake %d at date %ld for %ld seconds\n", (int) id,
427              (long) now, (long) (timeToWake - now));
428 #endif
429 
430     return id;
431 }
432 
433 
434 /* queue up a new timeout request to off TIMETOSLEEP seconds from now */
435 TimeoutId
prepareSleep(EndpTCB func,int timeToSleep,void * clientData)436 prepareSleep(EndpTCB func, int timeToSleep, void *clientData)
437 {
438     time_t now = theTime();
439     TimeoutId id;
440 
441     id = timerElemAdd(now + timeToSleep, func, clientData);
442 
443 #if defined(INNFEED_DEBUG)
444     d_printf(1, "Preparing sleep %d at date %ld for %ld seconds\n", (int) id,
445              (long) now, (long) timeToSleep);
446 #endif
447 
448     return id;
449 }
450 
451 
452 /* Updates an existing timeout request to go off TIMETOSLEEP seconds from
453    now, or queues a new request.  Always returns a new ID. */
454 TimeoutId
updateSleep(TimeoutId tid,EndpTCB func,int timeToSleep,void * clientData)455 updateSleep(TimeoutId tid, EndpTCB func, int timeToSleep, void *clientData)
456 {
457     if (tid == 0)
458         return prepareSleep(func, timeToSleep, clientData);
459     else {
460         /* XXX - quick and dirty but CPU wasteful implementation */
461         removeTimeout(tid);
462         return prepareSleep(func, timeToSleep, clientData);
463     }
464 }
465 
466 
467 /* Remove a timeout that was previously prepared. 0 is a legal value that
468    is just ignored. */
469 bool
removeTimeout(TimeoutId tid)470 removeTimeout(TimeoutId tid)
471 {
472     TimerElem n = timeoutQueue;
473     TimerElem p = NULL;
474 
475     if (tid == 0)
476         return true;
477 
478     while (n != NULL && n->id != tid) {
479         p = n;
480         n = n->next;
481     }
482 
483     if (n == NULL)
484         return false;
485 
486     if (p == NULL) /* at the head. */
487         timeoutQueue = n->next;
488     else
489         p->next = n->next;
490 
491     n->next = timeoutPool;
492     timeoutPool = n;
493 
494     timeoutQueueLength--;
495 
496     return true;
497 }
498 
499 
500 /* The main routine. This is a near-infinite loop that drives the whole
501    program. */
502 void
Run(void)503 Run(void)
504 {
505     fd_set rSet;
506     fd_set wSet;
507     fd_set eSet;
508 
509     keepSelecting = 1;
510     xsignal(SIGPIPE, pipeHandler);
511 
512     while (keepSelecting) {
513         struct timeval timeout;
514         struct timeval *twait;
515         int sval;
516         unsigned int idx;
517         bool modifiedTime = false;
518 
519         twait = getTimeout(&timeout);
520 
521         memcpy(&rSet, &rdSet, sizeof(rdSet));
522         memcpy(&wSet, &wrSet, sizeof(wrSet));
523         memcpy(&eSet, &exSet, sizeof(exSet));
524 
525         if (highestFd < 0 && twait == NULL) /* no fds and no timeout */
526             break;
527         else if (twait != NULL
528                  && (twait->tv_sec != 0 || twait->tv_usec != 0)) {
529             /* if we have any workprocs registered we poll rather than
530                block on the fds */
531             for (idx = 0; idx < priorityCount; idx++)
532                 if (priorityList[idx] != NULL
533                     && priorityList[idx]->workCbk != NULL) {
534                     modifiedTime = true;
535                     twait->tv_sec = 0;
536                     twait->tv_usec = 0;
537 
538                     break;
539                 }
540         }
541 
542         /* calculate host backlog statistics */
543         TMRstart(TMR_BACKLOGSTATS);
544         gCalcHostBlStat();
545         TMRstop(TMR_BACKLOGSTATS);
546 
547         TMRstart(TMR_IDLE);
548         sval = select(highestFd + 1, &rSet, &wSet, &eSet, twait);
549         TMRstop(TMR_IDLE);
550 
551         timePasses();
552         if (innconf->timer != 0 && TMRnow() > innconf->timer * 1000) {
553             TMRsummary("ME", timer_name);
554         }
555 
556         if (sval == 0 && twait == NULL)
557             die("No fd's ready and no timeouts");
558         else if (sval < 0 && errno == EINTR) {
559             handleSignals();
560         } else if (sval < 0) {
561             syswarn("ME exception: select failed: %d", sval);
562             stopRun();
563         } else if (sval > 0) {
564             IoStatus rval;
565             int readyCount = sval;
566             int endpointsServiced = 1;
567 
568             handleSignals();
569 
570             for (idx = 0; idx < priorityCount; idx++) {
571                 EndPoint ep = priorityList[idx];
572                 bool specialCheck = false;
573 
574                 if (readyCount > 0 && ep != NULL) {
575                     int fd = ep->myFd;
576                     int selectHit = 0, readMiss = 0, writeMiss = 0;
577 
578                     /* Every SELECT_RATIO times we service an endpoint in this
579                        loop we check to see if the mainEndPoint fd is ready to
580                        read or write. If so we process it and do the current
581                        endpoint next time around. */
582                     if (((endpointsServiced % (SELECT_RATIO + 1)) == 0)
583                         && ep != mainEndPoint && mainEndPoint != NULL
584                         && !mainEpIsReg) {
585                         fd_set trSet, twSet;
586                         struct timeval tw;
587                         int checkRead = FD_ISSET(mainEndPoint->myFd, &rdSet);
588                         int checkWrite = FD_ISSET(mainEndPoint->myFd, &wrSet);
589 
590                         endpointsServiced++;
591 
592                         if (checkRead || checkWrite) {
593                             fd = mainEndPoint->myFd;
594 
595                             tw.tv_sec = tw.tv_usec = 0;
596                             memset(&trSet, 0, sizeof(trSet));
597                             memset(&twSet, 0, sizeof(twSet));
598 
599                             if (checkRead)
600                                 FD_SET(fd, &trSet);
601                             if (checkWrite)
602                                 FD_SET(fd, &twSet);
603 
604                             sval = select(fd + 1, &trSet, &twSet, 0, &tw);
605 
606                             if (sval > 0) {
607                                 idx--;
608                                 ep = mainEndPoint;
609                                 specialCheck = true;
610                                 if (checkRead && FD_ISSET(fd, &trSet)) {
611                                     FD_SET(fd, &rSet);
612                                     readyCount++;
613                                 }
614                                 if (checkWrite && FD_ISSET(fd, &twSet)) {
615                                     FD_SET(fd, &wSet);
616                                     readyCount++;
617                                 }
618                             } else if (sval < 0) {
619                                 syswarn("ME exception: select failed: %d",
620                                         sval);
621                                 stopRun();
622                                 return;
623                             } else
624                                 fd = ep->myFd; /* back to original fd. */
625                         }
626                     }
627 
628                     FD_CLR(fd, &exSet);
629 
630                     if (FD_ISSET(fd, &rSet)) {
631                         readyCount--;
632                         endpointsServiced++;
633                         selectHit = 1;
634 
635                         if ((rval = doRead(ep)) != IoIncomplete) {
636                             Buffer *buff = ep->inBuffer;
637 
638                             FD_CLR(fd, &rdSet);
639 
640                             /* incase callback wants to issue read */
641                             ep->inBuffer = NULL;
642 
643                             if (ep->inCbk != NULL)
644                                 (*ep->inCbk)(ep, rval, buff, ep->inClientData);
645                             else
646                                 freeBufferArray(buff);
647                         } else {
648                             if (InputFile == NULL)
649                                 FD_SET(ep->myFd, &exSet);
650                         }
651                     } else if (FD_ISSET(fd, &rdSet))
652                         readMiss = 1;
653 
654                     /* get it again as the read callback may have deleted the
655                      */
656                     /* endpoint */
657                     if (specialCheck)
658                         ep = mainEndPoint;
659                     else
660                         ep = priorityList[idx];
661 
662                     if (readyCount > 0 && ep != NULL && FD_ISSET(fd, &wSet)) {
663                         readyCount--;
664                         endpointsServiced++;
665                         selectHit = 1;
666 
667                         if ((rval = doWrite(ep)) != IoIncomplete
668                             && rval != IoProgress) {
669                             Buffer *buff = ep->outBuffer;
670 
671                             FD_CLR(fd, &wrSet);
672 
673                             /* incase callback wants to issue a write */
674                             ep->outBuffer = NULL;
675 
676                             if (ep->outDoneCbk != NULL)
677                                 (*ep->outDoneCbk)(ep, rval, buff,
678                                                   ep->outClientData);
679                             else
680                                 freeBufferArray(buff);
681                         } else if (rval == IoProgress) {
682                             Buffer *buff = ep->outBuffer;
683 
684                             if (ep->outProgressCbk != NULL)
685                                 (*ep->outProgressCbk)(ep, rval, buff,
686                                                       ep->outClientData);
687                         } else {
688                             FD_SET(ep->myFd, &exSet);
689                         }
690                     } else if (FD_ISSET(fd, &wrSet))
691                         writeMiss = 1;
692 
693                     /* get it again as the write callback may have deleted the
694                      */
695                     /* endpoint */
696                     if (specialCheck)
697                         ep = mainEndPoint;
698                     else
699                         ep = priorityList[idx];
700 
701                     if (ep != NULL) {
702                         ep->selectHits *= 0.9;
703                         if (selectHit)
704                             ep->selectHits += 1.0;
705                         else if (readMiss && writeMiss)
706                             ep->selectHits -= 1.0;
707                     }
708 
709                     if (readyCount > 0 && ep != NULL && FD_ISSET(fd, &eSet))
710                         doExcept(ep);
711                 }
712             }
713 
714             reorderPriorityList();
715         } else if (sval == 0 && !modifiedTime)
716             doTimeout();
717 
718         /* now we're done processing all read fds and/or the
719            timeout(s). Next we do the work callbacks for all the endpoints
720            whose fds weren't ready. */
721         for (idx = 0; idx < priorityCount; idx++) {
722             EndPoint ep = priorityList[idx];
723 
724             if (ep != NULL) {
725                 int fd = ep->myFd;
726 
727                 if (!FD_ISSET(fd, &rSet) && !FD_ISSET(fd, &wSet))
728                     if (ep->workCbk != NULL) {
729                         EndpWorkCbk func = ep->workCbk;
730                         void *data = ep->workData;
731 
732                         ep->workCbk = NULL;
733                         ep->workData = NULL;
734                         TMRstart(TMR_CALLBACK);
735                         func(ep, data);
736                         TMRstop(TMR_CALLBACK);
737                     }
738             }
739         }
740     }
741 }
742 
743 void *
addWorkCallback(EndPoint endp,EndpWorkCbk cbk,void * data)744 addWorkCallback(EndPoint endp, EndpWorkCbk cbk, void *data)
745 {
746     void *oldBk = endp->workData;
747 
748     endp->workCbk = cbk;
749     endp->workData = data;
750 
751     return oldBk;
752 }
753 
754 /* Tell the Run routine to stop next time around. */
755 void
stopRun(void)756 stopRun(void)
757 {
758     keepSelecting = 0;
759 }
760 
761 
762 int
endPointErrno(EndPoint endp)763 endPointErrno(EndPoint endp)
764 {
765     return endp->myErrno;
766 }
767 
768 bool
readIsPending(EndPoint endp)769 readIsPending(EndPoint endp)
770 {
771     return (endp->inBuffer != NULL ? true : false);
772 }
773 
774 bool
writeIsPending(EndPoint endp)775 writeIsPending(EndPoint endp)
776 {
777     return (endp->outBuffer != NULL ? true : false);
778 }
779 
780 void
setMainEndPoint(EndPoint endp)781 setMainEndPoint(EndPoint endp)
782 {
783     struct stat buf;
784 
785     mainEndPoint = endp;
786     if (endp->myFd >= 0 && fstat(endp->myFd, &buf) < 0)
787         syslog(LOG_ERR, "Can't fstat mainEndPoint fd (%d): %m", endp->myFd);
788     else if (endp->myFd < 0)
789         syslog(LOG_ERR, "Negative fd for mainEndPoint???");
790     else
791         mainEpIsReg = (S_ISREG(buf.st_mode) ? true : false);
792 }
793 
794 int
getMainEndPointFd(void)795 getMainEndPointFd(void)
796 {
797     return (mainEndPoint->myFd);
798 }
799 
800 void
freeTimeoutQueue(void)801 freeTimeoutQueue(void)
802 {
803     TimerElem p, n;
804 
805     p = timeoutQueue;
806     while (p) {
807         n = p->next;
808         p->next = timeoutPool;
809         timeoutPool = p;
810         p = n;
811         timeoutQueueLength--;
812     }
813 }
814 
815 
816 /***********************************************************************/
817 /*                      STATIC FUNCTIONS BELOW HERE                    */
818 /***********************************************************************/
819 
820 
821 /*
822  * called when the file descriptor on this endpoint is read ready.
823  */
824 static IoStatus
doRead(EndPoint endp)825 doRead(EndPoint endp)
826 {
827     int i = 0;
828     unsigned int idx;
829     unsigned int bCount = 0;
830     struct iovec *vp = NULL;
831     Buffer *buffers = endp->inBuffer;
832     unsigned int currIdx = endp->inBufferIdx;
833     size_t amt = 0;
834     IoStatus rval = IoIncomplete;
835 
836     TMRstart(TMR_READ);
837     for (i = currIdx; buffers && buffers[i] != NULL; i++)
838         bCount++;
839 
840     bCount = (bCount > IOV_MAX ? IOV_MAX : bCount);
841 
842     /* now set up the iovecs for the readv */
843     if (bCount > 0) {
844         char *bbase;
845         size_t bds, bs;
846 
847         vp = xcalloc(bCount, sizeof(struct iovec));
848 
849         bbase = bufferBase(buffers[currIdx]);
850         bds = bufferDataSize(buffers[currIdx]);
851         bs = bufferSize(buffers[currIdx]);
852 
853         /* inIndex is an index in the virtual array of the read, not directly
854            into the buffer. */
855         vp[0].iov_base = bbase + bds + endp->inIndex;
856         vp[0].iov_len = bs - bds - endp->inIndex;
857 
858         amt = vp[0].iov_len;
859 
860         for (idx = currIdx + 1; idx < bCount; idx++) {
861             bbase = bufferBase(buffers[idx]);
862             bds = bufferDataSize(buffers[idx]);
863             bs = bufferSize(buffers[idx]);
864 
865             vp[idx].iov_base = bbase;
866             vp[idx].iov_len = bs - bds;
867             amt += (bs - bds);
868         }
869 
870         i = readv(endp->myFd, vp, (int) bCount);
871 
872         if (i > 0) {
873             size_t readAmt = (size_t) i;
874 
875             endp->inAmtRead += readAmt;
876 
877             /* check if we filled the first buffer */
878             if (readAmt >= (size_t) vp[0].iov_len) { /* we did */
879                 bufferIncrDataSize(buffers[currIdx], vp[0].iov_len);
880                 readAmt -= vp[0].iov_len;
881                 endp->inBufferIdx++;
882             } else {
883                 endp->inIndex += readAmt;
884                 bufferIncrDataSize(buffers[currIdx], readAmt);
885                 readAmt = 0;
886             }
887 
888             /* now check the rest of the buffers */
889             for (idx = 1; readAmt > 0; idx++) {
890                 ASSERT(idx < bCount);
891 
892                 bs = bufferSize(buffers[currIdx + idx]);
893                 bbase = bufferBase(buffers[currIdx + idx]);
894                 bds = bufferDataSize(buffers[currIdx + idx]);
895 
896                 if (readAmt >= (bs - bds)) {
897                     bufferSetDataSize(buffers[currIdx + idx], bs);
898                     readAmt -= bs;
899                     endp->inBufferIdx++;
900                 } else {
901                     endp->inIndex = readAmt;
902                     bufferIncrDataSize(buffers[currIdx + idx], readAmt);
903                     memset(bbase + bds + readAmt, 0, bs - bds - readAmt);
904                     readAmt = 0;
905                 }
906             }
907 
908             if (endp->inAmtRead >= endp->inMinLen) {
909                 endp->inIndex = 0;
910                 rval = IoDone;
911             }
912         } else if (i < 0 && errno != EINTR && errno != EAGAIN) {
913             endp->myErrno = errno;
914             rval = IoFailed;
915         } else if (i < 0 && errno == EINTR) {
916             handleSignals();
917         } else if (i == 0)
918             rval = IoEOF;
919         else /* i < 0 && errno == EAGAIN */
920             rval = IoIncomplete;
921 
922         free(vp);
923     } else
924         rval = IoDone;
925     TMRstop(TMR_READ);
926     return rval;
927 }
928 
929 /* called when the file descriptor on the endpoint is write ready. */
930 static IoStatus
doWrite(EndPoint endp)931 doWrite(EndPoint endp)
932 {
933     unsigned int idx;
934     int i = 0;
935     size_t amt = 0;
936     unsigned int bCount = 0;
937     struct iovec *vp = NULL;
938     Buffer *buffers = endp->outBuffer;
939     unsigned int currIdx = endp->outBufferIdx;
940     IoStatus rval = IoIncomplete;
941 
942     TMRstart(TMR_WRITE);
943     for (i = currIdx; buffers && buffers[i] != NULL; i++)
944         bCount++;
945 
946     bCount = (bCount > IOV_MAX ? IOV_MAX : bCount);
947 
948     if (bCount > 0) {
949         vp = xcalloc(bCount, sizeof(struct iovec));
950 
951         vp[0].iov_base = bufferBase(buffers[currIdx]);
952         vp[0].iov_base = (char *) vp[0].iov_base + endp->outIndex;
953         vp[0].iov_len = bufferDataSize(buffers[currIdx]) - endp->outIndex;
954 
955         amt = vp[0].iov_len;
956 
957         for (idx = 1; idx < bCount; idx++) {
958             vp[idx].iov_base = bufferBase(buffers[idx + currIdx]);
959             vp[idx].iov_len = bufferDataSize(buffers[idx + currIdx]);
960             amt += vp[idx].iov_len;
961         }
962 
963         ASSERT(endp->myFd >= 0);
964         ASSERT(vp != 0);
965         ASSERT(bCount > 0);
966 
967         i = writev(endp->myFd, vp, (int) bCount);
968 
969         if (i > 0) {
970             size_t writeAmt = (size_t) i;
971 
972             endp->outAmtWritten += writeAmt;
973 
974             /* now figure out which buffers got completely written */
975             for (idx = 0; writeAmt > 0; idx++) {
976                 if (writeAmt >= (size_t) vp[idx].iov_len) {
977                     endp->outBufferIdx++;
978                     endp->outIndex = 0;
979                     writeAmt -= vp[idx].iov_len;
980                 } else {
981                     /* this buffer was not completly written */
982                     endp->outIndex += writeAmt;
983                     writeAmt = 0;
984                 }
985             }
986 
987             if (endp->outAmtWritten == endp->outSize)
988                 rval = IoDone;
989             else
990                 rval = IoProgress;
991         } else if (i < 0 && errno == EINTR) {
992             handleSignals();
993         } else if (i < 0 && errno == EAGAIN) {
994             rval = IoIncomplete;
995         } else if (i < 0) {
996             endp->myErrno = errno;
997             rval = IoFailed;
998         } else
999             d_printf(1, "Wrote 0 bytes in doWrite()?\n");
1000 
1001         free(vp);
1002     } else
1003         rval = IoDone;
1004 
1005     TMRstop(TMR_WRITE);
1006     return rval;
1007 }
1008 
1009 
1010 static IoStatus
doExcept(EndPoint endp)1011 doExcept(EndPoint endp)
1012 {
1013     int optval;
1014     socklen_t size;
1015     int fd = endPointFd(endp);
1016 
1017     if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &optval, &size) != 0)
1018         syswarn("ME exception: getsockopt (%d)", fd);
1019     else if (optval != 0) {
1020         errno = optval;
1021         syswarn("ME exception: fd %d", fd);
1022     } else
1023         syswarn("ME exception: fd %d: Unknown error", fd);
1024 
1025 #if defined(INNFEED_DEBUG)
1026     sleep(5);
1027     abort();
1028     /* NOTREACHED */
1029 #endif
1030 
1031     return IoFailed;
1032 }
1033 
1034 
1035 static void
signalHandler(int s)1036 signalHandler(int s)
1037 {
1038     sigFlags[s] = 1;
1039 #ifndef HAVE_SIGACTION
1040     xsignal(s, signalHandler);
1041 #endif
1042 }
1043 
1044 
1045 static void
pipeHandler(int s)1046 pipeHandler(int s)
1047 {
1048     xsignal(s, pipeHandler);
1049 }
1050 
1051 
1052 /* compare the hit ratio of two endpoint for qsort. We're sorting the
1053    endpoints on their relative activity */
1054 static int
hitCompare(const void * v1,const void * v2)1055 hitCompare(const void *v1, const void *v2)
1056 {
1057     const struct endpoint_s *e1 = *((const struct endpoint_s *const *) v1);
1058     const struct endpoint_s *e2 = *((const struct endpoint_s *const *) v2);
1059     double e1Hit = e1->selectHits;
1060     double e2Hit = e2->selectHits;
1061 
1062     if (e1 == mainEndPoint)
1063         return -1;
1064     else if (e2 == mainEndPoint)
1065         return 1;
1066     else if (e1Hit < e2Hit)
1067         return 1;
1068     else if (e1Hit > e2Hit)
1069         return -1;
1070 
1071     return 0;
1072 }
1073 
1074 
1075 /* We maintain the endpoints in order of the percent times they're ready
1076    for read/write when they've been selected. This helps us favour the more
1077    active endpoints. */
1078 static void
reorderPriorityList(void)1079 reorderPriorityList(void)
1080 {
1081     unsigned int i, j;
1082     static int thisTime = 4;
1083 
1084     /* only sort every 4th time since it's so expensive */
1085     if (--thisTime > 0)
1086         return;
1087 
1088     thisTime = 4;
1089 
1090     for (i = j = 0; i < priorityCount; i++)
1091         if (priorityList[i] != NULL) {
1092             if (i != j)
1093                 priorityList[j] = priorityList[i];
1094             j++;
1095         }
1096 
1097     for (i = j; i < priorityCount; i++)
1098         priorityList[i] = NULL;
1099 
1100     priorityCount = j;
1101 
1102     qsort(priorityList, (size_t) priorityCount, sizeof(EndPoint), &hitCompare);
1103 }
1104 
1105 
1106 #define TIMEOUT_POOL_SIZE \
1107     ((4096 - 2 * (sizeof(void *))) / (sizeof(TimerElemStruct)))
1108 
1109 /* create a new timeout data structure properly initialized. */
1110 static TimerElem
newTimerElem(TimeoutId i,time_t w,EndpTCB f,void * d)1111 newTimerElem(TimeoutId i, time_t w, EndpTCB f, void *d)
1112 {
1113     TimerElem p;
1114 
1115     if (timeoutPool == NULL) {
1116         unsigned int j;
1117 
1118         timeoutPool = xmalloc(sizeof(TimerElemStruct) * TIMEOUT_POOL_SIZE);
1119 
1120         for (j = 0; j < TIMEOUT_POOL_SIZE - 1; j++)
1121             timeoutPool[j].next = &(timeoutPool[j + 1]);
1122         timeoutPool[TIMEOUT_POOL_SIZE - 1].next = NULL;
1123     }
1124 
1125     p = timeoutPool;
1126     timeoutPool = timeoutPool->next;
1127 
1128     ASSERT(p != NULL);
1129 
1130     p->id = i;
1131     p->when = w;
1132     p->func = f;
1133     p->data = d;
1134     p->next = NULL;
1135 
1136     return p;
1137 }
1138 
1139 
1140 /* add a new timeout structure to the global list. */
1141 static TimeoutId
timerElemAdd(time_t when,EndpTCB func,void * data)1142 timerElemAdd(time_t when, EndpTCB func, void *data)
1143 {
1144     TimerElem p = newTimerElem(++nextId ? nextId : ++nextId, when, func, data);
1145     TimerElem n = timeoutQueue;
1146     TimerElem q = NULL;
1147 
1148     while (n != NULL && n->when <= when) {
1149         q = n;
1150         n = n->next;
1151     }
1152 
1153     if (n == NULL && q == NULL) /* empty list so put at head */
1154         timeoutQueue = p;
1155     else if (q == NULL) /* put at head of list */
1156     {
1157         p->next = timeoutQueue;
1158         timeoutQueue = p;
1159     } else if (n == NULL) /* put at end of list */
1160         q->next = p;
1161     else /* in middle of list */
1162     {
1163         p->next = q->next;
1164         q->next = p;
1165     }
1166 
1167     timeoutQueueLength++;
1168 
1169     return p->id;
1170 }
1171 
1172 
1173 /* Fills in TOUT with the timeout to use on the next call to
1174  * select. Returns TOUT. If there is no timeout, then returns NULL.  If the
1175  * timeout has already passed, then it calls the timeout handling routine
1176  * first.
1177  */
1178 static struct timeval *
getTimeout(struct timeval * tout)1179 getTimeout(struct timeval *tout)
1180 {
1181     struct timeval *rval = NULL;
1182 
1183     if (timeoutQueue != NULL) {
1184         time_t now = theTime();
1185 
1186         while (timeoutQueue && now > timeoutQueue->when)
1187             doTimeout();
1188 
1189         if (timeoutQueue != NULL && now == timeoutQueue->when) {
1190             tout->tv_sec = 0;
1191             tout->tv_usec = 0;
1192             rval = tout;
1193         } else if (timeoutQueue != NULL) {
1194             tout->tv_sec = timeoutQueue->when - now;
1195             tout->tv_usec = 0;
1196             rval = tout;
1197         }
1198     }
1199 
1200     return rval;
1201 }
1202 
1203 
1204 static void
doTimeout(void)1205 doTimeout(void)
1206 {
1207     EndpTCB cbk = timeoutQueue->func;
1208     void *data = timeoutQueue->data;
1209     TimerElem p = timeoutQueue;
1210     TimeoutId tid = timeoutQueue->id;
1211 
1212     timeoutQueue = timeoutQueue->next;
1213 
1214     p->next = timeoutPool;
1215     timeoutPool = p;
1216 
1217     timeoutQueueLength--;
1218 
1219     if (cbk)
1220         (*cbk)(tid, data); /* call the callback function */
1221 }
1222 
1223 
1224 #if defined(WANT_MAIN)
1225 
1226 #    define BUFF_SIZE 100
1227 
1228 static void
lineIsWritten(EndPoint ep,IoStatus status,Buffer * buffers,void * d UNUSED)1229 lineIsWritten(EndPoint ep, IoStatus status, Buffer *buffers, void *d UNUSED)
1230 {
1231     int i;
1232 
1233     if (status == IoDone)
1234         d_printf(1, "LINE was written\n");
1235     else {
1236         int oldErrno = errno;
1237 
1238         errno = endPointErrno(ep);
1239         perror("write failed");
1240         errno = oldErrno;
1241     }
1242 
1243     for (i = 0; buffers && buffers[i]; i++) {
1244         delBuffer(buffers[i]);
1245     }
1246 }
1247 
1248 static void
lineIsRead(EndPoint myEp,IoStatus status,Buffer * buffers,void * d UNUSED)1249 lineIsRead(EndPoint myEp, IoStatus status, Buffer *buffers, void *d UNUSED)
1250 {
1251     Buffer *writeBuffers, *readBuffers;
1252     Buffer newBuff1, newBuff2;
1253     Buffer newInputBuffer;
1254     char *data, *p;
1255     size_t len;
1256 
1257     if (status == IoFailed) {
1258         int oldErrno = errno;
1259 
1260         errno = endPointErrno(myEp);
1261         perror("read failed");
1262         errno = oldErrno;
1263 
1264         return;
1265     } else if (status == IoEOF) {
1266         d_printf(1, "EOF on endpoint.\n");
1267         delEndPoint(myEp);
1268 
1269         return;
1270     }
1271 
1272 
1273     data = bufferBase(buffers[0]);
1274     len = bufferDataSize(buffers[0]);
1275 
1276     if (data[len - 1] == '\r' || data[len - 1] == '\n')
1277         bufferDecrDataSize(buffers[0], 1);
1278     if (data[len - 1] == '\r' || data[len - 1] == '\n')
1279         bufferDecrDataSize(buffers[0], 1);
1280 
1281     data[len] = '\0';
1282 
1283     d_printf(1, "Got a line: %s\n", data);
1284 
1285     newBuff1 = newBuffer(len + 50);
1286     newBuff2 = newBuffer(len + 50);
1287     newInputBuffer = newBuffer(BUFF_SIZE);
1288 
1289     p = bufferBase(newBuff1);
1290     strlcpy(p, "Thanks for that \"", bufferSize(newBuff1));
1291     bufferSetDataSize(newBuff1, strlen(p));
1292 
1293     p = bufferBase(newBuff2);
1294     strlcpy(p, "\" very tasty\n", bufferSize(newBuff2));
1295     bufferSetDataSize(newBuff2, strlen(p));
1296 
1297     writeBuffers = makeBufferArray(newBuff1, buffers[0], newBuff2, NULL);
1298     readBuffers = makeBufferArray(newInputBuffer, NULL);
1299 
1300     prepareWrite(myEp, writeBuffers, NULL, lineIsWritten, NULL);
1301     prepareRead(myEp, readBuffers, lineIsRead, NULL, 1);
1302 }
1303 
1304 
1305 static void
printDate(TimeoutId tid,void * data)1306 printDate(TimeoutId tid, void *data)
1307 {
1308     char dateString[30];
1309     const time_t t = theTime();
1310 
1311     timeToString(t, dateString, sizeof(dateString));
1312     d_printf(1, "Timeout (%d) time now is %ld %s\n", (int) tid, (long) t,
1313              dateString);
1314 
1315     if (timeoutQueue == NULL) {
1316         int ti = (rand() % 10) + 1;
1317 
1318         prepareSleep(printDate, ti, data);
1319     }
1320 }
1321 
1322 TimeoutId rm;
1323 
1324 static void
Timeout(TimeoutId tid,void * data)1325 Timeout(TimeoutId tid, void *data)
1326 {
1327     static int seeded;
1328     static int howMany;
1329     static int i;
1330     char dateString[30];
1331     const time_t t = theTime();
1332 
1333     if (!seeded) {
1334         srand(t);
1335         seeded = 1;
1336     }
1337 
1338     timeToString(t, dateString, sizeof(dateString));
1339     d_printf(1, "Timeout (%d) time now is %ld %s\n", (int) tid, (long) t,
1340              dateString);
1341 
1342     if (timeoutQueue != NULL && timeoutQueue->next != NULL)
1343         d_printf(1, "%s timeout id %d\n",
1344                  (removeTimeout(rm) ? "REMOVED" : "FAILED TO REMOVE"), rm);
1345     rm = 0;
1346 
1347     howMany = (rand() % 10) + (timeoutQueue == NULL ? 1 : 0);
1348 
1349     for (i = 0; i < howMany; i++) {
1350         TimeoutId id;
1351         int count = (rand() % 30) + 1;
1352 
1353         id = (i % 2 == 0 ? prepareSleep(Timeout, count, data)
1354                          : prepareWake(Timeout, t + count, data));
1355 
1356         if (rm == 0)
1357             rm = id;
1358     }
1359 }
1360 
1361 
1362 static void
newConn(EndPoint ep,IoStatus status UNUSED,Buffer * buffers UNUSED,void * d UNUSED)1363 newConn(EndPoint ep, IoStatus status UNUSED, Buffer *buffers UNUSED,
1364         void *d UNUSED)
1365 {
1366     EndPoint newEp;
1367     struct sockaddr_in in;
1368     Buffer *readBuffers;
1369     Buffer newBuff = newBuffer(BUFF_SIZE);
1370     socklen_t len = sizeof(in);
1371     int fd;
1372 
1373     memset(&in, 0, sizeof(in));
1374 
1375     fd = accept(ep->myFd, (struct sockaddr *) &in, &len);
1376 
1377     if (fd < 0) {
1378         perror("::accept");
1379         return;
1380     }
1381 
1382     newEp = newEndPoint(fd);
1383 
1384     prepareRead(ep, NULL, newConn, NULL, 0);
1385 
1386     readBuffers = makeBufferArray(newBuff, NULL);
1387 
1388     prepareRead(newEp, readBuffers, lineIsRead, NULL, 1);
1389 
1390     d_printf(1, "Set up a new connection\n");
1391 }
1392 
1393 
1394 int
main(int argc,char ** argv)1395 main(int argc, char **argv)
1396 {
1397     EndPoint accConn;
1398     struct sockaddr_in accNet;
1399     int accFd = socket(AF_INET, SOCK_STREAM, 0);
1400     unsigned short port = atoi(argc > 1 ? argv[1] : "10000");
1401     time_t t = theTime();
1402 
1403 
1404     char *program = strrchr(argv[0], '/');
1405 
1406     if (!program)
1407         program = argv[0];
1408     else
1409         program++;
1410 
1411     ASSERT(accFd >= 0);
1412 
1413     memset(&accNet, 0, sizeof(accNet));
1414     accNet.sin_family = AF_INET;
1415     accNet.sin_addr.s_addr = htonl(INADDR_ANY);
1416     accNet.sin_port = htons(port);
1417 
1418 #    ifdef LOG_PERROR
1419     openlog(program, LOG_PERROR | LOG_PID, LOG_NEWS);
1420 #    else
1421     openlog(program, LOG_PID, LOG_NEWS);
1422 #    endif
1423 
1424     if (bind(accFd, (struct sockaddr *) &accNet, sizeof(accNet)) < 0) {
1425         perror("bind: %m");
1426         exit(1);
1427     }
1428 
1429     listen(accFd, 5);
1430 
1431     accConn = newEndPoint(accFd);
1432 
1433     prepareRead(accConn, NULL, newConn, NULL, 0);
1434 
1435     prepareSleep(Timeout, 5, (void *) 0x10);
1436 
1437     t = theTime();
1438     d_printf(1, "Time now is %s", ctime(&t));
1439 
1440     prepareWake(printDate, t + 16, NULL);
1441 
1442     Run();
1443 
1444     return 0;
1445 }
1446 #endif /* WANT_MAIN */
1447 
1448 /* Probably doesn't do the right thing for SIGCHLD */
1449 void
setSigHandler(int signum,void (* ptr)(int))1450 setSigHandler(int signum, void (*ptr)(int))
1451 {
1452     unsigned int i;
1453 
1454     if (sigHandlers == NULL) {
1455         sigHandlers = xmalloc(sizeof(sigfn) * NSIG);
1456         sigFlags = xmalloc(sizeof(sig_atomic_t) * NSIG);
1457         for (i = 0; i < NSIG; i++) {
1458             sigHandlers[i] = NULL;
1459             sigFlags[i] = 0;
1460         }
1461     }
1462 
1463     if (signum >= NSIG) {
1464         syslog(LOG_ERR, "ME signal number bigger than NSIG: %d vs %d", signum,
1465                NSIG);
1466         return;
1467     }
1468 
1469     if (xsignal(signum, signalHandler) == SIG_ERR)
1470         die("signal failed: %s", strerror(errno));
1471 
1472     sigHandlers[signum] = ptr;
1473 }
1474 
1475 static void
handleSignals(void)1476 handleSignals(void)
1477 {
1478     int i;
1479 
1480     for (i = 1; i < NSIG; i++) {
1481         if (sigFlags[i]) {
1482 #if defined(HAVE_SIGACTION)
1483             sigset_t set, oset;
1484 
1485             if (sigemptyset(&set) != 0 || sigaddset(&set, i) != 0)
1486                 die("sigemptyset or sigaddset failed");
1487             if (sigprocmask(SIG_BLOCK, &set, &oset) != 0)
1488                 die("sigprocmask failed: %s", strerror(errno));
1489 #else
1490             /* hope for the best */
1491 #endif
1492 
1493             sigFlags[i] = 0;
1494 
1495             if (sigHandlers[i] != NULL && sigHandlers[i] != SIG_IGN
1496                 && sigHandlers[i] != SIG_DFL)
1497                 (sigHandlers[i])(i);
1498 
1499 #if defined(HAVE_SIGACTION)
1500             if (sigprocmask(SIG_SETMASK, &oset, (sigset_t *) NULL) != 0)
1501                 die("sigprocmask failed: %s", strerror(errno));
1502 #else
1503                 /* hope for the best */
1504 #endif
1505         }
1506     }
1507 }
1508 
1509 
1510 int
endpointConfigLoadCbk(void * data)1511 endpointConfigLoadCbk(void *data)
1512 {
1513     FILE *fp = (FILE *) data;
1514     long ival;
1515     int rval = 1;
1516 
1517     if (getInteger(topScope, "stdio-fdmax", &ival, NO_INHERIT)) {
1518         stdioFdMax = ival;
1519 
1520 #if !defined(FD_SETSIZE)
1521 
1522         if (stdioFdMax > 0) {
1523             logOrPrint(LOG_ERR, fp, NO_STDIO_FDMAX);
1524             stdioFdMax = 0;
1525             rval = 0;
1526         }
1527 
1528 #else
1529 
1530         if (stdioFdMax > FD_SETSIZE) {
1531             logOrPrint(LOG_ERR, fp,
1532                        "ME config: value of %s (%ld) in %s is higher"
1533                        " than maximum of %ld. Using %ld",
1534                        "stdio-fdmax", ival, "global scope", (long) FD_SETSIZE,
1535                        (long) FD_SETSIZE);
1536             stdioFdMax = FD_SETSIZE;
1537             rval = 0;
1538         }
1539 
1540 #endif
1541 
1542     } else
1543         stdioFdMax = 0;
1544 
1545     return rval;
1546 }
1547 
1548 
1549 static void
endpointCleanup(void)1550 endpointCleanup(void)
1551 {
1552     free(endPoints);
1553     free(priorityList);
1554     free(sigHandlers);
1555     endPoints = NULL;
1556     priorityList = NULL;
1557     sigHandlers = NULL;
1558 }
1559