V8/usr/src/cmd/uucp/uucleanup.c

Compare this file to the similar file:
Show the results in this format:

/*	@(#)uucleanup.c	1.6

 *	uucleanup - This is a program based on the heuristics
 *	by Brian Redman for cleaning up and doing something
 *	useful with old files left in the uucp queues.
 *	It also will send warning messags to users where requests are not
 *	going out due to failure to contact the remote system.
 *
 *	This program knows a lot about the construction and
 *	contents of the C., D. and X. files.  In addition, it
 *	thinks it knows what mail and netnews data files look like.
 *
 *	At present, this is what is done:
 *	For WARNING messages:
 *	C. files of age given by -W option are read, looking for
 *		either user files to be sent or received, or
 *		mail to be sent.  (Other remote execution that
 *		does not involve sending user files is not checked
 *		for now.)  In either of the cases, the user is
 *		informed by mail that the request is not being
 *		processed due to lack of communications with the remote
 *		system, and the request will be deleted in the future
 *		if it the condition remains for several more days.
 *
 *	For DELETIONS:
 *	C. files - if they reference only D. files, the C. is
 *		merely deleted, because the D. files are usually
 *		mail or news, and the later D. processing will
 *		take care of them.
 *		- if they reference files from the file system,
 *		a message is constructed that will contain a
 *		lines like
 *		   We can't contact the remote.
 *
 *		   local!file -> remote!otherfile
 *
 *		   can't be executed.
 *	X. files - merely deleted at present - D.s will be taken
 *		care of later. Besides, some of the D.s are
 *		missing or else the X. wouldn't be left around.
 *	D. files - mail type data is sent to a local person if that
 *		is where it originated.  If not, it is returned to the
 *		sender -- assumed to be from the first From line.  If
 *		a sender can't be determing, the file is merely deleted.
 *		- rnews: if locally generated, just delete.  If remote,
 *		the X. got lost, so execute rnews.
 *	other files - just delete them.
 *
 *	Deletions and executions are logged in
 *	(CLEANUPLOG)--/usr/spool/uucp/.Admin/uucleanup
*/

