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 ? "&gt;" : ">",
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 ? "&lt;" : "<", 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