"fixactive" -- repair corrupt netnews active file
Dave Taylor
taylor at limbo.Intuitive.Com
Wed Feb 28 19:57:12 AEST 1990
One of the fun things about netnews software is that it's a very
fragile house of cards that can start acting strangely for any of
a large number of reasons. Well, on "limbo", for some completely
inexplicable reason, the behaviour we're seeing is that the numbers
in the "active" file end up not reflecting the reality of the
articles available on the disk.
No idea why, but after running "expire" and having it completely
mangle my active file rather than rebuild it as promised, I wrote
"fixactive", a simple program to compare the information on disk with
the information in the active file, and if different, to rewrite the
active file to be correct.
Usage of the program is pretty simple:
fixactive
if you're root will scan through things and fix the active file if
there's anything wrong with it. If you're not root, it will simply
scan and report any discrepencies between the disk and active file.
The other options modify this behaviour as follows:
-a fn Use 'fn' as the active file
(default is '/usr/lib/news/active')
-h dir Use 'dir' as the netnews spool home
(default is '/usr/spool/news')
-n Show what you'd need to do, but don't do it
-v Verbose: lots of output along the way
I recommend that you try it out by "fixactive -n".
-- Dave Taylor
Intuitive Systems
Mountain View, CA 94043
taylor at limbo.intuitive.com or {uunet!}{decwrl,apple}!limbo!taylor
-- Attachment:
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Dave Taylor <taylor at limbo> on Wed Feb 28 00:52:04 1990
#
# This archive contains:
# fixactive.c README
#
# Error checking via wc(1) will be performed.
# Error checking via sum(1) will be performed.
LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH
if sum -r </dev/null >/dev/null 2>&1
then
sumopt='-r'
else
sumopt=''
fi
echo x - fixactive.c
cat >fixactive.c <<'@EOF'
/** fixactive.c **/
/** Simple program to rebuild the active file to ensure that it
correctly points to the oldest and newest articles available
in each of the groups on this computer.
(C) Copyright 1990, Dave Taylor <taylor at Intuitive.Com>
(C) Copyright 1990, Intuitive Systems
Permission is hereby given for anyone to distribute this
software through any means, without cost to the end consumer,
and such that the above copyright notice remains intact.
**/
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <dirent.h>
#define ACTIVE "/usr/lib/news/active"
#define NEWSHOME "/usr/spool/news"
#define MODERATED 'm'
#define UNMODERATED 'y'
#define MAXGROUPS 1000
#define NLEN 40
#define SLEN 80
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
#define plural(n) ( n == 1 ? "" : "s")
struct grouprec {
char name[NLEN]; /* newsgroup name */
int oldest, /* oldest article */
newest, /* newest article */
moderated; /* moderated? */
} active[MAXGROUPS];
int groups = 0,
moderated = 0,
verbose = FALSE;
char active_file[SLEN], news_home[SLEN];
extern char *optarg;
void exit();
unsigned short getuid();
void perror();
char *strcpy();
main(argc, argv)
int argc;
char **argv;
{
FILE *fd;
char buffer[SLEN], name[SLEN], modflag;
int i, j, oldest, newest, changed = FALSE, dont_fix = FALSE;
int bad_groups = 0, c;
/** first pass initialization.. **/
strcpy(active_file, ACTIVE);
strcpy(news_home, NEWSHOME);
/* set flags, if necessary */
while ((c = getopt(argc, argv, "a:h:nv")) != EOF) {
switch (c) {
case 'a' : strcpy(active_file, optarg); break;
case 'h' : strcpy(news_home, optarg); break;
case 'n' : dont_fix = TRUE; break;
case 'v' : verbose = TRUE; break;
default : usage(); exit(0);
}
}
/* check to see if we're root or not? */
if (getuid() != 0) dont_fix = TRUE;
/**** FIRST let's open the existing active file and build
a list of all the groups available on this computer.
*****/
if ((fd = fopen(active_file, "r")) == NULL) {
perror("fopen: can't open active file");
exit(1);
}
while (fgets(buffer, SLEN, fd) != NULL) {
for (j=0, i=0; buffer[i] != ' '; i++)
name[j++] = buffer[i];
name[j] = '\0';
sscanf( (char *) buffer + i, "%d %d %c",
&newest, &oldest, &modflag);
/* and copy into our data structure */
strcpy(active[groups].name, name);
active[groups].newest = newest;
active[groups].oldest = oldest;
if (modflag == MODERATED) {
moderated++;
active[groups].moderated = TRUE;
}
else
active[groups].moderated = FALSE;
/* and verify... */
if (verbose)
printf("Group '%s', oldest = %d, newest = %d, moderated = %s\n",
active[groups].name, active[groups].oldest,
active[groups].newest,
active[groups].moderated? "YES": "NO");
groups++;
}
(void) fclose(fd);
if (verbose) printf("\n");
printf("Read in %d groups total, of which %d are moderated.\n\n",
groups, moderated);
/** now let's go through each group and recompute the newest
and oldest based on the actual information in the directory
for that group.
**/
for (i = 0; i < groups; i++) {
printf("\r%s ", active[i].name);
fflush(stdout);
if (reset_values_for(i)) {
changed = TRUE;
bad_groups++;
}
}
if (bad_groups) {
printf("\r "); /* clear line */
printf(
"\nFound %d group%s incorrectly marked in the active file\n\n",
bad_groups, plural(bad_groups));
}
else
printf("\r .. no problems found .. \n");
/** And now let's rewrite the active file if there are any changes
that we've encountered... **/
if (changed) {
if (dont_fix) {
printf(
"Note: No changes made since you're not root. Please notify your local news\n");
printf(
"administrator about the discrepancy between the active file and the actual\n");
printf(
"articles stored on the disk.\n\n");
}
else {
rewrite_active_file();
printf("done!\n");
}
}
}
int
reset_values_for(index)
int index;
{
/** Given group[index], open that particular directory and then
scan through, setting lowest and highest as appropriate.
If there are any changes, make them in the data structure
and return TRUE. Otherwise, return FALSE
**/
DIR *dirp;
struct dirent *dp;
int i, j, lowest = 9999999, highest = 0, entries_checked = 0;
char dirname[SLEN], directory_name[SLEN];
/** first step is to get the right directory name... **/
for (j=0, i=0; active[index].name[i] != '\0'; ) {
if (active[index].name[i] == '.') {
dirname[j++] = '/';
i++;
}
else
dirname[j++] = active[index].name[i++];
}
dirname[j] = '\0';
/** now prepend the home directory for netnews... **/
sprintf(directory_name, "%s/%s", news_home, dirname);
/** and let's open this baby up! **/
if ((dirp = opendir(directory_name)) == NULL) {
fprintf(stderr, "Couldn't open directory '%s' for reading?\n",
directory_name);
perror("opendir");
return(FALSE);
}
while ((dp = readdir(dirp)) != NULL) {
if (isdigit(dp->d_name[0])) {
i = atoi(dp->d_name);
if (i < lowest) lowest = i;
if (i > highest) highest = i;
entries_checked++;
}
}
(void) closedir(dirp);
/** and now let's check the information out and proceed accordingly **/
if (entries_checked == 0) /* no articles at all! */
return(FALSE);
if (active[index].oldest == lowest && active[index].newest == highest)
return(FALSE);
if (active[index].oldest != lowest) {
printf("\r(reset oldest article for '%s' from %d to %d)\n",
active[index].name, active[index].oldest,
lowest);
active[index].oldest = lowest;
}
if (active[index].newest != highest) {
printf("\r(reset newest article for '%s' from %d to %d)\n",
active[index].name, active[index].newest,
highest);
active[index].newest = highest;
}
return(TRUE);;
}
rewrite_active_file()
{
/** open the active file for writing, and then replace all the
information therein with the information in our data structure.
Note the funky output format for digits:
groupname 5-digit-integer 5-digit-integer modflag
where numeric values must be padded with leading zeroes,
and "modflag" is "m" if moderated, or "y" otherwise.
**/
FILE *fd;
int i;
if ((fd = fopen(active_file, "w")) == NULL) {
fprintf(stderr, "I couldn't open the active file for writing!\n");
perror("fopen(ACTIVE)");
exit(1);
}
/** now line by line output the information required **/
for (i=0 ; i < groups; i++)
fprintf(fd, "%s %.5d %.5d %c\n",
active[i].name, active[i].newest, active[i].oldest,
active[i].moderated? MODERATED : UNMODERATED);
/** and we're done. Close it and let's boogie! **/
fclose(fd);
}
usage()
{
/** who what when where and why and all that jazz **/
printf("Usage: fixactive [flags]\n\n");
printf("Where [flags] can be any of:\n");
printf(" -a fn \tUse 'fn' as the active file\n");
printf(" \t (default is '%s')\n", ACTIVE);
printf(" -h dir \tUse 'dir' as the netnews spool home\n");
printf(" \t (default is '%s')\n", NEWSHOME);
printf(" -n \tShow what you'd need to do, but don't do it\n");
printf(" -v \tVerbose: lots of output along the way\n\n");
}
@EOF
set `sum $sumopt <fixactive.c`; if test $1 -ne 6044
then
echo ERROR: fixactive.c checksum is $1 should be 6044
fi
set `wc -lwc <fixactive.c`
if test $1$2$3 != 2989977502
then
echo ERROR: wc results of fixactive.c are $* should be 298 997 7502
fi
chmod 644 fixactive.c
echo x - README
cat >README <<'@EOF'
FIXACTIVE -- or more News Bugs From Hell
One of the fun things about netnews software is that it's a very
fragile house of cards that can start acting strangely for any of
a large number of reasons. Well, on "limbo", for some completely
inexplicable reason, the behaviour we're seeing is that the numbers
in the "active" file end up not reflecting the reality of the
articles available on the disk.
For example, we might have articles 364 to 377 on disk, while the
active file would only reflect an available article range of 370-377
or similar.
No idea why, but after running "expire" and having it completely
mangle my active file rather than rebuild it as promised, I wrote
"fixactive", a simple program to compare the information on disk with
the information in the active file, and if different, to rewrite the
active file to be correct. This is the program that is distributed in
this shar file.
Usage is pretty simple:
fixactive
if you're root will scan through things and fix the active file if
there's anything wrong with it. If you're not root, it will simply
scan and report any discrepencies between the disk and active file.
The other options modify this behaviour as follows:
-a fn Use 'fn' as the active file
(default is '/usr/lib/news/active')
-h dir Use 'dir' as the netnews spool home
(default is '/usr/spool/news')
-n Show what you'd need to do, but don't do it
-v Verbose: lots of output along the way
I recommend that you try it out by "fixactive -n".
Remember: it's going to scan through the directory of every available
newsgroup on your computer, so it'll take a few minutes to run through
'em all. Give it time. (on my HP 9000, with 541 available groups, it
takes approximately 2.5 minutes to complete)
If anyone has any ideas why the active file might get corrupted in the
fashion described, I would be most interested in hearing about it. Also,
if anyone makes any interesting or useful additions or modifications to
this program, please do let me (and the net) know.
-- Dave Taylor
Intuitive Systems
201 Ada Avenue, Suite 41
Mountain View, CA 94043
(415) 966-1151
taylor at limbo.intuitive.com or {uunet!}{decwrl,apple}!limbo!taylor
@EOF
set `sum $sumopt <README`; if test $1 -ne 41665
then
echo ERROR: README checksum is $1 should be 41665
fi
set `wc -lwc <README`
if test $1$2$3 != 603702260
then
echo ERROR: wc results of README are $* should be 60 370 2260
fi
chmod 644 README
exit 0
More information about the Alt.sources
mailing list