xfernews software
sources-request at genrad.UUCP
sources-request at genrad.UUCP
Thu Jan 31 07:27:51 AEST 1985
From: spaf at gatech
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 at hou3c, hokey at plus5 and to
rick at 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 <stdio.h>
X#include <sys/types.h>
X#ifdef USG
X#define u_short ushort
X#endif
X#include "dir.h"
X#include <sys/stat.h>
X#include <signal.h>
X#include <errno.h>
X#ifdef USG
X#include <fcntl.h>
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 <sys/types.h>
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 <setjmp.h>
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
More information about the Mod.sources
mailing list