1 /*
2 ** The implementation of the innfeed Host class.
3 **
4 ** Written by James Brister <brister@vix.com>
5 */
6
7 #include "portable/system.h"
8
9 #include "innfeed.h"
10 #include "portable/socket.h"
11
12 #include <assert.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <float.h>
16 #include <math.h>
17 #include <netdb.h>
18 #include <sys/param.h>
19 #include <syslog.h>
20
21 #ifdef HAVE_LIMITS_H
22 # include <limits.h>
23 #endif
24
25 #include "inn/innconf.h"
26 #include "inn/libinn.h"
27 #include "inn/messages.h"
28 #include "inn/network.h"
29 #include "inn/version.h"
30
31 #include "article.h"
32 #include "buffer.h"
33 #include "configfile.h"
34 #include "connection.h"
35 #include "endpoint.h"
36 #include "host.h"
37 #include "innlistener.h"
38 #include "tape.h"
39
40 #define REQ 1
41 #define NOTREQ 0
42 #define NOTREQNOADD 2
43
44 #define VALUE_OK 0
45 #define VALUE_TOO_HIGH 1
46 #define VALUE_TOO_LOW 2
47 #define VALUE_MISSING 3
48 #define VALUE_WRONG_TYPE 4
49
50 #define METHOD_STATIC 0
51 #define METHOD_APS 1
52 #define METHOD_QUEUE 2
53 #define METHOD_COMBINED 3
54
55 /* the limit of number of connections open when a host is
56 set to 0 to mean "infinite" */
57 #define MAXCON 500
58 #define MAXCONLIMIT(xx) ((xx == 0) ? MAXCON : xx)
59
60 #define BACKLOGFILTER 0.7
61 #define BACKLOGLWM 20.0
62 #define BACKLOGHWM 50.0
63
64 /* time between retrying blocked hosts in seconds */
65 #define TRYBLOCKEDHOSTPERIOD 120
66
67 /* Disable float-equal GCC warning as it reports correct code
68 * to be changed in this file.
69 * <https://github.com/InterNetNews/inn/issues/212> */
70
71 /* the host keeps a couple lists of these */
72 typedef struct proc_q_elem {
73 Article article;
74 struct proc_q_elem *next;
75 struct proc_q_elem *prev;
76 time_t whenToRequeue;
77 } * ProcQElem;
78
79 typedef struct host_param_s {
80 char *peerName;
81 char *ipName;
82 char *bindAddr;
83 char *bindAddr6;
84 unsigned int articleTimeout;
85 unsigned int responseTimeout;
86 unsigned int initialConnections;
87 unsigned int absMaxConnections;
88 unsigned int maxChecks;
89 unsigned short portNum;
90 bool forceIPv4;
91 unsigned int closePeriod;
92 unsigned int dynamicMethod;
93 bool wantStreaming;
94 bool dropDeferred;
95 bool minQueueCxn;
96 double lowPassLow; /* as percentages */
97 double lowPassHigh;
98 double lowPassFilter;
99 unsigned int backlogLimit;
100 unsigned int backlogLimitHigh;
101 double backlogFactor;
102 double dynBacklogFilter;
103 double dynBacklogLowWaterMark;
104 double dynBacklogHighWaterMark;
105 bool backlogFeedFirst;
106 char *username;
107 char *password;
108 } * HostParams;
109
110 struct host_s {
111 InnListener listener; /* who created me. */
112 struct sockaddr **ipAddrs; /* the ip addresses of the remote */
113 int nextIpAddr; /* the next ip address to hand out */
114
115 Connection *connections; /* NULL-terminated list of all connections */
116 bool *cxnActive; /* true if the corresponding cxn is active */
117 bool *cxnSleeping; /* true if the connection is sleeping */
118 unsigned int maxConnections; /* maximum no of cxns controlled by method */
119 unsigned int activeCxns; /* number of connections currently active */
120 unsigned int sleepingCxns; /* number of connections currently sleeping */
121 Connection blockedCxn; /* the first connection to get the 400 banner*/
122 Connection notThisCxn; /* don't offer articles to this connection */
123
124 HostParams params; /* Parameters from config file */
125
126 bool remoteStreams; /* true if remote supports streaming */
127
128 ProcQElem queued; /* articles done nothing with yet. */
129 ProcQElem queuedTail;
130
131 ProcQElem processed; /* articles given to a Connection */
132 ProcQElem processedTail;
133
134 ProcQElem deferred; /* articles which have been deferred by */
135 ProcQElem deferredTail; /* a connection */
136
137 TimeoutId statsId; /* timeout id for stats logging. */
138 TimeoutId ChkCxnsId; /* timeout id for dynamic connections */
139 TimeoutId deferredId; /* timeout id for deferred articles */
140
141 Tape myTape;
142
143 bool backedUp; /* set to true when all cxns are full */
144 unsigned int backlog; /* number of arts in `queued' queue */
145 unsigned int deferLen; /* number of arts in `deferred' queue */
146
147 bool loggedModeOn; /* true if we logged going into no-CHECK mode */
148 bool loggedModeOff; /* true if we logged going out of no-CHECK mode */
149
150 bool loggedBacklog; /* true if we already logged the fact */
151 bool notifiedChangedRemBlckd; /* true if we logged a new response 400 */
152 bool removeOnReload; /* true if host should be removed at end of
153 * config reload
154 */
155 bool isDynamic; /* true if host created dynamically */
156
157 /* these numbers get reset periodically (after a 'final' logging). */
158 unsigned int artsOffered; /* # of articles we offered to remote. */
159 unsigned int artsOffered_checkpoint;
160 unsigned int artsAccepted; /* # of articles succesfully transferred */
161 unsigned int artsAccepted_checkpoint;
162 unsigned int artsNotWanted; /* # of articles remote already had */
163 unsigned int artsNotWanted_checkpoint;
164 unsigned int artsRejected; /* # of articles remote rejected */
165 unsigned int artsRejected_checkpoint;
166 unsigned int artsDeferred; /* # of articles remote asked us to retry */
167 unsigned int artsDeferred_checkpoint;
168 unsigned int artsMissing; /* # of articles whose file was missing. */
169 unsigned int artsMissing_checkpoint;
170 unsigned int artsToTape; /* # of articles given to tape */
171 unsigned int artsToTape_checkpoint;
172 unsigned int
173 artsQueueOverflow; /* # of articles that overflowed `queued' */
174 unsigned int artsCxnDrop; /* # of articles caught in dead cxn */
175 unsigned int artsCxnDrop_checkpoint;
176 unsigned int artsHostSleep; /* # of articles spooled by sleeping host */
177 unsigned int artsHostSleep_checkpoint;
178 unsigned int artsHostClose; /* # of articles caught by closing host */
179 unsigned int artsHostClose_checkpoint;
180 unsigned int artsFromTape; /* # of articles we pulled off tape */
181 unsigned int artsFromTape_checkpoint;
182 double artsSizeAccepted; /* size of articles succesfully transferred */
183 double artsSizeAccepted_checkpoint;
184 double artsSizeRejected; /* size of articles remote rejected */
185 double artsSizeRejected_checkpoint;
186
187 /* Dynamic Peerage - MGF */
188 unsigned int
189 artsProcLastPeriod; /* # of articles processed in last period */
190 unsigned int secsInLastPeriod; /* Number of seconds in last period */
191 unsigned int lastCheckPoint; /* total articles at end of last period */
192 unsigned int
193 lastSentCheckPoint; /* total articles sent end of last period */
194 unsigned int
195 lastTotalCheckPoint; /* total articles total end of last period */
196 bool maxCxnChk; /* check for maxConnections */
197 time_t lastMaxCxnTime; /* last time a maxConnections increased */
198 time_t lastChkTime; /* last time a check was made for maxConnect */
199 unsigned int nextCxnTimeChk; /* next check for maxConnect */
200
201 double backlogFilter; /* IIR filter for size of backlog */
202
203 /* These numbers are as above, but for the life of the process. */
204 unsigned int gArtsOffered;
205 unsigned int gArtsAccepted;
206 unsigned int gArtsNotWanted;
207 unsigned int gArtsRejected;
208 unsigned int gArtsDeferred;
209 unsigned int gArtsMissing;
210 unsigned int gArtsToTape;
211 unsigned int gArtsQueueOverflow;
212 unsigned int gArtsCxnDrop;
213 unsigned int gArtsHostSleep;
214 unsigned int gArtsHostClose;
215 unsigned int gArtsFromTape;
216 double gArtsSizeAccepted;
217 double gArtsSizeRejected;
218 unsigned int gCxnQueue;
219 unsigned int gNoQueue;
220
221 time_t firstConnectTime; /* time of first connect. */
222 time_t connectTime; /* the time the first connection was fully
223 set up (MODE STREAM and everything
224 else). */
225 time_t connectTime_checkpoint;
226
227 time_t spoolTime; /* the time the Host had to revert to
228 spooling articles to tape. */
229 time_t spoolTime_checkpoint;
230
231 time_t lastSpoolTime; /* the time the last time the Host had to
232 revert to spooling articles to tape. */
233 time_t nextIpLookup; /* time of last IP name resolution */
234
235 char *blockedReason; /* what the 400 from the remote says. */
236
237 Host next; /* for global list of hosts. */
238
239 unsigned long dlAccum; /* cumulative deferLen */
240 unsigned int blNone; /* number of times the backlog was 0 */
241 unsigned int blFull; /* number of times the backlog was full */
242 unsigned int blQuartile[4]; /* number of times in each quartile */
243 unsigned long blAccum; /* cumulative backlog for computing mean */
244 unsigned int blCount; /* the sample count */
245 };
246
247 /* A holder for the info we got out of the config file, but couldn't create
248 the Host object for (normally due to lock-file problems).*/
249
250 typedef struct host_holder_s {
251 HostParams params;
252 struct host_holder_s *next;
253 } * HostHolder;
254
255
256 /* These numbers are as above, but for all hosts over
257 the life of the process. */
258 static long procArtsOffered;
259 static long procArtsAccepted;
260 static long procArtsNotWanted;
261 static long procArtsRejected;
262 static long procArtsDeferred;
263 static long procArtsMissing;
264 static double procArtsSizeAccepted;
265 static double procArtsSizeRejected;
266 static long procArtsToTape;
267 static long procArtsFromTape;
268
269 static HostParams defaultParams = NULL;
270
271 static HostHolder blockedHosts; /* lists of hosts we can't lock */
272 static TimeoutId tryBlockedHostsId = 0;
273 static time_t lastStatusLog;
274
275 /*
276 * Host object private methods.
277 */
278 static void articleGone(Host host, Connection cxn, Article article);
279 static void hostStopSpooling(Host host);
280 static void hostStartSpooling(Host host);
281 static void hostLogStats(Host host, bool final);
282 static void hostStatsTimeoutCbk(TimeoutId tid, void *data);
283 static void hostDeferredArtCbk(TimeoutId tid, void *data);
284 static void backlogToTape(Host host);
285 static void queuesToTape(Host host);
286 static bool amClosing(Host host);
287 static void hostLogStatus(void);
288 static void hostPrintStatus(Host host, FILE *fp);
289 static int validateBool(FILE *fp, const char *name, int required, bool setval,
290 scope *sc, unsigned int inh);
291 static int validateReal(FILE *fp, const char *name, double low, double high,
292 int required, double setval, scope *sc,
293 unsigned int inh);
294 static int validateInteger(FILE *fp, const char *name, long low, long high,
295 int required, long setval, scope *sc,
296 unsigned int inh);
297
298 static HostParams newHostParams(HostParams p);
299 static void freeHostParams(HostParams params);
300
301 static HostHolder FindBlockedHost(const char *name);
302 static void addBlockedHost(HostParams params);
303 static void tryBlockedHosts(TimeoutId tid, void *data);
304 static Host newHost(InnListener listener, HostParams p);
305
306 static HostParams getHostInfo(void);
307 static HostParams hostDetails(scope *s, char *name, bool isDefault, FILE *fp);
308
309 static Host findHostByName(char *name);
310 static void hostCleanup(void);
311 static void hostAlterMaxConnections(Host host, unsigned int absMaxCxns,
312 unsigned int maxCxns, bool makeConnect);
313
314 /* article queue management functions */
315 static Article remHead(ProcQElem *head, ProcQElem *tail);
316 static void queueArticle(Article article, ProcQElem *head, ProcQElem *tail,
317 time_t when);
318 static bool remArticle(Article article, ProcQElem *head, ProcQElem *tail);
319
320
321 /*
322 * Host class data
323 */
324
325 /* if true then when a Host logs its stats, it has all its connections
326 log theirs too. */
327 static bool logConnectionStats = (bool) LOG_CONNECTION_STATS;
328
329 /* The frequency in seconds with which a Host will log its stats. */
330 static time_t statsPeriod = STATS_PERIOD;
331 static time_t statsResetPeriod = STATS_RESET_PERIOD;
332
333 static Host gHostList = NULL;
334
335 static unsigned int gHostCount = 0;
336
337 static unsigned int maxIpNameLen = 0;
338 static unsigned int maxPeerNameLen = 0;
339
340 static time_t start;
341 static char startTime[30]; /* for timeToString */
342 static pid_t myPid;
343 static char *statusFile = NULL;
344 static unsigned int dnsRetPeriod;
345 static unsigned int dnsExpPeriod;
346
347 bool genHtml = false;
348 unsigned int hostHighwater = HOST_HIGHWATER;
349
350 /*******************************************************************/
351 /* PUBLIC FUNCTIONS */
352 /*******************************************************************/
353
354
355 /* function called when the config file is loaded */
356 int
hostConfigLoadCbk(void * data)357 hostConfigLoadCbk(void *data)
358 {
359 int rval = 1, bval;
360 long iv;
361 FILE *fp = (FILE *) data;
362 char *p;
363
364
365 d_printf(1, "hostConfigLoadCbk\n");
366
367 if (defaultParams) {
368 freeHostParams(defaultParams);
369 defaultParams = NULL;
370 }
371
372 /* get optional global defaults */
373 if (getInteger(topScope, "dns-retry", &iv, NO_INHERIT)) {
374 if (iv < 1) {
375 rval = 0;
376 logOrPrint(LOG_ERR, fp,
377 "ME config: value of %s (%ld) in %s cannot be less"
378 " than 1. Using %ld",
379 "dns-retry", iv, "global scope",
380 (long) DNS_RETRY_PERIOD);
381 iv = DNS_RETRY_PERIOD;
382 }
383 } else
384 iv = DNS_RETRY_PERIOD;
385 dnsRetPeriod = (unsigned int) iv;
386
387
388 if (getInteger(topScope, "dns-expire", &iv, NO_INHERIT)) {
389 if (iv < 1) {
390 rval = 0;
391 logOrPrint(LOG_ERR, fp,
392 "ME config: value of %s (%ld) in %s cannot be less"
393 " than 1. Using %ld",
394 "dns-expire", iv, "global scope",
395 (long) DNS_EXPIRE_PERIOD);
396 iv = DNS_EXPIRE_PERIOD;
397 }
398 } else
399 iv = DNS_EXPIRE_PERIOD;
400 dnsExpPeriod = (unsigned int) iv;
401
402 if (getBool(topScope, "gen-html", &bval, NO_INHERIT))
403 genHtml = (bval ? true : false);
404 else
405 genHtml = GEN_HTML;
406
407 if (getString(topScope, "status-file", &p, NO_INHERIT)) {
408 hostSetStatusFile(p);
409 free(p);
410 } else
411 hostSetStatusFile(INNFEED_STATUS);
412
413 if (getBool(topScope, "connection-stats", &bval, NO_INHERIT))
414 logConnectionStats = (bval ? true : false);
415 else
416 logConnectionStats = (LOG_CONNECTION_STATS ? true : false);
417
418 if (getInteger(topScope, "host-queue-highwater", &iv, NO_INHERIT)) {
419 if (iv < 0) {
420 rval = 0;
421 logOrPrint(LOG_ERR, fp,
422 "ME config: value of %s (%ld) in %s cannot be less"
423 " than 0. Using %ld",
424 "host-queue-highwater", iv, "global scope",
425 (long) HOST_HIGHWATER);
426 iv = HOST_HIGHWATER;
427 }
428 } else
429 iv = HOST_HIGHWATER;
430 hostHighwater = (unsigned int) iv;
431
432 if (getInteger(topScope, "stats-period", &iv, NO_INHERIT)) {
433 if (iv < 0) {
434 rval = 0;
435 logOrPrint(LOG_ERR, fp,
436 "ME config: value of %s (%ld) in %s cannot be less"
437 " than 0. Using %ld",
438 "stats-period", iv, "global scope",
439 (long) STATS_PERIOD);
440 iv = STATS_PERIOD;
441 }
442 } else
443 iv = STATS_PERIOD;
444 statsPeriod = (unsigned int) iv;
445
446
447 if (getInteger(topScope, "stats-reset", &iv, NO_INHERIT)) {
448 if (iv < 0) {
449 rval = 0;
450 logOrPrint(LOG_ERR, fp,
451 "ME config: value of %s (%ld) in %s cannot be less"
452 " than 0. Using %ld",
453 "stats-reset", iv, "global scope",
454 (long) STATS_RESET_PERIOD);
455 iv = STATS_RESET_PERIOD;
456 }
457 } else
458 iv = STATS_RESET_PERIOD;
459 statsResetPeriod = (unsigned int) iv;
460
461 defaultParams = hostDetails(topScope, NULL, true, fp);
462 ASSERT(defaultParams != NULL);
463
464 return rval;
465 }
466
467 /*
468 * make a new HostParams structure copying an existing one
469 * or from compiled defaults
470 */
471
472 static HostParams
newHostParams(HostParams p)473 newHostParams(HostParams p)
474 {
475 HostParams params;
476
477 params = xmalloc(sizeof(struct host_param_s));
478
479 if (p != NULL) {
480 /* Copy old stuff in */
481 memcpy((char *) params, (char *) p, sizeof(struct host_param_s));
482 if (params->peerName)
483 params->peerName = xstrdup(params->peerName);
484 if (params->ipName)
485 params->ipName = xstrdup(params->ipName);
486 if (params->bindAddr)
487 params->bindAddr = xstrdup(params->bindAddr);
488 if (params->bindAddr6)
489 params->bindAddr6 = xstrdup(params->bindAddr6);
490 } else {
491 /* Fill in defaults */
492 params->peerName = NULL;
493 params->ipName = NULL;
494 params->bindAddr = NULL;
495 params->bindAddr6 = NULL;
496 params->articleTimeout = ARTTOUT;
497 params->responseTimeout = RESPTOUT;
498 params->initialConnections = INIT_CXNS;
499 params->absMaxConnections = MAX_CXNS;
500 params->maxChecks = MAX_Q_SIZE;
501 params->portNum = PORTNUM;
502 params->forceIPv4 = FORCE_IPv4;
503 params->closePeriod = CLOSE_PERIOD;
504 params->dynamicMethod = METHOD_COMBINED;
505 params->wantStreaming = STREAM;
506 params->dropDeferred = false;
507 params->minQueueCxn = false;
508 params->lowPassLow = NOCHECKLOW;
509 params->lowPassHigh = NOCHECKHIGH;
510 params->lowPassFilter = FILTERVALUE;
511 params->backlogLimit = BLOGLIMIT;
512 params->backlogLimitHigh = BLOGLIMIT_HIGH;
513 params->backlogFactor = LIMIT_FUDGE;
514 params->dynBacklogFilter = BACKLOGFILTER;
515 params->dynBacklogLowWaterMark = BACKLOGLWM;
516 params->dynBacklogHighWaterMark = BACKLOGHWM;
517 params->backlogFeedFirst = false;
518 params->username = NULL;
519 params->password = NULL;
520 }
521 return (params);
522 }
523
524 /*
525 * Free up a param structure
526 */
527
528 static void
freeHostParams(HostParams params)529 freeHostParams(HostParams params)
530 {
531 ASSERT(params != NULL);
532 if (params->peerName)
533 free(params->peerName);
534 if (params->ipName)
535 free(params->ipName);
536 if (params->bindAddr)
537 free(params->bindAddr);
538 if (params->bindAddr6)
539 free(params->bindAddr6);
540 free(params);
541 }
542
543 static void
hostReconfigure(Host h,HostParams params)544 hostReconfigure(Host h, HostParams params)
545 {
546 unsigned int i;
547 double oldBacklogFilter;
548
549 if (strcmp(h->params->ipName, params->ipName) != 0) {
550 free(h->params->ipName);
551 h->params->ipName = xstrdup(params->ipName);
552 h->nextIpLookup = theTime();
553 }
554
555 /* Put in new parameters
556 Unfortunately we can't blat on top of absMaxConnections
557 as we need to do some resizing here
558 */
559
560 ASSERT(h->params != NULL);
561
562 oldBacklogFilter = h->params->dynBacklogFilter;
563 i = h->params->absMaxConnections; /* keep old value */
564 /* Use this set of params and allocate, and free
565 * up the old
566 */
567 freeHostParams(h->params);
568 h->params = params;
569 h->params->absMaxConnections = i; /* restore old value */
570
571 /* If the backlog filter value has changed, reset the
572 * filter as the value therein will be screwy
573 */
574 if (h->params->dynBacklogFilter != oldBacklogFilter)
575 h->backlogFilter = ((h->params->dynBacklogLowWaterMark
576 + h->params->dynBacklogHighWaterMark)
577 / 200.0 / (1.0 - h->params->dynBacklogFilter));
578
579 /* We call this anyway - it does nothing if the values
580 * haven't changed. This is because doing things like
581 * just changing "dynamic-method" requires this call
582 * to be made
583 */
584 hostAlterMaxConnections(h, h->params->absMaxConnections,
585 h->params->absMaxConnections, false);
586
587 for (i = 0; i < MAXCONLIMIT(h->params->absMaxConnections); i++)
588 if (h->connections[i] != NULL)
589 cxnSetCheckThresholds(h->connections[i], h->params->lowPassLow,
590 h->params->lowPassHigh,
591 h->params->lowPassFilter);
592
593 /* XXX how to handle initCxns change? */
594 }
595
596
597 void
configHosts(bool talkSelf)598 configHosts(bool talkSelf)
599 {
600 Host nHost, h, q;
601 HostHolder hh, hi;
602 HostParams params;
603
604 /* Remove the current blocked host list */
605 for (hh = blockedHosts, hi = NULL; hh != NULL; hh = hi) {
606 freeHostParams(hh->params);
607 hi = hh->next;
608 free(hh);
609 }
610 blockedHosts = NULL;
611
612 closeDroppedArticleFile();
613 openDroppedArticleFile();
614
615 while ((params = getHostInfo()) != NULL) {
616 h = findHostByName(params->peerName);
617 /* We know the host isn't blocked as we cleared the blocked list */
618 /* Have we already got this host up and running ?*/
619 if (h != NULL) {
620 hostReconfigure(h, params);
621 h->removeOnReload = false; /* Don't remove at the end */
622 } else {
623
624 /* It's a host we haven't seen from the config file before */
625 nHost = newHost(mainListener, params);
626
627 if (nHost == NULL) {
628 addBlockedHost(params);
629
630 warn("ME locked cannot setup peer %s", params->peerName);
631 } else {
632 if (params->initialConnections == 0 && talkSelf)
633 notice("%s config ignored batch mode with initial"
634 " connection count of 0",
635 params->peerName);
636
637 if (!listenerAddPeer(mainListener, nHost))
638 die("failed to add a new peer\n");
639 }
640 }
641 }
642
643
644 for (h = gHostList; h != NULL; h = q) {
645 q = h->next;
646 if (h->removeOnReload) {
647 if (h->isDynamic) {
648 /* change to the new default parameters */
649 params = newHostParams(defaultParams);
650 ASSERT(params->peerName == NULL);
651 ASSERT(params->ipName == NULL);
652 ASSERT(h->params->peerName != NULL);
653 ASSERT(h->params->ipName != NULL);
654 params->peerName = xstrdup(h->params->peerName);
655 params->ipName = xstrdup(h->params->ipName);
656 hostReconfigure(h, params);
657 h->removeOnReload = true;
658 } else
659 hostClose(h); /* h may be deleted in here. */
660 } else
661 /* prime it for the next config file read */
662 h->removeOnReload = true;
663 }
664
665 hostLogStatus();
666 }
667
668
669 static void
hostAlterMaxConnections(Host host,unsigned int absMaxCxns,unsigned int maxCxns,bool makeConnect)670 hostAlterMaxConnections(Host host, unsigned int absMaxCxns,
671 unsigned int maxCxns, bool makeConnect)
672 {
673 unsigned int lAbsMaxCxns;
674 unsigned int i;
675
676 /* Fix 0 unlimited case */
677 lAbsMaxCxns = MAXCONLIMIT(absMaxCxns);
678
679 /* Don't accept 0 for maxCxns */
680 maxCxns = MAXCONLIMIT(maxCxns);
681
682 if (host->params->dynamicMethod == METHOD_STATIC) {
683 /* If running static, ignore the maxCxns passed in, we'll
684 just use absMaxCxns
685 */
686 maxCxns = lAbsMaxCxns;
687 }
688
689 if (maxCxns > lAbsMaxCxns) {
690 /* ensure maxCxns is of the correct form */
691 maxCxns = lAbsMaxCxns;
692 }
693
694 if ((maxCxns < host->maxConnections) && (host->connections != NULL)) {
695 /* We are going to have to nuke some connections, as the current
696 max is now greater than the new max
697 */
698 for (i = host->maxConnections; i > maxCxns; i--) {
699 /* XXX this is harsh, and arguably there could be a
700 cleaner way of doing it. the problem being addressed
701 by doing it this way is that eventually a connection
702 closed cleanly via cxnClose can end up ultimately
703 calling hostCxnDead after h->maxConnections has
704 been lowered and the relevant arrays downsized.
705 If trashing the old, unallocated space in
706 hostCxnDead doesn't kill the process, the
707 ASSERT against h->maxConnections surely will.
708 */
709 if (host->connections[i - 1] != NULL) {
710 cxnLogStats(host->connections[i - 1], true);
711 cxnNuke(host->connections[i - 1]);
712 host->connections[i - 1] = NULL;
713 }
714 }
715 host->maxConnections = maxCxns;
716 }
717
718 if (host->connections)
719 for (i = host->maxConnections;
720 i <= MAXCONLIMIT(host->params->absMaxConnections); i++) {
721 /* Ensure we've got an empty values only beyond the maxConnection
722 water mark.
723 */
724 ASSERT(host->connections[i] == NULL);
725 }
726
727 if ((lAbsMaxCxns != MAXCONLIMIT(host->params->absMaxConnections))
728 || (host->connections == NULL)) {
729 /* we need to change the size of the connection array */
730 if (host->connections == NULL) {
731 /* not yet allocated */
732
733 host->connections = xcalloc(lAbsMaxCxns + 1, sizeof(Connection));
734
735 ASSERT(host->cxnActive == NULL);
736 host->cxnActive = xcalloc(lAbsMaxCxns, sizeof(bool));
737
738 ASSERT(host->cxnSleeping == NULL);
739 host->cxnSleeping = xcalloc(lAbsMaxCxns, sizeof(bool));
740
741 for (i = 0; i < lAbsMaxCxns; i++) {
742 host->connections[i] = NULL;
743 host->cxnActive[i] = false;
744 host->cxnSleeping[i] = false;
745 }
746 host->connections[lAbsMaxCxns] = NULL;
747 } else {
748 host->connections = xrealloc(
749 host->connections, sizeof(Connection) * (lAbsMaxCxns + 1));
750 host->cxnActive =
751 xrealloc(host->cxnActive, sizeof(bool) * lAbsMaxCxns);
752 host->cxnSleeping =
753 xrealloc(host->cxnSleeping, sizeof(bool) * lAbsMaxCxns);
754
755 if (lAbsMaxCxns > MAXCONLIMIT(host->params->absMaxConnections)) {
756 for (i = MAXCONLIMIT(host->params->absMaxConnections);
757 i < lAbsMaxCxns; i++) {
758 host->connections[i + 1] =
759 NULL; /* array always 1 larger */
760 host->cxnActive[i] = false;
761 host->cxnSleeping[i] = false;
762 }
763 }
764 }
765 host->params->absMaxConnections = absMaxCxns;
766 }
767 /* if maximum was raised, establish the new connexions
768 (but don't start using them).
769 */
770 if (maxCxns > host->maxConnections) {
771 i = host->maxConnections;
772 /* need to set host->maxConnections before cxnWait() */
773 host->maxConnections = maxCxns;
774
775 while (i < maxCxns) {
776 host->cxnActive[i] = false;
777 host->cxnSleeping[i] = false;
778 /* create a new connection */
779 host->connections[i] = newConnection(
780 host, i, host->params->ipName, host->params->articleTimeout,
781 host->params->portNum, host->params->responseTimeout,
782 host->params->closePeriod, host->params->lowPassLow,
783 host->params->lowPassHigh, host->params->lowPassFilter);
784
785 /* connect if low enough numbered, or we were forced to */
786 if ((i < host->params->initialConnections) || makeConnect)
787 cxnConnect(host->connections[i]);
788 else
789 cxnWait(host->connections[i]);
790 i++;
791 }
792 }
793 }
794
795 /*
796 * Find a host on the blocked host list
797 */
798
799 static HostHolder
FindBlockedHost(const char * name)800 FindBlockedHost(const char *name)
801 {
802 HostHolder hh = blockedHosts;
803 while (hh != NULL)
804 if ((hh->params) && (hh->params->peerName)
805 && (strcmp(name, hh->params->peerName) == 0))
806 return hh;
807 else
808 hh = hh->next;
809 return NULL;
810 }
811
812 static void
addBlockedHost(HostParams params)813 addBlockedHost(HostParams params)
814 {
815 HostHolder hh;
816
817 hh = xmalloc(sizeof(struct host_holder_s));
818 /* Use this set of params */
819
820 hh->params = params;
821
822 hh->next = blockedHosts;
823 blockedHosts = hh;
824 }
825
826 /*
827 * We iterate through the blocked host list and try and reconnect ones
828 * where we couldn't get a lock
829 */
830 static void
tryBlockedHosts(TimeoutId tid UNUSED,void * data UNUSED)831 tryBlockedHosts(TimeoutId tid UNUSED, void *data UNUSED)
832 {
833 HostHolder hh, hi;
834 HostParams params;
835
836 hh = blockedHosts; /* Get start of our queue */
837 blockedHosts = NULL; /* remove them all from the queue of hosts */
838
839 while (hh != NULL) {
840 params = hh->params;
841 hi = hh->next;
842 free(hh);
843 hh = hi;
844
845 if (params && params->peerName) {
846 if (findHostByName(params->peerName) != NULL) {
847 /* Wierd, someone's managed to start it when it's on
848 * the blocked list. Just silently discard.
849 */
850 freeHostParams(params);
851 } else {
852 Host nHost;
853 nHost = newHost(mainListener, params);
854
855 if (nHost == NULL) {
856 addBlockedHost(params);
857
858 warn("ME locked cannot setup peer %s", params->peerName);
859 } else {
860 d_printf(1, "Unblocked host %s\n", params->peerName);
861
862 if (params->initialConnections == 0
863 && listenerIsDummy(mainListener) /*talk to self*/)
864 notice("%s config ignored batch mode with initial"
865 " connection count of 0",
866 params->peerName);
867
868 if (!listenerAddPeer(mainListener, nHost))
869 die("failed to add a new peer\n");
870 }
871 }
872 }
873 }
874 tryBlockedHostsId =
875 prepareSleep(tryBlockedHosts, TRYBLOCKEDHOSTPERIOD, NULL);
876 }
877
878
879 /*
880 * Create a new Host object with default parameters. Called by the
881 * InnListener.
882 */
883
884 Host
newDefaultHost(InnListener listener,const char * name)885 newDefaultHost(InnListener listener, const char *name)
886 {
887 HostParams p;
888 Host h = NULL;
889
890 if (FindBlockedHost(name) == NULL) {
891
892 p = newHostParams(defaultParams);
893 ASSERT(p != NULL);
894
895 /* relies on fact listener and names are null in default*/
896 p->peerName = xstrdup(name);
897 p->ipName = xstrdup(name);
898
899 h = newHost(listener, p);
900 if (h == NULL) {
901 /* Couldn't get a lock - add to list of blocked peers */
902 addBlockedHost(p);
903
904 warn("ME locked cannot setup peer %s", p->peerName);
905
906 return NULL;
907 }
908
909 h->isDynamic = true;
910 h->removeOnReload = true;
911
912 notice("ME unconfigured peer %s added", p->peerName);
913 }
914 return h;
915 }
916
917 /*
918 * Create a new host and attach the supplied param structure
919 */
920
921 static bool inited = false;
922 static Host
newHost(InnListener listener,HostParams p)923 newHost(InnListener listener, HostParams p)
924 {
925 Host nh;
926
927 ASSERT(p->maxChecks > 0);
928
929 if (!inited) {
930 inited = true;
931 atexit(hostCleanup);
932 }
933
934 /*
935 * Once only, init the first blocked host check
936 */
937 if (tryBlockedHostsId == 0)
938 tryBlockedHostsId =
939 prepareSleep(tryBlockedHosts, TRYBLOCKEDHOSTPERIOD, NULL);
940
941 nh = xcalloc(1, sizeof(struct host_s));
942
943 nh->params = p;
944 nh->listener = listener;
945
946 nh->connections = NULL; /* We'll get these allocated later */
947 nh->cxnActive = NULL;
948 nh->cxnSleeping = NULL;
949
950 nh->activeCxns = 0;
951 nh->sleepingCxns = 0;
952
953 nh->blockedCxn = NULL;
954 nh->notThisCxn = NULL;
955
956 nh->queued = NULL;
957 nh->queuedTail = NULL;
958
959 nh->processed = NULL;
960 nh->processedTail = NULL;
961
962 nh->deferred = NULL;
963 nh->deferredTail = NULL;
964
965 nh->statsId = 0;
966 nh->ChkCxnsId = 0;
967 nh->deferredId = 0;
968
969 nh->myTape = newTape(nh->params->peerName, listenerIsDummy(nh->listener));
970 if (nh->myTape == NULL) { /* tape couldn't be locked, probably */
971 free(nh->connections);
972 free(nh->cxnActive);
973 free(nh->cxnSleeping);
974
975 free(nh);
976 return NULL; /* note we don't free up p */
977 }
978
979 nh->backedUp = false;
980 nh->backlog = 0;
981 nh->deferLen = 0;
982
983 nh->loggedBacklog = false;
984 nh->loggedModeOn = false;
985 nh->loggedModeOff = false;
986 nh->notifiedChangedRemBlckd = false;
987 nh->removeOnReload = false; /* ready for config file reload */
988 nh->isDynamic = false;
989
990 nh->artsOffered = 0;
991 nh->artsOffered_checkpoint = 0;
992 nh->artsAccepted = 0;
993 nh->artsAccepted_checkpoint = 0;
994 nh->artsNotWanted = 0;
995 nh->artsNotWanted_checkpoint = 0;
996 nh->artsRejected = 0;
997 nh->artsRejected_checkpoint = 0;
998 nh->artsDeferred = 0;
999 nh->artsDeferred_checkpoint = 0;
1000 nh->artsMissing = 0;
1001 nh->artsMissing_checkpoint = 0;
1002 nh->artsToTape = 0;
1003 nh->artsToTape_checkpoint = 0;
1004 nh->artsQueueOverflow = 0;
1005 nh->artsCxnDrop = 0;
1006 nh->artsCxnDrop_checkpoint = 0;
1007 nh->artsHostSleep = 0;
1008 nh->artsHostSleep_checkpoint = 0;
1009 nh->artsHostClose = 0;
1010 nh->artsHostClose_checkpoint = 0;
1011 nh->artsFromTape = 0;
1012 nh->artsFromTape_checkpoint = 0;
1013 nh->artsSizeAccepted = 0;
1014 nh->artsSizeAccepted_checkpoint = 0;
1015 nh->artsSizeRejected = 0;
1016 nh->artsSizeRejected_checkpoint = 0;
1017
1018 nh->artsProcLastPeriod = 0;
1019 nh->secsInLastPeriod = 0;
1020 nh->lastCheckPoint = 0;
1021 nh->lastSentCheckPoint = 0;
1022 nh->lastTotalCheckPoint = 0;
1023 nh->maxCxnChk = true;
1024 nh->lastMaxCxnTime = time(0);
1025 nh->lastChkTime = time(0);
1026 nh->nextCxnTimeChk = 30;
1027 nh->backlogFilter = ((nh->params->dynBacklogLowWaterMark
1028 + nh->params->dynBacklogHighWaterMark)
1029 / 200.0 / (1.0 - nh->params->dynBacklogFilter));
1030
1031 nh->gArtsOffered = 0;
1032 nh->gArtsAccepted = 0;
1033 nh->gArtsNotWanted = 0;
1034 nh->gArtsRejected = 0;
1035 nh->gArtsDeferred = 0;
1036 nh->gArtsMissing = 0;
1037 nh->gArtsToTape = 0;
1038 nh->gArtsQueueOverflow = 0;
1039 nh->gArtsCxnDrop = 0;
1040 nh->gArtsHostSleep = 0;
1041 nh->gArtsHostClose = 0;
1042 nh->gArtsFromTape = 0;
1043 nh->gArtsSizeAccepted = 0;
1044 nh->gArtsSizeRejected = 0;
1045 nh->gCxnQueue = 0;
1046 nh->gNoQueue = 0;
1047
1048 nh->firstConnectTime = 0;
1049 nh->connectTime = 0;
1050 nh->connectTime_checkpoint = 0;
1051
1052 nh->spoolTime = 0;
1053 nh->spoolTime_checkpoint = 0;
1054
1055 nh->blNone = 0;
1056 nh->blFull = 0;
1057 nh->blQuartile[0] = nh->blQuartile[1] = nh->blQuartile[2] =
1058 nh->blQuartile[3] = 0;
1059 nh->dlAccum = 0;
1060 nh->blAccum = 0;
1061 nh->blCount = 0;
1062
1063
1064 nh->maxConnections = 0; /* we currently have no connections allocated */
1065
1066 /* Note that the following will override the initialCxns specified as
1067 maxCxns if we are on non-dyamic feed
1068 */
1069 hostAlterMaxConnections(nh, nh->params->absMaxConnections,
1070 nh->params->initialConnections, false);
1071
1072 nh->next = gHostList;
1073 gHostList = nh;
1074 gHostCount++;
1075
1076 if (maxIpNameLen == 0) {
1077 start = theTime();
1078 timeToString(start, startTime, sizeof(startTime));
1079 myPid = getpid();
1080 }
1081
1082 if (strlen(nh->params->ipName) > maxIpNameLen)
1083 maxIpNameLen = strlen(nh->params->ipName);
1084 if (strlen(nh->params->peerName) > maxPeerNameLen)
1085 maxPeerNameLen = strlen(nh->params->peerName);
1086
1087 return nh;
1088 }
1089
1090 struct sockaddr *
hostIpAddr(Host host)1091 hostIpAddr(Host host)
1092 {
1093 int i;
1094 struct sockaddr **newIpAddrPtrs = NULL;
1095 struct sockaddr_storage *newIpAddrs = NULL;
1096 struct sockaddr *returnAddr;
1097
1098 ASSERT(host->params != NULL);
1099
1100 /* check to see if need to look up the host name */
1101 if (host->nextIpLookup <= theTime()) {
1102 int gai_ret;
1103 struct addrinfo *res, *p;
1104 struct addrinfo hints;
1105 char port[20];
1106
1107 memset(&hints, 0, sizeof(hints));
1108 hints.ai_family = AF_UNSPEC;
1109 #ifdef HAVE_INET6
1110 if (host->params->bindAddr
1111 && strcmp(host->params->bindAddr, "none") == 0)
1112 hints.ai_family = AF_INET6;
1113 if (host->params->bindAddr6
1114 && strcmp(host->params->bindAddr6, "none") == 0)
1115 hints.ai_family = AF_INET;
1116 #endif
1117 hints.ai_socktype = SOCK_STREAM;
1118 hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
1119 snprintf(port, sizeof(port), "%hu", host->params->portNum);
1120 gai_ret = getaddrinfo(host->params->ipName, port, &hints, &res);
1121 if (gai_ret != 0 || res == NULL) {
1122 warn("%s can't resolve hostname %s: %s", host->params->peerName,
1123 host->params->ipName,
1124 gai_ret == 0 ? "no addresses returned"
1125 : gai_strerror(gai_ret));
1126 } else {
1127 /* figure number of pointers that need space */
1128 i = 0;
1129 for (p = res; p; p = p->ai_next)
1130 ++i;
1131
1132 newIpAddrPtrs = (struct sockaddr **) xmalloc(
1133 (i + 1) * sizeof(struct sockaddr *));
1134
1135 newIpAddrs = (struct sockaddr_storage *) xmalloc(
1136 i * sizeof(struct sockaddr_storage));
1137
1138 i = 0;
1139 /* copy the addresses from the getaddrinfo linked list */
1140 for (p = res; p; p = p->ai_next) {
1141 memcpy(&newIpAddrs[i], p->ai_addr, p->ai_addrlen);
1142 newIpAddrPtrs[i] = (struct sockaddr *) (&newIpAddrs[i]);
1143 ++i;
1144 }
1145 newIpAddrPtrs[i] = NULL;
1146 freeaddrinfo(res);
1147 }
1148
1149 if (newIpAddrs) {
1150 if (host->ipAddrs) {
1151 if (host->ipAddrs[0])
1152 free(host->ipAddrs[0]);
1153 free(host->ipAddrs);
1154 }
1155 host->ipAddrs = newIpAddrPtrs;
1156 host->nextIpAddr = 0;
1157 host->nextIpLookup = theTime() + dnsExpPeriod;
1158 } else {
1159 /* failed to setup new addresses */
1160 host->nextIpLookup = theTime() + dnsRetPeriod;
1161 }
1162 }
1163
1164 if (host->ipAddrs)
1165 returnAddr = host->ipAddrs[host->nextIpAddr];
1166 else
1167 returnAddr = NULL;
1168
1169 return returnAddr;
1170 }
1171
1172
1173 /*
1174 * Delete IPv4 addresses from the address list.
1175 */
1176 void
hostDeleteIpv4Addr(Host host)1177 hostDeleteIpv4Addr(Host host)
1178 {
1179 int i, j;
1180
1181 if (!host->ipAddrs)
1182 return;
1183 for (i = 0, j = 0; host->ipAddrs[i]; i++) {
1184 if (host->ipAddrs[i]->sa_family != AF_INET)
1185 host->ipAddrs[j++] = host->ipAddrs[i];
1186 }
1187 host->ipAddrs[j] = 0;
1188 if (host->nextIpAddr >= j)
1189 host->nextIpAddr = 0;
1190 }
1191
1192
1193 void
hostIpFailed(Host host)1194 hostIpFailed(Host host)
1195 {
1196 if (host->ipAddrs)
1197 if (host->ipAddrs[++host->nextIpAddr] == NULL)
1198 host->nextIpAddr = 0;
1199 }
1200
1201
1202 void
gPrintHostInfo(FILE * fp,unsigned int indentAmt)1203 gPrintHostInfo(FILE *fp, unsigned int indentAmt)
1204 {
1205 Host h;
1206 char indent[INDENT_BUFFER_SIZE];
1207 unsigned int i;
1208
1209 for (i = 0; i < MIN(INDENT_BUFFER_SIZE - 1, indentAmt); i++)
1210 indent[i] = ' ';
1211 indent[i] = '\0';
1212
1213 fprintf(fp, "%sGlobal Host list : (count %u) {\n", indent, gHostCount);
1214
1215 for (h = gHostList; h != NULL; h = h->next)
1216 printHostInfo(h, fp, indentAmt + INDENT_INCR);
1217
1218 fprintf(fp, "%s}\n", indent);
1219 }
1220
1221
1222 void
printHostInfo(Host host,FILE * fp,unsigned int indentAmt)1223 printHostInfo(Host host, FILE *fp, unsigned int indentAmt)
1224 {
1225 char dateString[30];
1226 char indent[INDENT_BUFFER_SIZE];
1227 unsigned int i;
1228 ProcQElem qe;
1229 double cnt;
1230
1231 for (i = 0; i < MIN(INDENT_BUFFER_SIZE - 1, indentAmt); i++)
1232 indent[i] = ' ';
1233 indent[i] = '\0';
1234
1235 if (host == NULL) {
1236 fprintf(fp, "%sHost : NULL {\n%s}\n", indent, indent);
1237 return;
1238 }
1239
1240 cnt = (host->blCount) ? (host->blCount) : 1.0;
1241
1242 fprintf(fp, "%sHost : %p {\n", indent, (void *) host);
1243 fprintf(fp, "%s peer-name : %s\n", indent, host->params->peerName);
1244 fprintf(fp, "%s ip-name : %s\n", indent, host->params->ipName);
1245 fprintf(fp, "%s bindaddress : %s\n", indent,
1246 host->params->bindAddr ? host->params->bindAddr : "any");
1247 fprintf(fp, "%s bindaddress6 : %s\n", indent,
1248 host->params->bindAddr6 ? host->params->bindAddr6 : "any");
1249 fprintf(fp, "%s abs-max-connections : %u\n", indent,
1250 host->params->absMaxConnections);
1251 fprintf(fp, "%s active-connections : %u\n", indent, host->activeCxns);
1252 fprintf(fp, "%s sleeping-connections : %u\n", indent,
1253 host->sleepingCxns);
1254 fprintf(fp, "%s initial-connections : %u\n", indent,
1255 host->params->initialConnections);
1256 fprintf(fp, "%s want-streaming : %s\n", indent,
1257 boolToString(host->params->wantStreaming));
1258 fprintf(fp, "%s drop-deferred : %s\n", indent,
1259 boolToString(host->params->dropDeferred));
1260 fprintf(fp, "%s min-queue-connection : %s\n", indent,
1261 boolToString(host->params->minQueueCxn));
1262 fprintf(fp, "%s remote-streams : %s\n", indent,
1263 boolToString(host->remoteStreams));
1264 fprintf(fp, "%s max-checks : %u\n", indent, host->params->maxChecks);
1265 fprintf(fp, "%s article-timeout : %u\n", indent,
1266 host->params->articleTimeout);
1267 fprintf(fp, "%s response-timeout : %u\n", indent,
1268 host->params->responseTimeout);
1269 fprintf(fp, "%s close-period : %u\n", indent,
1270 host->params->closePeriod);
1271 fprintf(fp, "%s port : %u\n", indent, host->params->portNum);
1272 fprintf(fp, "%s dynamic-method : %u\n", indent,
1273 host->params->dynamicMethod);
1274 fprintf(fp, "%s dynamic-backlog-filter : %2.1f\n", indent,
1275 host->params->dynBacklogFilter);
1276 fprintf(fp, "%s dynamic-backlog-lwm : %2.1f\n", indent,
1277 host->params->dynBacklogLowWaterMark);
1278 fprintf(fp, "%s dynamic-backlog-hwm : %2.1f\n", indent,
1279 host->params->dynBacklogHighWaterMark);
1280 fprintf(fp, "%s no-check on : %2.1f\n", indent,
1281 host->params->lowPassHigh);
1282 fprintf(fp, "%s no-check off : %2.1f\n", indent,
1283 host->params->lowPassLow);
1284 fprintf(fp, "%s no-check filter : %2.1f\n", indent,
1285 host->params->lowPassFilter);
1286 fprintf(fp, "%s backlog-limit : %u\n", indent,
1287 host->params->backlogLimit);
1288 fprintf(fp, "%s backlog-limit-highwater : %u\n", indent,
1289 host->params->backlogLimitHigh);
1290 fprintf(fp, "%s backlog-factor : %2.1f\n", indent,
1291 host->params->backlogFactor);
1292 fprintf(fp, "%s max-connections : %u\n", indent, host->maxConnections);
1293 fprintf(fp, "%s backlog-feed-first : %s\n", indent,
1294 boolToString(host->params->backlogFeedFirst));
1295
1296
1297 fprintf(fp, "%s statistics-id : %d\n", indent, host->statsId);
1298 fprintf(fp, "%s ChkCxns-id : %d\n", indent, host->ChkCxnsId);
1299 fprintf(fp, "%s deferred-id : %d\n", indent, host->deferredId);
1300 fprintf(fp, "%s backed-up : %s\n", indent,
1301 boolToString(host->backedUp));
1302 fprintf(fp, "%s backlog : %u\n", indent, host->backlog);
1303 fprintf(fp, "%s deferLen : %u\n", indent, host->deferLen);
1304 fprintf(fp, "%s loggedModeOn : %s\n", indent,
1305 boolToString(host->loggedModeOn));
1306 fprintf(fp, "%s loggedModeOff : %s\n", indent,
1307 boolToString(host->loggedModeOff));
1308 fprintf(fp, "%s logged-backlog : %s\n", indent,
1309 boolToString(host->loggedBacklog));
1310 fprintf(fp, "%s streaming-type changed : %s\n", indent,
1311 boolToString(host->notifiedChangedRemBlckd));
1312 fprintf(fp, "%s articles offered : %u\n", indent, host->artsOffered);
1313 fprintf(fp, "%s articles accepted : %u\n", indent, host->artsAccepted);
1314 fprintf(fp, "%s articles not wanted : %u\n", indent,
1315 host->artsNotWanted);
1316 fprintf(fp, "%s articles rejected : %u\n", indent, host->artsRejected);
1317 fprintf(fp, "%s articles deferred : %u\n", indent, host->artsDeferred);
1318 fprintf(fp, "%s articles missing : %u\n", indent, host->artsMissing);
1319 fprintf(fp, "%s articles spooled : %u\n", indent, host->artsToTape);
1320 fprintf(fp, "%s because of queue overflow : %u\n", indent,
1321 host->artsQueueOverflow);
1322 fprintf(fp, "%s when the we closed the host : %u\n", indent,
1323 host->artsHostClose);
1324 fprintf(fp, "%s because the host was asleep : %u\n", indent,
1325 host->artsHostSleep);
1326 fprintf(fp, "%s articles unspooled : %u\n", indent, host->artsFromTape);
1327 fprintf(fp, "%s articles requeued from dropped connections : %u\n",
1328 indent, host->artsCxnDrop);
1329
1330 fprintf(fp, "%s process articles offered : %u\n", indent,
1331 host->gArtsOffered);
1332 fprintf(fp, "%s process articles accepted : %u\n", indent,
1333 host->gArtsAccepted);
1334 fprintf(fp, "%s process articles not wanted : %u\n", indent,
1335 host->gArtsNotWanted);
1336 fprintf(fp, "%s process articles rejected : %u\n", indent,
1337 host->gArtsRejected);
1338 fprintf(fp, "%s process articles deferred : %u\n", indent,
1339 host->gArtsDeferred);
1340 fprintf(fp, "%s process articles missing : %u\n", indent,
1341 host->gArtsMissing);
1342 fprintf(fp, "%s process articles spooled : %u\n", indent,
1343 host->gArtsToTape);
1344 fprintf(fp, "%s because of queue overflow : %u\n", indent,
1345 host->gArtsQueueOverflow);
1346 fprintf(fp, "%s when the we closed the host : %u\n", indent,
1347 host->gArtsHostClose);
1348 fprintf(fp, "%s because the host was asleep : %u\n", indent,
1349 host->gArtsHostSleep);
1350 fprintf(fp, "%s process articles unspooled : %u\n", indent,
1351 host->gArtsFromTape);
1352 fprintf(fp,
1353 "%s process articles requeued from dropped connections : %u\n",
1354 indent, host->gArtsCxnDrop);
1355
1356 fprintf(fp, "%s average (mean) defer length : %.1f\n", indent,
1357 (double) host->dlAccum / cnt);
1358 fprintf(fp, "%s average (mean) queue length : %.1f\n", indent,
1359 (double) host->blAccum / cnt);
1360 fprintf(fp, "%s percentage of the time empty : %.1f\n", indent,
1361 100.0 * host->blNone / cnt);
1362 fprintf(fp, "%s percentage of the time >0%%-25%% : %.1f\n", indent,
1363 100.0 * host->blQuartile[0] / cnt);
1364 fprintf(fp, "%s percentage of the time 25%%-50%% : %.1f\n", indent,
1365 100.0 * host->blQuartile[1] / cnt);
1366 fprintf(fp, "%s percentage of the time 50%%-75%% : %.1f\n", indent,
1367 100.0 * host->blQuartile[2] / cnt);
1368 fprintf(fp, "%s percentage of the time 75%%-<100%% : %.1f\n", indent,
1369 100.0 * host->blQuartile[3] / cnt);
1370 fprintf(fp, "%s percentage of the time full : %.1f\n", indent,
1371 100.0 * host->blFull / cnt);
1372 fprintf(fp, "%s number of samples : %u\n", indent, host->blCount);
1373
1374 timeToString(host->firstConnectTime, dateString, sizeof(dateString));
1375 fprintf(fp, "%s firstConnectTime : %s\n", indent, dateString);
1376
1377 timeToString(host->connectTime, dateString, sizeof(dateString));
1378 fprintf(fp, "%s connectTime : %s\n", indent, dateString);
1379
1380 timeToString(host->spoolTime, dateString, sizeof(dateString));
1381 fprintf(fp, "%s spoolTime : %s\n", indent, dateString);
1382
1383 timeToString(host->lastSpoolTime, dateString, sizeof(dateString));
1384 fprintf(fp, "%s last-spool-time : %s\n", indent, dateString);
1385
1386 #if defined(INNFEED_DEBUG)
1387 fprintf(fp, "%s tape {\n", indent);
1388 printTapeInfo(host->myTape, fp, indentAmt + INDENT_INCR);
1389 fprintf(fp, "%s }\n", indent);
1390 #else
1391 fprintf(fp, "%s tape : %p\n", indent, (void *) host->myTape);
1392 #endif
1393
1394 fprintf(fp, "%s QUEUED articles {\n", indent);
1395 for (qe = host->queued; qe != NULL; qe = qe->next) {
1396 #if defined(INNFEED_DEBUG)
1397 printArticleInfo(qe->article, fp, indentAmt + INDENT_INCR);
1398 #else
1399 fprintf(fp, "%s %p\n", indent, (void *) qe->article);
1400 #endif
1401 }
1402
1403 fprintf(fp, "%s }\n", indent);
1404
1405 fprintf(fp, "%s IN PROCESS articles {\n", indent);
1406 for (qe = host->processed; qe != NULL; qe = qe->next) {
1407 #if defined(INNFEED_DEBUG)
1408 printArticleInfo(qe->article, fp, indentAmt + INDENT_INCR);
1409 #else
1410 fprintf(fp, "%s %p\n", indent, (void *) qe->article);
1411 #endif
1412 }
1413
1414 fprintf(fp, "%s }\n", indent);
1415 fprintf(fp, "%s DEFERRED articles {\n", indent);
1416 for (qe = host->deferred; qe != NULL; qe = qe->next) {
1417 #if defined(INNFEED_DEBUG)
1418 printArticleInfo(qe->article, fp, indentAmt + INDENT_INCR);
1419 #else
1420 fprintf(fp, "%s %p\n", indent, (void *) qe->article);
1421 #endif
1422 }
1423
1424 fprintf(fp, "%s }\n", indent);
1425 fprintf(fp, "%s DEFERRED articles {\n", indent);
1426 for (qe = host->deferred; qe != NULL; qe = qe->next) {
1427 #if defined(INNFEED_DEBUG)
1428 printArticleInfo(qe->article, fp, indentAmt + INDENT_INCR);
1429 #else
1430 fprintf(fp, "%s %p\n", indent, (void *) qe->article);
1431 #endif
1432 }
1433
1434 fprintf(fp, "%s }\n", indent);
1435
1436
1437 fprintf(fp, "%s Connections {\n", indent);
1438 for (i = 0; i < host->maxConnections; i++) {
1439 #if defined(INNFEED_DEBUG)
1440 if (host->connections[i] != NULL)
1441 printCxnInfo(host->connections[i], fp, indentAmt + INDENT_INCR);
1442 #else
1443 fprintf(fp, "%s %p\n", indent, (void *) host->connections[i]);
1444 #endif
1445 }
1446 fprintf(fp, "%s }\n", indent);
1447
1448 fprintf(fp, "%s Active Connections {\n%s ", indent, indent);
1449 for (i = 0; i < host->maxConnections; i++)
1450 if (host->cxnActive[i])
1451 fprintf(fp, " [%u:%p]", i, (void *) host->connections[i]);
1452 fprintf(fp, "\n%s }\n", indent);
1453
1454 fprintf(fp, "%s Sleeping Connections {\n%s ", indent, indent);
1455 for (i = 0; i < host->maxConnections; i++)
1456 if (host->cxnSleeping[i])
1457 fprintf(fp, " [%u:%p]", i, (void *) host->connections[i]);
1458 fprintf(fp, "\n%s }\n", indent);
1459
1460 fprintf(fp, "%s}\n", indent);
1461 }
1462
1463
1464 /* close down all the connections of the Host. All articles that are in
1465 * processes are still pushed out and then a QUIT is issued. The Host will
1466 * also spool all inprocess articles to tape incase the process is about to
1467 * be killed (they'll be refused next time around). When all Connections
1468 * report that they're gone, then the Host will delete itself.
1469 */
1470 void
hostClose(Host host)1471 hostClose(Host host)
1472 {
1473 unsigned int i;
1474 unsigned int cxnCount;
1475
1476 d_printf(1, "Closing host %s\n", host->params->peerName);
1477
1478 queuesToTape(host);
1479 delTape(host->myTape);
1480 host->myTape = NULL;
1481
1482 hostLogStats(host, true);
1483
1484 clearTimer(host->statsId);
1485 clearTimer(host->ChkCxnsId);
1486 clearTimer(host->deferredId);
1487
1488 host->connectTime = 0;
1489
1490 /* when we call cxnTerminate() on the last Connection, the Host objects
1491 will end up getting deleted out from under us (via hostCxnGone()). If
1492 we are running with a malloc that scribbles over memory after freeing
1493 it, then we'd fail in the second for loop test. Trying to access
1494 host->maxConnections. */
1495 for (i = 0, cxnCount = 0; i < host->maxConnections; i++)
1496 cxnCount += (host->connections[i] != NULL ? 1 : 0);
1497 for (i = 0; i < cxnCount; i++)
1498 if (host->connections[i] != NULL)
1499 cxnTerminate(host->connections[i]);
1500 }
1501
1502
1503 /*
1504 * check if host should get more connections opened, or some closed...
1505 */
1506 void
hostChkCxns(TimeoutId tid UNUSED,void * data)1507 hostChkCxns(TimeoutId tid UNUSED, void *data)
1508 {
1509 Host host = (Host) data;
1510 unsigned int currArticles, currSentArticles, currTotalArticles, newMaxCxns;
1511 double lastAPS, currAPS, percentTaken, ratio;
1512 double backlogRatio, backlogMult;
1513
1514 if (!host->maxCxnChk)
1515 return;
1516
1517 ASSERT(host->params != NULL);
1518
1519 if (host->secsInLastPeriod > 0)
1520 lastAPS = host->artsProcLastPeriod / (host->secsInLastPeriod * 1.0);
1521 else
1522 lastAPS = host->artsProcLastPeriod * 1.0;
1523
1524 newMaxCxns = host->maxConnections;
1525
1526 currArticles = (host->gArtsAccepted + host->gArtsRejected
1527 + (host->gArtsNotWanted / 4))
1528 - host->lastCheckPoint;
1529
1530 host->lastCheckPoint = (host->gArtsAccepted + host->gArtsRejected
1531 + (host->gArtsNotWanted / 4));
1532
1533 currSentArticles =
1534 host->gArtsAccepted + host->gArtsRejected - host->lastSentCheckPoint;
1535
1536 host->lastSentCheckPoint = host->gArtsAccepted + host->gArtsRejected;
1537
1538 currTotalArticles = host->gArtsAccepted + host->gArtsRejected
1539 + host->gArtsRejected + host->gArtsQueueOverflow
1540 - host->lastTotalCheckPoint;
1541
1542 host->lastTotalCheckPoint = host->gArtsAccepted + host->gArtsRejected
1543 + host->gArtsRejected
1544 + host->gArtsQueueOverflow;
1545
1546 currAPS = currArticles / (host->nextCxnTimeChk * 1.0);
1547
1548 percentTaken = currSentArticles * 1.0
1549 / ((currTotalArticles == 0) ? 1 : currTotalArticles);
1550
1551 /* Get how full the queue is currently */
1552 backlogRatio = (host->backlog * 1.0 / hostHighwater);
1553 backlogMult = 1.0 / (1.0 - host->params->dynBacklogFilter);
1554
1555 d_printf(
1556 1, "%s hostChkCxns - entry filter=%3.3f blmult=%3.3f blratio=%3.3f\n",
1557 host->params->peerName, host->backlogFilter, backlogMult,
1558 backlogRatio);
1559
1560 ratio = 0.0; /* ignore APS by default */
1561
1562 switch (host->params->dynamicMethod) {
1563 case METHOD_COMBINED:
1564 /* When a high % of articles is being taken, take notice of the
1565 * APS values. However for smaller %s, quickly start to ignore this
1566 * and concentrate on queue sizes
1567 */
1568 ratio = percentTaken * percentTaken;
1569 goto fallthrough;
1570 case METHOD_QUEUE:
1571 fallthrough:
1572 /* backlogFilter is an IIR filtered version of the backlogRatio.
1573 */
1574 host->backlogFilter *= host->params->dynBacklogFilter;
1575 /* Penalise anything over the backlog HWM twice as severely
1576 * (otherwise we end up feeding some sites constantly
1577 * just below the HWM. This way random noise makes
1578 * such sites jump to one more connection
1579 *
1580 * Use factor (1-ratio) so if ratio is near 1 we ignore this
1581 */
1582 if (backlogRatio > host->params->dynBacklogLowWaterMark / 100.0)
1583 host->backlogFilter += (backlogRatio + 1.0) / 2.0 * (1.0 - ratio);
1584 else
1585 host->backlogFilter += backlogRatio * (1.0 - ratio);
1586
1587 /*
1588 * Now bump it around for APS too
1589 */
1590 if ((currAPS - lastAPS) >= 0.1)
1591 host->backlogFilter += ratio * ((currAPS - lastAPS) + 1.0);
1592 else if ((currAPS - lastAPS) < -.2)
1593 host->backlogFilter -= ratio;
1594
1595 d_printf(
1596 1,
1597 "%s hostChkCxns - entry hwm=%3.3f lwm=%3.3f new=%3.3f "
1598 "[%3.3f,%3.3f]\n",
1599 host->params->peerName, host->params->dynBacklogHighWaterMark,
1600 host->params->dynBacklogLowWaterMark, host->backlogFilter,
1601 (host->params->dynBacklogLowWaterMark * backlogMult / 100.0),
1602 (host->params->dynBacklogHighWaterMark * backlogMult / 100.0));
1603
1604 if (host->backlogFilter
1605 < (host->params->dynBacklogLowWaterMark * backlogMult / 100.0))
1606 newMaxCxns--;
1607 else if (host->backlogFilter > (host->params->dynBacklogHighWaterMark
1608 * backlogMult / 100.0))
1609 newMaxCxns++;
1610 break;
1611 case METHOD_STATIC:
1612 /* well not much to do, just check maxConnection = absMaxConnections */
1613 ASSERT(host->maxConnections
1614 == MAXCONLIMIT(host->params->absMaxConnections));
1615 break;
1616 case METHOD_APS:
1617 if ((currAPS - lastAPS) >= 0.1)
1618 newMaxCxns += (int) (currAPS - lastAPS) + 1;
1619 else if ((currAPS - lastAPS) < -.2)
1620 newMaxCxns--;
1621 break;
1622 }
1623
1624 d_printf(1, "hostChkCxns: Chngs %f\n", currAPS - lastAPS);
1625
1626 if (newMaxCxns < 1)
1627 newMaxCxns = 1;
1628 if (newMaxCxns > MAXCONLIMIT(host->params->absMaxConnections))
1629 newMaxCxns = MAXCONLIMIT(host->params->absMaxConnections);
1630
1631 if (newMaxCxns != host->maxConnections) {
1632 notice("%s hostChkCxns - maxConnections was %d now %d",
1633 host->params->peerName, host->maxConnections, newMaxCxns);
1634
1635 host->backlogFilter = ((host->params->dynBacklogLowWaterMark
1636 + host->params->dynBacklogHighWaterMark)
1637 / 200.0 * backlogMult);
1638 host->artsProcLastPeriod = currArticles;
1639 host->secsInLastPeriod = host->nextCxnTimeChk;
1640
1641 /* Alter MaxConnections and in doing so ensure we connect new
1642 cxns immediately if we are adding stuff
1643 */
1644 hostAlterMaxConnections(host, host->params->absMaxConnections,
1645 newMaxCxns, true);
1646 }
1647
1648 if (host->nextCxnTimeChk <= 240)
1649 host->nextCxnTimeChk *= 2;
1650 else
1651 host->nextCxnTimeChk = 300;
1652 d_printf(1, "prepareSleep hostChkCxns, %d\n", host->nextCxnTimeChk);
1653 host->ChkCxnsId = prepareSleep(hostChkCxns, host->nextCxnTimeChk, host);
1654 }
1655
1656
1657 /*
1658 * have the Host transmit the Article if possible.
1659 */
1660 void
hostSendArticle(Host host,Article article)1661 hostSendArticle(Host host, Article article)
1662 {
1663 ASSERT(host->params != NULL);
1664 if (host->spoolTime > 0) { /* all connections are asleep */
1665 host->artsHostSleep++;
1666 host->gArtsHostSleep++;
1667 host->artsToTape++;
1668 host->gArtsToTape++;
1669 procArtsToTape++;
1670 tapeTakeArticle(host->myTape, article);
1671 return;
1672 }
1673
1674 /* at least one connection is feeding or waiting and there's no backlog */
1675 if (host->queued == NULL) {
1676 unsigned int idx;
1677 Article extraRef;
1678 Connection cxn = NULL;
1679
1680 extraRef = artTakeRef(article); /* the referrence we give away */
1681
1682 /* stick on the queue of articles we've handed off--we're hopeful. */
1683 queueArticle(article, &host->processed, &host->processedTail, 0);
1684
1685 if (host->params->minQueueCxn) {
1686 Connection x_cxn = NULL;
1687 unsigned int x_queue = host->params->maxChecks + 1;
1688
1689 for (idx = 0; x_queue > 0 && idx < host->maxConnections; idx++)
1690 if ((cxn = host->connections[idx]) != host->notThisCxn
1691 && cxn != NULL) {
1692 if (!host->cxnActive[idx]) {
1693 if (!host->cxnSleeping[idx]) {
1694 if (cxnTakeArticle(cxn, extraRef)) {
1695 host->gNoQueue++;
1696 return;
1697 } else
1698 d_printf(1,
1699 "%s Inactive connection %d refused "
1700 "an article\n",
1701 host->params->peerName, idx);
1702 }
1703 } else {
1704 unsigned int queue =
1705 host->params->maxChecks - cxnQueueSpace(cxn);
1706 if (queue < x_queue) {
1707 x_queue = queue;
1708 x_cxn = cxn;
1709 }
1710 }
1711 }
1712
1713 if (x_cxn != NULL && cxnTakeArticle(x_cxn, extraRef)) {
1714 if (x_queue == 0)
1715 host->gNoQueue++;
1716 else
1717 host->gCxnQueue += x_queue;
1718 return;
1719 }
1720
1721 } else {
1722
1723 /* first we try to give it to one of our active connections. We
1724 simply start at the bottom and work our way up. This way
1725 connections near the end of the list will get closed sooner from
1726 idleness. */
1727 for (idx = 0; idx < host->maxConnections; idx++) {
1728 if (host->cxnActive[idx]
1729 && (cxn = host->connections[idx]) != host->notThisCxn
1730 && cxn != NULL && cxnTakeArticle(cxn, extraRef)) {
1731 unsigned int queue =
1732 host->params->maxChecks - cxnQueueSpace(cxn) - 1;
1733 if (queue == 0)
1734 host->gNoQueue++;
1735 else
1736 host->gCxnQueue += queue;
1737 return;
1738 }
1739 }
1740
1741 /* Wasn't taken so try to give it to one of the waiting
1742 * connections. */
1743 for (idx = 0; idx < host->maxConnections; idx++)
1744 if (!host->cxnActive[idx] && !host->cxnSleeping[idx]
1745 && (cxn = host->connections[idx]) != host->notThisCxn
1746 && cxn != NULL) {
1747 if (cxnTakeArticle(cxn, extraRef)) {
1748 unsigned int queue =
1749 host->params->maxChecks - cxnQueueSpace(cxn) - 1;
1750 if (queue == 0)
1751 host->gNoQueue++;
1752 else
1753 host->gCxnQueue += queue;
1754 return;
1755 } else
1756 d_printf(
1757 1,
1758 "%s Inactive connection %d refused an article\n",
1759 host->params->peerName, idx);
1760 }
1761 }
1762
1763 /* this'll happen if all connections are feeding and all
1764 their queues are full, or if those not feeding are asleep. */
1765 d_printf(1, "Couldn't give the article to a connection\n");
1766
1767 delArticle(extraRef);
1768
1769 remArticle(article, &host->processed, &host->processedTail);
1770
1771 /* cxn can be NULL if it has never been affected in the loop above. */
1772 if (cxn == NULL || !cxnCheckstate(cxn)) {
1773 host->artsToTape++;
1774 host->gArtsToTape++;
1775 procArtsToTape++;
1776 tapeTakeArticle(host->myTape, article);
1777 return;
1778 }
1779 }
1780
1781 /* Either all the peer connection queues were full or we already had
1782 a backlog, so there was no sense in checking. */
1783 queueArticle(article, &host->queued, &host->queuedTail, 0);
1784
1785 host->backlog++;
1786 backlogToTape(host);
1787 }
1788
1789
1790 /*
1791 * called by the Host's connection when the remote is refusing postings
1792 * from us becasue we're not allowed (banner code 400).
1793 */
1794 void
hostCxnBlocked(Host host,Connection cxn,char * reason)1795 hostCxnBlocked(Host host, Connection cxn, char *reason)
1796 {
1797 ASSERT(host->params != NULL);
1798 #ifndef NDEBUG
1799 {
1800 unsigned int i;
1801
1802 for (i = 0; i < host->maxConnections; i++)
1803 if (host->connections[i] == cxn)
1804 ASSERT(host->cxnActive[i] == false);
1805 }
1806 #endif
1807
1808 if (host->blockedReason == NULL)
1809 host->blockedReason = xstrdup(reason);
1810
1811 if (host->activeCxns == 0 && host->spoolTime == 0) {
1812 host->blockedCxn = cxn; /* to limit log notices */
1813 notice("%s remote cannot accept articles initial: %s",
1814 host->params->peerName, reason);
1815 } else if (host->activeCxns > 0 && !host->notifiedChangedRemBlckd) {
1816 notice("%s remote cannot accept articles change: %s",
1817 host->params->peerName, reason);
1818 host->notifiedChangedRemBlckd = true;
1819 } else if (host->spoolTime != 0 && host->blockedCxn == cxn) {
1820 notice("%s remote cannot accept articles still: %s",
1821 host->params->peerName, reason);
1822 }
1823 }
1824
1825
1826 /*
1827 * Called by the Connection when it gets a response back to the MODE
1828 * STREAM command. It's now that we consider the connection usable.
1829 */
1830 void
hostRemoteStreams(Host host,Connection cxn,bool doesStreaming)1831 hostRemoteStreams(Host host, Connection cxn, bool doesStreaming)
1832 {
1833 unsigned int i;
1834
1835 host->blockedCxn = NULL;
1836 if (host->blockedReason != NULL)
1837 free(host->blockedReason);
1838 host->blockedReason = NULL;
1839
1840 /* we may have told the connection to quit while it was in the middle
1841 of connecting */
1842 if (amClosing(host))
1843 return;
1844
1845 if (host->connectTime == 0) /* first connection for this cycle. */
1846 {
1847 if (doesStreaming && host->params->wantStreaming)
1848 notice("%s remote MODE STREAM", host->params->peerName);
1849 else if (doesStreaming)
1850 notice("%s remote MODE STREAM disabled", host->params->peerName);
1851 else
1852 notice("%s remote MODE STREAM failed", host->params->peerName);
1853
1854 if (host->spoolTime > 0)
1855 hostStopSpooling(host);
1856
1857 /* set up the callback for statistics logging. */
1858 if (host->statsId != 0)
1859 clearTimer(host->statsId);
1860 host->statsId = prepareSleep(hostStatsTimeoutCbk, statsPeriod, host);
1861
1862 if (host->ChkCxnsId != 0)
1863 clearTimer(host->ChkCxnsId);
1864 host->ChkCxnsId = prepareSleep(hostChkCxns, 30, host);
1865
1866 host->remoteStreams =
1867 (host->params->wantStreaming ? doesStreaming : false);
1868
1869 host->connectTime = theTime();
1870 host->connectTime_checkpoint = host->connectTime;
1871
1872 if (host->firstConnectTime == 0)
1873 host->firstConnectTime = host->connectTime;
1874 } else if (host->remoteStreams != doesStreaming
1875 && host->params->wantStreaming)
1876 notice("%s remote MODE STREAM change", host->params->peerName);
1877
1878 for (i = 0; i < host->maxConnections; i++)
1879 if (host->connections[i] == cxn) {
1880 host->cxnActive[i] = true;
1881 if (host->cxnSleeping[i])
1882 host->sleepingCxns--;
1883 host->cxnSleeping[i] = false;
1884 break;
1885 }
1886
1887 ASSERT(i != host->maxConnections);
1888
1889 host->activeCxns++;
1890
1891 hostLogStatus();
1892 }
1893
1894
1895 /*
1896 * Called by the connection when it is no longer connected to the
1897 * remote. Perhaps due to getting a code 400 to an IHAVE, or due to a
1898 * periodic close.
1899 */
1900 void
hostCxnDead(Host host,Connection cxn)1901 hostCxnDead(Host host, Connection cxn)
1902 {
1903 unsigned int i;
1904
1905 for (i = 0; i < host->maxConnections; i++)
1906 if (host->connections[i] == cxn) {
1907 if (host->cxnActive[i]) /* won't be active if got 400 on banner */
1908 {
1909 host->cxnActive[i] = false;
1910 host->activeCxns--;
1911
1912 if (!amClosing(host) && host->activeCxns == 0) {
1913 clearTimer(host->statsId);
1914 clearTimer(host->ChkCxnsId);
1915 hostLogStats(host, true);
1916 host->connectTime = 0;
1917 }
1918 } else if (host->cxnSleeping[i]) /* cxnNuke can be called on
1919 sleepers */
1920 {
1921 host->cxnSleeping[i] = false;
1922 host->sleepingCxns--;
1923 }
1924
1925 break;
1926 }
1927
1928 ASSERT(i < host->maxConnections);
1929 hostLogStatus();
1930 }
1931
1932
1933 /*
1934 * Called by the Connection when it is going to sleep so the Host won't
1935 * bother trying to give it Articles
1936 */
1937 void
hostCxnSleeping(Host host,Connection cxn)1938 hostCxnSleeping(Host host, Connection cxn)
1939 {
1940 unsigned int i;
1941
1942 for (i = 0; i < host->maxConnections; i++)
1943 if (host->connections[i] == cxn) {
1944 if (!host->cxnSleeping[i]) {
1945 host->cxnSleeping[i] = true;
1946 host->sleepingCxns++;
1947 }
1948
1949 if (host->spoolTime == 0
1950 && host->sleepingCxns >= host->maxConnections)
1951 hostStartSpooling(host);
1952
1953 break;
1954 }
1955
1956 ASSERT(i < host->maxConnections);
1957
1958 hostLogStatus();
1959 }
1960
1961
1962 /*
1963 * Called by the Connection when it goes into the waiting state.
1964 */
1965 void
hostCxnWaiting(Host host,Connection cxn)1966 hostCxnWaiting(Host host, Connection cxn)
1967 {
1968 unsigned int i;
1969
1970 for (i = 0; i < host->maxConnections; i++)
1971 if (host->connections[i] == cxn) {
1972 if (host->cxnSleeping[i])
1973 host->sleepingCxns--;
1974 host->cxnSleeping[i] = false;
1975 break;
1976 }
1977
1978 ASSERT(i < host->maxConnections);
1979
1980 if (host->spoolTime > 0)
1981 hostStopSpooling(host);
1982
1983 hostLogStatus();
1984 }
1985
1986
1987 /*
1988 * Called by the Connection when it is about to delete itself.
1989 */
1990 bool
hostCxnGone(Host host,Connection cxn)1991 hostCxnGone(Host host, Connection cxn)
1992 {
1993 unsigned int i;
1994 bool oneThere = false;
1995 char msgstr[SMBUF];
1996
1997 /* forget about the Connection and see if we are still holding any live
1998 connections still. */
1999 for (i = 0; i < host->maxConnections; i++)
2000 if (host->connections[i] == cxn) {
2001 if (!amClosing(host)) {
2002 warn("%s:%d connection vanishing", host->params->peerName, i);
2003 }
2004 host->connections[i] = NULL;
2005 if (host->cxnActive[i]) {
2006 host->cxnActive[i] = false;
2007 host->activeCxns--;
2008 } else if (host->cxnSleeping[i]) {
2009 host->cxnSleeping[i] = false;
2010 host->sleepingCxns--;
2011 }
2012 } else if (host->connections[i] != NULL)
2013 oneThere = true;
2014
2015 /* remove the host if it has no connexions */
2016 if (!oneThere) {
2017 time_t now = theTime();
2018 unsigned int hostsLeft;
2019
2020 if (host->firstConnectTime > 0) {
2021 snprintf(msgstr, sizeof(msgstr), "accsize %.0f rejsize %.0f",
2022 host->gArtsSizeAccepted, host->gArtsSizeRejected);
2023 notice("%s global seconds %ld offered %d accepted %d refused %d"
2024 " rejected %d missing %d %s spooled %d unspooled %d",
2025 host->params->peerName,
2026 (long) (now - host->firstConnectTime), host->gArtsOffered,
2027 host->gArtsAccepted, host->gArtsNotWanted,
2028 host->gArtsRejected, host->gArtsMissing, msgstr,
2029 host->gArtsToTape, host->gArtsFromTape);
2030 }
2031
2032 hostsLeft = listenerHostGone(host->listener, host);
2033 delHost(host);
2034
2035 if (hostsLeft == 0) {
2036 snprintf(msgstr, sizeof(msgstr), "accsize %.0f rejsize %.0f",
2037 (double) procArtsSizeAccepted,
2038 (double) procArtsSizeRejected);
2039 notice("ME global seconds %ld offered %ld accepted %ld refused %ld"
2040 " rejected %ld missing %ld %s spooled %ld unspooled %ld",
2041 (long) (now - start), procArtsOffered, procArtsAccepted,
2042 procArtsNotWanted, procArtsRejected, procArtsMissing,
2043 msgstr, procArtsToTape, procArtsFromTape);
2044 }
2045
2046 /* return true if that was the last host */
2047 return (hostsLeft == 0 ? true : false);
2048 }
2049
2050 /* return false because there is still at least one host (this one) */
2051 return false;
2052 }
2053
2054
2055 /*
2056 * The connections has offered an article to the remote.
2057 */
2058 void
hostArticleOffered(Host host,Connection cxn UNUSED)2059 hostArticleOffered(Host host, Connection cxn UNUSED)
2060 {
2061 host->artsOffered++;
2062 host->gArtsOffered++;
2063 procArtsOffered++;
2064 }
2065
2066
2067 /*
2068 * Article was succesfully transferred.
2069 */
2070 void
hostArticleAccepted(Host host,Connection cxn,Article article)2071 hostArticleAccepted(Host host, Connection cxn, Article article)
2072 {
2073 const char *filename = artFileName(article);
2074 const char *msgid = artMsgId(article);
2075 double len = artSize(article);
2076
2077 d_printf(5, "Article %s (%s) was transferred\n", msgid, filename);
2078
2079 host->artsAccepted++;
2080 host->gArtsAccepted++;
2081 procArtsAccepted++;
2082 host->artsSizeAccepted += len;
2083 host->gArtsSizeAccepted += len;
2084 procArtsSizeAccepted += len;
2085
2086 /* host has two references to the article here... the parameter `article'
2087 and the queue */
2088
2089 delArticle(article); /* drop the parameter reference */
2090
2091 if (!amClosing(host))
2092 articleGone(host, cxn, article); /* and the one in the queue */
2093 }
2094
2095
2096 /*
2097 * remote said no thanks to an article.
2098 */
2099 void
hostArticleNotWanted(Host host,Connection cxn,Article article)2100 hostArticleNotWanted(Host host, Connection cxn, Article article)
2101 {
2102 const char *filename = artFileName(article);
2103 const char *msgid = artMsgId(article);
2104
2105 d_printf(5, "Article %s (%s) was not wanted\n", msgid, filename);
2106
2107 host->artsNotWanted++;
2108 host->gArtsNotWanted++;
2109 procArtsNotWanted++;
2110
2111
2112 /* host has two references to the article here... `article' and the
2113 queue */
2114
2115 delArticle(article); /* drop the `article' reference */
2116
2117 if (!amClosing(host))
2118 articleGone(host, cxn, article); /* and the one in the queue */
2119 }
2120
2121
2122 /*
2123 * remote rejected the article after it was was transferred
2124 */
2125 void
hostArticleRejected(Host host,Connection cxn,Article article)2126 hostArticleRejected(Host host, Connection cxn, Article article)
2127 {
2128 const char *filename = artFileName(article);
2129 const char *msgid = artMsgId(article);
2130 double len = artSize(article);
2131
2132 d_printf(5, "Article %s (%s) was rejected\n", msgid, filename);
2133
2134 host->artsRejected++;
2135 host->gArtsRejected++;
2136 procArtsRejected++;
2137 host->artsSizeRejected += len;
2138 host->gArtsSizeRejected += len;
2139 procArtsSizeRejected += len;
2140
2141 /* host has two references to the article here... `article' and the queue
2142 */
2143
2144 delArticle(article); /* drop the `article' reference */
2145
2146 if (!amClosing(host))
2147 articleGone(host, cxn, article);
2148 }
2149
2150
2151 /*
2152 * The remote wants us to retry the article later.
2153 */
2154 void
hostArticleDeferred(Host host,Connection cxn,Article article)2155 hostArticleDeferred(Host host, Connection cxn, Article article)
2156 {
2157 host->artsDeferred++;
2158 host->gArtsDeferred++;
2159 procArtsDeferred++;
2160
2161
2162 if (!amClosing(host)) {
2163 Article extraRef;
2164 int deferTimeout = 5; /* XXX - should be tunable */
2165 time_t now = theTime();
2166
2167 extraRef = artTakeRef(article); /* hold a reference until requeued */
2168 articleGone(host, cxn, article); /* drop from the queue */
2169
2170 if (host->deferred == NULL) {
2171 if (host->deferredId != 0)
2172 clearTimer(host->deferredId);
2173 host->deferredId =
2174 prepareSleep(hostDeferredArtCbk, deferTimeout, host);
2175 }
2176
2177 queueArticle(article, &host->deferred, &host->deferredTail,
2178 now + deferTimeout);
2179 host->deferLen++;
2180 backlogToTape(host);
2181 delArticle(extraRef);
2182 } else
2183 delArticle(article); /*drop parameter reference if not sent to tape*/
2184 }
2185
2186
2187 /*
2188 * The Connection is giving the article back to the Host, but it doesn't
2189 * want a new one in return.
2190 */
2191 void
hostTakeBackArticle(Host host,Connection cxn UNUSED,Article article)2192 hostTakeBackArticle(Host host, Connection cxn UNUSED, Article article)
2193 {
2194 if (!amClosing(host)) {
2195 Article extraRef;
2196
2197 host->artsCxnDrop++;
2198 host->gArtsCxnDrop++;
2199 extraRef = artTakeRef(article); /* hold a reference until requeued */
2200 articleGone(host, NULL, article); /* drop from the queue */
2201 host->notThisCxn = cxn;
2202 hostSendArticle(host, article); /* requeue it */
2203 host->notThisCxn = NULL;
2204 delArticle(extraRef);
2205 } else
2206 delArticle(article); /*drop parameter reference if not sent to tape*/
2207 }
2208
2209
2210 /*
2211 * The disk file for the article is no longer valid
2212 */
2213 void
hostArticleIsMissing(Host host,Connection cxn,Article article)2214 hostArticleIsMissing(Host host, Connection cxn, Article article)
2215 {
2216 const char *filename = artFileName(article);
2217 const char *msgid = artMsgId(article);
2218
2219 d_printf(5, "%s article is missing %s %s\n", host->params->peerName, msgid,
2220 filename);
2221
2222 host->artsMissing++;
2223 host->gArtsMissing++;
2224 procArtsMissing++;
2225
2226 /* host has two references to the article here... `article' and the
2227 queue */
2228
2229 delArticle(article); /* drop the `article' reference */
2230
2231 if (!amClosing(host))
2232 articleGone(host, cxn, article); /* and the one in the queue */
2233 }
2234
2235
2236 /* The Connection wants something to do. This is called by the Connection
2237 * after it has transferred an article. This is what keeps the pipes full
2238 * of data off the tapes if the input from inn is idle.
2239 */
2240 bool
hostGimmeArticle(Host host,Connection cxn)2241 hostGimmeArticle(Host host, Connection cxn)
2242 {
2243 Article article = NULL;
2244 bool gaveSomething = false;
2245 size_t amtToGive = cxnQueueSpace(cxn); /* may be more than one */
2246 int feed = 0;
2247
2248 if (amClosing(host)) {
2249 d_printf(5, "%s no article to give due to closing\n",
2250 host->params->peerName);
2251
2252 return false;
2253 }
2254
2255 if (amtToGive == 0)
2256 d_printf(5, "%s Queue space is zero....\n", host->params->peerName);
2257
2258 while (amtToGive > 0) {
2259 bool tookIt;
2260 unsigned int queue = host->params->maxChecks - amtToGive;
2261
2262 if (host->params->backlogFeedFirst) {
2263 if ((article = getArticle(host->myTape)) != NULL)
2264 feed = 2;
2265 else if ((article = remHead(&host->queued, &host->queuedTail))
2266 != NULL)
2267 feed = 1;
2268 else
2269 feed = 3;
2270 } else {
2271 if ((article = remHead(&host->queued, &host->queuedTail)) != NULL)
2272 feed = 1;
2273 else if ((article = getArticle(host->myTape)) != NULL)
2274 feed = 2;
2275 else
2276 feed = 3;
2277 }
2278
2279 switch (feed) {
2280 case 1:
2281 host->backlog--;
2282 tookIt = cxnQueueArticle(cxn, artTakeRef(article));
2283
2284 ASSERT(tookIt == true);
2285
2286 if (queue == 0)
2287 host->gNoQueue++;
2288 else
2289 host->gCxnQueue += queue;
2290
2291 queueArticle(article, &host->processed, &host->processedTail, 0);
2292 amtToGive--;
2293
2294 gaveSomething = true;
2295 break;
2296
2297 case 2:
2298 /* go to the tapes */
2299 tookIt = cxnQueueArticle(cxn, artTakeRef(article));
2300
2301 ASSERT(tookIt == true);
2302
2303 if (queue == 0)
2304 host->gNoQueue++;
2305 else
2306 host->gCxnQueue += queue;
2307
2308 host->artsFromTape++;
2309 host->gArtsFromTape++;
2310 procArtsFromTape++;
2311 queueArticle(article, &host->processed, &host->processedTail, 0);
2312 amtToGive--;
2313
2314 gaveSomething = true;
2315
2316 break;
2317
2318 case 3:
2319 /* we had nothing left to give... */
2320
2321 if (host->processed == NULL) /* and if nothing outstanding... */
2322 listenerHostIsIdle(host->listener, host); /* tell our owner */
2323
2324 amtToGive = 0;
2325
2326 break;
2327 }
2328 }
2329
2330 return gaveSomething;
2331 }
2332
2333
2334 /*
2335 * get the name that INN uses for this host
2336 */
2337 const char *
hostPeerName(Host host)2338 hostPeerName(Host host)
2339 {
2340 ASSERT(host != NULL);
2341
2342 return host->params->peerName;
2343 }
2344
2345 /*
2346 * Get the IPv4 bindAddr.
2347 */
2348 const char *
hostBindAddr(Host host)2349 hostBindAddr(Host host)
2350 {
2351 ASSERT(host != NULL);
2352
2353 return host->params->bindAddr;
2354 }
2355
2356 /*
2357 * Get the IPv6 bindAddr6.
2358 */
2359 const char *
hostBindAddr6(Host host)2360 hostBindAddr6(Host host)
2361 {
2362 ASSERT(host != NULL);
2363
2364 return host->params->bindAddr6;
2365 }
2366
2367 /*
2368 * get the username and password for authentication
2369 */
2370 const char *
hostUsername(Host host)2371 hostUsername(Host host)
2372 {
2373 ASSERT(host != NULL);
2374
2375 return host->params->username;
2376 }
2377 const char *
hostPassword(Host host)2378 hostPassword(Host host)
2379 {
2380 ASSERT(host != NULL);
2381
2382 return host->params->password;
2383 }
2384
2385
2386 /* return true if the Connections for this host should attempt to do
2387 streaming. */
2388 bool
hostWantsStreaming(Host host)2389 hostWantsStreaming(Host host)
2390 {
2391 return host->params->wantStreaming;
2392 }
2393
2394 unsigned int
hostMaxChecks(Host host)2395 hostMaxChecks(Host host)
2396 {
2397 return host->params->maxChecks;
2398 }
2399
2400 bool
hostDropDeferred(Host host)2401 hostDropDeferred(Host host)
2402 {
2403 return host->params->dropDeferred;
2404 }
2405
2406
2407 /**********************************************************************/
2408 /** CLASS FUNCTIONS **/
2409 /**********************************************************************/
2410
2411 /*
2412 * Set the state of whether each Connection is told to log its stats when
2413 * its controlling Host logs its stats.
2414 */
2415 void
hostLogConnectionStats(bool val)2416 hostLogConnectionStats(bool val)
2417 {
2418 logConnectionStats = val;
2419 }
2420
2421
2422 bool
hostLogConnectionStatsP(void)2423 hostLogConnectionStatsP(void)
2424 {
2425 return logConnectionStats;
2426 }
2427
2428
2429 /*
2430 * Called by one of the Host's Connection's when it (the Connection)
2431 * switches into or out of no-CHECK mode.
2432 */
2433 void
hostLogNoCheckMode(Host host,bool on,double low,double cur,double high)2434 hostLogNoCheckMode(Host host, bool on, double low, double cur, double high)
2435 {
2436 if (on && host->loggedModeOn == false) {
2437 notice("%s mode no-CHECK entered [%.2f,%.2f,%.2f]",
2438 host->params->peerName, low, cur, high);
2439 host->loggedModeOn = true;
2440 } else if (!on && host->loggedModeOff == false) {
2441 notice("%s mode no-CHECK exited [%.2f,%.2f,%.2f]",
2442 host->params->peerName, low, cur, high);
2443 host->loggedModeOff = true;
2444 }
2445 }
2446
2447
2448 void
hostSetStatusFile(const char * filename)2449 hostSetStatusFile(const char *filename)
2450 {
2451 FILE *fp;
2452
2453 if (filename == NULL)
2454 die("Can't set status file name with a NULL filename\n");
2455 else if (*filename == '\0')
2456 die("Can't set status file name with an empty string\n");
2457
2458 /* statusFile may already be initialized (several status-file: lines,
2459 * reload of the config file, shutdown process). */
2460 free(statusFile);
2461
2462 if (*filename == '/')
2463 statusFile = xstrdup(filename);
2464 else {
2465 if (genHtml)
2466 statusFile = concatpath(innconf->pathhttp, filename);
2467 else
2468 statusFile = concatpath(innconf->pathlog, filename);
2469 }
2470
2471 if ((fp = fopen(statusFile, "w")) == NULL) {
2472 syslog(LOG_ERR, "Status file is not a valid pathname: %s", statusFile);
2473 free(statusFile);
2474 statusFile = NULL;
2475 } else
2476 fclose(fp);
2477 }
2478
2479 void
gHostStats(void)2480 gHostStats(void)
2481 {
2482 Host h;
2483 time_t now = theTime();
2484 char msgstr[SMBUF];
2485
2486 for (h = gHostList; h != NULL; h = h->next)
2487 if (h->firstConnectTime > 0) {
2488 snprintf(msgstr, sizeof(msgstr), "accsize %.0f rejsize %.0f",
2489 h->gArtsSizeAccepted, h->gArtsSizeRejected);
2490 notice("%s global seconds %ld offered %d accepted %d refused %d"
2491 " rejected %d missing %d %s spooled %d unspooled %d",
2492 h->params->peerName, (long) (now - h->firstConnectTime),
2493 h->gArtsOffered, h->gArtsAccepted, h->gArtsNotWanted,
2494 h->gArtsRejected, h->gArtsMissing, msgstr, h->gArtsToTape,
2495 h->gArtsFromTape);
2496 }
2497 }
2498
2499
2500 /**********************************************************************/
2501 /** PRIVATE FUNCTIONS **/
2502 /**********************************************************************/
2503
2504
2505 static HostParams
hostDetails(scope * s,char * name,bool isDefault,FILE * fp)2506 hostDetails(scope *s, char *name, bool isDefault, FILE *fp)
2507 {
2508 long iv;
2509 int bv, vival, inherit;
2510 HostParams p;
2511 char *q;
2512 double rv, l, h;
2513 value *v;
2514
2515 p = newHostParams(isDefault ? NULL : defaultParams);
2516
2517 if (isDefault) {
2518 ASSERT(name == NULL);
2519 } else {
2520 if (name) {
2521 p->peerName = xstrdup(name);
2522 }
2523
2524 if (s != NULL) {
2525 if (getString(s, IP_NAME, &q, NO_INHERIT))
2526 p->ipName = q;
2527 else
2528 p->ipName = xstrdup(name);
2529 }
2530
2531 if (getString(s, "username", &q, NO_INHERIT))
2532 p->username = q;
2533 if (getString(s, "password", &q, NO_INHERIT))
2534 p->password = q;
2535
2536 if (p->username != NULL && p->password == NULL)
2537 logOrPrint(LOG_ERR, fp, "cannot find password for %s",
2538 p->peerName);
2539 if (p->username == NULL && p->password != NULL)
2540 logOrPrint(LOG_ERR, fp, "cannot find username for %s",
2541 p->peerName);
2542 }
2543
2544 if (getString(s, "bindaddress", &q, isDefault ? NO_INHERIT : INHERIT)) {
2545 if (p->bindAddr)
2546 free(p->bindAddr);
2547 p->bindAddr = q;
2548 }
2549 if (getString(s, "bindaddress6", &q, isDefault ? NO_INHERIT : INHERIT)) {
2550 if (p->bindAddr6)
2551 free(p->bindAddr6);
2552 p->bindAddr6 = q;
2553 }
2554 if (p->bindAddr && strcmp(p->bindAddr, "none") == 0 && p->bindAddr6
2555 && strcmp(p->bindAddr6, "none") == 0) {
2556 logOrPrint(LOG_ERR, fp,
2557 "cannot set both bindaddress and bindaddress6"
2558 " to \"none\" -- ignoring them for %s",
2559 p->peerName);
2560 free(p->bindAddr);
2561 free(p->bindAddr6);
2562 p->bindAddr = NULL;
2563 p->bindAddr6 = NULL;
2564 }
2565
2566 /* check required global defaults are there and have good values */
2567
2568
2569 #define GETINT(sc, f, n, min, max, req, val, inh) \
2570 vival = validateInteger(f, n, min, max, req, val, sc, inh); \
2571 if (isDefault) \
2572 do { \
2573 if (vival == VALUE_WRONG_TYPE) { \
2574 logOrPrint(LOG_CRIT, fp, "cannot continue"); \
2575 exit(1); \
2576 } else if (vival != VALUE_OK) \
2577 val = 0; \
2578 } while (0); \
2579 iv = 0; \
2580 getInteger(sc, n, &iv, inh); \
2581 val = (unsigned int) iv;
2582
2583 #define GETREAL(sc, f, n, min, max, req, val, inh) \
2584 vival = validateReal(f, n, min, max, req, val, sc, inh); \
2585 if (isDefault) \
2586 do { \
2587 if (vival == VALUE_WRONG_TYPE) { \
2588 logOrPrint(LOG_CRIT, fp, "cannot continue"); \
2589 exit(1); \
2590 } else if (vival != VALUE_OK) \
2591 rv = 0; \
2592 } while (0); \
2593 rv = 0; \
2594 getReal(sc, n, &rv, inh); \
2595 val = rv;
2596
2597 #define GETBOOL(sc, f, n, req, val, inh) \
2598 vival = validateBool(f, n, req, val, sc, inh); \
2599 if (isDefault) \
2600 do { \
2601 if (vival == VALUE_WRONG_TYPE) { \
2602 logOrPrint(LOG_CRIT, fp, "cannot continue"); \
2603 exit(1); \
2604 } else if (vival != VALUE_OK) \
2605 bv = 0; \
2606 } while (0); \
2607 bv = 0; \
2608 getBool(sc, n, &bv, inh); \
2609 val = (bv ? true : false);
2610
2611 inherit = isDefault ? NO_INHERIT : INHERIT;
2612 GETINT(s, fp, "article-timeout", 0, LONG_MAX, NOTREQ, p->articleTimeout,
2613 inherit);
2614 GETINT(s, fp, "response-timeout", 0, LONG_MAX, NOTREQ, p->responseTimeout,
2615 inherit);
2616 GETINT(s, fp, "close-period", 0, LONG_MAX, NOTREQ, p->closePeriod,
2617 inherit);
2618 GETINT(s, fp, "initial-connections", 0, LONG_MAX, NOTREQ,
2619 p->initialConnections, inherit);
2620 GETINT(s, fp, "max-connections", 0, LONG_MAX, NOTREQ, p->absMaxConnections,
2621 inherit);
2622 GETINT(s, fp, "max-queue-size", 1, LONG_MAX, NOTREQ, p->maxChecks,
2623 inherit);
2624 GETBOOL(s, fp, "streaming", NOTREQ, p->wantStreaming, inherit);
2625 GETBOOL(s, fp, "drop-deferred", NOTREQ, p->dropDeferred, inherit);
2626 GETBOOL(s, fp, "min-queue-connection", NOTREQ, p->minQueueCxn, inherit);
2627 GETREAL(s, fp, "no-check-high", 0.0, 100.0, NOTREQ, p->lowPassHigh,
2628 inherit);
2629 GETREAL(s, fp, "no-check-low", 0.0, 100.0, NOTREQ, p->lowPassLow, inherit);
2630 GETREAL(s, fp, "no-check-filter", 0.1, DBL_MAX, NOTREQ, p->lowPassFilter,
2631 inherit);
2632 GETINT(s, fp, "port-number", 0, LONG_MAX, NOTREQ, p->portNum, inherit);
2633 GETINT(s, fp, "backlog-limit", 0, LONG_MAX, NOTREQ, p->backlogLimit,
2634 inherit);
2635
2636 GETBOOL(s, fp, "force-ipv4", NOTREQ, p->forceIPv4, inherit);
2637 if (p->forceIPv4) {
2638 if (p->bindAddr && strcmp(p->bindAddr, "none") == 0) {
2639 free(p->bindAddr);
2640 p->bindAddr = NULL;
2641 }
2642 if (p->bindAddr6) {
2643 free(p->bindAddr6);
2644 }
2645 /* Force bindaddress6: to "none". */
2646 p->bindAddr6 = xstrdup("none");
2647 }
2648
2649 if (findValue(s, "backlog-factor", inherit) == NULL
2650 && findValue(s, "backlog-limit-highwater", inherit) == NULL) {
2651 /* This is not an error. A default value will be used.
2652 *
2653 * logOrPrint (LOG_ERR,fp,
2654 "ME config: must define at least one of backlog-factor"
2655 " and backlog-limit-highwater. Adding %s: %f",
2656 "backlog-factor", LIMIT_FUDGE) ;
2657 */
2658 addReal(s, "backlog-factor", LIMIT_FUDGE);
2659 rv = 0;
2660 }
2661
2662 GETBOOL(s, fp, "backlog-feed-first", NOTREQ, p->backlogFeedFirst, inherit);
2663
2664 /* Innfeed should emit a warning if backlog-feed-first is set
2665 to "true" for any peer that doesn't have max-connections and
2666 initial-connections both set to "1" */
2667 if ((p->backlogFeedFirst)
2668 && ((p->initialConnections <= 1) || (p->absMaxConnections != 1))) {
2669 if (p->peerName != NULL)
2670 logOrPrint(LOG_WARNING, fp,
2671 "ME config: innfeed will make more than one connection"
2672 " to peer %s, but backlog-feed-first is set",
2673 p->peerName);
2674 else
2675 logOrPrint(LOG_WARNING, fp,
2676 "ME config: innfeed will make more than one connection"
2677 " to peer, but backlog-feed-first is set");
2678 }
2679
2680 GETINT(s, fp, "backlog-limit-highwater", 0, LONG_MAX, NOTREQNOADD,
2681 p->backlogLimitHigh, inherit);
2682 GETREAL(s, fp, "backlog-factor", 1.0, DBL_MAX, NOTREQNOADD,
2683 p->backlogFactor, inherit);
2684
2685 GETINT(s, fp, "dynamic-method", 0, 3, NOTREQ, p->dynamicMethod, inherit);
2686 GETREAL(s, fp, "dynamic-backlog-filter", 0.0, DBL_MAX, NOTREQ,
2687 p->dynBacklogFilter, inherit);
2688 GETREAL(s, fp, "dynamic-backlog-low", 0.0, 100.0, NOTREQ,
2689 p->dynBacklogLowWaterMark, inherit);
2690 GETREAL(s, fp, "dynamic-backlog-high", 0.0, 100.0, NOTREQ,
2691 p->dynBacklogHighWaterMark, inherit);
2692
2693 l = p->lowPassLow;
2694 h = p->lowPassHigh;
2695 if (l > h) {
2696 logOrPrint(LOG_ERR, fp,
2697 "ME config: no-check-low value greater than no-check-high"
2698 " (%f vs %f). Setting to %f and %f",
2699 l, h, NOCHECKLOW, NOCHECKHIGH);
2700 rv = 0;
2701 v = findValue(s, "no-check-low", NO_INHERIT);
2702 v->v.real_val = p->lowPassLow = NOCHECKLOW;
2703 v = findValue(s, "no-check-high", NO_INHERIT);
2704 v->v.real_val = p->lowPassHigh = NOCHECKHIGH;
2705 } else if (h - l < 5.0)
2706 logOrPrint(LOG_WARNING, fp,
2707 "ME config: no-check-low and no-check-high are close"
2708 " together (%f vs %f)",
2709 l, h);
2710
2711 return p;
2712 }
2713
2714
2715 static HostParams
getHostInfo(void)2716 getHostInfo(void)
2717 {
2718 static int idx = 0;
2719 value *v;
2720 scope *s;
2721 HostParams p = NULL;
2722
2723 if (topScope == NULL)
2724 return p;
2725
2726 while ((v = getNextPeer(&idx)) != NULL) {
2727 if (!ISPEER(v))
2728 continue;
2729
2730 s = v->v.scope_val;
2731
2732 p = hostDetails(s, v->name, false, NULL);
2733
2734 break;
2735 }
2736
2737 if (v == NULL)
2738 idx = 0; /* start over next time around */
2739
2740 return p;
2741 }
2742
2743
2744 /*
2745 * fully delete and clean up the Host object.
2746 */
2747 void
delHost(Host host)2748 delHost(Host host)
2749 {
2750 Host h, q;
2751
2752 for (h = gHostList, q = NULL; h != NULL; q = h, h = h->next)
2753 if (h == host) {
2754 if (gHostList == h)
2755 gHostList = gHostList->next;
2756 else
2757 q->next = h->next;
2758 break;
2759 }
2760
2761 ASSERT(h != NULL);
2762
2763 delTape(host->myTape);
2764
2765 free(host->connections);
2766 free(host->cxnActive);
2767 free(host->cxnSleeping);
2768 free(host->params->peerName);
2769 free(host->params->ipName);
2770
2771 if (host->ipAddrs) {
2772 if (host->ipAddrs[0])
2773 free(host->ipAddrs[0]);
2774 free(host->ipAddrs);
2775 }
2776
2777 free(host);
2778 gHostCount--;
2779 }
2780
2781
2782 static Host
findHostByName(char * name)2783 findHostByName(char *name)
2784 {
2785 Host h;
2786
2787 for (h = gHostList; h != NULL; h = h->next)
2788 if (strcmp(h->params->peerName, name) == 0)
2789 return h;
2790
2791 return NULL;
2792 }
2793
2794
2795 /*
2796 * the article can be dropped from the process queue and the connection can
2797 * take a new article if there are any to be had.
2798 */
2799 static void
articleGone(Host host,Connection cxn,Article article)2800 articleGone(Host host, Connection cxn, Article article)
2801 {
2802 if (!remArticle(article, &host->processed, &host->processedTail))
2803 die("remArticle in articleGone failed");
2804
2805 delArticle(article);
2806
2807 if (cxn != NULL)
2808 hostGimmeArticle(host, cxn); /* may not give anything over */
2809 }
2810
2811
2812 /*
2813 * One of the Connections for this Host has reestablished itself, so stop
2814 * spooling article info to disk.
2815 */
2816 static void
hostStopSpooling(Host host)2817 hostStopSpooling(Host host)
2818 {
2819 ASSERT(host->spoolTime != 0);
2820
2821 clearTimer(host->statsId);
2822 hostLogStats(host, true);
2823
2824 host->spoolTime = 0;
2825 }
2826
2827
2828 /*
2829 * No connections are active and we're getting response 201 or 400 (or some
2830 * such) so that we need to start spooling article info to disk.
2831 */
2832 static void
hostStartSpooling(Host host)2833 hostStartSpooling(Host host)
2834 {
2835 ASSERT(host->spoolTime == 0);
2836
2837 queuesToTape(host);
2838
2839 hostLogStats(host, true);
2840
2841 host->spoolTime = theTime();
2842 host->spoolTime_checkpoint = host->spoolTime;
2843
2844 if (host->firstConnectTime == 0)
2845 host->firstConnectTime = host->spoolTime;
2846
2847 /* don't want to log too frequently */
2848 if (SPOOL_LOG_PERIOD > 0
2849 && (host->spoolTime - host->lastSpoolTime) > SPOOL_LOG_PERIOD) {
2850 notice("%s spooling no active connections", host->params->peerName);
2851 host->lastSpoolTime = host->spoolTime;
2852 }
2853
2854 host->connectTime = 0;
2855
2856 host->notifiedChangedRemBlckd = false;
2857
2858 clearTimer(host->statsId);
2859 host->statsId = prepareSleep(hostStatsTimeoutCbk, statsPeriod, host);
2860 }
2861
2862
2863 /*
2864 * Time to log the statistics for the Host. If FINAL is true then the
2865 * counters will be reset.
2866 */
2867 static void
hostLogStats(Host host,bool final)2868 hostLogStats(Host host, bool final)
2869 {
2870 time_t now = theTime();
2871 time_t *startPeriod;
2872 double cnt = (host->blCount) ? (host->blCount) : 1.0;
2873
2874 if (host->spoolTime == 0 && host->connectTime == 0)
2875 return; /* host has never connected and never started spooling. */
2876
2877 startPeriod =
2878 (host->spoolTime != 0 ? &host->spoolTime : &host->connectTime);
2879
2880 if (now - *startPeriod >= statsResetPeriod)
2881 final = true;
2882
2883 if (host->spoolTime != 0) {
2884 /* Log a checkpoint in any case. */
2885 notice("%s checkpoint seconds %ld spooled %d on_close %d sleeping %d",
2886 host->params->peerName,
2887 (long) (now - host->spoolTime_checkpoint),
2888 host->artsToTape - host->artsToTape_checkpoint,
2889 host->artsHostClose - host->artsHostClose_checkpoint,
2890 host->artsHostSleep - host->artsHostSleep_checkpoint);
2891
2892 host->spoolTime_checkpoint = now;
2893 host->artsToTape_checkpoint = host->artsToTape;
2894 host->artsHostClose_checkpoint = host->artsHostClose;
2895 host->artsHostSleep_checkpoint = host->artsHostSleep;
2896
2897 if (final) {
2898 notice("%s final seconds %ld spooled %d on_close %d sleeping %d",
2899 host->params->peerName, (long) (now - host->spoolTime),
2900 host->artsToTape, host->artsHostClose, host->artsHostSleep);
2901 }
2902 } else {
2903 /* Log a checkpoint in any case.
2904 *
2905 * Note that deferred and queue values are cumulative
2906 * (and not treated by innreport). */
2907 notice(
2908 "%s checkpoint seconds %ld offered %d accepted %d refused %d "
2909 "rejected %d"
2910 " missing %d accsize %.0f rejsize %.0f spooled %d on_close %d "
2911 "unspooled %d"
2912 " deferred %d/%.1f requeued %d"
2913 " queue %.1f/%d:%.0f,%.0f,%.0f,%.0f,%.0f,%.0f",
2914 host->params->peerName,
2915 (long) (now - host->connectTime_checkpoint),
2916 host->artsOffered - host->artsOffered_checkpoint,
2917 host->artsAccepted - host->artsAccepted_checkpoint,
2918 host->artsNotWanted - host->artsNotWanted_checkpoint,
2919 host->artsRejected - host->artsRejected_checkpoint,
2920 host->artsMissing - host->artsMissing_checkpoint,
2921 host->artsSizeAccepted - host->artsSizeAccepted_checkpoint,
2922 host->artsSizeRejected - host->artsSizeRejected_checkpoint,
2923 host->artsToTape - host->artsToTape_checkpoint,
2924 host->artsHostClose - host->artsHostClose_checkpoint,
2925 host->artsFromTape - host->artsFromTape_checkpoint,
2926 host->artsDeferred - host->artsDeferred_checkpoint,
2927 (double) host->dlAccum / cnt,
2928 host->artsCxnDrop - host->artsCxnDrop_checkpoint,
2929 (double) host->blAccum / cnt, hostHighwater,
2930 (100.0 * host->blNone) / cnt, (100.0 * host->blQuartile[0]) / cnt,
2931 (100.0 * host->blQuartile[1]) / cnt,
2932 (100.0 * host->blQuartile[2]) / cnt,
2933 (100.0 * host->blQuartile[3]) / cnt, (100.0 * host->blFull) / cnt);
2934
2935 host->connectTime_checkpoint = now;
2936 host->artsOffered_checkpoint = host->artsOffered;
2937 host->artsAccepted_checkpoint = host->artsAccepted;
2938 host->artsNotWanted_checkpoint = host->artsNotWanted;
2939 host->artsRejected_checkpoint = host->artsRejected;
2940 host->artsMissing_checkpoint = host->artsMissing;
2941 host->artsSizeAccepted_checkpoint = host->artsSizeAccepted;
2942 host->artsSizeRejected_checkpoint = host->artsSizeRejected;
2943 host->artsToTape_checkpoint = host->artsToTape;
2944 host->artsHostClose_checkpoint = host->artsHostClose;
2945 host->artsFromTape_checkpoint = host->artsFromTape;
2946 host->artsDeferred_checkpoint = host->artsDeferred;
2947 host->artsCxnDrop_checkpoint = host->artsCxnDrop;
2948
2949 if (final) {
2950 notice("%s final seconds %ld offered %d accepted %d refused %d "
2951 "rejected %d"
2952 " missing %d accsize %.0f rejsize %.0f spooled %d on_close "
2953 "%d unspooled %d"
2954 " deferred %d/%.1f requeued %d"
2955 " queue %.1f/%d:%.0f,%.0f,%.0f,%.0f,%.0f,%.0f",
2956 host->params->peerName, (long) (now - host->connectTime),
2957 host->artsOffered, host->artsAccepted, host->artsNotWanted,
2958 host->artsRejected, host->artsMissing,
2959 host->artsSizeAccepted, host->artsSizeRejected,
2960 host->artsToTape, host->artsHostClose, host->artsFromTape,
2961 host->artsDeferred, (double) host->dlAccum / cnt,
2962 host->artsCxnDrop, (double) host->blAccum / cnt,
2963 hostHighwater, (100.0 * host->blNone) / cnt,
2964 (100.0 * host->blQuartile[0]) / cnt,
2965 (100.0 * host->blQuartile[1]) / cnt,
2966 (100.0 * host->blQuartile[2]) / cnt,
2967 (100.0 * host->blQuartile[3]) / cnt,
2968 (100.0 * host->blFull) / cnt);
2969 }
2970 }
2971
2972 if (logConnectionStats) {
2973 unsigned int i;
2974
2975 for (i = 0; i < host->maxConnections; i++)
2976 if (host->connections[i] != NULL && host->cxnActive[i])
2977 cxnLogStats(host->connections[i], final);
2978 }
2979
2980 /* one 'spooling backlog' message per stats logging period */
2981 host->loggedBacklog = false;
2982 host->loggedModeOn = host->loggedModeOff = false;
2983
2984 if (final) {
2985 /* We also reset checkpoints because the same host structure
2986 * may be used again. */
2987 host->artsOffered = 0;
2988 host->artsOffered_checkpoint = 0;
2989 host->artsAccepted = 0;
2990 host->artsAccepted_checkpoint = 0;
2991 host->artsNotWanted = 0;
2992 host->artsNotWanted_checkpoint = 0;
2993 host->artsRejected = 0;
2994 host->artsRejected_checkpoint = 0;
2995 host->artsDeferred = 0;
2996 host->artsDeferred_checkpoint = 0;
2997 host->artsMissing = 0;
2998 host->artsMissing_checkpoint = 0;
2999 host->artsToTape = 0;
3000 host->artsToTape_checkpoint = 0;
3001 host->artsQueueOverflow = 0;
3002 host->artsCxnDrop = 0;
3003 host->artsCxnDrop_checkpoint = 0;
3004 host->artsHostSleep = 0;
3005 host->artsHostSleep_checkpoint = 0;
3006 host->artsHostClose = 0;
3007 host->artsHostClose_checkpoint = 0;
3008 host->artsFromTape = 0;
3009 host->artsFromTape_checkpoint = 0;
3010 host->artsSizeAccepted = 0;
3011 host->artsSizeAccepted_checkpoint = 0;
3012 host->artsSizeRejected = 0;
3013 host->artsSizeRejected_checkpoint = 0;
3014
3015 *startPeriod = theTime(); /* in of case STATS_RESET_PERIOD */
3016 }
3017
3018 /* Reset these each log period. */
3019 host->blNone = 0;
3020 host->blFull = 0;
3021 host->blQuartile[0] = host->blQuartile[1] = host->blQuartile[2] =
3022 host->blQuartile[3] = 0;
3023 host->dlAccum = 0;
3024 host->blAccum = 0;
3025 host->blCount = 0;
3026
3027 #if defined(INNFEED_DEBUG)
3028 /* Get a snapshot at each log period. */
3029 if (gPrintInfo != NULL)
3030 gPrintInfo();
3031 #endif
3032 }
3033
3034
3035 static double
convsize(double size,char ** tsize)3036 convsize(double size, char **tsize)
3037 {
3038 double dsize;
3039 static char tTB[] = "TB";
3040 static char tGB[] = "GB";
3041 static char tMB[] = "MB";
3042 static char tKB[] = "KB";
3043 static char tB[] = "B";
3044
3045 if (size / ((double) 1024 * 1024 * 1024 * 1000) >= 1.) {
3046 dsize = size / ((double) 1024 * 1024 * 1024 * 1024);
3047 *tsize = tTB;
3048 } else if (size / (1024 * 1024 * 1000) >= 1.) {
3049 dsize = size / (1024 * 1024 * 1024);
3050 *tsize = tGB;
3051 } else if (size / (1024 * 1000) >= 1.) {
3052 dsize = size / (1024 * 1024);
3053 *tsize = tMB;
3054 } else if (size / 1000 >= 1.) {
3055 dsize = size / 1024;
3056 *tsize = tKB;
3057 } else {
3058 dsize = size;
3059 *tsize = tB;
3060 }
3061 return dsize;
3062 }
3063
3064
3065 /*
3066 * Log the status of the Hosts.
3067 */
3068 static void
hostLogStatus(void)3069 hostLogStatus(void)
3070 {
3071 FILE *fp = NULL;
3072 Host h;
3073 bool anyToLog = false;
3074 unsigned int peerNum = 0, actConn = 0, slpConn = 0, maxcon = 0;
3075 static bool logged = false;
3076 static bool flogged = false;
3077
3078 if (statusFile == NULL && !logged) {
3079 syslog(LOG_ERR, "No status file to write to");
3080 logged = true;
3081 return;
3082 }
3083
3084 logged = false;
3085
3086 for (h = gHostList; h != NULL; h = h->next)
3087 if (h->myTape
3088 != NULL) /* the host deletes its tape when it's closing */
3089 {
3090 anyToLog = true;
3091 peerNum++;
3092 actConn += h->activeCxns;
3093 slpConn += h->sleepingCxns;
3094 maxcon += h->maxConnections;
3095 }
3096
3097 if (!anyToLog)
3098 return;
3099
3100 lastStatusLog = theTime();
3101
3102 TMRstart(TMR_STATUSFILE);
3103 if ((fp = fopen(statusFile, "w")) == NULL) {
3104 if (!flogged)
3105 syswarn("ME oserr status file open: %s", statusFile);
3106 flogged = true;
3107 } else {
3108 char timeString[30];
3109 time_t now;
3110 long sec;
3111 long offered;
3112 double size, totalsize;
3113 char *tsize;
3114
3115 flogged = false;
3116
3117 now = time(NULL);
3118 sec = (long) (now - start);
3119 timeToString(now, timeString, sizeof(timeString));
3120
3121 if (genHtml) {
3122 fprintf(fp, "<!DOCTYPE html>\n"
3123 "<html lang=\"en\">\n"
3124 "<head>\n");
3125 fprintf(fp, "<meta http-equiv=\"refresh\" content=\"%lu\">\n",
3126 (unsigned long) statsPeriod);
3127 fprintf(fp, "<title>%s: outgoing feeds</title>\n",
3128 innconf->pathhost);
3129 fprintf(fp, "</head>\n"
3130 "<body>\n"
3131 "<pre>\n");
3132 }
3133
3134 fprintf(fp, "innfeed from %s\npid %d started %s\n\nUpdated: %s\n",
3135 INN_VERSION_STRING, (int) myPid, startTime, timeString);
3136 fprintf(fp, "Stats period: %-5ld Stats reset: %ld\n",
3137 (long) statsPeriod, (long) statsResetPeriod);
3138 fprintf(fp,
3139 "(peers: %u, active-cxns: %u, sleeping-cxns: %u, idle-cxns: "
3140 "%u)\n\n",
3141 peerNum, actConn, slpConn, (maxcon - (actConn + slpConn)));
3142
3143 fprintf(fp, "Configuration file: %s\n\n", configFile);
3144
3145 if (genHtml) {
3146 fprintf(fp, "</pre>\n\n");
3147 fprintf(fp, "<ul>\n");
3148 for (h = gHostList; h != NULL; h = h->next)
3149 fprintf(fp, "<li><a href=\"#%s\">%s</a></li>\n",
3150 h->params->peerName, h->params->peerName);
3151 fprintf(fp, "</ul>\n\n");
3152 fprintf(fp, "<pre>\n");
3153 }
3154
3155 mainLogStatus(fp);
3156 listenerLogStatus(fp);
3157
3158 /*
3159 Default peer configuration parameters:
3160 article timeout: 600 initial connections: 1
3161 response timeout: 300 max connections: 5
3162 close period: 6000 max checks: 25
3163 want streaming: true dynamic method: 1
3164 no-check on: 95.0% dynamic backlog low: 25%
3165 no-check off: 90.0% dynamic backlog high: 50%
3166 no-check filter: 50.0 dynamic backlog filter: 0.7
3167 backlog low limit: 1024 port num: 119
3168 backlog high limit: 1280 backlog feed first: false
3169 backlog factor: 1.1
3170 */
3171 fprintf(fp, "%sDefault peer configuration parameters:%s\n",
3172 genHtml ? "<strong>" : "", genHtml ? "</strong>" : "");
3173 fprintf(fp, " article timeout: %-5u initial connections: %u\n",
3174 defaultParams->articleTimeout,
3175 defaultParams->initialConnections);
3176 fprintf(fp, " response timeout: %-5u max connections: %u\n",
3177 defaultParams->responseTimeout,
3178 defaultParams->absMaxConnections);
3179 fprintf(fp, " reconnection time: %-5u max reconnection time: %u\n",
3180 init_reconnect_period, max_reconnect_period);
3181 fprintf(fp, " close period: %-5u max checks: %u\n",
3182 defaultParams->closePeriod, defaultParams->maxChecks);
3183 fprintf(fp, " DNS retry period: %-5u DNS expire period: %u\n",
3184 dnsRetPeriod, dnsExpPeriod);
3185 fprintf(fp, " port num: %-5u force IPv4: %s\n",
3186 defaultParams->portNum,
3187 defaultParams->forceIPv4 ? "true " : "false");
3188 fprintf(fp, " want streaming: %-5s dynamic method: %u\n",
3189 defaultParams->wantStreaming ? "true " : "false",
3190 defaultParams->dynamicMethod);
3191 fprintf(fp,
3192 " no-check on: %-2.1f%% dynamic backlog low: "
3193 "%-2.1f%%\n",
3194 defaultParams->lowPassHigh,
3195 defaultParams->dynBacklogLowWaterMark);
3196 fprintf(fp,
3197 " no-check off: %-2.1f%% dynamic backlog high: "
3198 "%-2.1f%%\n",
3199 defaultParams->lowPassLow,
3200 defaultParams->dynBacklogHighWaterMark);
3201 fprintf(
3202 fp,
3203 " no-check filter: %-2.1f dynamic backlog filter: %-2.1f\n",
3204 defaultParams->lowPassFilter, defaultParams->dynBacklogFilter);
3205 fprintf(fp, " backlog limit low: %-7u drop-deferred: %s\n",
3206 defaultParams->backlogLimit,
3207 defaultParams->dropDeferred ? "true " : "false");
3208 fprintf(fp, " backlog limit high: %-7u min-queue-cxn: %s\n",
3209 defaultParams->backlogLimitHigh,
3210 defaultParams->minQueueCxn ? "true " : "false");
3211 fprintf(fp, " backlog feed first: %s\n",
3212 defaultParams->backlogFeedFirst ? "true " : "false");
3213 fprintf(fp, " backlog factor: %1.1f\n\n",
3214 defaultParams->backlogFactor);
3215
3216 tapeLogGlobalStatus(fp);
3217
3218 fprintf(fp, "\n");
3219 fprintf(fp, "%sglobal (process)%s\n", genHtml ? "<strong>" : "",
3220 genHtml ? "</strong>" : "");
3221
3222 fprintf(fp, " seconds: %ld\n", sec);
3223 if (sec == 0)
3224 sec = 1; /* to avoid division by zero */
3225 offered = procArtsOffered ? procArtsOffered : 1;
3226 totalsize = procArtsSizeAccepted + procArtsSizeRejected;
3227 if (!(totalsize > 0))
3228 totalsize = 1.; /* avoid divide by zero here too */
3229
3230 fprintf(fp, " offered: %-5ld\t%6.2f art/s\n", procArtsOffered,
3231 (double) procArtsOffered / (double) sec);
3232 fprintf(fp, " accepted: %-5ld\t%6.2f art/s\t%5.1f%%\n",
3233 procArtsAccepted, (double) procArtsAccepted / (double) sec,
3234 (double) procArtsAccepted * 100. / (double) offered);
3235 fprintf(fp, " refused: %-5ld\t%6.2f art/s\t%5.1f%%\n",
3236 procArtsNotWanted, (double) procArtsNotWanted / (double) sec,
3237 (double) procArtsNotWanted * 100. / (double) offered);
3238 fprintf(fp, " rejected: %-5ld\t%6.2f art/s\t%5.1f%%\n",
3239 procArtsRejected, (double) procArtsRejected / (double) sec,
3240 (double) procArtsRejected * 100. / (double) offered);
3241 fprintf(fp, " missing: %-5ld\t%6.2f art/s\t%5.1f%%\n",
3242 procArtsMissing, (double) procArtsMissing / (double) sec,
3243 (double) procArtsMissing * 100. / (double) offered);
3244 fprintf(fp, " deferred: %-5ld\t%6.2f art/s\t%5.1f%%\n",
3245 procArtsDeferred, (double) procArtsDeferred / (double) sec,
3246 (double) procArtsDeferred * 100. / (double) offered);
3247
3248 size = convsize(procArtsSizeAccepted, &tsize);
3249 fprintf(fp, "accpt size: %.3g %s", size, tsize);
3250 size = convsize(procArtsSizeAccepted / (double) sec, &tsize);
3251 fprintf(fp, " \t%6.3g %s/s\t%5.1f%%\n", size, tsize,
3252 procArtsSizeAccepted * 100. / totalsize);
3253
3254 size = convsize(procArtsSizeRejected, &tsize);
3255 fprintf(fp, "rejct size: %.3g %s", size, tsize);
3256 size = convsize(procArtsSizeRejected / (double) sec, &tsize);
3257 fprintf(fp, " \t%6.3g %s/s\t%5.1f%%\n", size, tsize,
3258 procArtsSizeRejected * 100. / totalsize);
3259
3260 fprintf(fp, "\n");
3261
3262 for (h = gHostList; h != NULL; h = h->next)
3263 hostPrintStatus(h, fp);
3264
3265 if (genHtml) {
3266 fprintf(fp, "</pre>\n");
3267 fprintf(fp, "</body>\n");
3268 fprintf(fp, "</html>\n");
3269 }
3270
3271 fclose(fp);
3272 }
3273 TMRstop(TMR_STATUSFILE);
3274 }
3275
3276 /*
3277 * This prints status information for each host. An example of the
3278 * format of the output is:
3279 *
3280 * sitename
3281 * Addr 1 : IPv4 12.0.0.42
3282 * seconds: 351 art. timeout: 400 ip name: foo.bar
3283 * offered: 1194 resp. timeout: 240 port: 119
3284 * accepted: 178 want streaming: yes active cxns: 6
3285 * refused: 948 is streaming: yes sleeping cxns: 0
3286 * rejected: 31 max checks: 25 initial cxns: 5
3287 * missing: 0 no-check on: 95.0% idle cxns: 4
3288 * deferred: 0 no-check off: 95.0% max cxns: 8/10
3289 * requeued: 0 no-check fltr: 50.0 queue length: 0.0/200
3290 * spooled: 0 dynamic method: 0 empty: 100.0%
3291 *[overflow]: 0 dyn b'log low: 25% >0%-25%: 0.0%
3292 *[on_close]: 0 dyn b'log high: 50% 25%-50%: 0.0%
3293 *[sleeping]: 0 dyn b'log stat: 37% 50%-75%: 0.0%
3294 * unspooled: 0 dyn b'log fltr: 0.7 75%-<100%: 0.0%
3295 * no queue: 1234 avr.cxns queue: 0.0 full: 0.0%
3296 *accpt size: 121.1 MB drop-deferred: false defer length: 0
3297 *rejct size: 27.1 MB min-queue-cxn: false
3298 * backlog low limit: 1000000
3299 * backlog high limit: 2000000 (factor 2.0)
3300 * backlog shrinkage: 0 bytes (from current file)
3301 * offered: 1.13 art/s accepted: 0.69 art/s (101.71 KB/s)
3302 * refused: 0.01 art/s rejected: 0.42 art/s (145.11 KB/s)
3303 * missing 0 spooled 0
3304 *
3305 */
3306 static void
hostPrintStatus(Host host,FILE * fp)3307 hostPrintStatus(Host host, FILE *fp)
3308 {
3309 time_t now = theTime();
3310 double cnt = (host->blCount) ? (host->blCount) : 1.0;
3311 double size;
3312 char *tsize;
3313 char buf[] = "1.234e+05 TB"; /* usual length is shorter, like "12.34 MB" */
3314
3315 ASSERT(host != NULL);
3316 ASSERT(fp != NULL);
3317
3318 if (genHtml)
3319 fprintf(fp, "<strong id=\"%s\">%s</strong>", host->params->peerName,
3320 host->params->peerName);
3321 else
3322 fprintf(fp, "%s", host->params->peerName);
3323
3324 if (host->blockedReason != NULL)
3325 fprintf(fp, " (remote status: ``%s'')", host->blockedReason);
3326
3327 fputc('\n', fp);
3328
3329 if (host->ipAddrs) {
3330 size_t i;
3331 char ip_addr[INET6_ADDRSTRLEN];
3332 const char *family;
3333
3334 for (i = 0; host->ipAddrs[i] != NULL; i++) {
3335 switch (host->ipAddrs[i]->sa_family) {
3336 case AF_INET:
3337 family = "IPv4";
3338 break;
3339 #ifdef HAVE_INET6
3340 case AF_INET6:
3341 family = "IPv6";
3342 break;
3343 #endif
3344 default:
3345 family = "????";
3346 break;
3347 }
3348
3349 network_sockaddr_sprint(ip_addr, sizeof(ip_addr),
3350 host->ipAddrs[i]);
3351 fprintf(fp, " Addr %-2lu: %-4.4s %s\n", (unsigned long) (i + 1),
3352 family, ip_addr);
3353 }
3354 }
3355
3356 fprintf(fp, " seconds: %-7ld art. timeout: %-5u ip name: %s\n",
3357 host->firstConnectTime > 0 ? (long) (now - host->firstConnectTime)
3358 : 0,
3359 host->params->articleTimeout, host->params->ipName);
3360
3361 fprintf(fp, " offered: %-7ld resp. timeout: %-5u port: %u\n",
3362 (long) host->gArtsOffered, host->params->responseTimeout,
3363 host->params->portNum);
3364
3365 fprintf(fp, " accepted: %-7ld want streaming: %s active cxns: %u\n",
3366 (long) host->gArtsAccepted,
3367 (host->params->wantStreaming ? "yes" : "no "), host->activeCxns);
3368
3369 fprintf(fp, " refused: %-7ld is streaming: %s sleeping cxns: %u\n",
3370 (long) host->gArtsNotWanted, (host->remoteStreams ? "yes" : "no "),
3371 host->sleepingCxns);
3372
3373 fprintf(fp, " rejected: %-7ld max checks: %-5u initial cxns: %u\n",
3374 (long) host->gArtsRejected, host->params->maxChecks,
3375 host->params->initialConnections);
3376
3377 fprintf(fp,
3378 " missing: %-7ld no-check on: %-3.1f%% idle cxns: %u\n",
3379 (long) host->gArtsMissing, host->params->lowPassHigh,
3380 host->maxConnections - (host->activeCxns + host->sleepingCxns));
3381
3382 fprintf(
3383 fp,
3384 " deferred: %-7ld no-check off: %-3.1f%% max cxns: %u/%u\n",
3385 (long) host->gArtsDeferred, host->params->lowPassLow,
3386 host->maxConnections, host->params->absMaxConnections);
3387
3388 fprintf(fp,
3389 " requeued: %-7ld no-check fltr: %-3.1f queue length: "
3390 "%-3.1f/%u\n",
3391 (long) host->gArtsCxnDrop, host->params->lowPassFilter,
3392 (double) host->blAccum / cnt, hostHighwater);
3393
3394 fprintf(
3395 fp,
3396 " spooled: %-7ld dynamic method: %-5u empty: %-3.1f%%\n",
3397 (long) host->gArtsToTape, host->params->dynamicMethod,
3398 100.0 * host->blNone / cnt);
3399
3400 fprintf(fp,
3401 "[overflow]: %-7ld dyn b'log low: %-3.1f%% %s0%%-25%%: "
3402 "%-3.1f%%\n",
3403 (long) host->gArtsQueueOverflow,
3404 host->params->dynBacklogLowWaterMark, genHtml ? ">" : ">",
3405 100.0 * host->blQuartile[0] / cnt);
3406
3407 fprintf(fp,
3408 "[on_close]: %-7ld dyn b'log high: %-3.1f%% 25%%-50%%: "
3409 "%-3.1f%%\n",
3410 (long) host->gArtsHostClose, host->params->dynBacklogHighWaterMark,
3411 100.0 * host->blQuartile[1] / cnt);
3412
3413 fprintf(fp,
3414 "[sleeping]: %-7ld dyn b'log stat: %-3.1f%% 50%%-75%%: "
3415 "%-3.1f%%\n",
3416 (long) host->gArtsHostSleep,
3417 host->backlogFilter * 100.0
3418 * (1.0 - host->params->dynBacklogFilter),
3419 100.0 * host->blQuartile[2] / cnt);
3420
3421 fprintf(fp,
3422 " unspooled: %-7ld dyn b'log fltr: %-3.1f 75%%-%s100%%: "
3423 "%-3.1f%%\n",
3424 (long) host->gArtsFromTape, host->params->dynBacklogLowWaterMark,
3425 genHtml ? "<" : "<", 100.0 * host->blQuartile[3] / cnt);
3426
3427 fprintf(fp,
3428 " no queue: %-7ld avr.cxns queue: %-3.1f full: "
3429 "%-3.1f%%\n",
3430 (long) host->gNoQueue,
3431 (double) host->gCxnQueue
3432 / (host->gArtsOffered ? host->gArtsOffered : 1),
3433 100.0 * host->blFull / cnt);
3434 size = convsize(host->gArtsSizeAccepted, &tsize);
3435 snprintf(buf, sizeof(buf), "%.3g %s", size, tsize);
3436 fprintf(fp,
3437 "accpt size: %-8s drop-deferred: %-5s defer length: %-3.1f\n",
3438 buf, host->params->dropDeferred ? "true " : "false",
3439 (double) host->dlAccum / cnt);
3440 size = convsize(host->gArtsSizeRejected, &tsize);
3441 snprintf(buf, sizeof(buf), "%.3g %s", size, tsize);
3442 fprintf(fp, "rejct size: %-8s min-queue-cxn: %s\n", buf,
3443 host->params->minQueueCxn ? "true " : "false");
3444
3445 tapeLogStatus(host->myTape, fp);
3446
3447 {
3448 time_t sec = (time_t)(now - host->connectTime);
3449 double or, ar, rr, jr;
3450 double ars, jrs;
3451 char *tars, *tjrs;
3452 if (sec != 0) {
3453 or = (double) host->artsOffered / (double) sec;
3454 ar = (double) host->artsAccepted / (double) sec;
3455 rr = (double) host->artsNotWanted / (double) sec;
3456 jr = (double) host->artsRejected / (double) sec;
3457 ars = convsize(host->artsSizeAccepted / (double) sec, &tars);
3458 jrs = convsize(host->artsSizeRejected / (double) sec, &tjrs);
3459 fprintf(
3460 fp,
3461 " offered: %5.2f art/s accepted: %5.2f art/s, %.3g %s/s\n",
3462 or, ar, ars, tars);
3463 fprintf(
3464 fp,
3465 " refused: %5.2f art/s rejected: %5.2f art/s, %.3g %s/s\n",
3466 rr, jr, jrs, tjrs);
3467 }
3468 fprintf(fp, " missing %u spooled %u\n", host->artsMissing,
3469 host->artsToTape);
3470 }
3471
3472 #ifdef XXX_STATSHACK
3473 {
3474 time_t now2 = time(NULL);
3475 time_t sec2 = (long) (now2 - host->connectTime);
3476 double or, ar, rr, jr;
3477
3478 if (sec2 != 0) {
3479 or = (double) host->artsOffered / (double) sec2;
3480 ar = (double) host->artsAccepted / (double) sec2;
3481 rr = (double) host->artsNotWanted / (double) sec2;
3482 jr = (double) host->artsRejected / (double) sec2;
3483 fprintf(fp, " or %02.2f ar %02.2f rr %02.2f jr %02.2f\n", or, ar,
3484 rr, jr);
3485 }
3486 fprintf(fp, " missing %u spooled %u\n", host->artsMissing,
3487 host->artsToTape);
3488 }
3489 #endif /* XXX_STATSHACK */
3490
3491 fprintf(fp, "\n\n");
3492 }
3493
3494
3495 /*
3496 * The callback function for the statistics timer to call.
3497 */
3498 static void
hostStatsTimeoutCbk(TimeoutId tid UNUSED,void * data)3499 hostStatsTimeoutCbk(TimeoutId tid UNUSED, void *data)
3500 {
3501 Host host = (Host) data;
3502 time_t now = theTime();
3503
3504 ASSERT(tid == host->statsId);
3505
3506 if (!amClosing(host))
3507 hostLogStats(host, false);
3508
3509 if (now - lastStatusLog >= statsPeriod)
3510 hostLogStatus();
3511
3512 host->statsId = prepareSleep(hostStatsTimeoutCbk, statsPeriod, host);
3513 }
3514
3515
3516 /*
3517 * The callback function for the deferred article timer to call.
3518 */
3519 static void
hostDeferredArtCbk(TimeoutId tid UNUSED,void * data)3520 hostDeferredArtCbk(TimeoutId tid UNUSED, void *data)
3521 {
3522 Host host = (Host) data;
3523 time_t now = theTime();
3524 Article article;
3525
3526 ASSERT(tid == host->deferredId);
3527
3528 while (host->deferred && host->deferred->whenToRequeue <= now) {
3529 article = remHead(&host->deferred, &host->deferredTail);
3530 host->deferLen--;
3531 hostSendArticle(host, article); /* requeue it */
3532 }
3533
3534 if (host->deferred)
3535 host->deferredId = prepareSleep(
3536 hostDeferredArtCbk, host->deferred->whenToRequeue - now, host);
3537 else
3538 host->deferredId = 0;
3539 }
3540
3541
3542 /* if the host has too many unprocessed articles so we send some to the tape.
3543 */
3544 static void
backlogToTape(Host host)3545 backlogToTape(Host host)
3546 {
3547 Article article;
3548
3549 while ((host->backlog + host->deferLen) > hostHighwater) {
3550 if (!host->loggedBacklog) {
3551 host->loggedBacklog = true;
3552 }
3553
3554 if (host->deferred != NULL) {
3555 article = remHead(&host->deferred, &host->deferredTail);
3556 host->deferLen--;
3557 } else {
3558 article = remHead(&host->queued, &host->queuedTail);
3559 host->backlog--;
3560 }
3561
3562 ASSERT(article != NULL);
3563
3564 host->artsQueueOverflow++;
3565 host->gArtsQueueOverflow++;
3566 host->artsToTape++;
3567 host->gArtsToTape++;
3568 procArtsToTape++;
3569 tapeTakeArticle(host->myTape, article);
3570 }
3571 }
3572
3573
3574 /*
3575 * Returns true of the Host is in the middle of closing down.
3576 */
3577 static bool
amClosing(Host host)3578 amClosing(Host host)
3579 {
3580 return (host->myTape == NULL ? true : false);
3581 }
3582
3583
3584 /*
3585 * flush all queued articles all the way out to disk.
3586 */
3587 static void
queuesToTape(Host host)3588 queuesToTape(Host host)
3589 {
3590 Article art;
3591
3592 while ((art = remHead(&host->processed, &host->processedTail)) != NULL) {
3593 host->artsHostClose++;
3594 host->gArtsHostClose++;
3595 host->artsToTape++;
3596 host->gArtsToTape++;
3597 procArtsToTape++;
3598 tapeTakeArticle(host->myTape, art);
3599 }
3600
3601 while ((art = remHead(&host->queued, &host->queuedTail)) != NULL) {
3602 host->backlog--;
3603 host->artsHostClose++;
3604 host->gArtsHostClose++;
3605 host->artsToTape++;
3606 host->gArtsToTape++;
3607 procArtsToTape++;
3608 tapeTakeArticle(host->myTape, art);
3609 }
3610
3611 while ((art = remHead(&host->deferred, &host->deferredTail)) != NULL) {
3612 host->deferLen--;
3613 host->artsHostClose++;
3614 host->gArtsHostClose++;
3615 host->artsToTape++;
3616 host->gArtsToTape++;
3617 procArtsToTape++;
3618 tapeTakeArticle(host->myTape, art);
3619 }
3620
3621 while ((art = remHead(&host->deferred, &host->deferredTail)) != NULL) {
3622 host->deferLen--;
3623 host->artsHostClose++;
3624 host->gArtsHostClose++;
3625 host->artsToTape++;
3626 host->gArtsToTape++;
3627 procArtsToTape++;
3628 tapeTakeArticle(host->myTape, art);
3629 }
3630 }
3631
3632
3633 #define QUEUE_ELEM_POOL_SIZE \
3634 ((4096 - 2 * (sizeof(void *))) / (sizeof(struct proc_q_elem)))
3635
3636 static ProcQElem queueElemPool;
3637
3638 /*
3639 * Add an article to the given queue.
3640 */
3641 static void
queueArticle(Article article,ProcQElem * head,ProcQElem * tail,time_t when)3642 queueArticle(Article article, ProcQElem *head, ProcQElem *tail, time_t when)
3643 {
3644 ProcQElem elem;
3645
3646 if (queueElemPool == NULL) {
3647 unsigned int i;
3648
3649 queueElemPool =
3650 xmalloc(sizeof(struct proc_q_elem) * QUEUE_ELEM_POOL_SIZE);
3651
3652 for (i = 0; i < QUEUE_ELEM_POOL_SIZE - 1; i++)
3653 queueElemPool[i].next = &(queueElemPool[i + 1]);
3654 queueElemPool[QUEUE_ELEM_POOL_SIZE - 1].next = NULL;
3655 }
3656
3657 elem = queueElemPool;
3658 ASSERT(elem != NULL);
3659 queueElemPool = queueElemPool->next;
3660
3661 elem->article = article;
3662 elem->next = NULL;
3663 elem->prev = *tail;
3664 elem->whenToRequeue = when;
3665 if (*tail != NULL)
3666 (*tail)->next = elem;
3667 else
3668 *head = elem;
3669 *tail = elem;
3670 }
3671
3672
3673 /*
3674 * remove the article from the queue
3675 */
3676 static bool
remArticle(Article article,ProcQElem * head,ProcQElem * tail)3677 remArticle(Article article, ProcQElem *head, ProcQElem *tail)
3678 {
3679 ProcQElem elem;
3680
3681 ASSERT(head != NULL);
3682 ASSERT(tail != NULL);
3683
3684 /* we go backwards down the list--probably faster */
3685 elem = *tail;
3686 while (elem != NULL && elem->article != article)
3687 elem = elem->prev;
3688
3689 if (elem != NULL) {
3690 if (elem->prev != NULL)
3691 elem->prev->next = elem->next;
3692 if (elem->next != NULL)
3693 elem->next->prev = elem->prev;
3694 if (*head == elem)
3695 *head = elem->next;
3696 if (*tail == elem)
3697 *tail = elem->prev;
3698
3699 elem->next = queueElemPool;
3700 queueElemPool = elem;
3701
3702 return true;
3703 } else
3704 return false;
3705 }
3706
3707
3708 /*
3709 * remove the article that's at the head of the queue and return
3710 * it. Returns NULL if the queue is empty.
3711 */
3712 static Article
remHead(ProcQElem * head,ProcQElem * tail)3713 remHead(ProcQElem *head, ProcQElem *tail)
3714 {
3715 ProcQElem elem;
3716 Article art;
3717
3718 ASSERT(head != NULL);
3719 ASSERT(tail != NULL);
3720 ASSERT((*head == NULL && *tail == NULL)
3721 || (*head != NULL && *tail != NULL));
3722
3723 if (*head == NULL)
3724 return NULL;
3725
3726 elem = *head;
3727 art = elem->article;
3728 *head = elem->next;
3729 if (elem->next != NULL)
3730 elem->next->prev = NULL;
3731
3732 if (*tail == elem)
3733 *tail = NULL;
3734
3735 elem->next = queueElemPool;
3736 queueElemPool = elem;
3737
3738 return art;
3739 }
3740
3741
3742 static int
validateInteger(FILE * fp,const char * name,long low,long high,int required,long setval,scope * sc,unsigned int inh)3743 validateInteger(FILE *fp, const char *name, long low, long high, int required,
3744 long setval, scope *sc, unsigned int inh)
3745 {
3746 int rval = VALUE_OK;
3747 value *v;
3748 scope *s;
3749 char *p = strrchr(name, ':');
3750
3751 v = findValue(sc, name, inh);
3752 if (v == NULL && required != NOTREQNOADD) {
3753 s = findScope(sc, name, 0);
3754 addInteger(s, p ? p + 1 : name, setval);
3755 if (required == REQ) {
3756 rval = VALUE_MISSING;
3757 logOrPrint(LOG_ERR, fp,
3758 "ME config: no definition for required key %s", name);
3759 } else
3760 logOrPrint(LOG_INFO, fp,
3761 "ME config: adding default value for key %s: %ld", name,
3762 setval);
3763 } else if (v != NULL && v->type != intval) {
3764 rval = VALUE_WRONG_TYPE;
3765 logOrPrint(LOG_ERR, fp, "ME config: value of %s is not an integer",
3766 name);
3767 } else if (v != NULL && low != LONG_MIN && v->v.int_val < low) {
3768 rval = VALUE_TOO_LOW;
3769 logOrPrint(LOG_ERR, fp,
3770 "ME config: value of %s (%ld) in %s is lower than minimum"
3771 " of %ld. Using %ld",
3772 name, v->v.int_val, "global scope", low, low);
3773 v->v.int_val = low;
3774 } else if (v != NULL && high != LONG_MAX && v->v.int_val > high) {
3775 rval = VALUE_TOO_HIGH;
3776 logOrPrint(LOG_ERR, fp,
3777 "ME config: value of %s (%ld) in %s is higher than maximum"
3778 " of %ld. Using %ld",
3779 name, v->v.int_val, "global scope", high, high);
3780 v->v.int_val = high;
3781 }
3782
3783 return rval;
3784 }
3785
3786
3787 static int
validateReal(FILE * fp,const char * name,double low,double high,int required,double setval,scope * sc,unsigned int inh)3788 validateReal(FILE *fp, const char *name, double low, double high, int required,
3789 double setval, scope *sc, unsigned int inh)
3790 {
3791 int rval = VALUE_OK;
3792 value *v;
3793 scope *s;
3794 char *p = strrchr(name, ':');
3795
3796 v = findValue(sc, name, inh);
3797 if (v == NULL && required != NOTREQNOADD) {
3798 s = findScope(sc, name, 0);
3799 addReal(s, p ? p + 1 : name, setval);
3800 if (required == REQ) {
3801 rval = VALUE_MISSING;
3802 logOrPrint(LOG_ERR, fp,
3803 "ME config: no definition for required key %s", name);
3804 } else
3805 logOrPrint(LOG_INFO, fp,
3806 "ME config: adding default value for key %s: %f", name,
3807 setval);
3808 } else if (v != NULL && v->type != realval) {
3809 rval = VALUE_WRONG_TYPE;
3810 logOrPrint(LOG_ERR, fp,
3811 "ME config: value of %s is not a floating point number",
3812 name);
3813 } else if (v != NULL && low != -DBL_MAX && v->v.real_val < low) {
3814 logOrPrint(LOG_ERR, fp,
3815 "ME config: value of %s (%f) is lower than minimum of %f",
3816 name, v->v.real_val, low);
3817 v->v.real_val = setval;
3818 } else if (v != NULL && high != DBL_MAX && v->v.real_val > high) {
3819 logOrPrint(LOG_ERR, fp,
3820 "ME config: value of %s (%f) is higher than maximum of %f",
3821 name, v->v.real_val, high);
3822 v->v.real_val = setval;
3823 }
3824
3825 return rval;
3826 }
3827
3828
3829 static int
validateBool(FILE * fp,const char * name,int required,bool setval,scope * sc,unsigned int inh)3830 validateBool(FILE *fp, const char *name, int required, bool setval, scope *sc,
3831 unsigned int inh)
3832 {
3833 int rval = VALUE_OK;
3834 value *v;
3835 scope *s;
3836 char *p = strrchr(name, ':');
3837
3838 v = findValue(sc, name, inh);
3839 if (v == NULL && required != NOTREQNOADD) {
3840 s = findScope(sc, name, 0);
3841 addBoolean(s, p ? p + 1 : name, setval ? 1 : 0);
3842 if (required == REQ) {
3843 rval = VALUE_MISSING;
3844 logOrPrint(LOG_ERR, fp,
3845 "ME config: no definition for required key %s", name);
3846 } else
3847 logOrPrint(LOG_INFO, fp,
3848 "ME config: adding default value for key %s: %s", name,
3849 (setval ? "true" : "false"));
3850 } else if (v != NULL && v->type != boolval) {
3851 rval = VALUE_WRONG_TYPE;
3852 logOrPrint(LOG_ERR, fp, "ME config: value of %s is not a boolean",
3853 name);
3854 }
3855
3856 return rval;
3857 }
3858
3859
3860 void
gCalcHostBlStat(void)3861 gCalcHostBlStat(void)
3862 {
3863 Host h;
3864
3865 for (h = gHostList; h != NULL; h = h->next) {
3866 h->dlAccum += h->deferLen;
3867 h->blAccum += h->backlog;
3868 if (h->backlog == 0)
3869 h->blNone++;
3870 else if (h->backlog >= (hostHighwater - h->deferLen))
3871 h->blFull++;
3872 else
3873 h->blQuartile[(4 * h->backlog) / (hostHighwater - h->deferLen)]++;
3874 h->blCount++;
3875 }
3876 }
3877 static void
hostCleanup(void)3878 hostCleanup(void)
3879 {
3880 if (statusFile != NULL)
3881 free(statusFile);
3882 statusFile = NULL;
3883 }
3884