#include	"uucp.h"
VERSION(@(#)uucleanup.c	1.6);

#ifdef	V7
#define O_RDONLY	0
#endif

#define USAGE	"[-oDAYS] [-mSTRING] [-Cdays] [-Ddays] [-Wdays] [-Xdays] [-xLEVEL] [-sSYSTEM]"

extern int _age();		/* find the age of a file */
extern void oprocess(), xprocess(), cprocess();
extern void dXprocess(), dNprocess(), dMprocess(), dDprocess(), wprocess(); 
extern int toWho(), sendMail(), execRnews();
extern void logit();

/* need these dummys to satisy some .o files */
cleanup(){}
void systat(){}
void logent(){}


/* types of D. files */
#define D_MAIL	1
#define D_NEWS	2
#define D_XFILE	3
#define D_DATA	4
#define FULLNAME(full,dir,file)	(void) sprintf(full, "%s/%s", dir, file);

int _Ddays = 7;			/* D. limit */
int _Cdays = 7;			/* C. limit */
int _Xdays = 2;			/* X. limit */
int _Odays = 2;			/* O. limit */
int _Wdays = 1;			/* Warning limit for C. files */
char _ShortLocal[6];		/* 5 char or less version of local name */

char *_Undeliverable[] = {
"Subject: Undeliverable Mail\n",
"This mail message is undeliverable.\n",
"(Probably to or from system '%s')\n",
"It was sent to you or by you.\n",
"Sorry for the inconvenience.\n",
"",
};

#define CANT1	2	/* first line to fill in */
#define CANT2	3	/* second line to fill in */
char *_CantContact[] = {
"Subject: Job Killed By uucp\n",
"We can't contact machine '%s'.\n",
" ",	/* uucleanup will fill in variable text here */
" ",	/* fill in jobid of killed job */
"",
};

#define WARN1	2
#define WARN2	5
#define WARN3	6
char *_Warning[] = {
"Subject: Warning From uucp\n",
"We have been unable to contact machine '%s' since you queued your job.\n",
" ",	/*  wprocess FILLS IN THIS LINE OF TEXT */
"The job will be deleted in several days if the problem is not corrected.\n",
"If you care to kill the job, execute the following command:\n",
" ",	/*  wprocess FILLS IN THIS LINE WITH:  uustat -kJOBid */
" ",	/* FILL IN THE -m STRING IF SPECIFIED */
"",
};

main(argc, argv, envp)
char *argv[];
char **envp;
{
	DIR *spooldir, *subdir;
	char f[MAXFULLNAME], subf[MAXFULLNAME];
	char fullname[MAXFULLNAME];
	int pass = 0;	/* pass counter - multi passes through directory */
	char soptName[MAXFULLNAME];	/* name from -s option */
	int i, value;

	soptName[0] = NULLCHAR;
	(void) strcpy(Logfile, CLEANUPLOGFILE);
	uucpname(Myname);
	(void) strncpy(_ShortLocal, Myname, 5);
	_ShortLocal[5] = NULLCHAR;
	(void) strcpy(Progname, "uucleanup");
	while ((i = getopt(argc, argv, "C:D:W:X:m:o:s:x:")) != EOF) {
		switch(i){
		case 's':	/* for debugging - choose system */
			(void) strcpy(soptName, optarg);
			break;
		case 'x':
			Debug = atoi(optarg);
			if (Debug <= 0)
				Debug = 1;
			break;
		case 'm':
			_Warning[WARN3] = optarg;
			break;
		default:
			(void) fprintf(stderr, "\tusage: %s %s\n",
			    Progname, USAGE);
			exit(1);

		case 'C':
		case 'D':
		case 'W':
		case 'X':
		case 'o':
		    value = atoi(optarg);
		    if (value < 1) {
			fprintf(stderr," Options: CDWXo require value > 0\n");
			exit(1);
		    }
		    switch(i) {
		    case 'C':
			_Cdays = value;
			break;
		    case 'D':
			_Ddays = value;
			break;
		    case 'W':
			_Wdays = value;
			break;
		    case 'X':
			_Xdays = value;
			break;
		    case 'o':
			_Odays = value;
			break;
		    }
		    break;
		}
	}

	if (argc != optind) {
		(void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
		exit(1);
	}

	DEBUG(5, "Progname (%s): STARTED\n", Progname);
	DEBUG(5, "Myname (%s), ", Myname);
	DEBUG(5, "_ShortLocal (%s)\n", _ShortLocal);
	DEBUG(5, "Days C.(%d), ", _Cdays);
	DEBUG(5, "D.(%d), ", _Ddays);
	DEBUG(5, "W.(%d), ", _Wdays);
	DEBUG(5, "X.(%d), ", _Xdays);
	DEBUG(5, "other (%d)\n", _Odays);

	if (chdir(SPOOL) != 0) {
	    (void) fprintf(stderr, "CAN'T CHDIR (%s): errno (%d)\n",
		SPOOL, errno);
		exit(1);
	}
	if ( (spooldir = opendir(SPOOL)) == NULL) {
	    (void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n",
		SPOOL, errno);
		exit(1);
	}

	while (gnamef(spooldir, f) == TRUE) {
	    if (EQUALSN("LCK..", f, 5))
		continue;

	    if (*soptName && !EQUALS(soptName, f))
		continue;

	    if (DIRECTORY(f)) {
		(void) strcpy(Rmtname, f);
		pass = 0;
		/*
		 * pass 1 - process old C. and X. files
		 * pass 2 - process D.s that are going to X.
		 * pass 3 - processs D. mail and netnews and other D.s
		 * pass 4 - process other
		 */
		while ( (++pass < 5) && (subdir = opendir(f)) ) {
		    DEBUG(7, "Directory: %s\n", f);
		    while (gnamef(subdir, subf) == TRUE) {
			DEBUG(9, "file: %s\n", subf);
			FULLNAME(fullname, f, subf);
			switch (pass) {
			case 1:
			    if (EQUALSN(subf, "C.", 2)
			    &&  _age(fullname) >=_Cdays)
					cprocess(fullname);
			    else if (EQUALSN(subf, "C.", 2)
			    &&  _age(fullname) ==_Wdays)
					wprocess(f, subf);
			    else if (EQUALSN(subf, "X.", 2)
			    &&  _age(fullname) >=_Xdays)
					xprocess(fullname);
			    break;

			case 2:	/* pass 2 */
			    if (EQUALSN(subf, "D.", 2)
			    &&  _age(fullname) >= _Ddays
			    && dType(fullname) == D_XFILE)
					dXprocess(fullname);
			    break;

			case 3:	/* pass 3 */
			    if (EQUALSN(subf, "D.", 2)
			    &&  _age(fullname) >= _Ddays)
				switch (dType(fullname)) {
				case D_MAIL:
					dMprocess(f, subf);
					break;
				case D_NEWS:
					dNprocess(f, subf);
					break;
				case D_DATA:
					dDprocess(fullname);
					break;
				default:	/* do nothing */
					break;
				}
			    break;
			case 4:
			    if ( _age(fullname) >= _Odays
				 && !EQUALSN(subf, "C.", 2)
				 && !EQUALSN(subf, "D.", 2)
				 && !EQUALSN(subf, "X.", 2) )
					oprocess(fullname);
			    break;
			}
		    }
		    closedir(subdir);
		}
	    }
	}
	exit(0);
}

/* xprocess - X. file processing -- just remove the X. for now */

void
xprocess(fullname)
char *fullname;
{
	char text[BUFSIZ];

	DEBUG(5, "xprocess(%s), ", fullname);
	DEBUG(5, "unlink(%s)\n", fullname);
	(void) sprintf(text, "xprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
}

/*
 * cprocess - Process old C. files
 *
 */

#define CMFMT  "\n\t%s!%s -> %s!%s   (Date %2.2d/%2.2d)\n\nCan't be executed."
#define XFMT  "\n\t%s!%s  (Date %2.2d/%2.2d)\n"
#define WFMT  "\n\t%s!%s -> %s!%s   (Date %2.2d/%2.2d)\n"
void
cprocess(fullname)
char *fullname;
{
	struct stat s;
	register struct tm *tp;
	char buf[BUFSIZ], user[9];
	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
	char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ];
	FILE *fp;
	int ret;

	DEBUG(5, "cprocess(%s)\n", fullname);


	fp = fopen(fullname, "r");
	if (fp == NULL) {
		DEBUG(5, "Can't open file (%s), ", fullname);
		DEBUG(5, "errno=%d -- skip it!\n", errno);
		return;
	}
	if (fstat(fileno(fp), &s) != 0) {
	    /* can't happen; _age() did stat of this file and file is opened */
	    (void) fclose(fp);
	    return;
	}
	tp = localtime(&s.st_mtime);

	if (s.st_size == 0) { /* dummy C. for polling */
		DEBUG(5, "dummy C. -- unlink(%s)\n", fullname);
		(void) sprintf(text, "dDprocess: dummy C. unlinked(%s)",
			fullname);
		errno = 0;
		(void) unlink(fullname);
		logit(text, errno);
		(void) fclose(fp);
		return;
	}

	/* Read the C. file and process it */
	while (fgets(buf, BUFSIZ, fp) != NULL) {
	    buf[strlen(buf)-1] = NULLCHAR; /* remove \n */
	    if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
	      user, opt, file3) <5) {
		(void) sprintf(text, "cprocess: Bad C. %s, unlink(%s)",
		    buf, fullname);
		break;
	    }

	    *text = NULLCHAR;
	    ret = 0;
	    /* fill in line 3 of text */
	    (void) sprintf(text2, "Job (%s) killed!\n",
		BASENAME(fullname, '/')+2);
	    _CantContact[CANT2] = text2;
	    if (*type == 'S') {
	        if (EQUALSN(file1, "D.", 2))
	        /* generated file (mail/news) I think */
	        /* D. processing will return it later */
		    continue;

		/* some data was requested -- tell user */
		
		(void) sprintf(text1, CMFMT, Myname, file1, Rmtname, file2,
		    tp->tm_mon + 1, tp->tm_mday);
		_CantContact[CANT1] = text1;
		ret = sendMail((char *) NULL, user, "", _CantContact);
	    }
	    else if (*type == 'R') {
		(void) sprintf(text1, CMFMT, Rmtname, file1, Myname, file2,
		    tp->tm_mon + 1, tp->tm_mday);
		_CantContact[CANT1] = text1;
		ret = sendMail((char *) NULL, user, "", _CantContact);
	    }
	}

	if (!*text) {
	    (void) sprintf(text,
		"cprocess: C. %s, mail returned (%d), unlink(%s)",
		    buf, ret, fullname);
	}
	DEBUG(3, "text (%s)\n", text);

	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);

	(void) fclose(fp);
	return;
}

