*BSD News Article 69106


Return to BSD News archive

Path: euryale.cc.adfa.oz.au!newshost.anu.edu.au!harbinger.cc.monash.edu.au!news.mira.net.au!Germany.EU.net!EU.net!howland.reston.ans.net!newsfeed.internetmci.com!news.sover.net!not-for-mail
From: erikl@sover.net (Erik R. Leo)
Newsgroups: comp.unix.bsd.bsdi.misc,news.software.b,news.software.nntp
Subject: Re: INN 1.4unoff4, SharedActive Patch & BSDi 2.1??
Followup-To: comp.unix.bsd.bsdi.misc,news.software.b,news.software.nntp
Date: 22 May 1996 13:21:03 GMT
Organization: SoVerNet, Inc.
Lines: 635
Message-ID: <4nv4bv$2ae@thrush.sover.net>
References: <4nnqim$9sm@news.abs.net>
NNTP-Posting-Host: honeybee.sover.net
X-Newsreader: TIN [UNIX 1.3 950824BETA PL0]
Xref: euryale.cc.adfa.oz.au comp.unix.bsd.bsdi.misc:3794 news.software.b:17851 news.software.nntp:22903

Howard Leadmon (howardl@u1.abs.net) wrote:
: 
:    I was wondering if anyone here has tried to get the INN 1.4unoff4
: nnrpd working with the shared-active patches??  From what I can see 
: this works fine on SysV platforms, and BSDi 2.1 seems to have the 
: required shared memory calls if I include the -lipc library during the
: compile process.  

	I'm running this configuration.  My config is in part 9 of the 
INN FAQ.  An additional piece of info that might help (I kinda' thought 
it was obvious, after reading the shmat(3) manpage) is that BSD/OS's 
implementation of the "System V compatible IPC functions" uses files in 
/tmp, so if you aren't using an mfs(8)-based /tmp, you aren't getting 
what you think you want.

	The version of the sharedactive patch I'm using was snarfed from:

ftp://ftp.math.psu.edu/pub/INN/patches/sharedactive.patch

and modified locally (as per various recommendations in this newsfroup :).
I've appended it below.

-Erik
-- 
=====================================================================
Erik R. Leo, Net Worker       SoVerNet
Tel:    +1(802)463-2111       Vermont's Sovereign Internet Connection
Fax:    +1(802)463-2110       5 Rockingham Street
Email:  erikl@sover.net       Bellows Falls, Vermont 05101
=====================================================================
*** nnrpd/group.c.orig	Fri Jan 29 18:51:57 1993
--- nnrpd/group.c	Sat Jan 27 19:33:38 1996
***************
*** 1,4 ****
! /*  $Revision: 1.13 $
  **
  **  Newsgroups and the active file.
  */
--- 1,4 ----
! /*  $Revision: 1.32 $
  **
  **  Newsgroups and the active file.
  */
***************
*** 5,11 ****
--- 5,45 ----
  #include "nnrpd.h"
  #include "mydir.h"
  
+ /*
+  * [++doug] Configuration for using shared memory.  If GROUP_SHARED
+  * is left undefined, the rest can be ignored.
+  *
+  * When using shared memory, we statically allocate the GRPentries
+  * array and the GRPactive array.  Otherwise, need a storage allocator
+  * to manage space in shared memory.  This means putting a fixed limit
+  * on the number of groups and things.
+  *
+  * Define XSEM_UNDO to be SEM_UNDO if you want to use the kernel
+  * semaphore undo feature which fixes up a semaphore if a process dies
+  * while holding it.  However, the default system-wide limit on undo
+  * structs is 30 under HPUX 8.0.  Since the fwpnews server often has
+  * over 200 nnrpds running, would need to increase semmnu in dfile to
+  * a few hundred.  If XSEM_UNDO is set to 0, then killing an nnrpd process
+  * while it holds the semaphore will block all other nnrpds from running
+  * until reboot.  Or until some other process removes or resets the semaphore.
+  * The idea is to not kill nnrpds.
+  *
+  * [++russell Jan 27 1996 <vincent@ucthpx.uct.ac.za>]
+  * Found a problem under Solaris 2.4 whereby the shm ptr returned wasn't
+  * always constant, so I had to removed the shared mem version of GRPentries,
+  * GRPnextentry and GRPtable.
+  * (Seems to have stopped core dumps)
+  */
+ #define GROUP_SHARED		/* Use shared memory */
  
