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