|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - downloadIndex: ┃ T m ┃
Length: 36267 (0x8dab) Types: TextFile Names: »multi-feed«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/news/misc/multi-feed«
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