+ #ifdef GROUP_SHARED
+ #  define MAX_ACTIVE_SIZE	1000000	/* Max size of active file           */
+ #  define MAX_GROUPS		30000	/* Max number of groups in active    */
+ #  define RELOAD_DELAY	60	/* min #secs before reloading active */
+ #  define XSEM_UNDO		0	/* Don't use kernel undo             */
+ #endif
+ 
+ 
  /*
  **  Newsgroup hashing stuff.  See comments in innd/ng.c.
  */
***************
*** 15,34 ****
  #define GRP_SIZE	512
  #define GRP_BUCKET(j)	&GRPtable[j & (GRP_SIZE - 1)]
  
! typedef struct _GRPHASH {
!     int		Size;
!     int		Used;
!     GROUPENTRY	**Groups;
! } GRPHASH;
  
  
! STATIC GRPHASH		GRPtable[GRP_SIZE];
! STATIC GROUPENTRY	*GRPentries;
! STATIC int		GRPbuckets;
  STATIC int		GRPsize;
  
  
  /*
  **  See if a given newsgroup exists.
  */
  GROUPENTRY *
--- 49,170 ----
  #define GRP_SIZE	512
  #define GRP_BUCKET(j)	&GRPtable[j & (GRP_SIZE - 1)]
  
! #define NEXTGROUP(g)	GRPnextentry[(g) - GRPentries]
  
+ #ifdef GROUP_SHARED
  
! GROUPENTRY	*_GRPtable[GRP_SIZE];		/* 2k   */
! GROUPENTRY	*_GRPnextentry[MAX_GROUPS];	/* 40k  */
! GROUPENTRY	*_GRPentries;
! time_t		GRPtimehash;			/* time last loaded */
! 
! static struct shared {
! 	BOOL		_GRPvalid;			/* loaded ok */
! 	time_t		_GRPtimestamp;			/* time last loaded */
! 	int		_GRPsize;			/* number of groups */
! 	char		_GRPactive[MAX_ACTIVE_SIZE];	/* 500k */
! } *shm;
! 
! #define GRPsize		shm->_GRPsize
! #define GRPactive	shm->_GRPactive
! #define GRPtimestamp	shm->_GRPtimestamp
! #define GRPvalid	shm->_GRPvalid
! #define GRPtable	_GRPtable
! #define GRPentries	_GRPentries
! #define GRPnextentry	_GRPnextentry
! 
! #include <sys/ipc.h>
! #include <sys/sem.h>
! #include <sys/shm.h>
! 
! #define SHMKEY	((key_t)12347)
! #define SEMKEY	((key_t)12348)
! 
! static int	SemId;
! static int	ShmId;
! 
! /*
!  * Routines to enforce mutual exclusion on shared memory.  Any
!  * nnrpd process can update and must make sure that nobody is
!  * reading it at the time.
!  */
! static int shm_alloc()
! {
!     if ((SemId = semget(SEMKEY, 1, 0666 | IPC_CREAT)) < 0) {
! 	syslog(L_ERROR, "%s Can't create semaphore %m", ClientHost);
! 	return (FALSE);
!     }
!     if ((ShmId = shmget(SHMKEY, sizeof(*shm), 0666 | IPC_CREAT)) < 0) {
! 	syslog(L_ERROR, "%s Can't create shared mem segment %m", ClientHost);
! 	return (FALSE);
!     }
!     shm = (struct shared *)shmat(ShmId, (void *)0, 0);
!     if (shm == (struct shared *)-1) {
! 	syslog(L_ERROR, "%s Can't attach shared memory %m", ClientHost);
! 	return (FALSE);
!     }
!     return (TRUE);
! }
! 
! /*
!  * lock and unlock borrowed from Stevens.
!  */
! static struct sembuf op_lock[2] = {
! 	0, 0, 0,			/* Wait for sem0 to become 0 */
! 	0, 1, XSEM_UNDO			/* Then increment by 1 */
! };
! 
! static struct sembuf op_unlock[1] = {
! 	0, -1, (IPC_NOWAIT|XSEM_UNDO)	/* Decrement by 1 (to 0) */
! };
! 
! static int shm_lock()
! {
!     static int shm_allocated;
! 
!     if (! shm_allocated) {
! 	if (! shm_alloc())
! 		return (FALSE);
! 	shm_allocated = TRUE;
!     }
!     if (semop(SemId, op_lock, 2) < 0) {
! 	syslog(L_ERROR, "%s acquiring semaphore %m", ClientHost);
! 	return (FALSE);
!     }
!     return (TRUE);
! }
! 
! static void shm_unlock()
! {
!     if (semop(SemId, op_unlock, 1) < 0)
! 	syslog(L_ERROR, "%s unlocking semaphore %m", ClientHost);
! }
! 
! #else
! 
  STATIC int		GRPsize;