/*
 * wprocess - send warning messages for C. == Wdays
 */

void
wprocess(dir, file)
char *dir, *file;
{
	struct stat s;
	register struct tm *tp;
	char fullname[BUFSIZ], xfile[BUFSIZ], xF_file[BUFSIZ];
	char buf[BUFSIZ], user[BUFSIZ];
	char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256];
	char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ];
	char *realuser, uline_m[NAMESIZE], uline_u[BUFSIZ], retaddr[BUFSIZ];
	FILE *fp, *xfp;
	int ret;

	FULLNAME(fullname, dir, file);
	DEBUG(5, "wprocess(%s)\n", fullname);

	fp = fopen(fullname, "r");
	if (fp == NULL) {
		DEBUG(4, "Can't open file (%s), ", fullname);
		DEBUG(4, "errno=%d -- skip it!\n", errno);
		return;
	}
	if (fstat(fileno(fp), &s) != 0) {
	    /* can't happen; _age() did stat of this file and file is opened */
	    (void) fclose(fp);
	    return;
	}
	tp = localtime(&s.st_mtime);

	if (s.st_size == 0) { /* dummy C. for polling */
		DEBUG(5, "dummy C. -- skip(%s)\n", fullname);
		(void) fclose(fp);
		return;
	}

	/* read C. and process it */
	while (fgets(buf, BUFSIZ, fp) != NULL) {
	    buf[strlen(buf)-1] = NULLCHAR; /* remove \n */
	    if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2,
	      user, opt, file3) <5) {
		DEBUG(5, "short line (%s): ", buf);
		DEBUG(5, "bad D. -- skip(%s)\n", fullname);
		(void) fclose(fp);
		return;
	    }

	    /* set up the 6th text line of the mail message */
	    (void) sprintf(text2,
		    "\n\tuustat -k%s\n", BASENAME(fullname, '/')+2);

	    _Warning[WARN2] = text2;
	    if (*type == 'S') {	/* Send type C. file processing */
		if (EQUALSN(file2, "X.", 2)) {
		    /* this is a uux job - tell user about it */
		    FULLNAME(xfile, dir, file1);
		    if ( (xfp = fopen(xfile, "r")) == NULL) {
			/* Can't read X. file ??? - just skip */
			DEBUG(3, "Can't read %s\n", xfile);
			break;
		    }
		    *retaddr = *uline_u = *uline_m = *text = NULLCHAR;
		    while (fgets(buf, BUFSIZ, xfp) != NULL) {
	    		buf[strlen(buf)-1] = NULLCHAR; /* remove \n */
			switch(*buf) {
			case 'F':	/* save the file name */
			    FULLNAME(xF_file, dir, &buf[2]);
			    break;
			case 'R':	/* save return address */
			    sscanf(buf+2, "%s", retaddr);
			    DEBUG(7, "retaddr (%s)\n", retaddr);
			    break;
			case 'U':	/* save machine, user */
			    sscanf(buf+2, "%s%s", uline_u, uline_m);
			    break;
			}
			if (buf[0] != 'C')
			    continue;
			realuser = uline_u;
			if (*retaddr != NULLCHAR)
			    realuser = retaddr;
			if (*realuser == NULLCHAR)
				strcpy(realuser, user);
			if (!EQUALS(uline_m, Myname))
			    sprintf(user, "%s!%s", uline_m, realuser);
			else
			    strcpy(user, realuser);
			
			(void) sprintf(text1, XFMT, Rmtname,
			    EQUALSN(&buf[2], "rmail", 5) ? &buf[3] :  &buf[2],
			    tp->tm_mon + 1, tp->tm_mday);
			_Warning[WARN1] = text1;
			if (EQUALSN(&buf[2], "rmail", 5) )
			    /* this is mail; append user mail (xF_file) */
			    ret = sendMail((char *) NULL, user, xF_file, _Warning);
			else
			    ret = sendMail((char *) NULL, user, "", _Warning);
			break;
		    }
		    (void) fclose(xfp);
		    break;
		}

	        if (EQUALSN(file1, "D.", 2))
	        /* generated file (mail/news) I think */
	        /* D. processing will return it later */
		    continue;

		/* some data was requested -- tell user */
		
		/* set up the 2nd text line of the mail message */
		(void) sprintf(text1, WFMT, Myname, file1, Rmtname, file2,
		    tp->tm_mon + 1, tp->tm_mday);
		_Warning[WARN1] = text1;
		ret = sendMail((char *) NULL, user, "", _Warning);
	    }

	    else if (*type == 'R') {	/* Receive C. file processing */
		if (EQUALSN(file1, "D.", 2) && EQUALSN(file2, "D.", 2))
		    continue;
		(void) sprintf(text1, WFMT, Rmtname, file1, Myname, file2,
		    tp->tm_mon + 1, tp->tm_mday);
		_Warning[WARN1] = text1;
		ret = sendMail((char *) NULL, user, "", _Warning);
	    }
	}	/* end while - read C. lines loop */

	(void) sprintf(text,
		"wprocess: %s: %s, warning message sent to %s, returned (%d)",
		    fullname, buf, user, ret);
	DEBUG(3, "text (%s)\n", text);

	logit(text, errno);

	(void) fclose(fp);
	return;
}
/*
 * oprocess - some unknown file just remove the file
 */

