[net.news.adm] expire/rnews interlocking - revisited

rad@tekcbi.UUCP (Richard Doty) (04/30/86)

I'm listing Brian Reid's article entitled "great glacier news flood
problem solved" in the references line, since that was the inspiration
for this article.  I implemented the mechanism for interlocking expire
and rnews that Brian described, with good results (briefly,
/usr/bin/rnews is replaced by an imposter shell script that checks for the
presence of a lock file - if the lock file exists, standard input is
copied to a temporary file for later processing; otherwise, standard
input is passed to the "real" rnews.  Commands "newsoff" and "newson"
are used to respectively set the lock, and clear the lock and pass all
the waiting articles to rnews).

Like I said, I used Brian's method, and it worked like a charm, with a
couple niggles.  It doesn't keep locals from posting articles and
changing the active file anyway.  Besides that, it *is* kind of
expensive to have to exec an extra shell for *every* article (not to
mention the test for the lock file, if the shell doesn't have a
built-in test).  Also, there is no check to see if the temporary file
*already* exists.  But the thing that bothered me the most was that I
had an rnews that wasn't really rnews, and the only way I could tell
the two apart was to look at their sizes.  But after all that, when it
takes me 10 hours to run expire I'm *really* happy that my active file
is kosher when I'm finished.  Thanks, Brian, for bringing this up again
so I finally looked closely, and thanks Chuq for the original idea.

What I decided to do was to add this capability to inews.  That way,
rnews is still rnews, I can catch *all* the articles being posted, and
I save 2 execs for each article.  All at the expense of a little
(#ifdeffed) non-standard code in inews.c.  A context diff is appended.
Depending on your news version (mine says "B 2.10.3 4.3bsd-beta
6/6/85"), line numbers and/or exact implementation may be different,
but the idea is simplicity itself.  Right before processing the
article, call interlock().  If it returns, then the lock is not
present.  The fact that interlock() has the side-effect of saving the
article and exitting, is an arguably un-elegant way to do things.  It
does seem to make the addition less complex though, and there are
plenty of precedents in the netnews code.  Let your conscience be your
guide.

Also, it was suggested to me that expire set the lock itself.  I
decided not to do that, because inews doesn't know how to automatically
scoop up the saved articles.  Done this way, if someone doesn't know
about the feature, s/he won't get hurt.

Oh, one last (!) thing.  I made a small addition to Brian's newsoff
program.  If the lock already exists, or for some reason it can't
create the lock file, newsoff exits 1.  Else, exit 0.  That way, if a
lock file gets left from something else, I can tell.  In case that was
too obscure, here's the way I use newsoff:

	if newsoff
		expire
		newson
	else	echo "could not lock for expire" | mail newsa
	fi

Sorry for the length.  I hope you think it was worth it.

Richard.

*** /tmp/,RCSt1019159	Tue Apr 29 17:00:52 1986
--- /tmp/,RCSt2019159	Tue Apr 29 17:00:54 1986
***************
*** 93,98
  		ptr = *argv - 1;
  	if (!strncmp(ptr+1, "rnews", 5)) {
  		mode = PROC;
  #ifdef NICENESS
  		nice(NICENESS);
  #endif /* NICENESS */

--- 93,101 -----
  		ptr = *argv - 1;
  	if (!strncmp(ptr+1, "rnews", 5)) {
  		mode = PROC;
+ #ifdef TEK_INTERLOCK
+ 		interlock(stdin, mode);
+ #endif /* TEK_INTERLOCK */
  #ifdef NICENESS
  		nice(NICENESS);
  #endif /* NICENESS */
***************
*** 402,407
  	if (mode != PROC)
  		input();
  
  	/* Do the actual insertion. */
  	insert();
  }

--- 405,413 -----
  	if (mode != PROC)
  		input();
  
+ #ifdef TEK_INTERLOCK
+ 	interlock(infp, mode);
+ #endif /* TEK_INTERLOCK */
  	/* Do the actual insertion. */
  	insert();
  }
***************
*** 405,410
  	/* Do the actual insertion. */
  	insert();
  }
  
  /*
   *	Create a newsgroup

--- 411,467 -----
  	/* Do the actual insertion. */
  	insert();
  }
+ 
+ #ifdef TEK_INTERLOCK
+ /*
+  * if the file LIBDIR/rnews.lock exists, then copy our input into a
+  * unique SPOOLDIR/rnews.xxxx file, and exit.  The primary intent
+  * of this is to permit expire to run without having articles localized
+  * behind its back, and so we don't have to have two kinds of inews/rnews.
+  * The lock can also be used to keep news processing from happening
+  * (e.g. during prime time) even if news gets delivered to you.
+  *
+  * Notes: a) this even works with arts posted via inews
+  *	  b) you have to retrieve the rnews.xxxx files yourself.
+  *
+  * Usage: create LIBDIR/lock (any incoming news will be dumped into SPOOLDIR)
+  *	  run expire (or whatever else you want to do without news processing)
+  *	  remove LIBDIR/lock
+  *	  for i in SPOOLDIR/rnews.*
+  *	  do	rnews <$i && rm $i
+  *	  done
+  */
+ interlock(infp,mode)
+ 	FILE *infp;
+ 	int mode;
+ {
+ 	char fname[BUFLEN], buf[BUFLEN];
+ 	FILE *outfp;
+ 	struct stat sb;
+ 	extern int sys_nerr;
+ 	extern char *sys_errlist;
+ 
+ 	sprintf(fname,"%s/rnews.lock",LIBDIR);
+ 	if (stat(fname, &sb) == -1) {
+ 		if (errno == ENOENT) {
+ 			return; /* no lock.  let's go */
+ 		} else if (errno <= sys_nerr) {
+ 			sprintf(buf,"interlock stat: %s",sys_errlist[errno]);
+ 			xerror(buf);
+ 		} else {
+ 			sprintf(buf,"interlock stat: unknown error %d",errno);
+ 			xerror(buf);
+ 		}
+ 	}
+ 	sprintf(fname,"%s/rnews.XXXXXX",SPOOLDIR);
+ 	outfp = xfopen(mktemp(fname), "w");
+ 	if (mode != PROC)
+ 		lhwrite(&header, outfp);
+ 	while (fgets(buf, BUFLEN, infp) != NULL)
+ 		fputs(buf, outfp);
+ 	xxit(0);
+ }
+ #endif /* TEK_INTERLOCK */
  
  /*
   *	Create a newsgroup