+ STATIC GROUPENTRY	*GRPtable[GRP_SIZE];
+ STATIC GROUPENTRY	*GRPentries;
+ STATIC GROUPENTRY	*GRPnextentry;
+ STATIC char		*GRPactive;
  
+ #define shm_lock()	TRUE
+ #define shm_unlock()
  
+ #endif
+ 
  /*
+ **  [++doug]
+ **  GRPfind called from:   newgroups command -- once for each new group
+ **                         group command     -- once
+ **                         validnewsgroups   -- once for each newsgroup in
+ **                                              Newsgroups: line.
+ **
+ **  The group command is by far the most used.  During peak time with
+ **  ~200 readers, called about once every two seconds.
+ */
+ 
+ /*
  **  See if a given newsgroup exists.
  */
  GROUPENTRY *
***************
*** 37,53 ****
  {
      register char		*p;
      register unsigned int	j;
!     register int		i;
!     register GROUPENTRY		**gpp;
!     GRPHASH			*htp;
      char			c;
  
      /* SUPPRESS 6 *//* Over/underflow from plus expression */
      GRP_HASH(group, p, j);
!     htp = GRP_BUCKET(j);
!     for (c = *group, gpp = htp->Groups, i = htp->Used; --i >= 0; gpp++)
! 	if (c == gpp[0]->Name[0] && EQ(group, gpp[0]->Name))
! 	    return gpp[0];
      return NULL;
  }
  
--- 173,224 ----
  {
      register char		*p;
      register unsigned int	j;
!     register GROUPENTRY		*gp;
      char			c;
  
+     if (! shm_lock()) {
+ 	return (NULL);
+    }
+ #ifdef GROUP_SHARED
+     if (! GRPvalid) {
+ 	shm_unlock();
+ 	if (! GetGroupList())
+ 	    return (NULL);
+ 	if (! shm_lock())
+ 	    return (NULL);
+ 	if (! GRPvalid)
+ 	    return (NULL);
+     }
+ #endif
      /* SUPPRESS 6 *//* Over/underflow from plus expression */
+     if (GRPtimehash <= GRPtimestamp) ReHashGroupList();
      GRP_HASH(group, p, j);
!     c = *group;
!     for (gp = *GRP_BUCKET(j); gp != NULL; gp = NEXTGROUP(gp)) {
! 	if (c == gp->Name[0] && EQ(group, gp->Name)) {
! #ifdef GROUP_SHARED
! 	    static GROUPENTRY		grp;
! 	    static char			groupname[256];
! 	    static char			groupalias[256];
! 
! 	    strncpy(groupname, gp->Name, sizeof(groupname) - 1);
! 	    grp.Name = groupname;
! 	    grp.High = gp->High;
! 	    grp.Low = gp->Low;
! 	    grp.Flag = gp->Flag;
! 	    if (gp->Alias != NULL)
! 		strncpy(groupalias, gp->Alias, sizeof(groupalias) - 1);
! 	    else
! 		groupalias[0] = '\0';
! 	    grp.Alias = groupalias;
! 	    shm_unlock();
! 	    return (&grp);
! #else
! 	    return gp;
! #endif
! 	}
!     }
!     shm_unlock();
      return NULL;
  }
  
