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.