From harvard!uucp Mon Nov 18 19:34:23 1985 Received: from harvard.HARVARD.EDU by seismo.CSS.GOV with SMTP; Mon, 18 Nov 85 19:29:05 EST Received: by harvard.HARVARD.EDU; Mon, 18 Nov 85 19:29:09 EST From: harvard!uucp (Black Hole) Return-Path: Received: by panda.LOCAL on Mon, 18 Nov 85 18:39:26 est Date: Mon, 18 Nov 85 18:39:26 est Message-Id: <8511182339.AA22145@panda.LOCAL> To: talcott!seismo!rick Subject: xfernews Status: R Date: Wed, 30 Jan 85 16:20:06 est From: allegra!gatech!spaf Subject: xfernews software Newsgroups: mod.sources The following software package is the "xfernews" suite for handling batched transmission of news articles directly from the source directories. These programs were written by Kenneth Almquist and have been used by many sites around the net with much success for quite a while. Everything should be documented as to what you need to do to install these programs. I will note that there is rumor that these may be included as part of the standard 2.10.3 news release, so you may want to wait until then to play with these. A couple of suggestions: 1) there is a naming conflict between the 2.10.2 sendnews and the xfernews sendnews. I renamed the latter to be xsendnews. Don't get bitten by this one. 2) Don't run recvnews too often. The locking mechanism has a bug and you'll get multiple versions tromping on each other. We have great success running sendnews every 4 hours, and recvnews every 2 or 3 hours. If you find any bugs, please mail them to me, to ka@hou3c, hokey@plus5 and to rick@seismo. Enjoy! : to unbundle, "sh" this file -- DO NOT use csh : SHAR archive format. Archive created Tue Jan 29 15:00:06 EST 1985 echo x - NROFFME sed 's/^X//' > NROFFME <<'+FUNKY+STUFF+' X.hy X.de p X.sp X.ti +5 X.. X.de Np X'bp X'sp 5 X.ns X.. X.wh 61 Np X.de h X.sp 2 X.ne 4 X.nr h +1 X\\nh)\ \ \\$1 X.p X.. X.h "What is xfernews?" XXfernews is a package of software for transporting news, Xand optionally mail, between machines. XIt is designed to be efficient, reliable, and to run on top of Xvanilla uucp. X.p XThe memo is divided into five sections. XSection 2 documents the protocal used by xfernews. XSection 3 gives an overview of how the xfernews software works. XSection 4 and 5 discuss the compilation and installation of xfernews. XFinally, section 6 talks about error messages. X.h "The xfernews protocal" XThe two news transport methods described in the X.ul XUSENET Interchange Standard Xare based upon remote execution and mail, respectively. XXfernews is based upon file transfer, Xwhich is handled better by uucp and many other networks. X.p XAssuming that two systems communicate using the xfernews Xprotocal, each systems has an input directory which the other Xsystem sends files to. XEach system periodicly checks its input directory and processes Xany files which it may find there. XThe name of the file identifies its contents. XThe first character of the name is the type; Xa list of types is given below. XThe next 9 characters contain the value in decimal returned by time(2) Xwhen the file was queued for transfer. XThis should be used by the receiving system to process Xnews in the same order that it was queued. XThe final character of the filename is a letter chosen to make the Xfile name unique. X.p XThere are three file types currently defined. XType 'n' files contain news articles. X.p XType 'm' files contain mail. XThe use of this protocal for mail is optional, Xbut is recomended for links which carry large amounts of mail. XThe first line of the file contains the three characters "To " Xfollowed by the destination of the mail. XThe rest of the file contains the letter. XType 'a' files are acknowledgement files. XAn acknowledgement file contains a list of files received Xby the system which sent the acknowledgement file. XIf a system fails to acknowledge a file, Xthe file should be resent. X.h "The Xfernews Software" XThis gives an overview of the implementation of the xfernews protocal Xfor use with uucp. XThree programs are provided. XQnews queues news for transmission to another system. XSendnews sends the news which has been queued up Xto another system. XRecvnews processes news files sent from another system. X.p XFor each system talked to using xfernews, Xthere is a spool directory. XThe contents of this directory are: X.de l X.sp X.ti 0 X.ta 16 X\\$1 \c X.ta 8,16,24,32,40,48,56,64,72,80 X.. X.in 16 X.l in XThe input directory used by the remote system (see section 2). X.l out XNews to be sent to the remote system is placed here by queuenews. X.l sent XWhen sendnews sends news to the remote system, it moves it from Xthe out directory to the sent directory. XThe news remains in the sent directory Xuntil the remote system acknowledges it. X.l ackfile XThis file contains a list of input files Xwhich have been processed. XSendnews sends the contents of this file Xto the remote system Xas an acknowledgement file. X.l lastack XThis file contains the time of the last file acknowledged Xby the remote system. XIt is used to avoid resending files which haven't been acknowledged Xbecause the remote system is down. X.l resentflag XWhen sendnews resends some news, Xit creates this file. XThe next time sendnews is invoked, it will not resned any news Xin order to give the remote system time to acknowledge the files Xalready resent. X.l bad XWhen a file is found in one of the directories "in", "out", or "sent" Xwhich cannot be processed, Xit is moved to this directory Xand you are informed of the fact by mail. X.in 0 X.h "Compiling the Xfernews Software" XCompiling the xfernews software is simple: all you have to do Xis to type "make". XHowever, you will probably want to modify Xsome compile time parameters first. X.p XIf you are not running System 3 or System 5, Xyou should remove the "#define USG 1" line Xfrom common.h. XThis will get you code which should run using Version 6 system calls. XThe version of the library routines provided with 4.1 BSD should Xwork with this code. XIf you have 4.2 BSD, the directory format is different; Xeliminate the 4.2 compatability routines in dir.c and dir.h, Xand use the real routines provided by Berkeley. X.p XThere are several compile time parameters which you may want to change: X.in 16 X.l RNEWS Xis the path name of the rnews program Xused for processing news. XBe warned that no path search will be performed. XFurthermore, the rnews program cannot be a Xshell procedure. XThe default is "/usr/bin/rnews". X.l RMAIL Xis the path name of a program for processing mail. XIf you don't use xfernews for transferring mail, Xthis isn't used. XThe default is "/bin/rmail". X.l UUCP Xis the path name of the uucp command. XThe default is "/usr/bin/uucp". X.l MAILCMD Xis the command passed to popen to inform the system administrator Xof problems. XThe default is "mail\ usenet". X.l RECVLOCK Xis the name of the lock file used to prevent two copies of recvnews Xfrom running simultaneously. XHaving two copies of recvnews running simultaneously in the same directory Xwill cause problems. XWe have one system wide lock rather than one lock per directory Xbecause inews running multiple copies of inews Xseems to result in "news system locked up" messages. X.l NETNEWS Xis the numeric user id which is to be used by sendnews Xand recvnews when they are invoked as root. XIf they are not invoked as root then this has no effect. X.l DESTLEN Xis the maximum length of a mail destination. X.l MAXARGS Xis the maximum number of files which can be processed Xby a given invokation of sendnews or recvnews. X.l MINACK Xspecifies the minimum number of files needing to be acknowledged Xbefore an acknowledgement file will be sent. XSetting this to zero causes the systems to keep trading acknowledgements Xeven when the link is idle. (Each acknowledgement has to be acked.) Note Xthat any pending acknowledgements are always sent if a connection has Xto be established to transfer news or mail anyway. XMINACK is specified as the number of bytes; since each file currently Xtakes 12 bytes, divide by 12 to get the number of files. X.in 0 X.h " Installing Xfernews" XOnce xfernews is compiled, Xyou can set up links to other machines using the xfernews protocal. X.p XThe first step is to create xfernews spool directories Xfor the systems you want to talk to. XThe shell procedure mkspool creates xfernews spool directories. XBy default, these are created as subdirectories of /usr/spool. This Xmay be changed by editing the mkspool script. X.p XRecvnews is invoked as "recvnews directory...". XEach directory is the name of an xfernews spool directory. XIt is recomended that xfernews be invoked from cron Xquite frequently, say once every 15 minutes, Xso that news will be processed as quickly as possible. X.p XSendnews is invoked as "sendnews [\ -r\ ][\ -c\ ] directory to". XDirectory is the name of an xfernews spool directory. XTo is the name of the input directory on the remote system Xin uucp format (see example below). XThe -r and -c options are passed directly to uucp; X-r tells uucp not to start up the uucp daemon, and -c tells it Xto transfer directly from the source files without making copies in Xthe spool directory. This can be particularly beneficial if the Xnews and uucp spool directories are on the same file system since Xit allows news to be passed on to other systems with practically no Xdisk overhead. X.p XIt is recomended that both systems using xfernews Xinvoke sendnews simultaneously. XTo avoid extra phone calls, one system should specify Xthe -r option. XNormally you will want to alternate specifying the -r option Xin order to share the phone bill. XFor example, if a link exists between spanky and tpsa, Xthe crontab entries on spanky might look like X.in +4 X.nf X X0 * * * * /usr/lib/news/sendnews -c /usr/spool/tpsa tpsa!/usr/spool/spanky/in X30 * * * * /usr/lib/news/sendnews -r -c /usr/spool/tpsa tpsa!/usr/spool/spanky/in X X.fi X.in -4 Xand the corresponding entries on tpsa would be X.in +4 X.nf X X0 * * * * /usr/lib/news/sendnews -r -c /usr/spool/spanky spanky!/usr/spool/tpsa/in X30 * * * * /usr/lib/news/sendnews -c /usr/spool/spanky spanky!/usr/spool/tpsa/in X X.fi X.in -4 XThis arranges systems to alternate the job of calling each other. XIn this example, news is transferred every half hour; Xover a long distance telephone connection you would want to Xtransfer it less frequently. X.p XOnce the connection is set up, you can begin feeding news into it Xusing the qnews program. XFor a normal interface with netnews, place the command X"qnews directory/out" in the fourth field of the sys file entry Xfor the system you wish to talk to. XDirectory should be replaced by the name of the spool directory Xfor the system you wish to talk to. For example, if you send news Xto a system named spanky, the sys entry might look like this: X.in +4 X.nf X Xspanky:mod,net,to.spanky:B:/usr/lib/news/qnews /usr/spool/spanky X X.fi X.in -4 XThis will cause qnews to read its standard input Xand copy it to a file in the directory specified as its argument. X.p XIn version 2.10 of netnews, it is possible to reference the name Xof the article as it is stored in the netnews spool directory, Xthereby allowing the article to be linked into the spool directory Xrather than being copied there. XTo use this feature, the netnews spool directory and the xfernews Xspool directory must be in the same file system. XAdd the U flag to the third field of the entry in the sys file, Xand in the fourth field say: "qnews directory/out %s": X.in +4 X.nf X Xspanky:mod,net,to.spanky:BU:/usr/lib/news/qnews /usr/spool/spanky %s X X.fi X.in -4 XInews will replace the %s with the name of the article in the spool Xdirectory before invoking qnews. XIf the article is a control message, the article is not placed in Xthe netnews spool directory and the %s is passed to qnews unchanged. XQnews checks for this case and reads the article from its standard input. XNote that even in this case, inews copies the article to a file, Xso you may want to modify inews to pass the name of the file to qnews Xeven when it is a control message. X.p XIf the cost of a phone connection is very high, or you are Xhaving problems with mail being lost, you may want to transfer mail Xas well as news using xfernews. XYou will probably have to modify your mailer code. XThe basic idea is you first figure out how your mail system transfers Xmail using uux. XIt will invoke uux by saying something like: X.nf X X sprintf(cmd, "uux - %s!rnews \\(%s\\)", system, dest) ; X fp = popen(cmd, "w") ; X X.fi XAssuming spanky is the system you want to send mail to using xfernews, Xchange this to: X.nf X X if (strcmp(system, "spanky") == 0) { X sprintf(cmd, "qnews -tm %s /usr/spool/spanky", dest) ; X fp = popen(cmd, "w") ; X if (fp != NULL) X fprintf(fp, "To %s\\n", dest) ; X } else { X sprintf(cmd, "uux - %s!rnews \\(%s\\)", system, dest) ; X fp = popen(cmd) ; X } X X.fi XThe -t option to qnews specifies the type of file to be created; Xin this case 'm' or mail. X.h "Administration of xfernews" XWhen an error occurs in the xfernews package, Xyou will be informed by mail. XIt is important that the mail command work; Xtry invoking sendnews without any arguments and see if an error message Xis mailed to you. XMost error messages refer to errors which "can't happen" (i. e. Xthe problem is either a bug in the package or an error in installation). XYou may have to grep through the code or contact the aouthor to identify these. XYou are also informed when the RNEWS or RMAIL programs exit with non-zero Xstatus. XWhen one of these programs fails, the mail or news is still acknowledged Xand the file is linked into the directory "bad" where you can fix the Xproblem manually. XThe exit status of the program and any error messages generated by the program Xare included in in the message. XSometimes the problem is transient, so just running rnews again will fix Xthe problem. XIf you run 2.10, you may want to inews to exit with an error indication Xwhen an unknown newsgroup is received. XThis way you can fix the problem and resubmit the article. XThe following version of the routine getapproval (in inews.c) Xdoes the trick, at least for the beta release: X.nf X Xgetapproval(ng) Xchar *ng; X{ X char buf[128] ; X sprintf(buf, "inews: unrecognized newsgroup %s\n", ng) ; X log(buf) ; X printf("%s", buf) ; X xxit(4) ; X} X X.fi XFiles in the directories "in", "out", and "sent" with unrecognized names Xwill also be moved to the directory "bad". XIf rnews dies with a core dump, the core file will be left in "in", Xand the next invocation of recvnews will move it to "bad". +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 12678 Nov 29 1983 NROFFME (as sent)' chmod u=rw,g=r,o=r NROFFME ls -l NROFFME echo x - README sed 's/^X//' > README <<'+FUNKY+STUFF+' XSee NROFFME.OUT for an explanation of how this "batching" scheme Xworks. X XModify common.h and mkspool to suit your configuration. NROFFME Xcontains explanations of what various parameters mean. X XModify the makefile to reference the appropriate directories and Xuser/group names, then as root type make install. X Xnews.send and news.recv are sample shell procedures to be invoked Xfrom cron periodically. The applicable crontab entries at gatech Xare X X 4,14,24,34,44,54 * * * * /usr/lib/news/news.recv X 18 * * * * /usr/lib/news/news.send X XNote that the news.send script should be scheduled a few minutes Xbefore a uucp contact is expected to minimize the latency of Xarticles waiting in the batching queue. +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 702 Nov 29 1983 README (as sent)' chmod u=rw,g=r,o=r README ls -l README echo x - common.h sed 's/^X//' > common.h <<'+FUNKY+STUFF+' X#include X#include X#ifdef USG X#define u_short ushort X#endif X#include "dir.h" X#include X#include X#include X#ifdef USG X#include X#endif X X#define FNLEN 15 /* max file name length (including nul) */ X#define PATHLEN 100 /* max path name length */ X#define DESTLEN 256 /* max length of mail destination */ X#define MAXARGS 200 /* max number of args to uucp */ X#define SETIN 01 /* flag to run: reset stdin */ X#define RNEWS "/usr/bin/rnews" X#define RMAIL "/bin/rmail" X#define UUCP "/usr/bin/uucp" X#define MAILCMD "/bin/csmail usenet" X#define RECVLOCK "/usr/tmp/recv.lock" X#define MINACK (10 * 12) X#define NETNEWS 65 /* netnews user id */ X X X#ifndef USG X#define strchr index X#endif X X/* routine to determine if a process exists */ X#define procexists(pid) (kill(pid, 0) >= 0 || errno == EPERM) X Xlong time(), atol() ; Xchar *strcpy(), *strchr() ; Xchar *malloc() ; XFILE *popen() ; Xint comp() ; X Xextern int errno ; +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 962 Nov 22 1983 common.h (as sent)' chmod u=rw,g=r,o=r common.h ls -l common.h echo x - dir.c sed 's/^X//' > dir.c <<'+FUNKY+STUFF+' X#include X#include "dir.h" X X/* X * close a directory. X */ Xvoid Xclosedir(dirp) X register DIR *dirp; X{ X close(dirp->dd_fd); X dirp->dd_fd = -1; X dirp->dd_loc = 0; X free(dirp); X} X X X X/* X * open a directory. X */ XDIR * Xopendir(name) X char *name; X{ X register DIR *dirp; X register int fd; X X if ((fd = open(name, 0)) == -1) X return NULL; X if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { X close (fd); X return NULL; X } X dirp->dd_fd = fd; X dirp->dd_loc = 0; X return dirp; X} X X X X/* X * read an old style directory entry and present it as a new one X */ X X#define ODIRSIZ 14 X Xstruct olddirect { X ino_t od_ino; X char od_name[ODIRSIZ]; X}; X X/* X * get next entry in a directory. X */ Xstruct direct * Xreaddir(dirp) X register DIR *dirp; X{ X register struct olddirect *dp; X static struct direct dir; X X for (;;) { X if (dirp->dd_loc == 0) { X dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, X DIRBLKSIZ); X if (dirp->dd_size <= 0) X return NULL; X } X if (dirp->dd_loc >= dirp->dd_size) { X dirp->dd_loc = 0; X continue; X } X dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc); X dirp->dd_loc += sizeof(struct olddirect); X if (dp->od_ino == 0) X continue; X dir.d_ino = dp->od_ino; X strncpy(dir.d_name, dp->od_name, ODIRSIZ); X dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ X dir.d_namlen = strlen(dir.d_name); X dir.d_reclen = DIRBLKSIZ; X return (&dir); X } X} +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 1382 Nov 22 1983 dir.c (as sent)' chmod u=rw,g=r,o=r dir.c ls -l dir.c echo x - dir.h sed 's/^X//' > dir.h <<'+FUNKY+STUFF+' X/* dir.h 4.4 82/07/25 */ X X/* X * A directory consists of some number of blocks of DIRBLKSIZ X * bytes, where DIRBLKSIZ is chosen such that it can be transferred X * to disk in a single atomic operation (e.g. 512 bytes on most machines). X * X * Each DIRBLKSIZ byte block contains some number of directory entry X * structures, which are of variable length. Each directory entry has X * a struct direct at the front of it, containing its inode number, X * the length of the entry, and the length of the name contained in X * the entry. These are followed by the name padded to a 4 byte boundary X * with null bytes. All names are guaranteed null terminated. X * The maximum length of a name in a directory is MAXNAMLEN. X * X * The macro DIRSIZ(dp) gives the amount of space required to represent X * a directory entry. Free space in a directory is represented by X * entries which have dp->d_reclen >= DIRSIZ(dp). All DIRBLKSIZ bytes X * in a directory block are claimed by the directory entries. This X * usually results in the last entry in a directory having a large X * dp->d_reclen. When entries are deleted from a directory, the X * space is returned to the previous entry in the same directory X * block by increasing its dp->d_reclen. If the first entry of X * a directory block is free, then its dp->d_ino is set to 0. X * Entries other than the first in a directory do not normally have X * dp->d_ino set to 0. X */ X#define DIRBLKSIZ 512 X#define MAXNAMLEN 255 X X#ifdef pdp11 X#define u_long long X#endif X Xstruct direct { X u_long d_ino; /* inode number of entry */ X u_short d_reclen; /* length of this record */ X u_short d_namlen; /* length of string in d_name */ X char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */ X}; X X/* X * The DIRSIZ macro gives the minimum record length which will hold X * the directory entry. This requires the amount of space in struct direct X * without the d_name field, plus enough space for the name with a terminating X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. X */ X#undef DIRSIZ X#define DIRSIZ(dp) \ X ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)) X X#ifndef KERNEL X/* X * Definitions for library routines operating on directories. X */ Xtypedef struct _dirdesc { X int dd_fd; X long dd_loc; X long dd_size; X char dd_buf[DIRBLKSIZ]; X} DIR; X#ifndef NULL X#define NULL 0 X#endif Xextern DIR *opendir(); Xextern struct direct *readdir(); Xextern long telldir(); Xextern void seekdir(); X#define rewinddir(dirp) seekdir((dirp), (long)0) Xextern void closedir(); X#endif KERNEL +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 2546 Nov 22 1983 dir.h (as sent)' chmod u=rw,g=r,o=r dir.h ls -l dir.h echo x - makefile sed 's/^X//' > makefile <<'+FUNKY+STUFF+' Xall: recvnews xsendnews qnews X Xrecvnews: recvnews.o dir.o X $(CC) -o $@ recvnews.o dir.o X Xxsendnews: sendnews.o dir.o X $(CC) -o $@ sendnews.o dir.o X Xqnews: qnews.c common.h X $(CC) -o $@ $(CFLAGS) qnews.c X Xsendnews.o recvnews.o: common.h X Xinstall: all X cp mkspool xsendnews recvnews qnews /usr/lib/news X cd /usr/lib/news; chown news xsendnews recvnews qnews X cd /usr/lib/news; chgrp news xsendnews recvnews qnews X chmod ug+s /usr/lib/news/xsendnews /usr/lib/news/recvnews /usr/lib/news/qnews +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 490 Oct 1 21:52 makefile (as sent)' chmod u=rw,g=r,o=r makefile ls -l makefile echo x - mkspool sed 's/^X//' > mkspool <<'+FUNKY+STUFF+' X#! /bin/sh X# Create an xfernews spool directory X Xif [ $# = 0 ] Xthen X echo usage: mkspool sysname ... X exit 1 Xfi X Xcd /usr/spool Xumask 022 X Xfor sys do X if [ -z "$sys" -o "$sys" != "`basename \"$sys\"`" ] X then X echo $sys: bad system name X continue X elif [ -f $sys -o -d $sys ] X then X echo $sys: conflicts with existing file or directory X continue X fi X if mkdir $sys X then X mkdir $sys/in X mkdir $sys/sent X mkdir $sys/out X mkdir $sys/bad X echo 0 > $sys/lastack X chown news $sys $sys/in $sys/sent $sys/out $sys/bad $sys/lastack X chgrp news $sys $sys/in $sys/sent $sys/out $sys/bad $sys/lastack X chmod a+w $sys/in X ls -ld $sys $sys/* X fi Xdone +FUNKY+STUFF+ echo '-rwxr-xr-x 1 wan 650 Dec 15 1983 mkspool (as sent)' chmod u=rwx,g=rx,o=rx mkspool ls -l mkspool echo x - news.recv sed 's/^X//' > news.recv <<'+FUNKY+STUFF+' X#! /bin/sh X X PATH=/usr/lib/news:/bin:/usr/bin X X cd /usr/spool X umask 022 X recvnews akgua emory >> /usr/lib/news/recv.log 2>&1 +FUNKY+STUFF+ echo '-rwxr-xr-x 1 wan 126 Dec 19 1983 news.recv (as sent)' chmod u=rwx,g=rx,o=rx news.recv ls -l news.recv echo x - news.send sed 's/^X//' > news.send <<'+FUNKY+STUFF+' X#! /bin/sh X X PATH=/usr/lib/news:/bin:/usr/bin X X cd /usr/spool X umask 022 X sendnews -c -r akgua akgua!/usr/spool/gatech/in X sendnews -c -r emory emory!/usr/spool/gatech/in +FUNKY+STUFF+ echo '-rwxr-xr-x 1 wan 171 Dec 19 1983 news.send (as sent)' chmod u=rwx,g=rx,o=rx news.send ls -l news.send echo x - qnews.c sed 's/^X//' > qnews.c <<'+FUNKY+STUFF+' X#include "common.h" X X Xchar *directory ; Xchar *file ; X X Xmain(argc, argv) X char **argv ; X { X long t ; X char *from, *lastc ; X char to[PATHLEN] ; X char **ap ; X char prefix ; X int fd ; X X prefix = 'n' ; X ap = argv + 1 ; X if (ap[0][0] == '-' && ap[0][1] == 't') { X if ((prefix = ap[0][2]) == '\0') Xusage: fatal("usage: qnews [ -tc ] directory [ file ]") ; X ap++ ; X } X if ((directory = *ap++) == NULL) X goto usage ; X from = *ap ; X if (from != NULL && strcmp(from, "%s") == 0) X from = NULL ; X time(&t) ; X sprintf(to, "%s/%c%lda", directory, prefix, t) ; X lastc = to + strlen(to) - 1 ; X signal(SIGTERM, SIG_IGN) ; X for (;;) { X if (from != NULL) X fd = link(from, to) ; X else { X#ifdef USG X fd = open(to, O_WRONLY | O_CREAT | O_EXCL, 0444) ; X#else X fd = creat(to, 0444) ; X#endif X } X if (fd >= 0) X break ; X if (errno != EEXIST && errno != EPERM || *lastc == 'z') X fatal("can't create %s", to) ; X *lastc += 1 ; X } X if (from == NULL) { X char buf[BUFSIZ] ; X int count ; X X file = to ; X while ((count = read(0, buf, BUFSIZ)) > 0) { X if (write(fd, buf, count) != count) { X fatal("write error") ; X } X } X if (count < 0) { X fatal("read error") ; X } X } X exit(0) ; X} X X X X/* X * Fatal error. X * Print error message and send mail to administrator. X */ X Xfatal(fmt, a1, a2, a3, a4) X char *fmt ; X { X static int reentered = 0 ; X X if (reentered) { X fprintf(stderr, "fatal entered recursively\n") ; X fprintf(stderr, fmt, a1, a2, a3, a4) ; X if (file != NULL) X unlink(file) ; X exit(3) ; X } X reentered = 1 ; X msg(fmt, a1, a2, a3, a4) ; X if (file != NULL) X if (unlink(file) < 0) X msg("unlink failed: %s", file) ; X exit(2) ; X} X X X/* X * Send mail to administrator. Flag is set to indicate fatal error. X */ X Xmsg(fmt, a1, a2, a3, a4) X char *fmt ; X { X FILE *fp ; X int e = errno ; X X fprintf(stderr, fmt, a1, a2, a3, a4) ; X putc('\n', stderr) ; X if ((fp = popen(MAILCMD, "w")) == NULL) X fatal("popen failed") ; X fputs("Subject: error in qnews\n\n", fp) ; X fprintf(fp, fmt, a1, a2, a3, a4) ; X if (directory != NULL) { X fputs("\nprocessing ", fp) ; X fputs(directory, fp) ; X } X putc('\n', fp) ; X fprintf(fp, "errno = %d\n", e) ; X if (pclose(fp) != 0) X fatal("msg failed") ; X} +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 2853 Sep 9 1983 qnews.c (as sent)' chmod u=rw,g=r,o=r qnews.c ls -l qnews.c echo x - recvnews.c sed 's/^X//' > recvnews.c <<'+FUNKY+STUFF+' X#define RECVNEWS 1 X#include "common.h" X X#ifdef USG X#include X#define setexit() setjmp(nextdir) X#define reset() longjmp(nextdir, 1) Xjmp_buf nextdir ; /* label to jump to on major error */ X#endif X Xstruct arglist { X int nargs ; X char *arg[MAXARGS] ; X} ; X Xchar *directory ; /* directory currently being processed */ Xint errflag ; /* set if any errors */ Xchar lockfile[] = RECVLOCK ; X X X Xmain(argc, argv) Xchar **argv ; X{ X char **ap ; X X nice(10) ; X setuid(NETNEWS) ; /* in case invoked as root by cron */ X if (setlock(lockfile) == 0) { X printf("recvnews locked\n") ; X exit(0) ; X } X ap = argv + 1 ; X setexit() ; X while (*ap != NULL) { X directory = *ap++ ; X if (chdir(directory) < 0) X fatal("directory nonexistant") ; X inputnews() ; X if (chdir("..") < 0) { X printf("can't chdir to %s/..\n", directory) ; X break ; X } X } X if (unlink(lockfile) < 0) X msg("can't unlink lock") ; X exit(errflag) ; X} X X X Xinputnews() { X struct arglist in ; X DIR *dp ; X struct direct *d ; X int oflow ; X int i ; X X in.nargs = 0 ; X oflow = 0 ; X if (chdir("in") < 0) X fatal("in missing") ; X dp = opendir(".") ; X if (dp == NULL) X fatal("no .") ; X while ((d = readdir(dp)) != NULL) { X if (d->d_name[0] != '.') { X if (in.nargs < MAXARGS) X addarg(d->d_name, &in) ; X else X oflow++ ; X } X } X if (oflow > 0) X msg("%d articles not processed", oflow) ; X closedir(dp) ; X if (in.nargs == 0) X goto out ; X qsort((char *)in.arg, in.nargs, sizeof(char *), comp) ; X sleep(5) ; /* in case any files half written */ X for (i = 0 ; i < in.nargs ; i++) { X procfile(in.arg[i]) ; X free(in.arg[i]) ; X } Xout: X if (chdir("..") < 0) X fatal("can't chdir ..") ; X} X X X Xprocfile(name) X char *name ; X { X FILE *fp ; X int rc ; X X if (badname(name)) { X msg("bad input file name %s", name) ; X movebad(name) ; X return ; X } X if ((fp = fopen(name, "r")) == NULL) { X msg("unreadable file %s", name) ; X movebad(name) ; X return ; X } X switch (name[0]) { X case 'a': X rc = procack(name, fp) ; X break ; X case 'n': X rc = procnews(name, fp) ; X break ; X case 'm': X rc = procmail(name, fp) ; X break ; X default: X fatal("can't happen %s", name) ; X break ; X } X if (rc < 0) X movebad(name) ; X else if (unlink(name) < 0) X msg("can't unlink %s", name) ; X fclose(fp) ; X if ((fp = fopen("../ackfile", "a")) == NULL) X fatal("Can't open ackfile") ; X fprintf(fp, "%s\n", name) ; X fclose(fp) ; X} X X X Xprocnews(name, fp) X char *name ; X FILE *fp ; X { X char *arg[2] ; X X arg[0] = RNEWS, arg[1] = NULL ; X return chkrun(arg, name, fp) ; X} X X Xprocmail(name, fp) X char *name ; X FILE *fp ; X { X char *arg[4] ; X char buf[DESTLEN] ; X char *p ; X X setbuf(fp, NULL) ; /* turn off buffering */ X if (fgets(buf, DESTLEN, fp) == NULL) { X msg("%s: empty file", name) ; X return -1 ; X } X if (strncmp(buf, "To ", 3) != 0) { X msg("corrupted mail %s", name) ; X return -1 ; X } X if ((p = strchr(buf, '\n')) == NULL) { X msg("destination too long, file %s", name) ; X return -1 ; X } X *p = '\0' ; X arg[0] = RMAIL, arg[1] = buf + 3, arg[2] = NULL ; X return chkrun(arg, name, fp) ; X} X X X Xprocack(name, fp) X char *name ; X FILE *fp ; X { X char line[FNLEN+2] ; X char *p ; X X if (chdir("../sent") < 0) X fatal("no sent dir") ; X while (fgets(line, FNLEN + 2, fp) != NULL) { X if ((p = strchr(line, '\n')) == NULL) { X msg("line too long, file %s", name) ; Xbad: if (chdir("../in") < 0) X fatal("return to in") ; X return -1 ; X } X *p = '\0' ; X if (badname(line)) { X msg("bad file %s acked in %s", line, name) ; X goto bad ; X } X if (unlink(line) < 0) X printf("Can't unlink %s/in/%s, ack file %s\n", X directory, line, name) ; X } X if (chdir("../in") < 0) X fatal("return to in") ; X if ((fp = fopen("../lastack", "w")) == NULL) X fatal("can't open lastack") ; X fprintf(fp, "%.9s\n", line + 1) ; X fclose(fp) ; X return 0 ; X} X X X Xbadname(fname) X char *fname ; X { X register c ; X X if ((c = *fname++) != 'a' && c != 'n' && c != 'm') X return -1 ; X if ((c = *fname++) < '0' || c > '9') X return -1 ; X return 0 ; X} X X X Xmovebad(fname) X char *fname ; X { X char bad[PATHLEN] ; X X sprintf(bad, "../bad/%s", fname) ; X unlink(bad) ; X if (link(fname, bad) < 0) X fatal("link to bad failed for %s", fname) ; X if (unlink(fname) < 0) X fatal("unlink bad file %s failed", fname) ; X} X X X Xcomp(a, b) X char **a, **b ; X { X return strcmp(*a, *b) ; X} X X X Xaddarg(fname, argl) X struct arglist *argl ; X char *fname ; X { X char *p ; X X if (argl->nargs >= MAXARGS) X fatal("too many articles") ; X if (fname == NULL) X p = NULL ; X else { X if ((p = malloc(strlen(fname) + 1)) == NULL) X fatal("out of space") ; X strcpy(p, fname) ; X } X argl->arg[argl->nargs++] = p ; X} X X X X/* X * Run a program, informing the system administrator if it fails. X */ X Xchkrun(arg, name, fp) X char *arg[] ; X char *name ; X FILE *fp ; X { X char *p ; X int outfd ; X int rc ; X FILE *mailfp ; X static char outfile[24] ; X X if (outfile[0] == '\0') X sprintf(outfile, "/tmp/recvnews%d", getpid()) ; X if ((outfd = creat(outfile, 0666)) < 0) X fatal("Can't create %s", outfile) ; X rc = run(arg, fileno(fp), outfd) ; X close(outfd) ; X if (rc != 0) { X if ((mailfp = popen(MAILCMD, "w")) == NULL) X fatal("Can't popen MAILCMD") ; X fprintf(mailfp, "Subject: error in recvnews\n\n") ; X if ((rc & 0177) == 0) { X fprintf(mailfp, "exit status %d from %s", rc >> 8, arg[0]) ; X } else { X fprintf(mailfp, "%s died with signal %d", arg[0], rc & 0177) ; X if (rc & 0200) X fprintf(mailfp, " - core dumped") ; X } X fprintf(mailfp, "\nfile %s/bad/%s\n", directory, name) ; X if ((fp = fopen(outfile, "r")) == NULL) X fprintf(mailfp, "Can't open %s\n", outfile) ; X else { X fprintf(mailfp, "Output of program:\n") ; X while ((rc = getc(fp)) != EOF) X putc(rc, mailfp) ; X fclose(fp) ; X } X pclose(mailfp) ; X if (unlink(outfile) < 0) X msg("can't unlink %s", outfile) ; X return -1 ; X } X if (unlink(outfile) < 0) X msg("can't unlink %s", outfile) ; X return 0 ; X} X X X Xrun(args, in, out) X char *args[] ; X int in, out ; X { X int pid ; X int status ; X int i ; X X#ifdef DEBUG X printf("run") ; /*DEBUG*/ X for (i = 0 ; args[i] != NULL ; i++) /*DEBUG*/ X printf(" %s", args[i]) ; /*DEBUG*/ X putchar('\n') ; /*DEBUG*/ X#endif X if ((pid = fork()) == -1) X fatal("Cannot fork") ; X if (pid == 0) { X if (in != 0) { X close(0) ; X if (dup(in) != 0) { X msg("Cannot redirect input") ; X exit(127) ; X } X close(in) ; X } X if (out != 1) { X close(1) ; X if (dup(out) != 1) { X msg("Cannot redirect output") ; X exit(127) ; X } X close(out) ; X close(2) ; X if (dup(1) != 2) { X msg("Cannot dup 1") ; X exit(127) ; X } X } X execv(args[0], args) ; X msg("exec failed") ; X exit(127) ; X } X while ((i = wait(&status)) != pid && i != -1) ; X return status ; X} X X X/* X * Fatal error. X * Print error message and send mail to administrator. X */ X Xfatal(fmt, a1, a2, a3, a4) X char *fmt ; X { X static int reentered = 0 ; X X if (reentered) { X fprintf(stderr, "fatal entered recursively\n") ; X fprintf(stderr, fmt, a1, a2, a3, a4) ; X exit(3) ; X } X reentered = 1 ; X msg(fmt, a1, a2, a3, a4) ; X reentered = 0 ; X reset() ; X} X X X/* X * Send mail to administrator. Flag is set to indicate fatal error. X */ X Xmsg(fmt, a1, a2, a3, a4) X char *fmt ; X { X FILE *fp ; X int e = errno ; X X errflag = 1 ; X fprintf(stderr, fmt, a1, a2, a3, a4) ; X putc('\n', stderr) ; X if ((fp = popen(MAILCMD, "w")) == NULL) X fatal("popen failed") ; X fputs("Subject: error in recvnews\n\n", fp) ; X fprintf(fp, fmt, a1, a2, a3, a4) ; X if (directory != NULL) { X fputs("\nprocessing ", fp) ; X fputs(directory, fp) ; X } X putc('\n', fp) ; X fprintf(fp, "errno = %d\n", e) ; X if (pclose(fp) != 0) X fatal("msg failed") ; X} X X X Xsetlock(name) X char *name ; X { X FILE *fp ; X char buf[10] ; X X if ((fp = fopen(name, "r")) != NULL) { X if (fgets(buf, 10, fp) == NULL) { X msg("empty lock file") ; X fclose(fp) ; X goto lock ; X } X fclose(fp) ; X if (buf[0] < '0' || buf[0] > '9') { X msg("no pid in lock file") ; X goto lock ; X } X if (! procexists(atoi(buf))) { X msg("previous recvnews didn't remove lock") ; X goto lock ; X } X return 0 ; X } Xlock: X if ((fp = fopen(name, "w")) == NULL) X fatal("cannot create lock file") ; X fprintf(fp, "%d\n", getpid()) ; X fclose(fp) ; X return 1 ; X} +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 10786 Dec 15 1983 recvnews.c (as sent)' chmod u=rw,g=r,o=r recvnews.c ls -l recvnews.c echo x - sendnews.c sed 's/^X//' > sendnews.c <<'+FUNKY+STUFF+' X#define DEBUG X#include "common.h" X X#define ASKIP 4 X Xstruct arglist { X int nargs ; X char **first ; X char *arg[MAXARGS] ; X} ; X Xstruct arglist uuargs ; Xchar *directory ; Xint errflag ; Xchar resentflag[] = "resentflag" ; Xchar lastack[] = "lastack" ; Xchar ackfile[] = "ackfile" ; X#ifndef NOTGATECH Xint cflag ; X#endif X X X Xmain(argc, argv) X char **argv ; X { X register char **ap ; X int rflag ; X char *p ; X int i ; X struct stat statb ; X X setuid(NETNEWS) ; X ap = argv + 1 ; X rflag = 0 ; X while ((p = *ap++) != NULL && *p == '-') { X if (strcmp(p, "-r") == 0) X rflag++ ; X#ifndef NOTGATECH X else if (strcmp(p, "-c") == 0) X cflag++ ; X#endif X else Xusage: fatal("usage: sendnews [ -r ] from to") ; X } X if (p == NULL || *ap == NULL) X goto usage ; X directory = p ; X if (chdir(p) < 0) X fatal("no directory") ; X uuargs.first = &uuargs.arg[ASKIP] ; X uuargs.nargs = ASKIP ; X if (unlink(resentflag) < 0) { X sendnews(1) ; X } X if (uuargs.nargs > ASKIP) { X if ((i = creat(resentflag, 0666)) < 0) X msg("can't create resent flag") ; X close(i) ; X msg("resent %d files", uuargs.nargs - ASKIP) ; X } X sendnews(0) ; X if (stat(ackfile, &statb) >= 0 X && (uuargs.nargs > ASKIP || statb.st_size >= MINACK)) { X long t ; X char buf[FNLEN + 5] ; X X time(&t) ; X sprintf(buf, "sent/a%lda", t) ; X if (link(ackfile, buf) < 0) { X msg("can't link ackfile") ; X goto uu ; X } X if (unlink(ackfile) < 0) { X msg("can't unlink ackfile") ; X goto uu ; X } X insarg(buf + 5, &uuargs) ; X } Xuu: X uucp(*ap, rflag) ; X exit(errflag) ; X} X X X Xsendnews(resend) { X char *dir ; X char sentname[PATHLEN] ; X DIR *dp ; X FILE *fp ; X struct direct *d ; X long last ; X int oflow ; X X if (resend == 0) { X dir = "out" ; X } else { X dir = "sent" ; X if ((fp = fopen(lastack, "r")) == NULL) { X msg("can't open lastack") ; X return ; X } X if (fgets(sentname, FNLEN, fp) == NULL) { X /* Can occur bacause no locking done */ X msg("lastack is empty file") ; X fclose(fp) ; X return ; X } X fclose(fp) ; X last = atol(sentname) - 3600L ; X } X if (chdir(dir) < 0) X fatal("chdir %s failed", dir) ; X if ((dp = opendir(".")) == NULL) X fatal("no .") ; X oflow = 0 ; X while ((d = readdir(dp)) != NULL) { X if (d->d_name[0] == '.') X continue ; X else if (badname(d->d_name)) { X msg("bad file %s in %s", d->d_name, dir) ; X movebad(d->d_name) ; X continue ; X } X if (resend) { X if (atol(d->d_name + 1) > last) X continue ; X printf("resending %s\n", d->d_name) ; X } X if (uuargs.nargs >= MAXARGS - 3) { X oflow++ ; X continue ; X } X addarg(d->d_name, &uuargs) ; X if (! resend) { X sprintf(sentname, "../sent/%s", d->d_name) ; X if (link(d->d_name, sentname) < 0) X msg("link %s failed", d->d_name) ; X else if (unlink(d->d_name) < 0) X msg("unlink %s failed", d->d_name) ; X } X } X closedir(dp) ; X if (oflow > 0) X msg("too many files: %d not sent", oflow) ; X if (chdir("..") < 0) X fatal("no ..") ; X} X X X Xuucp(to, rflag) X char *to ; X { X if (uuargs.first == uuargs.arg + uuargs.nargs) X return ; X if (chdir("sent") < 0) X fatal("no sent dir") ; X qsort((char *)(uuargs.arg + ASKIP), uuargs.nargs - ASKIP, sizeof(char *), comp) ; X addarg(to, &uuargs) ; X addarg(NULL, &uuargs) ; X if (rflag) insarg("-r", &uuargs) ; X#ifndef NOTGATECH X if (cflag) insarg("-c", &uuargs) ; X#endif X insarg(UUCP, &uuargs) ; X if (run(uuargs.first, 0, 0) != 0) X fatal("uucp failed") ; X if (chdir("..") < 0) X fatal("no ..") ; X} X X X Xbadname(fname) X char *fname ; X { X register c ; X X if ((c = *fname++) != 'a' && c != 'n' && c != 'm') X return -1 ; X if ((c = *fname++) < '0' || c > '9') X return -1 ; X return 0 ; X} X X X Xmovebad(fname) X char *fname ; X { X char bad[PATHLEN] ; X X sprintf(bad, "../bad/%s", fname) ; X unlink(bad) ; X if (link(fname, bad) < 0) X fatal("link to bad failed for %s", fname) ; X if (unlink(fname) < 0) X fatal("unlink bad file %s failed", fname) ; X} X X X Xcomp(a, b) X char **a, **b ; X { X return strcmp(*a, *b) ; X} X X X Xaddarg(fname, argl) X struct arglist *argl ; X char *fname ; X { X char *p ; X X if (argl->nargs >= MAXARGS) X fatal("too many articles") ; X if (fname == NULL) X p = NULL ; X else { X if ((p = malloc(strlen(fname) + 1)) == NULL) X fatal("out of space") ; X strcpy(p, fname) ; X } X argl->arg[argl->nargs++] = p ; X} X X Xinsarg(fname, argl) X struct arglist *argl ; X char *fname ; X { X char *p ; X X if (argl->first <= argl->arg) X fatal("insarg failed") ; X if (fname == NULL) X p = NULL ; X else { X if ((p = malloc(strlen(fname) + 1)) == NULL) X fatal("out of space") ; X strcpy(p, fname) ; X } X *--(argl->first) = p ; X} X X X Xrun(args, flags, fd) X char *args[] ; X int flags ; X int fd ; X { X int pid ; X int status ; X int i ; X X#ifdef DEBUG X printf("run") ; /*DEBUG*/ X for (i = 0 ; args[i] != NULL ; i++) /*DEBUG*/ X printf(" %s", args[i]) ; /*DEBUG*/ X putchar('\n') ; /*DEBUG*/ X#endif X if ((pid = fork()) == -1) X fatal("Cannot fork") ; X if (pid == 0) { X if (flags & SETIN) { X close(0) ; X if (dup(fd) != 0) { X msg("Cannot redirect input") ; X exit(127) ; X } X close(fd) ; X } X execv(args[0], args) ; X msg("exec failed") ; X exit(127) ; X } X while ((i = wait(&status)) != pid && i != -1) ; X return status ; X} X X X/* X * Fatal error. X * Print error message and send mail to administrator. X */ X Xfatal(fmt, a1, a2, a3, a4) X char *fmt ; X { X static int reentered = 0 ; X X if (reentered) { X fprintf(stderr, "fatal entered recursively\n") ; X fprintf(stderr, fmt, a1, a2, a3, a4) ; X exit(3) ; X } X reentered = 1 ; X msg(fmt, a1, a2, a3, a4) ; X exit(2) ; X} X X X/* X * Send mail to administrator. Flag is set to indicate fatal error. X */ X Xmsg(fmt, a1, a2, a3, a4) X char *fmt ; X { X FILE *fp ; X X errflag = 1 ; X fprintf(stderr, fmt, a1, a2, a3, a4) ; X putc('\n', stderr) ; X if ((fp = popen(MAILCMD, "w")) == NULL) X fatal("popen failed") ; X fputs("Subject: error in sendnews\n\n", fp) ; X fprintf(fp, fmt, a1, a2, a3, a4) ; X if (directory != NULL) { X fputs("\nprocessing ", fp) ; X fputs(directory, fp) ; X } X putc('\n', fp) ; X if (pclose(fp) != 0) X fatal("msg failed") ; X} +FUNKY+STUFF+ echo '-rw-r--r-- 1 wan 7817 Nov 29 1983 sendnews.c (as sent)' chmod u=rw,g=r,o=r sendnews.c ls -l sendnews.c exit 0