***************
*** 55,79 ****
  STATIC void
  GRPhash()
  {
-     register char		*p;
      register int		i;
      register GROUPENTRY		*gp;
      register unsigned int	j;
!     register GRPHASH		*htp;
  
!     /* Set up the default hash buckets. */
!     GRPbuckets = GRPsize / GRP_SIZE;
!     if (GRPbuckets == 0)
! 	GRPbuckets = 1;
!     if (GRPtable[0].Groups)
! 	for (i = GRP_SIZE, htp = GRPtable; --i >= 0; htp++)
! 	    htp->Used = 0;
!     else
! 	for (i = GRP_SIZE, htp = GRPtable; --i >= 0; htp++) {
! 	    htp->Size = GRPbuckets;
! 	    htp->Groups = NEW(GROUPENTRY*, htp->Size);
! 	    htp->Used = 0;
! 	}
  
      /* Now put all groups into the hash table. */
      for (i = GRPsize, gp = GRPentries; --i >= 0; gp++) {
--- 226,240 ----
  STATIC void
  GRPhash()
  {
      register int		i;
      register GROUPENTRY		*gp;
      register unsigned int	j;
!     register GROUPENTRY		**htp;
!     register char		*p;
  
!     /* Clear out the hash table. */
!     for (i = GRP_SIZE, htp = GRPtable; --i >= 0; htp++)
! 	*htp = NULL;
  
      /* Now put all groups into the hash table. */
      for (i = GRPsize, gp = GRPentries; --i >= 0; gp++) {
***************
*** 80,96 ****
  	/* SUPPRESS 6 *//* Over/underflow from plus expression */
  	GRP_HASH(gp->Name, p, j);
  	htp = GRP_BUCKET(j);
! 	if (htp->Used >= htp->Size) {
! 	    htp->Size += GRPbuckets;
! 	    RENEW(htp->Groups, GROUPENTRY*, htp->Size);
! 	}
! 	htp->Groups[htp->Used++] = gp;
      }
  
      /* Note that we don't sort the buckets. */
  }
  
  
  /*
  **  Read the active file into memory, sort it, and set the number of
  **  newsgroups read in.  Return TRUE if okay, FALSE on error.
--- 241,288 ----
  	/* SUPPRESS 6 *//* Over/underflow from plus expression */
  	GRP_HASH(gp->Name, p, j);
  	htp = GRP_BUCKET(j);
! 	NEXTGROUP(gp) = *htp;
! 	*htp = gp;
      }
  
      /* Note that we don't sort the buckets. */
  }
  
