Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Posting-Version: version B 2.10.2 9/5/84; site yale.ARPA
Path: utzoo!watmath!clyde!cbosgd!ihnp4!zehntel!dual!amdcad!decwrl!decvax!yale!kaufman
From: kaufman@yale.ARPA (Qux the Barbarian)
Newsgroups: net.wanted.sources
Subject: Re: local netnews usage statistics
Message-ID: <6644@yale.ARPA>
Date: Sun, 2-Dec-84 22:07:50 EST
Article-I.D.: yale.6644
Posted: Sun Dec  2 22:07:50 1984
Date-Received: Tue, 4-Dec-84 19:35:29 EST
Reply-To: kaufman@yale-comix.UUCP (Qux the Barbarian)
Distribution: net
Organization: Cordon Bulldog (Yale School of Strike Survival)
Lines: 266

As long as we're all posting our News statistics generators...

Unlike the others, this is a C program not a shell script (sorry, Henry!).
Since it computes disk usages, it takes a while to run.  I run it out of
/usr/lib/news/trimlib.

shar+enjoy,
    Qux

Kaufman@Yale.Arpa       Kaufman@YaleCS.Bitnet       ..!decvax!yale!kaufman

: to unbundle, "sh" this file -- DO NOT use csh
:  SHAR archive format.  Archive created Sun Dec 2 22:03:08 EST 1984
echo x - ngstats.c
sed 's/^X//' >ngstats.c <<'+FUNKY+STUFF+'
X#include    
X#include    
X#include    
X#include    
X#include    
X
X/* A hack!  A hack!  My kingdom for a hack! */
X
X/* ngstats.c
X *
X * Generate news readership statistics.  Best run by cron, perhaps from
X * /usr/lib/news/trimlib.
X *
X * Written by Qux the Barbarian (Kaufman@Yale.Arpa, Kaufman@YaleCS.Bitnet,
X * ..!decvax!yale!kaufman).  Hereby placed in the Public Domain, for what
X * that's worth;  I would appreciate hearing about bugs/fixes/extensions
X * and ports to other Operating Systems or versions of Unix.
X *
X * Tested under 4.2 BSD; should work without changes under 4.1 BSD with the
X * ndir upward compatibility routines.
X */
X
X#define ACTIVE "/usr/lib/news/active"
X#define SPOOLDIR "/usr/spool/news"
X
X#define WEEKS 7 * 24 * 60 * 60
X#define READ_DELTA (2*WEEKS)
X/*#define RAPIDOFLEX      /* don't actually gather stats */
X
Xstruct NG {
X    char name[100];
X    int rdrs;
X    int first_art;
X    int last_art;
X    int du;
X    int artcnt;
X} ngs[512];
X
Xint ngcnt = 0, unknownngcnt = 0;
Xint cmprdrs = -1;
X
X#define rdr_du(a) ((a).rdrs ? (a).du / (a).rdrs : (a).du)
X
Xngcmp(a, b)
Xstruct NG *a, *b; {
X
X    if ((cmprdrs && a->rdrs == b->rdrs) ||
X       (!cmprdrs && rdr_du(*a) == rdr_du(*b)))
X       return(strcmp(a->name, b->name));
X    else if (cmprdrs)
X       return(a->rdrs < b->rdrs ? 1 : -1);
X    else
X       return(rdr_du(*a) < rdr_du(*b) ? 1 : -1);
X}
X
Xmain (argc, argv)
Xint argc;
Xchar **argv; {
X    struct passwd *pp, *getpwent();
X    FILE *fp;
X    int newsrccnt = 0, i, noreadcnt = 0;
X    int notreadcnt = 0, norccnt = 0;
X    int accounts = 0, dusum = 0;
X    char buf[100], c;
X    long last_read_time;
X
X    /* set up some time junk */
X    time(&last_read_time);
X    last_read_time -= READ_DELTA;
X
X    /* read in active file */
X    if ((fp = fopen(ACTIVE, "r")) == NULL) {
X       perror(ACTIVE);
X       exit(1);
X    }
X    while (!feof(fp)) {
X       fscanf(fp, "%s %d %d %c\n",
X                  ngs[ngcnt].name, &ngs[ngcnt].last_art,
X                  &ngs[ngcnt].first_art, &c);
X       ngs[ngcnt].rdrs = 0;
X       ngs[ngcnt++].du = 0;
X    }
X    fclose(fp);
X
X    /* Compute disk usages (hack hack hack) */
X    for (i=0; i pw_dir);
X       if (access(buf, 0))
X           norccnt++;
X       else {
X           struct stat stbuf;
X
X           if (!stat(buf, &stbuf) && stbuf.st_mtime < last_read_time)
X               notreadcnt++;
X           else if ((fp = fopen(buf, "r")) == NULL)
X               noreadcnt++;
X           else {
X               newsrccnt++;
X               readnewsrc(fp, buf);
X               fclose(fp);
X           }
X       }
X    }
X
X    /* sort the stats */
X    qsort(ngs, ngcnt, sizeof(struct NG), ngcmp);
X
X    /* Now, print the statistics */
X    printf("\nFor %d accounts on system %s:\n", accounts, sysname());
X    printf("\t%d have no .newsrc\n", norccnt);
X    printf("\t%d have an unreadable .newsrc\n", noreadcnt);
X    printf("\t%d have an out of date .newsrc\n", notreadcnt);
X    printf("\t%d .newsrc's were read\n", newsrccnt);
X    printf("\nFor %d newsgroups received:\n", ngcnt);
X    printf("\t%d unknown newsgroups referenced in .newsrc's read.\n", unknownngcnt);
X    printf("\t%d blocks total disk usage\n\n", dusum);
X    puts("Statistics sorted by decreasing number of readers per newsgroup:\n");
X
X    puts("Readers\t  # Articles\tDisk Space\tBlocks/Rdr\tNewsgroup");
X    for (i=0; i < ngcnt; i++)
X       printf("%5d\t   %5d\t  %6d\t  %5d\t\t%s\n",
X           ngs[i].rdrs, ngs[i].artcnt,
X           ngs[i].du, rdr_du(ngs[i]),
X           ngs[i].name);
X
X
X    /* resort the stats */
X    cmprdrs = 0;
X    qsort(ngs, ngcnt, sizeof(struct NG), ngcmp);
X
X    /* Do it again sorted differently */
X    puts("\nSorted by decreasing number of blocks per newsgroup reader:\n");
X    puts("Readers\t  # Articles\tDisk Space\tBlocks/Rdr\tNewsgroup");
X    for (i=0; i < ngcnt; i++)
X       printf("%5d\t   %5d\t  %6d\t  %5d\t\t%s\n",
X           ngs[i].rdrs, ngs[i].artcnt,
X           ngs[i].du, rdr_du(ngs[i]),
X           ngs[i].name);
X
X}
X
Xreadnewsrc(rcp, filename)
Xchar *filename;
XFILE *rcp; {
X    char buf[100], *cp, *index();
X    int i;
X
X#ifdef RAPIDOFLEX
X    return;
X#endif RAPIDOFLEX
X
X    while (fgets(buf, 100, rcp)) {
X
X       if ((cp = index(buf, ':')) == NULL)
X           continue;
X
X       *cp = '\0';
X       for (i = 0; i < ngcnt; i++)
X           if (!strcmp(buf, ngs[i].name)) {
X               ngs[i].rdrs++;
X               break;
X           }
X       if (i == ngcnt)
X           unknownngcnt++;
X    }
X}
X
Xngdu(ng)
Xstruct NG *ng; {
X    char ngdir[256], ngart[256], *cp;
X    struct stat stbuf;
X    DIR *dir;
X    struct direct *dirent;
X    int pad = 0;    /* hack hack hack */
X    int i;
X
X#ifdef RAPIDOFLEX
X    return(0);
X#endif RAPIDOFLEX
X
X    /* create directory name */
X    sprintf(ngdir, "%s/%s", SPOOLDIR, ng->name);
X    cp = ngdir;
X    while (cp = index(cp, '.'))
X       *cp = '/';
X
X    /* calculate usage of all articles combined */
X    if ((dir = opendir(ngdir)) == NULL) {
X       perror(ngdir);
X       return(0);
X    }
X    seekdir(dir, 2);
X    while (dirent = readdir(dir)) {
X       /* skip deleted files */
X       if (!dirent->d_ino)
X           continue;
X
X       /* skip directories */
X       sprintf(ngart, "%s/%s", ngdir, dirent->d_name);
X       if (stat(ngart, &stbuf) || (stbuf.st_mode & S_IFMT) == S_IFDIR)
X           continue;
X
X       /* increment article count and disk usage */
X       ng->artcnt++;
X       if ((stbuf.st_mode & S_IFMT) == S_IFREG)
X           ng->du += (stbuf.st_size + 511) / 512;
X    }
X    closedir(dir);
X
X    /* compare article counts */
X    if (ng->last_art == 0 && ng->first_art == 1)
X       i = 0;
X    else
X       i = ng->last_art - ng->first_art;
X    if (ng->artcnt != i  && ng->artcnt != i+1) {
X          printf("Article count mismatch for newsgroup %s: ", ng->name);
X          printf("active claims %d, actual is %d.\n", i, ng->artcnt);
X    }
X    return(ng->du);
X}
+FUNKY+STUFF+
ls -l ngstats.c
echo x - sysname.c
sed 's/^X//' >sysname.c <<'+FUNKY+STUFF+'
X#include 
X
X/* sysname()
X * Ala 4.1, but now only an interface to gethostname().
X * David H. Kaufman
X */
X
X#define SYSNAMELEN 255
Xstatic char systemname[SYSNAMELEN];
X
Xchar *sysname() {
X    if (!*systemname)
X       if (gethostname(systemname, SYSNAMELEN))
X           return(NULL);
X
X    return(systemname);
X}
+FUNKY+STUFF+
ls -l sysname.c
exit 0