[net.news.b] News queueing system for 2.10.2.

stephen@comp.lancs.ac.uk (Stephen J. Muir) (04/10/86)

This is a news queueing system for 2.10.2 news.  The problem was, when using
NIFTP (Network-Independent File Transfer Protocol) for transmitting news, it
was found that several instances of  the "rnews" process were running.  This
is wrong and can cause problems as I'm sure you're aware.

These programs circumvent these problems.  The first is a program called
"seconds", used for generating filenames which are guaranteed to be unique.
It prints out the number of seconds since 1st January 1970.  This, together
with the process-id, is used to guarantee filename uniqueness.  The second is
a shell script to move these batches from the FTP spool area into the NEWS
spool area (avoiding copying if possible).  It takes the (FTP) temporary
filename as parameter.  The third is a program for unpacking the batches.
The order in which this is done is guaranteed to be the same as the order
in which the batches are received.

It's easy to arrange that "expire" doesn't run at the same time as "rnews",
although we don't find this necessary, so I haven't written it.  All you do
is change directory to "/usr/spool/news/batched", set a lock and run "expire".
Example entry in cron:
0 5 * * * cd /usr/spool/news/batched; setlock; /usr/lib/news/expire
Where program "setlock" would do:
fd = open (".", 0);
flock (fd, LOCK_EX);
exit (0);
The "expire" program will have this directory open (as it's its current
directory), thus keeping it locked.  When expire terminates, the lock will be
released.
----------------------------------- Cut Here ----------------------------------
#!/bin/sh
echo 'Start of pack.out, part 01 of 01:'
echo 'x - seconds.c'
sed 's/^X//' > seconds.c << '/'
X/* Written by Stephen J. Muir, Computing Dept., Lancaster University */
X
X/*ARGSUSED*/
Xmain (argc, argv, envp)
X	char	*argv [], *envp [];
X	{ printf ("%lu\n", time (0));
X	  exit (0);
X	}
/
echo 'x - qnews.sh'
sed 's/^X//' > qnews.sh << '/'
X#!/bin/sh
X# this shell script runs as super-user
Xspool=/usr/spool/news/batched
Xid=`seconds`.$$
X# two renames -- original file may be on a different file system
Xmv $1 $spool/qnews.$id
Xmv $spool/qnews.$id $spool/rnews.$id
X# start the unbatching daemon (it should fork and exit immediately)
X/usr/lib/news/unqnews
/
echo 'x - unqnews.c'
sed 's/^X//' > unqnews.c << '/'
X/* Written by Stephen J. Muir, Computing Dept., Lancaster University */
X
X# include <sys/types.h>
X# include <sys/errno.h>
X# include <sys/ioctl.h>
X# include <sys/file.h>
X# include <sys/dir.h>
X# ifdef DEBUG
X# include <stdio.h>
X# endif DEBUG
X
X# define NEWSUID 2
X# define NEWSGID 4
X
Xextern	errno;
X
Xchar	*devnull = "/dev/null",
X	*errlog = "/usr/lib/news/errlog",
X	*batchdir = "/usr/spool/news/batched",
X	*rnews = "/usr/lib/news/rnews",
X	batch [82];
X
XDIR	*dirfd;
X
Xerror (message)
X	char	*message;
X	{ static char	buf [134];
X	  strcpy (buf, "unqnews: ");
X	  strcat (buf, message);
X	  perror (buf);
X	}
X
X/* This routine returns non-zero if the current batch was received earlier than
X * the saved batch.
X */
Xearlier (current, saved)
X	char	*current, *saved;
X	{ register unsigned	c, s;
X	  /* find time fields */
X	  while (*current != '\0')
X		if (*current++ == '.')
X			break;
X	  while (*saved != '\0')
X		if (*saved++ == '.')
X			break;
X	  /* if different, decision is clear */
X	  if ((c = atoi (current)) != (s = atoi (saved)))
X		return (c < s);
X	  /* find pid fields */
X	  while (*current != '\0')
X		if (*current++ == '.')
X			break;
X	  while (*saved != '\0')
X		if (*saved++ == '.')
X			break;
X	  s = atoi (saved);
X	  /* check for pid wrap-around */
X	  if ((c = atoi (current)) < 3000 && s > 9000)
X		return (0);
X	  return (c < s);
X	}
X
X/*ARGSUSED*/
Xmain (argc, argv, envp)
X	char	*argv [], *envp [];
X	{ register int		i, locked, pid;
X	  register struct	direct	*dp;
X# ifndef DEBUG
X	  if ((i = open (errlog, O_WRONLY|O_APPEND, 0)) == -1)
X	  { error (errlog);
X	    exit (1);
X	  }
X	  if (i != 2)
X		dup2 (i, 2);
X	  while ((pid = fork ()) == -1)
X		sleep (1);
X	  if (pid)
X		exit (0);
X	  for (i = 0; i < 20; ++i)
X		if (i != 2)
X			close (i);
X	  if (open (devnull, O_WRONLY, 0))
X	  { error (devnull);
X	    exit (1);
X	  }
X	  dup2 (0, 1);
X	  if ((i = open ("/dev/tty", O_RDWR, 0)) != -1)
X	  { ioctl (i, TIOCNOTTY, 0);
X	    close (i);
X	  }
X# endif DEBUG
X	  setgid (NEWSGID);
X	  setuid (NEWSUID);
X	  if (chdir (batchdir) == -1 ||
X	      (i = open (".", O_RDONLY, 0)) == -1 ||
X	      (dirfd = opendir (".")) == NULL
X	     )
X	  { error (batchdir);
X	    exit (1);
X	  }
X	  for (;;)
X	  { locked = 0;
X# ifdef DEBUG
X	    fprintf (stderr, "Started main loop.\n");
X# endif DEBUG
X	    while ((dp = readdir (dirfd)) != NULL)
X		if (strncmp (dp->d_name, "rnews.", 6) == 0)
X		{
X# ifdef DEBUG
X		  fprintf (stderr, "Found \"%s\".\n", dp->d_name);
X# endif DEBUG
X		  if ( ! locked)
X		  { if (flock (i, LOCK_EX|LOCK_NB) == -1)
X			/* another instance is doing the work */
X			exit (0);
X		    ++locked;
X# ifdef DEBUG
X		    fprintf (stderr, "Lock applied.\n");
X# endif DEBUG
X		    strcpy (batch, dp->d_name);
X		  } else if (earlier (dp->d_name, batch))
X			strcpy (batch, dp->d_name);
X# ifdef DEBUG
X		  fprintf (stderr, "Current batch is \"%s\".\n", batch);
X# endif DEBUG
X		}
X	    if ( ! locked)
X		/* no batches left to process */
X		exit (0);
X# ifdef DEBUG
X	    fprintf (stderr, "Unpacking begins.\n");
X# endif DEBUG
X	    while ((pid = fork ()) == -1)
X		sleep (1);
X	    if (pid == 0)
X	    { close (0);
X	      /* "rnews" puts a copy of the errors in the log anyway */
X	      dup2 (1, 2);
X	      if (open (batch, O_RDONLY, 0))
X	      { /* race condition can cause the directory entry to disappear */
X		if (errno != ENOENT)
X			error (batch);
X		exit (1);
X	      }
X	      execl (rnews, "rnews-queued", 0);
X	      error (rnews);
X	      exit (1);
X	    }
X	    while (wait (0) != -1)
X		;
X# ifdef DEBUG
X	    fprintf (stderr, "Unpacking ends.\n");
X# endif DEBUG
X	    if (unlink (batch) == -1)
X	    { error (batch);
X	      exit (1);
X	    }
X	    flock (i, LOCK_UN);
X	    rewinddir (dirfd);
X	  }
X	}
/
echo 'Part 01 of pack.out complete.'
exit
----------------------------------- Cut Here ----------------------------------
-- 
UUCP:	...!seismo!mcvax!ukc!dcl-cs!stephen
DARPA:	stephen%comp.lancs.ac.uk@ucl-cs	| Post: University of Lancaster,
JANET:	stephen@uk.ac.lancs.comp	|	Department of Computing,
Phone:	+44 524 65201 Ext. 4120		|	Bailrigg, Lancaster, UK.
Project:Alvey ECLIPSE Distribution	|	LA1 4YR