+ /*
+ ** [+++russell]
+ ** Reload the Active file shared mem variables because the shared mem
+ ** copy changed while we weren't looking
+ */
+ BOOL
+ ReHashGroupList()
+ {
+     register char		*p;
+     register GROUPENTRY		*gp;
+     register int		i;
+     time_t			tt;
  
+     if (GRPentries != NULL) DISPOSE(GRPentries);
+     GRPentries = NEW(GROUPENTRY, GRPsize);
+     for (i = 0, gp = GRPentries, p = GRPactive; i<GRPsize; i++, gp++) {
+ 	gp->Name = p;
+ 	while (*p++ != '\0');
+ 
+ 	/* Get the high mark. */
+ 	gp->High = atol(p);
+ 	while (*p++ != '\0');
+ 
+ 	/* Get the low mark. */
+ 	gp->Low = atol(p);
+ 	while (*p++ != '\0');
+ 
+ 	gp->Flag = *p;
+ 	gp->Alias = gp->Flag == NF_FLAG_ALIAS ? p + 1 : NULL;
+ 	while (*p++ != '\0');
+     }
+ 
+     GRPhash();
+     GRPtimehash = time((time_t *)&tt);
+ }
  /*
  **  Read the active file into memory, sort it, and set the number of
  **  newsgroups read in.  Return TRUE if okay, FALSE on error.
***************
*** 98,133 ****
  BOOL
  GetGroupList()
  {
-     static char			*active;
      register char		*p;
      register char		*q;
      register GROUPENTRY		*gp;
      register int		i;
  
      /* If re-scanning, free previous groups. */
!     if (active != NULL) {
! 	DISPOSE(active);
  	DISPOSE(GRPentries);
      }
  
      /* Get the new file. */
!     active = ReadInFile(ACTIVE, (struct stat *)NULL);
!     if (active == NULL) {
  	syslog(L_ERROR, "%s cant read %s %m", ClientHost, ACTIVE);
  	return FALSE;
      }
  
      /* Count lines. */
!     for (p = active, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++)
  	continue;
  
      /* Fill in the group array. */
      GRPentries = NEW(GROUPENTRY, i);
