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