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