!     for (i = 0, gp = GRPentries, p = active; *p; i++, gp++, p = q + 1) {
  	gp->Name = p;
  	if ((p = strchr(p, ' ')) == NULL) {
  	    syslog(L_ERROR, "%s internal no_space1 \"%.20s...\"",
  		ClientHost, gp->Name);
  	    return FALSE;
  	}
  	*p++ = '\0';
--- 290,371 ----
  BOOL
  GetGroupList()
  {
      register char		*p;
      register char		*q;
      register GROUPENTRY		*gp;
      register int		i;
+     time_t			tt;
  
+     if (! shm_lock())
+ 	return (FALSE);
+ 
+ #ifdef GROUP_SHARED
+     /*
+      * Don't reload the active file if any nnrpd process has loaded it
+      * within the last RELOAD_DELAY seconds.  Saves a bit of I/O.
+      */
+     if (GRPtimestamp + RELOAD_DELAY > time((time_t *)&tt)) {
+ 	ReHashGroupList();
+ 	shm_unlock();
+ 	return (TRUE);
+     }
+ 
+     /*
+      * Mark data in shared memory invalid.  That way, if one nnrpd
+      * screws up shared memory while attempting to reload active, the
+      * others will notice and also try to reload.
+      */
+     GRPvalid = 0;
+ #endif
+ 
      /* If re-scanning, free previous groups. */
! #ifdef GROUP_SHARED
!     if (GRPentries != NULL) {
  	DISPOSE(GRPentries);
+ 	DISPOSE(GRPnextentry);
      }
+ #else
+     if (GRPactive != NULL) {
+ 	DISPOSE(GRPactive);
+ 	DISPOSE(GRPentries);
+ 	DISPOSE(GRPnextentry);
+     }
+ #endif
  
      /* Get the new file. */
! #ifdef GROUP_SHARED
!     if (! xReadInFile(ACTIVE, GRPactive, MAX_ACTIVE_SIZE)) {
! #else
!     GRPactive = ReadInFile(ACTIVE, (struct stat *)NULL);
!     if (GRPactive == NULL) {
! #endif
! 	shm_unlock();
  	syslog(L_ERROR, "%s cant read %s %m", ClientHost, ACTIVE);
  	return FALSE;
      }
  
      /* Count lines. */
!     for (p = GRPactive, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++)
  	continue;
  
      /* Fill in the group array. */
+ #ifdef GROUP_SHARED
+     if (i > MAX_GROUPS) {
+ 	shm_unlock();
+ 	syslog(L_ERROR, "%s too many groups (%d)", ClientHost, i);
+ 	return (FALSE);
+     }
      GRPentries = NEW(GROUPENTRY, i);
! #else
!     GRPentries = NEW(GROUPENTRY, i);
!     GRPnextentry = NEW(GROUPENTRY *, i);
! #endif
!     for (i = 0, gp = GRPentries, p = GRPactive; *p; i++, gp++, p = q + 1) {
  	gp->Name = p;
  	if ((p = strchr(p, ' ')) == NULL) {
  	    syslog(L_ERROR, "%s internal no_space1 \"%.20s...\"",
  		ClientHost, gp->Name);
+ 	    shm_unlock();
  	    return FALSE;
  	}
  	*p++ = '\0';
***************
*** 136,141 ****
--- 374,380 ----
  	if ((q = strchr(p, ' ')) == NULL) {
  	    syslog(L_ERROR, "%s internal no_space2 \"%.20s...\"",
  		ClientHost, gp->Name);
+ 	    shm_unlock();
  	    return FALSE;
  	}
  	*q++ = '\0';
***************
*** 145,150 ****
--- 384,390 ----
  	if ((p = strchr(q, ' ')) == NULL) {
  	    syslog(L_ERROR, "%s internal no_space3 \"%.20s...\"",
  		ClientHost, gp->Name);
+ 	    shm_unlock();
  	    return FALSE;
  	}
  	*p++ = '\0';
***************
*** 154,159 ****
--- 394,400 ----
  	if ((q = strchr(p, '\n')) == NULL) {
  	    syslog(L_ERROR, "%s internal newline \"%.20s...\"",
  		ClientHost, gp->Name);
+ 	    shm_unlock();
  	    return FALSE;
  	}
  	*q = '\0';
***************
*** 163,171 ****
--- 404,469 ----
  
      GRPsize = i;
      GRPhash();
+ #ifdef GROUP_SHARED
+     GRPtimestamp = time(&tt);
+     GRPvalid = 1;
+ #endif
+     shm_unlock();
      return TRUE;
  }
  
+ 
+ #ifdef GROUP_SHARED
+ 
+ /*
+ **  Used for reading active file into shared memory.
+ **
+ **  [++doug]
+ **
+ **  It isn't clear to me what ensures that nnrpd never attempts to
+ **  open the active while innd is writing it.  Even if the writev
+ **  that innd uses is atomic, there is still a window between where
+ **  it opens (and truncates) the active file, and where it writes the
+ **  current contents.  Anyway, this problem, if it really is a problem,
+ **  existed before my mods.
+ */
+ int xReadInFile(name, buf, bufsize)
+     char *name;
+     char *buf;
+     int  bufsize;
+ {
+     int fd;
+     struct stat	mystat;
+     int		oerrno;
+ 
+     if ((fd = open(name, O_RDONLY)) < 0)
+ 	return FALSE;
+ 
+     if (fstat(fd, &mystat) < 0) {
+ 	oerrno = errno;
+ 	(void)close(fd);
+ 	errno = oerrno;
+ 	return FALSE;
+     }
+     if (mystat.st_size >= bufsize) {
+ 	errno = ENOMEM;
+ 	return (FALSE);
+     }
+ 
+     if (xread(fd, buf, mystat.st_size) < 0) {
+ 	oerrno = errno;
+ 	(void)close(fd);
+ 	errno = oerrno;
+ 	return FALSE;
+     }
+ 
+     /* Terminate the string; terminate the routine. */
+     buf[mystat.st_size] = '\0';
+     (void)close(fd);
+     return (TRUE);
+ }
+ 
+ #endif
  
  /*
  **  Sorting predicate to put newsgroup names into numeric order.