home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-09-11 | 35.5 KB | 1,254 lines |
- Subject: v08i049: Simultaneous multi-site news feeder in C++
- Newsgroups: mod.sources
- Approved: mirror!rs
-
- Submitted by: "Stephen J. Muir" <stephen%computing.lancaster.ac.uk@Cs.Ucl.AC.UK>
- Mod.sources: Volume 8, Issue 49
- Archive-name: multi-feed.c++
-
- [ I do not have C++. --r$ ]
-
- #!/bin/sh
- echo 'Start of pack.out, part 01 of 01:'
- echo 'x - README'
- sed 's/^X//' > README << '/'
- XSimultaneous Multi-Site News Feeder (version 1.0)
- X--------------------------------------------------
- X
- XThis software may be freely copied provided that no charge is made and you
- Xdon't pretend you wrote it.
- X
- XThis is the simultaneous multi-site news feeder. It was written to solve the
- Xfollowing problems with existing news batchers:
- X
- X o When feeding several sites, each news article was the subject of
- X several batch runs, thus consuming a lot of processor power.
- X
- X o When feeding several sites, disk space would be gobbled up very
- X quickly.
- X
- X o If, for some reason, two batchers for the same site tried to run
- X simultaneously, chaos would ensue.
- X
- X o If the disk filled during batching, the batcher wouldn't notice and
- X carry on regardless.
- X
- X o If news was being unbatched during news batching, you would end up
- X with lots of tiny batches.
- X
- X o The log file(s) had to be trimmed from time to time.
- X
- X o The "maximum batch size" was, in fact, a minimum batch size!
- X
- XNow for the bad news! It is not very portable (yet!). These are the minimum
- Xrequirements:
- X
- X o 4.2 BSD UNIX
- X
- X o C++ version 1.1
- X
- X o 2.10.3 news (if you want the multi-site facility)
- X
- XIf you have a different environment, then I'm afraid you'll have to hack it.
- X
- XInstallation:
- X
- X 1) Edit the following files to your requirements:
- X Makefile
- X config.h
- X newsfeedlist.sh
- X
- X 2) There are several bugs in the include files in C++ version 1.1.
- X All but one of these will show up as compilation errors. The
- X remaining one, which will cause a memory fault if not fixed, is
- X that the "stat" structure is different from that used by 4.2 BSD
- X UNIX. If you have done everything properly, there should be no
- X errors or warnings during compilation. You should be able to
- X achieve this by editing the C++ include files alone.
- X
- X 3a) Edit your "sys" file, in order to make full use of the multi-site
- X capabilities of this news batcher, to use the MULTICAST facility.
- X This also has to be defined in inews. (It will still accept old
- X style batches.) Here is a rather contrived example:
- X
- X site1:M:world,comp,sci,rec,news:uucp-feed
- X site2:M:world,comp,sci,rec,news:uucp-feed
- X site3:M:world,comp,news:uucp-feed
- X uucp-feed:OF:all:
- X
- X All these sites will now be fed by "newsfeed uucp-feed"
- X (but see below).
- X
- X 3b) You now need a command file which will take a news batch and queue
- X it for several sites simultaneously (if you use the above feature).
- X To save disk space problems when batching, it should ideally make
- X one copy and use UNIX links for the remaining sites. Here is an
- X example, for an 8-bit compressed multi-feed, to put in file
- X "uucp-feed.cmd":
- X
- X #!/bin/sh
- X (echo "#! cunbatch"; /usr/lib/news/compress -q) | \
- X /usr/lib/news/uucp-multifeed $*
- X
- X You have to write "uucp-multifeed" yourself! It takes the sites to
- X queue for as arguments. (I only use UUCP for one of the sites I
- X feed, so I haven't bothered to write it.) If the command file
- X exits abnormally, batching will be abandoned and the various files
- X will be reset to their previous state. Thus, for example, if you
- X wish to stop batching when the disk is too full, you could add the
- X following to "uucp-feed.cmd" (after the first line):
- X
- X if /usr/lib/news/disktoofull
- X then
- X echo $0: DISK TOO FULL >> /usr/lib/news/errlog
- X exit 1
- X fi
- X
- X where "disktoofull" returns normally if your disk is too full.
- X
- XSend any bugs/improvements to me:
- X
- XName: Stephen J. Muir | Post: University of Lancaster,
- XEMAIL: stephen@comp.lancs.ac.uk | Department of Computing,
- XUUCP: ...!mcvax!ukc!dcl-cs!stephen | Bailrigg, Lancaster, UK.
- XPhone: +44 524 65201 Ext. 4120 | LA1 4YR
- /
- echo 'x - Makefile'
- sed 's/^X//' > Makefile << '/'
- XCC=CC
- XCFLAGS=-O
- XCONFIGOBJS=main.o log.o misc.o
- XOBJS=${CONFIGOBJS} feed.o batch.o io.o
- XINSTALLDIR=/usr/lib/news/
- X
- Xnewsfeed: ${OBJS}
- X ${CC} -o $@ ${OBJS}
- X
- X${OBJS}: defs.h
- X
- X${CONFIGOBJS}: config.h
- X
- Xinstall: newsfeed newsfeedlist.sh newsfeed.man newsfeedlist.man
- X install -o news -g news -s newsfeed ${INSTALLDIR}
- X cp newsfeedlist.sh ${INSTALLDIR}newsfeedlist
- X chmod 755 ${INSTALLDIR}newsfeedlist
- X chown news ${INSTALLDIR}newsfeedlist
- X chgrp news ${INSTALLDIR}newsfeedlist
- X cp newsfeed.man /usr/man/mann/newsfeed.n
- X cp newsfeedlist.man /usr/man/mann/newsfeedlist.n
- X
- Xclean:
- X rm -f newsfeed *.o *..o *..c core
- /
- echo 'x - config.h'
- sed 's/^X//' > config.h << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X// Any of the lines below may be commented out
- X
- X//# define DEBUG
- X
- X// News uid and gid.
- X# define NEWSUID 2
- X# define NEWSGID 4
- X
- X// Priority to run at.
- X//# define NICENESS 1
- X
- X// Working directory.
- X# define BATCH_OUT_DIR "/usr/spool/news/batchout/"
- X
- X// Default maximum size of batch.
- X# define DEFAULT_MAX_SIZE 200000
- X
- X// Stamp log even if there is nothing to batch.
- X//# define ALWAYS_STAMP_LOG
- X
- X// Number of seconds after which to delete log entries.
- X# define LOG_EXPIRE_SECONDS (14*24*60*60)
- X
- X// Error log.
- X# define ERROR_LOG "/usr/lib/news/errlog"
- /
- echo 'x - defs.h'
- sed 's/^X//' > defs.h << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X# include <sys/errno.h>
- X# include <sys/types.h>
- X# include <sys/file.h>
- X# include <sys/stat.h>
- X# include <sys/time.h>
- X# include <sys/resource.h>
- X# include <signal.h>
- X# include <stream.h>
- X# include <string.h>
- X# include <osfcn.h>
- X
- X# define BUFFER_SIZE 4096
- X# define WARNING(x) error(x,0,0)
- X# define SYS_WARNING(x) error(x,1,0)
- X# define FATAL(x) error(x,0,1)
- X# define SYS_FATAL(x) error(x,1,1)
- X
- Xstruct sitelist
- X{ const char *site_name;
- X short site_seen;
- X sitelist *site_next;
- X};
- X
- X// to free memory automagically
- Xclass auto_delete
- X{ void *stored_ptr;
- Xpublic:
- X auto_delete (void *ptr) { stored_ptr = ptr; }
- X ~auto_delete () { delete stored_ptr; }
- X};
- X
- X// to close files automagically
- Xclass auto_close
- X{ int stored_fd;
- Xpublic:
- X auto_close (int fd) { stored_fd = fd; }
- X ~auto_close () { int e = errno; close (stored_fd); errno = e; }
- X};
- X
- X// feed.o
- Xextern void feed_news (const char *, const char **, off_t);
- X// log.o
- Xextern void open_log (const char *);
- Xextern void add_to_log (const char *, const sitelist *);
- Xextern void close_log ();
- Xextern void sync_log ();
- Xextern void reset_log ();
- X// batch.o
- Xextern int open_batch (const char **, off_t);
- Xextern int send_article (const char *);
- Xextern int close_batch ();
- Xextern void kill_batch ();
- X// io.o
- Xextern void fatal_input_stream_error (const char *, int);
- Xextern void fatal_output_stream_error (const char *);
- Xextern int getline (const istream&, char *, int);
- Xextern int append_file (const char *, const char *);
- X// misc.o
- Xextern const char *rename_error;
- Xextern char *new_string (const char *);
- Xextern char *join_string (const char *, const char *);
- Xextern void error (const char *, int, int);
- X// libC.a
- Xextern void (*_new_handler) ();
- /
- echo 'x - batch.c'
- sed 's/^X//' > batch.c << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X# include "defs.h"
- X
- Xstatic short batch_error;
- Xstatic int pid, output_channel = -1, (*old_pipe_signal) ();
- Xstatic off_t current_size, max_size;
- Xstatic struct stat statbuf;
- X
- X// This routine sets up a pipe to the enqueueing command.
- X// Parameters:
- X// command -> command to queue the batch for transfer
- X// new_max_size -> maximum batch size
- X// Return value:
- X// -2: couldn't create pipe
- X// -1: invalid invocation (pipe already open)
- X// 0: ok (as far as the parent process can tell)
- Xopen_batch (const char **command, off_t new_max_size)
- X{ if (output_channel != -1)
- X return -1;
- X max_size = new_max_size;
- X int pipes [2];
- X if (pipe (pipes) == -1)
- X return -2;
- X // process table could be full, in which case we may be in trouble anyway,
- X // but at least it's not our fault for not reporting it
- X while ((pid = fork ()) == -1)
- X sleep (1);
- X if (pid == 0)
- X { // child process ...
- X close (pipes [1]);
- X if (dup2 (pipes [0], 0) == -1)
- X SYS_FATAL ("dup2()");
- X close (pipes [0]);
- X execv (*command, command);
- X // I hope this works!
- X SYS_FATAL (*command);
- X }
- X close (pipes [0]);
- X old_pipe_signal = signal (SIGPIPE, SIG_IGN);
- X output_channel = pipes [1];
- X batch_error = 0;
- X current_size = 0;
- X return 0;
- X}
- X
- X// This routine closes the pipe and (hopefully) reports whether or not it
- X// worked.
- X// Return value:
- X// -2: something went wrong!
- X// -1: invalid invocation (pipe not open)
- X// 0: ok (as far as we can tell)
- Xclose_batch ()
- X{ if (output_channel == -1)
- X return -1;
- X close (output_channel);
- X output_channel = -1;
- X signal (SIGPIPE, old_pipe_signal);
- X int status, wait_pid;
- X while ((wait_pid = wait (&status)) != pid && wait_pid != -1)
- X ;
- X return (wait_pid == -1 || status) ? -2 : 0;
- X}
- X
- X// This routine adds a file to the batch. If this would cause batch overflow to
- X// occur, the article is not added, assuming the caller will try again.
- X// Parameters:
- X// pathname -> article to be included
- X// Return value:
- X// -3: error (batch write error)
- X// -2: error (article read error)
- X// -1: invalid invocation (pipe not open or current batch in error)
- X// 0: ok (article added to batch)
- X// 1: ok (article didn't exist -- maybe it was cancelled/expired)
- X// 2: ok (article would overflow batch -- article not included)
- Xsend_article (const char *pathname)
- X{ if (output_channel == -1 || batch_error)
- X return -1;
- X register int article_fd = open (pathname, O_RDONLY, 0);
- X if (article_fd == -1)
- X return 1;
- X auto_close auto_article (article_fd);
- X if (fstat (article_fd, &statbuf) == -1)
- X return 1;
- X char *rnews_header = form ("#! rnews %d\n", statbuf.st_size);
- X int rnews_header_size = strlen (rnews_header);
- X if (current_size > 0 &&
- X max_size > 0 &&
- X (current_size + rnews_header_size + statbuf.st_size) > max_size
- X )
- X return 2;
- X char buffer [BUFFER_SIZE];
- X register int byte_count;
- X current_size += rnews_header_size + statbuf.st_size;
- X if (write (output_channel, rnews_header, rnews_header_size) !=
- X rnews_header_size
- X )
- X { batch_error = 1;
- X return -3;
- X }
- X while ((byte_count = read (article_fd, buffer, BUFFER_SIZE)) > 0)
- X if (write (output_channel, buffer, byte_count) != byte_count)
- X { batch_error = 1;
- X return -3;
- X }
- X if (byte_count)
- X { batch_error = 1;
- X return -2;
- X }
- X return 0;
- X}
- X
- X// This routine tries to kill the current batch.
- Xvoid kill_batch ()
- X{ if (output_channel != -1)
- X kill (pid, SIGTERM);
- X}
- /
- echo 'x - feed.c'
- sed 's/^X//' > feed.c << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X# include "defs.h"
- X
- Xstatic char *base_again, *base_hold, *base_cmd, *default_site;
- Xstatic short first_article;
- Xstatic int fd_again = -1, fd_hold = -1;
- Xstatic off_t max_batch_size;
- Xstatic sitelist *hold_list;
- Xstatic ostream *stream_again, *stream_hold;
- X
- X// This routine writes the given line to the "again" file for reprocessing.
- X// Parameters:
- X// article_line -> line to write
- X// Return value:
- X// exits on error
- Xstatic void write_again (const char *article_line)
- X{ if (fd_again == -1)
- X { if ((fd_again = open (base_again,
- X O_CREAT | O_TRUNC | O_WRONLY | O_APPEND,
- X 0666
- X )
- X ) == -1
- X )
- X SYS_FATAL (base_again);
- X fcntl (fd_again, F_SETFD, 1);
- X stream_again = new ostream (fd_again);
- X }
- X *stream_again << article_line << "\n";
- X}
- X
- X// This routine writes the given article to the "hold" file.
- X// Parameters:
- X// article -> article to write
- X// Return value:
- X// exits on error
- Xstatic void write_hold (const char *article)
- X{ if (fd_hold == -1)
- X { if ((fd_hold = open (base_hold, O_CREAT | O_WRONLY | O_APPEND, 0666))
- X == -1
- X )
- X SYS_FATAL (base_hold);
- X fcntl (fd_hold, F_SETFD, 1);
- X stream_hold = new ostream (fd_hold);
- X }
- X *stream_hold << article;
- X for (sitelist *site_ptr = hold_list;
- X site_ptr;
- X site_ptr = site_ptr->site_next
- X )
- X if (site_ptr->site_seen)
- X *stream_hold << " " << site_ptr->site_name;
- X *stream_hold << "\n";
- X}
- X
- X// This routine tries to write the given article to the current batch, starting
- X// one if none is current. If the article doesn't belong to the current batch,
- X// it will save it for further processing. Finally, it may add the article to
- X// the "hold" file.
- X// Parameters:
- X// article_line -> a line from the batch file
- X// Return value:
- X// exits on error
- Xstatic void feed_article (const char *article_line)
- X{ static short batch_finished;
- X static sitelist *feed_list;
- X register char *c_ptr;
- X register int ret, holding = 0;
- X register sitelist *site_ptr, **site_ptr_ptr;
- X
- X // wait until the whole input is reprocessed
- X if (first_article)
- X batch_finished = 0;
- X if (batch_finished)
- X { write_again (article_line);
- X return;
- X }
- X
- X // reset the "hold" list
- X for (site_ptr = hold_list; site_ptr; site_ptr = site_ptr->site_next)
- X site_ptr->site_seen = 0;
- X
- X register char *new_article_line = new_string (article_line);
- X auto_delete auto_new_article_line (new_article_line);
- X
- X // split line into (article, sitelist)
- X for (c_ptr = new_article_line; *c_ptr != ' ' && *c_ptr != '\0'; ++c_ptr)
- X ;
- X while (*c_ptr == ' ')
- X *c_ptr++ = '\0';
- X if (*c_ptr == '\0')
- X c_ptr = default_site;
- X
- X // if first article, make sure we can read it and set up the "feed" list
- X if (first_article)
- X { int site_count = 0;
- X // delete any previous "feed" list
- X for (site_ptr = feed_list; site_ptr; )
- X { sitelist *delete_ptr = site_ptr;
- X site_ptr = site_ptr->site_next;
- X delete delete_ptr->site_name;
- X delete delete_ptr;
- X }
- X feed_list = 0;
- X
- X // the first article *must* exist
- X if ((ret = access (new_article_line, R_OK)) == -1)
- X { if (errno == ENOENT)
- X return;
- X SYS_WARNING (new_article_line);
- X }
- X
- X // now set up the "feed" list
- X for (site_ptr_ptr = &feed_list; *c_ptr; )
- X { register char *new_c_ptr = c_ptr;
- X
- X while (*new_c_ptr != ' ' && *new_c_ptr != '\0')
- X ++new_c_ptr;
- X while (*new_c_ptr == ' ')
- X *new_c_ptr++ = '\0';
- X
- X // make sure it's not in the hold list
- X for (site_ptr = hold_list; site_ptr; site_ptr = site_ptr->site_next)
- X if ( ! strcmp (c_ptr, site_ptr->site_name))
- X { site_ptr->site_seen = holding = 1;
- X break;
- X }
- X
- X // if it's not in the hold list, add it to the feed list
- X if (site_ptr == 0)
- X { sitelist *new_entry = new sitelist;
- X new_entry->site_name = new_string (c_ptr);
- X //new_entry->site_seen = 1;
- X *site_ptr_ptr = new_entry;
- X site_ptr_ptr = &new_entry->site_next;
- X ++site_count;
- X }
- X
- X c_ptr = new_c_ptr;
- X }
- X *site_ptr_ptr = 0;
- X
- X // write the article to the hold list (if necessary)
- X if (holding)
- X write_hold (new_article_line);
- X
- X // we *must* have some sites to feed
- X if ( ! site_count)
- X return;
- X
- X // start a batch
- X const char **command_args = new char *[site_count + 2],
- X **args_ptr_ptr = command_args;
- X *args_ptr_ptr++ = base_cmd;
- X for (site_ptr = feed_list; site_ptr; site_ptr = site_ptr->site_next)
- X *args_ptr_ptr++ = site_ptr->site_name;
- X *args_ptr_ptr = 0;
- X if ((ret = open_batch (command_args, max_batch_size)) < 0)
- X FATAL (form ("open_batch() returns %d", ret));
- X delete command_args;
- X first_article = 0;
- X } else // ! first_article
- X { // reset feed list
- X for (site_ptr = feed_list; site_ptr; site_ptr = site_ptr->site_next)
- X site_ptr->site_seen = 0;
- X
- X // check each site
- X while (*c_ptr)
- X { register char *new_c_ptr = c_ptr;
- X while (*new_c_ptr != ' ' && *new_c_ptr != '\0')
- X ++new_c_ptr;
- X while (*new_c_ptr == ' ')
- X *new_c_ptr++ = '\0';
- X
- X // check hold list
- X for (site_ptr = hold_list; site_ptr; site_ptr = site_ptr->site_next)
- X if ( ! strcmp (c_ptr, site_ptr->site_name))
- X { site_ptr->site_seen = holding = 1;
- X break;
- X }
- X
- X // if not in hold list, try the feed list
- X if (site_ptr == 0)
- X for (site_ptr = feed_list;
- X site_ptr;
- X site_ptr = site_ptr->site_next
- X )
- X if ( ! strcmp (c_ptr, site_ptr->site_name))
- X { site_ptr->site_seen = 1;
- X break;
- X }
- X
- X // if not in either list, just push it out for later
- X if (site_ptr == 0)
- X { write_again (article_line);
- X return;
- X }
- X
- X c_ptr = new_c_ptr;
- X }
- X
- X // if feed list is not complete, just push it out for later
- X for (site_ptr = feed_list; site_ptr; site_ptr = site_ptr->site_next)
- X if ( ! site_ptr->site_seen)
- X { write_again (article_line);
- X return;
- X }
- X
- X // write the article to the hold list (if necessary)
- X if (holding)
- X write_hold (new_article_line);
- X }
- X
- X // try to add the article to the current batch
- X if ((ret = send_article (new_article_line)) < 0)
- X FATAL (form ("send_article() returns %d", ret));
- X
- X if (ret == 0)
- X add_to_log (new_article_line, feed_list);
- X else if (ret == 2) // batch would've overflowed
- X { ++batch_finished;
- X if (holding)
- X { for (site_ptr = feed_list;
- X site_ptr;
- X site_ptr = site_ptr -> site_next
- X )
- X // yuck ...
- X strcat (new_article_line, form (" %s", site_ptr->site_name));
- X write_again (new_article_line);
- X } else
- X write_again (article_line);
- X }
- X}
- X
- X// This routine does everything necessary to feed news batches from a file.
- X// The format of each line of the input file looks like:
- X// article_file
- X// or:
- X// article_file site1 site2 ... siteN
- X// In the first case, the site to be fed that article is assumed to be that
- X// given by the name of the base_file (old batch format).
- X// Parameters:
- X// base_file -> file containing articles to be batched
- X// hold_array -> list of sites NOT to be fed
- X// max_batch_size -> maximum size of batch (0 => unlimited)
- X// Return value:
- X// exits on error
- Xvoid feed_news (const char *base_file,
- X const char **hold_array,
- X off_t max_batch_size
- X )
- X{ char *base_input, *base_proc, *cp = rindex (base_file, '/'),
- X buffer [BUFFER_SIZE];
- X int proc_fd;
- X
- X // make this available to the routine that needs to know
- X ::max_batch_size = max_batch_size;
- X
- X // in case it's not a MULTIHOST batch ...
- X default_site = cp ? cp + 1 : base_file;
- X
- X // make our filenames
- X base_input = join_string (base_file, ".input");
- X base_proc = join_string (base_file, ".proc");
- X base_again = join_string (base_file, ".again");
- X base_hold = join_string (base_file, ".hold");
- X base_cmd = join_string (base_file, ".cmd");
- X
- X // open the log file
- X open_log (base_file);
- X
- X // error recovery from previous run ...
- X int ret;
- X if ((ret = append_file (base_input, base_proc)) < 0)
- X SYS_FATAL (ret != -2 ? base_input : base_proc);
- X
- X // prepare "hold" list
- X sitelist **site_ptr_ptr = &hold_list;
- X for (const char **cpp = hold_array; *cpp; ++cpp)
- X { sitelist *new_entry = new sitelist;
- X new_entry->site_name = *cpp;
- X *site_ptr_ptr = new_entry;
- X site_ptr_ptr = &new_entry->site_next;
- X }
- X *site_ptr_ptr = 0;
- X
- X // OK, time's up! Any more articles arriving will have to wait until the
- X // next time ...
- X if (rename (base_file, base_input) == -1 && errno != ENOENT)
- X SYS_FATAL (form (rename_error, base_file, base_input));
- X if ((ret = append_file (base_input, base_proc)) < 0)
- X SYS_FATAL (ret != -2 ? base_input : base_proc);
- X
- X // Main processing loop.
- X while ((proc_fd = open (base_proc, O_RDONLY, 0)) != -1)
- X { auto_close auto_proc_fd (proc_fd);
- X fcntl (proc_fd, F_SETFD, 1);
- X istream stream_proc (proc_fd, 0);
- X first_article = 1;
- X
- X // give each line of the file to the article feeder
- X while ((ret = getline (stream_proc, buffer, BUFFER_SIZE)) == 0)
- X feed_article (buffer);
- X if (ret != 1)
- X fatal_input_stream_error (base_proc, ret);
- X
- X // end of file ... finish current batch (if any)
- X if ( ! first_article && (ret = close_batch ()))
- X FATAL (form ("close_batch() returns %d", ret));
- X sync_log ();
- X
- X // tidy up the "again" file
- X if (fd_again != -1)
- X { (*stream_again).flush ();
- X if ( ! (*stream_again).good ())
- X fatal_output_stream_error (base_again);
- X delete stream_again;
- X close (fd_again);
- X fd_again = -1;
- X }
- X
- X // rename the "again" file to the "proc" file
- X if (rename (base_again, base_proc) == -1)
- X { if (errno == ENOENT)
- X { if (unlink (base_proc) != -1)
- X continue;
- X SYS_FATAL (base_proc);
- X }
- X else
- X SYS_FATAL (form (rename_error, base_again, base_proc));
- X }
- X }
- X
- X if (errno != ENOENT)
- X SYS_FATAL (base_proc);
- X
- X // tidy up the "hold" file
- X if (fd_hold != -1)
- X { (*stream_hold).flush ();
- X if ( ! (*stream_hold).good ())
- X fatal_output_stream_error (base_hold);
- X delete stream_hold;
- X }
- X
- X // close the log file
- X close_log ();
- X}
- /
- echo 'x - io.c'
- sed 's/^X//' > io.c << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X# include "defs.h"
- X
- X// This routine prints an error message and exits.
- X// Parameters:
- X// filename -> file on which the error occurred
- X// code -> cause of error
- Xvoid fatal_input_stream_error (const char *filename, int code)
- X{ register char *cp;
- X switch (code)
- X { case -3:
- X cp = "stream error";
- X break;
- X case -2:
- X cp = "incomplete last line";
- X break;
- X case -1:
- X cp = "line too long";
- X break;
- X case 1:
- X cp = "unexpected end of file";
- X break;
- X default:
- X cp = form ("something(code %d) is badly wrong", code);
- X break;
- X }
- X FATAL (form ("%s: %s", filename, cp));
- X}
- X
- X// This routine prints an error message and exits.
- X// Parameters:
- X// filename -> file on which the error occurred
- Xvoid fatal_output_stream_error (const char *filename)
- X{ FATAL (form ("%s: output stream error", filename)); }
- X
- X// This routine attempts to read the next line from the given input stream.
- X// Parameters:
- X// infile -> input stream
- X// buffer -> where to put the line
- X// buffer_size -> size of the above
- X// Return value:
- X// -3: stream failure (irrecoverable)
- X// -2: last line not terminated by a newline
- X// -1: line too long (irrecoverable)
- X// 0: ok
- X// 1: end of file
- Xgetline (const istream& infile, char *buffer, int buffer_size)
- X{ register char c = '\0';
- X if (infile.get (buffer, buffer_size))
- X { if (infile.get (c))
- X return (c == '\n') ? 0 : -1;
- X return infile.eof () ? -2 : -3;
- X }
- X return infile.eof () ? 1 : -3;
- X}
- X
- X// This routine attempts to copy the first file (if it exists) to the end of
- X// the second (which is created if it doesn't exist). Then, the first file is
- X// removed.
- X// Parameters:
- X// from -> name of input file
- X// to -> name of output file
- X// Return Value:
- X// -3: error in unlinking input file
- X// -2: error writing output file
- X// -1: error reading input file
- X// 0: ok
- X// 1: no input file
- X// 2: output file was created
- Xappend_file (const char *from, const char *to)
- X{ register int ret = 0, count, from_fd, to_fd;
- X if ((from_fd = open (from, O_RDONLY, 0)) == -1)
- X return (errno == ENOENT) ? 1 : -1;
- X auto_close auto_from_fd (from_fd);
- X if (access (to, F_OK) == -1)
- X ret = 2;
- X if ((to_fd = open (to, O_CREAT | O_WRONLY | O_APPEND, 0666)) == -1)
- X return -2;
- X auto_close auto_to_fd (to_fd);
- X char buffer [BUFFER_SIZE];
- X while ((count = read (from_fd, buffer, BUFFER_SIZE)) > 0)
- X if (write (to_fd, buffer, count) != count)
- X return -2;
- X if (count < 0 || close (from_fd) == -1)
- X return -1;
- X if (close (to_fd) == -1)
- X return -2;
- X if (unlink (from) == -1)
- X return -3;
- X return ret;
- X}
- /
- echo 'x - log.c'
- sed 's/^X//' > log.c << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X# include "defs.h"
- X# include "config.h"
- X
- Xstatic char *base_log;
- Xstatic int log_fd, log_size = -1;
- Xstatic ostream *stream_log;
- X
- X// This routine puts a date stamp on the log file, if this hasn't been done
- X// yet. It also creates the output stream for it (if necessary).
- Xstatic void start_log ()
- X{ static short log_started;
- X if (log_started)
- X return;
- X ++log_started;
- X if ( ! stream_log)
- X stream_log = new ostream (log_fd);
- X struct timeval tv;
- X struct timezone tz;
- X gettimeofday (&tv, &tz);
- X struct tm *tp = localtime (&tv.tv_sec);
- X char *ap = asctime (tp),
- X *tzn = timezone (tz.tz_minuteswest, tp->tm_isdst);
- X *stream_log << form (":%lu:%.20s%s%s",
- X tv.tv_sec,
- X ap,
- X tzn ? tzn : "",
- X ap + 19
- X );
- X}
- X
- X// This routine removes those articles expected to be expired from the log file
- X// and returns with the log file opened (for appending) and locked.
- X// Parameters:
- X// base_file -> file containing articles to be batched
- X// Return value:
- X// exits on error
- Xvoid open_log (const char *base_file)
- X{ if ((log_fd = open (base_log = join_string (base_file, ".log"),
- X O_CREAT | O_RDWR | O_APPEND,
- X 0666
- X )
- X ) == -1
- X )
- X SYS_FATAL (base_log);
- X fcntl (log_fd, F_SETFD, 1);
- X if (flock (log_fd, LOCK_EX | LOCK_NB) == -1)
- X FATAL (form ("previous batching for \"%s\" is still running", base_file)
- X );
- X# ifdef LOG_EXPIRE_SECONDS
- X istream stream_old_log (log_fd);
- X char buffer [BUFFER_SIZE];
- X register int ret;
- X if (ret = getline (stream_old_log, buffer, BUFFER_SIZE))
- X { if (ret == 1) // empty log file
- X return;
- X fatal_input_stream_error (base_log, ret);
- X }
- X if (buffer [0] != ':')
- X { WARNING ("bad log format -- not truncated");
- X return;
- X }
- X long expire_time = time (0) - LOG_EXPIRE_SECONDS;
- X if ((atol (&buffer [1]) - expire_time) > 0)
- X return;
- X
- X // we don't want the old log anymore
- X auto_close auto_log_fd (log_fd);
- X char *base_newlog = join_string (base_file, ".newlog");
- X if ((log_fd = open (base_newlog,
- X O_CREAT | O_TRUNC | O_WRONLY | O_APPEND,
- X 0666
- X )
- X ) == -1
- X )
- X SYS_FATAL (base_newlog);
- X fcntl (log_fd, F_SETFD, 1);
- X if (flock (log_fd, LOCK_EX | LOCK_NB) == -1)
- X SYS_FATAL (base_newlog);
- X stream_log = new ostream (log_fd);
- X short transferring = 0;
- X while ((ret = getline (stream_old_log, buffer, BUFFER_SIZE)) == 0)
- X { if ( ! transferring &&
- X buffer [0] == ':' &&
- X (atol (&buffer [1]) - expire_time) > 0
- X )
- X ++transferring;
- X if (transferring)
- X *stream_log << buffer << "\n";
- X }
- X if (ret != 1)
- X fatal_input_stream_error (base_log, ret);
- X
- X (*stream_log).flush ();
- X if ( ! (*stream_log).good ())
- X fatal_output_stream_error (base_newlog);
- X if (rename (base_newlog, base_log) == -1)
- X SYS_FATAL (form (rename_error, base_newlog, base_log));
- X# endif LOG_EXPIRE_SECONDS
- X}
- X
- X// This routine adds an article, together with the list of sites it was sent
- X// to, to the log file.
- Xvoid add_to_log (const char *article_line, const sitelist *fed_sites)
- X{ register const sitelist *site_ptr;
- X start_log ();
- X *stream_log << article_line;
- X for (site_ptr = fed_sites; site_ptr; site_ptr = site_ptr->site_next)
- X *stream_log << " " << site_ptr->site_name;
- X *stream_log << "\n";
- X}
- X
- X// This routine closes the log file.
- Xvoid close_log ()
- X{
- X# ifdef ALWAYS_STAMP_LOG
- X start_log ();
- X# endif ALWAYS_STAMP_LOG
- X if (stream_log)
- X { (*stream_log).flush ();
- X if ( ! (*stream_log).good ())
- X fatal_output_stream_error (base_log);
- X delete stream_log;
- X }
- X}
- X
- X// This routine attempts to flush all data to the log file and remember its size
- X// so that the next routine can truncate it if necessary.
- Xvoid sync_log ()
- X{ if ( ! stream_log)
- X FATAL ("sync_log(): log not open");
- X (*stream_log).flush ();
- X if ( ! (*stream_log).good ())
- X fatal_output_stream_error (base_log);
- X if ((log_size = lseek (log_fd, 0, L_INCR)) == -1)
- X SYS_WARNING ("sync_log()");
- X}
- X
- X// This routine attempts to truncate the log to the size determined by the
- X// previous routine.
- Xvoid reset_log ()
- X{ if (stream_log && log_size != -1)
- X if (ftruncate (log_fd, log_size) == -1)
- X SYS_WARNING ("reset_log()");
- X}
- /
- echo 'x - main.c'
- sed 's/^X//' > main.c << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X# include "defs.h"
- X# include "config.h"
- X
- Xstatic void new_handler ()
- X{ FATAL ("out of memory");
- X}
- X
- X// This program does a batched feed of several sites simultaneously. After
- X// changing directory to BATCH_OUT_DIR (in feed_news()), the sites in base_file
- X// are fed. If any (optional) hold sites are given, they are not fed but are
- X// saved for a future run. This could be done if, say, those sites are down
- X// for an extended period or perhaps ...
- Xmain (int argc, char **argv)
- X{ register int fd;
- X register off_t max_size = (off_t)DEFAULT_MAX_SIZE;
- X _new_handler = &new_handler;
- X
- X // just in case we've been given a duff environment
- X while ((fd = open ("/dev/null", O_RDWR, 0)) != -1 && fd < 3)
- X ;
- X if (fd != -1)
- X close (fd);
- X if (argc < 2 || (**++argv == '-' && argc < 3))
- X { cerr << "usage: newsfeed [-max_size] base_file [list_of_hold_sites]\n";
- X exit (1);
- X }
- X# ifndef DEBUG
- X# ifdef NICENESS
- X if (setpriority (PRIO_PROCESS, getpid (), NICENESS) == -1)
- X SYS_WARNING ("setpriority()");
- X# endif NICENESS
- X# ifdef NEWSGID
- X if (setgid (NEWSGID) == -1)
- X SYS_WARNING ("setgid()");
- X# endif NEWSGID
- X# ifdef NEWSUID
- X if (setuid (NEWSUID) == -1)
- X SYS_WARNING ("setuid()");
- X# endif NEWSUID
- X umask (022); // We are (usually) not invoked by a user.
- X# ifdef BATCH_OUT_DIR
- X char *batch_out_dir = BATCH_OUT_DIR; // saving space !!!
- X // go to where it's at!
- X if (chdir (batch_out_dir) == -1)
- X SYS_FATAL (batch_out_dir);
- X# endif BATCH_OUT_DIR
- X# endif DEBUG
- X
- X if (**argv == '-')
- X max_size = (off_t)atol (*argv++ + 1);
- X feed_news (*argv, argv + 1, max_size);
- X exit (0);
- X}
- /
- echo 'x - misc.c'
- sed 's/^X//' > misc.c << '/'
- X/* Written by Stephen J. Muir, Lancaster University, England, UK.
- X *
- X * EMAIL: stephen@comp.lancs.ac.uk
- X * UUCP: ...!mcvax!ukc!dcl-cs!stephen
- X *
- X * Version 1.0
- X */
- X
- X# include "defs.h"
- X# include "config.h"
- X
- X# if !defined(DEBUG) && defined(ERROR_LOG)
- Xstatic int error_fd;
- X# endif
- Xstatic ostream *error_stream;
- X
- Xconst char *rename_error = "rename(%s,%s)";
- X
- X// This routine prints an error message (and may exit).
- X// Parameters:
- X// string -> error message
- X// perror_style -> adds a system error message by looking up "errno"
- X// fatal -> exits at end
- X// Return value:
- X// exits if "fatal" is set or on error
- Xvoid error (const char *string, int perror_style, int fatal)
- X{
- X# if !defined(DEBUG) && defined(ERROR_LOG)
- X char *error_log = ERROR_LOG; // saving space !!!
- X if ( ! error_stream)
- X { if ((error_fd = open (error_log, O_WRONLY | O_APPEND, 0)) != -1)
- X { fcntl (error_fd, F_SETFD, 1);
- X error_stream = new ostream (error_fd);
- X } else
- X perror (error_log);
- X }
- X# else
- X error_stream = &cerr;
- X# endif
- X if (error_stream)
- X { *error_stream << "newsfeed: "
- X << (fatal ? "fatal: " : "warning: ")
- X << string
- X << (perror_style ? ": " : "")
- X << (perror_style ? (errno < sys_nerr ? sys_errlist [errno]
- X : "Unknown error"
- X )
- X : ""
- X )
- X << "\n";
- X (*error_stream).flush ();
- X }
- X if (fatal)
- X { kill_batch ();
- X reset_log ();
- X exit (1);
- X }
- X}
- X
- X// This routine returns a copy of its parameter.
- Xchar *new_string (const char *old_string)
- X{ char *string_ptr = new char [strlen (old_string) + 1];
- X strcpy (string_ptr, old_string);
- X return string_ptr;
- X}
- X
- X// This routine returns the concatenation of its first and second parameter.
- Xchar *join_string (const char *first_string, const char *second_string)
- X{ return new_string (form ("%s%s", first_string, second_string)); }
- /
- echo 'x - newsfeedlist.sh'
- sed 's/^X//' > newsfeedlist.sh << '/'
- X#! /bin/sh
- Xfor i in $*
- Xdo
- X case $i in
- X -h) hold=;
- X continue;;
- X -h*) hold="$hold `echo x$i|sed -e s/...//`"
- X continue;;
- X -) size=;
- X continue;;
- X -*) size=$i;
- X continue;;
- X //*) continue;;
- X esac
- X /usr/lib/news/newsfeed $size $i $hold
- Xdone
- /
- echo 'x - newsfeed.man'
- sed 's/^X//' > newsfeed.man << '/'
- X.TH NEWSFEED 1 "12 November 1986"
- X.SH NAME
- Xnewsfeed \- simultaneous multi-site news feeder
- X.SH SYNOPSIS
- X.B newsfeed
- X[-max_size] base_file [list_of_hold_sites]
- X.SH DESCRIPTION
- X.I newsfeed
- Xtakes a list of news articles (with sitenames) in file
- X.I base_file
- Xand batches them simultaneously for the given sites after changing to
- Xthe batching directory (whatever was compiled in).
- XEach line is a set of space\-separated strings,
- Xthe first string being the full pathname of a news article.
- XThe remaining strings are sites to which that news article is being fed.
- XIf there are no sites, the last component of
- X.I base_file
- Xis used (old batch format).
- X.PP
- XIf the
- X.I max_size
- Xparameter is given, each batch is limited to that maximum size
- X(unless a single article is greater than that size).
- XThus, a maximum batch size of 1 causes each article to be contained in its own
- Xbatch.
- XA maximum batch size of 0 means there is no upper limit.
- XThe default is whatever is compiled in.
- X.PP
- XAny
- X.I hold_sites
- Xgiven are saved to a file for later processing.
- XThis may be used, for example, when those sites are down for extended periods.
- X.SH FILES
- X.ta \w'base_file.newlog 'u
- Xbase_file input file
- X.br
- Xbase_file.input temporary file for error recovery
- X.br
- Xbase_file.proc batch being processed
- X.br
- Xbase_file.again batch to be reprocessed
- X.br
- Xbase_file.cmd command to queue the batch
- X.br
- X (takes a list of sites as parameters)
- X.br
- Xbase_file.log log file
- X.br
- Xbase_file.newlog temporary log file
- X.br
- Xbase_file.hold list of held articles and their sites
- X.SH "SEE ALSO"
- Xnewsfeedlist(1)
- X.SH AUTHOR
- XStephen J. Muir, Lancaster University, England, UK
- /
- echo 'x - newsfeedlist.man'
- sed 's/^X//' > newsfeedlist.man << '/'
- X.TH NEWSFEEDLIST 1 "12 November 1986"
- X.SH NAME
- Xnewsfeedlist \- invoke news feeder repeatedly
- X.SH SYNOPSIS
- X.B newsfeedlist
- X[arguments]
- X.SH DESCRIPTION
- X.I newsfeedlist
- Xtakes a list of arguments
- Xand calls the simultaneous multi-site news feeder with each one in turn.
- X.PP
- XInterspersed with the arguments may be any of the following options:
- X.TP 8
- X.B \-num
- Xset the maximum batch size
- X.TP
- X.B \-
- Xrestore the maximum batch size to the default
- X.TP
- X.B \-hsite
- Xadd
- X.B site
- Xto the list of
- X.I hold
- Xsites
- X.TP
- X.B -h
- Xdelete the list of
- X.I hold
- Xsites
- X.TP
- X.B //
- Xany argument beginning with this string is ignored
- X.SH "SEE ALSO"
- Xnewsfeed(1)
- X.SH AUTHOR
- XStephen J. Muir, Lancaster University, England, UK
- /
- echo 'Part 01 of pack.out complete.'
- exit
- ----------------------------------- Cut Here ----------------------------------
- --
- EMAIL: stephen@comp.lancs.ac.uk | Post: University of Lancaster,
- UUCP: ...!mcvax!ukc!dcl-cs!stephen | Department of Computing,
- Phone: +44 524 65201 Ext. 4120 | Bailrigg, Lancaster, UK.
- Project:Alvey ECLIPSE Distribution | LA1 4YR
-
-