void
oprocess(fullname)
char *fullname;
{

	char text[BUFSIZ];

	DEBUG(5, "oprocess(%s), ", fullname);
	DEBUG(5, "unlink(%s)\n", fullname);
	(void) sprintf(text, "oprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
}

/*
 * dDprocess - random D. file (not mail or rnews)
 *--just delete it for now
 */

void
dDprocess(fullname)
char *fullname;
{
	char text[BUFSIZ];

	DEBUG(5, "dDprocess(%s), ", fullname);
	DEBUG(5, "unlink(%s)\n", fullname);
	(void) sprintf(text, "dDprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
}

/*
 * dXprocess - process D. files that are destined for X. on remote
 * --for now just delete it
 */

void
dXprocess(fullname)
char *fullname;
{
	char text[BUFSIZ];

	DEBUG(5, "dXprocess(%s), ", fullname);
	DEBUG(5, "  unlink(%s)\n", fullname);
	(void) sprintf(text, "dXprocess: unlink(%s)", fullname);
	errno = 0;
	(void) unlink(fullname);
	logit(text, errno);
}

/*
 * dMprocess - process ophan D. mail files
 * There are two types: ones generated locally and
 * others that are from remotes.  They can be identified
 * by the system name following the D.
 * Local ones have the local name.
 */

void
dMprocess(dir, file)
char *dir, *file;
{
	int ret;
	char fullname[MAXFULLNAME];
	char *toUser, *toSystem;
	char text[BUFSIZ];

	(void) sprintf(fullname, "%s/%s", dir, file);
	DEBUG(5, "dMprocess(%s)\n", fullname);


	if (PREFIX(_ShortLocal, &file[2])) {
	    DEBUG(5, "  Local file %s: ", file);
	}
	else {
	    DEBUG(5, "  Remote file %s: ", file);
	}
	if (toWho(fullname, &toUser, &toSystem)) {
	    DEBUG(5, "toUser %s, ", toUser);
	    DEBUG(5, "toSystem %s  ", toSystem);
	    ret = sendMail(toSystem, toUser, fullname, _Undeliverable);
	    DEBUG(5, "Mail sent, unlink(%s)\n", fullname);
	    (void) sprintf(text,
		"dMprocess: mail %s to %s!%s, returned (%d),  unlink(%s)",
		fullname, toSystem, toUser, ret, fullname);
	    errno = 0;
	    (void) unlink(fullname);
	    logit(text, errno);
	}
}

/*
 * dNprocess - process ophan D. netnews files
 * There are two types: ones generated locally and
 * others that are from remotes.  They can be identified
 * by the system name following the D.
 * Local ones have the local name.
 */

void
dNprocess(dir, file)
char *dir, *file;
{
	char fullname[MAXFULLNAME];
	char text[BUFSIZ];
	int ret;

	(void) sprintf(fullname, "%s/%s", dir, file);
	DEBUG(5, "dNprocess(%s)\n", fullname);


	if (PREFIX(_ShortLocal, &file[2])) {
	    /* just delete it, the C. is gone */
	    DEBUG(5, "  Local file %s, ", file);
	    DEBUG(5, "unlink(%s)\n", fullname);
	    (void) unlink(fullname);
	    (void) sprintf(text, "dNprocess: Local news item unlink(%s)",
		fullname);
	    errno = 0;
	    (void) unlink(fullname);
	    logit(text, errno);
	}
	else {
	    /* execute rnews with this file - the X. is missing */
	    DEBUG(5, "  Remote file %s, ", file);
	    DEBUG(5, "exec rnews(%s), ", fullname);
	    ret = execRnews(fullname);
	    DEBUG(5, "unlink(%s)\n", fullname);
	    (void) sprintf(text,
		"dNprocess: Remote - exec rnews %s: returned (%d), unlink(%s)",
		fullname, ret, fullname);
	    errno = 0;
	    (void) unlink(fullname);
	    logit(text, errno);
	}

}



static long _sec_per_day = 86400L;

/*
 * _age - find the age of "file" in days
 * return:
 *	age of file
 *	0 - if stat fails
 */

int
_age(fullname)
char *fullname;
{
	static time_t ptime = 0;
	time_t time();
	struct stat stbuf;

	if (!ptime)
		(void) time(&ptime);
	if (stat(fullname, &stbuf) != -1) {
		return ((int)((ptime - stbuf.st_mtime)/_sec_per_day));
	}
	else
		return(0);
}

/*
 * dType - return the type of D. file
 * return:
 *	FAIL - can't read D. file
 *	D_MAIL - mail message D. file
 *	D_NEWS - netnews D. file
 *	D_DATA - other kind of D. file
 *	D_XFILE - destined for X. on destination machine
 */

/* NLINES - number of lines of D. file to read to determine type */
#define NLINES	10

int
dType(fullname)
char *fullname;
{
	char buf[BUFSIZ];
	FILE *fp;
	int i, type;

	fp = fopen(fullname, "r");
	if (fp == NULL) {
		DEBUG(4, "Can't open file (%s), ", fullname);
		DEBUG(4, "errno=%d -- skip it!\n", errno);
		return(FAIL);
	}
	type = D_DATA;

	/* read first NLINES lines to determine file type */
	for (i=0; i<NLINES; i++) {
	    DEBUG(9, "buf: %s\n", buf);
	    if (fgets(buf, BUFSIZ, fp) == NULL)
		break;	/* no more lines */
	    if (EQUALSN(buf, "From ", 5)) {
		type = D_MAIL;
		break;
	    }
	    if (EQUALSN(buf, "U ", 2)) {
		type = D_XFILE;
		break;
	    }
	    if (EQUALSN(buf, "Newsgroups: ", 12)) {
		type = D_NEWS;
		break;
	    }
	}

	(void) fclose(fp);
	return(type);
}

/*
 * sendMail - send mail file and message to user (local or remote)
 * return:
 *	the return from the pclose - mail exit status
 */

sendMail(system, user, file, mtext)
char *system, *user, *file;
char *mtext[];
{
	register FILE *fp, *fi;
	char cmd[BUFSIZ];

	DEBUG(5, "Mail %s to ", file);
	DEBUG(5, "%s\n", user);

	if (system != NULL && *system != '\0')
		(void) sprintf(cmd, "%s mail %s!%s", PATH, system, user);
	else
		(void) sprintf(cmd, "%s mail %s", PATH, user);
	DEBUG(7, "sendMail: %s\n", cmd);
	if ((fp = popen(cmd, "w")) == NULL)
		return(-errno);
	while (*mtext[0] )
	    (void) fprintf(fp, *mtext++, Rmtname);

	(void) fprintf(fp, "\n\tSincerely,\n\t%s!uucp\n", Myname);
	(void) fprintf(fp,
		"\n#############################################\n");

	if (*file) {
	    if ((fi= fopen(file, "r")) == NULL) /* never happen;I read once */
		 return(pclose(fp));
	    (void) fprintf(fp,
		"##### Data File: ############################\n");
	    xfappend(fi, fp);
	    (void) fclose(fi);
	}
	return(pclose(fp));
}

/*
 * execRnews - execute rnews command with stdin file
 * return:
 *	the return from the pclose - rnews exit status
 */

execRnews(file)
char *file;
{
	register FILE *fp, *fi;
	char cmd[BUFSIZ];

	DEBUG(5, "Rnews %s\n", file);

	(void) sprintf(cmd, "%s rnews ", PATH);
	if ((fp = popen(cmd, "w")) == NULL)
		return(-errno);

	if ( (fi = fopen(file, "r")) == NULL) /* never happen - I read once */
		return(pclose(fp));
	xfappend(fi, fp);
	(void) fclose(fi);

	return(pclose(fp));
}

/*
 * toWho - figure out who to send this dead mail to
 *	It is a guess;
 *	If there is a local address, send it there.
 *	If not, send it back where it came from.
 * return:
 *	0 - could not find system and user information
 *	1 - found it
 */

int
toWho(file, user, system)
char *file;	/* the D. mail message file */
char **system;	/* pointer to the system name */
char **user;	/* pointer to the user name */
{
	char buf[BUFSIZ];
	FILE *fp;
	int i;
	static char fuser[BUFSIZ], fsystem[MAXBASENAME+1];  /* from first From */
	static char luser[BUFSIZ], lsystem[MAXBASENAME+1];  /* from other From */

	*fuser = NULLCHAR;
	DEBUG(5, "toWho(%s)\n", file);
	fp = fopen(file, "r");
	for (i=0; i<NLINES; i++) {
	    if (fgets(buf, BUFSIZ, fp) == NULL)
		break;	/* no more lines */
	    DEBUG(9, "buf: %s\n", buf);
	    if (!analFrom(buf, luser, lsystem))
		continue;
	    if ( !*fuser) {
		(void) strcpy(fuser, luser);
		(void) strcpy(fsystem, lsystem);
	    }
	    if (EQUALS(Myname, lsystem)) {
		*user = luser;
		*system = lsystem;
		(void) fclose(fp);
		return(1);
	    }
	}

	/* could not find local user - use first line */
	(void) fclose(fp);
	if (!*fuser)	/* didn't find all information */
	    return(0);
	*user = fuser;
	*system = fsystem;
	return(1);
}

/* analFrom - analyze From line
 * return:
 *	0 - didn't find both from and remote from info
 *	1 - found info.
 */

int
analFrom(line, user, system)
char *line, *user, *system;
{
	char *s;
	int i;

	if (!PREFIX("From ", line) && !PREFIX(">From ", line))
	    return(0);
	
	s = strchr(line, ' ') + 1;
	for (i = 0;  *s && *s != ' ' && *s != '\n'; i++)
	    user[i] = *s++;
	user[i] = NULLCHAR;

	/* look for "remote from" */
	while (*s && ((s = strchr(s, ' ')) != NULL)) {
	    s++;
	    if (PREFIX("remote from ", s)) {	/* found it */
		s = s + strlen("remote from ");
		for (i = 0; (i<MAXBASENAME) && *s && *s != ' ' && *s != '\n'; i++)
		    system[i] = *s++;
		system[i] = NULLCHAR;
		return(1);
	    }
	}
	return(0);
}



static FILE	*_Lf = NULL;

/*
 * Make log entry
 *	text	-> ptr to text string
 *	status	errno number
 * Returns:
 *	none
 */

void
logit(text, status)
register char	*text;
int status;
{

	if (Nstat.t_pid == 0)
		Nstat.t_pid = getpid();

	if (_Lf == NULL) {
		_Lf = fopen(Logfile, "a");
		(void) chmod(Logfile, 0666);
		if (_Lf == NULL)
			return;
		setbuf(_Lf, CNULL);
	}
	(void) fseek(_Lf, 0L, 2);
	(void) fprintf(_Lf, "%s ", Rmtname);
	(void) fprintf(_Lf, "(%s,%d,%d) ", timeStamp(), Nstat.t_pid, Seqn);
	(void) fprintf(_Lf, "%s (%d)\n", text, status);
	return;
}