OpenSolaris_b135/cmd/bnu/cntrl.c

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

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "uucp.h"
#include "log.h"

void notify(), lnotify(), unlinkdf(), arrived();
static void stmesg();
static int nospace();

struct Proto {
	char P_id;
	int (*P_turnon)();
	int (*P_rdmsg)();
	int (*P_wrmsg)();
	int (*P_rddata)();
	int (*P_wrdata)();
	int (*P_turnoff)();
};

extern char _Protocol[];
extern char *findProto();

extern char uuxqtarg[];

extern int gturnon(), gturnoff();
extern int grdmsg(), grddata();
extern int gwrmsg(), gwrdata();

extern int wmesg(), rmesg(), expfile(), putinpub(), stptcl();
extern void setline(), TMname(), cleanup(), pfEndfile(), statlog(), mailst();

#ifdef	D_PROTOCOL
extern int dturnon(), dturnoff();
extern int drdmsg(), drddata();
extern int dwrmsg(), dwrdata();
#endif	/* D_PROTOCOL */

#ifdef	X_PROTOCOL
extern int xturnon(), xturnoff();
extern int xrdmsg(), xrddata();
extern int xwrmsg(), xwrdata();
#endif	/* X_PROTOCOL */

#ifdef E_PROTOCOL
extern int eturnon(), eturnoff();
extern int erdmsg(), erddata();
extern int ewrmsg(), ewrdata();
extern int trdmsg(), twrmsg();
extern int trddata(), twrdata();
#endif /* E_PROTOCOL */

#ifdef F_PROTOCOL
extern int fturnon(), fturnoff();
extern int frdmsg(), frddata();
extern int fwrmsg(), fwrdata();
#endif	/* F_PROTOCOL */

extern int imsg();
extern int omsg();
extern int turnoff();
extern long strtol();

struct Proto Ptbl[]={
	{'g', gturnon, grdmsg, gwrmsg, grddata, gwrdata, gturnoff},
	{'G', gturnon, grdmsg, gwrmsg, grddata, gwrdata, gturnoff},

#ifdef E_PROTOCOL
	{'e', eturnon, erdmsg, ewrmsg, erddata, ewrdata, eturnoff},
	{'t', eturnon, trdmsg, twrmsg, trddata, twrdata, eturnoff},
#endif /* E_PROTOCOL */ 

#ifdef	D_PROTOCOL
	{'d', dturnon, drdmsg, dwrmsg, drddata, dwrdata, dturnoff},
#endif	/* D_PROTOCOL */

#ifdef	X_PROTOCOL
	{'x', xturnon, xrdmsg, xwrmsg, xrddata, xwrdata, xturnoff},
#endif	/* X_PROTOCOL */

#ifdef	F_PROTOCOL
	{'f', fturnon, frdmsg, fwrmsg, frddata, fwrdata, fturnoff},
#endif	/* F_PROTOCOL */
	'\0'
};

#define VALIDSIZE sizeof(Ptbl)/sizeof(struct Proto)

int (*Rdmsg)()=imsg, (*Rddata)();
int (*Wrmsg)()=omsg, (*Wrdata)();
int (*Turnon)(), (*Turnoff)()=turnoff;


#define YES "Y"
#define NO "N"

#define TBUFSIZE	128	/* temporary buffer size */
#define FLENRADIX	(16)	/* output radix for file start point */

/*
 * failure messages
 */
#define EM_MAX		10
#define EM_LOCACC	"N1"	/* local access to file denied */
#define EM_RMTACC	"N2"	/* remote access to file/path denied */
#define EM_BADUUCP	"N3"	/* a bad uucp command was generated */
#define EM_NOTMP	"N4"	/* remote error - can't create temp */
#define EM_RMTCP	"N5"	/* can't copy to remote directory - file in public */
#define EM_LOCCP	"N6"	/* can't copy on local system */
#define EM_SEEK		"N7"	/* can't seek to checkpoint */
/* 	EM_		"N8" */	/* placeholder*/
/* 	EM_		"N9" */	/* placeholder*/
#define EM_ULIMIT	"N10"	/* receiver ulimit exceeded */

char *Em_msg[] = {
	"COPY FAILED (reason not given by remote)",
	"local access to file denied",
	"remote access to path/file denied",
	"system error - bad uucp command generated",
	"remote system can't create temp file",
	"can't copy to file/directory - file left in PUBDIR/user/file",
	"can't copy to file/directory - file left in PUBDIR/user/file",
	"can't seek to checkpoint",
	"COPY FAILED (reason not given by remote)", /* placeholder */
	"COPY FAILED (reason not given by remote)", /* placeholder */
	"file exceeds ulimit of receiving system",
	"forwarding error"
};


#define XUUCP 'X'	/* execute uucp (string) */
#define SLTPTCL 'P'	/* select protocol  (string)  */
#define USEPTCL 'U'	/* use protocol (character) */
#define RCVFILE 'R'	/* receive file (string) */
#define SNDFILE 'S'	/* send file (string) */
#define RQSTCMPT 'C'	/* request complete (string - yes | no) */
#define HUP     'H'	/* ready to hangup (string - yes | no) */
#define RESET	'X'	/* reset line modes */

#define W_MAX		10	/* maximum number of C. files per line */
#define W_MIN		7	/* min number of entries */
#define W_TYPE		wrkvec[0]
#define W_FILE1		wrkvec[1]
#define W_FILE2		wrkvec[2]
#define W_USER		wrkvec[3]
#define W_OPTNS		wrkvec[4]
#define W_DFILE		wrkvec[5]
#define W_MODE		wrkvec[6]
#define W_NUSER		wrkvec[7]
#define W_SFILE		wrkvec[8]
#define W_RDFILE	wrkvec[8]
#define W_POINT		wrkvec[9]
#define W_FSIZE		wrkvec[9]
#define W_RFILE		wrkvec[5]
#define W_XFILE		wrkvec[5]
char	*mf;

#define RMESG(m, s) if (rmesg(m, s) != 0) {(*Turnoff)(); return(FAIL);}
#define RAMESG(s) if (rmesg('\0', s) != 0) {(*Turnoff)(); return(FAIL);}
#define WMESG(m, s) if(wmesg(m, s) != 0) {(*Turnoff)(); return(FAIL);}

char Wfile[MAXFULLNAME] = {'\0'};
char Dfile[MAXFULLNAME];

char	*wrkvec[W_MAX+1];
int	statfopt;

/*
 * Create restart point filename
 */

static void
Pname(fileid, dfile, direct)
  char *fileid;
  char *dfile;
  int  direct;		/* indicates a direct delivery temp file nameneeded */
{
    char *p;

	    /*
	     * If the file is direct delivery, then its name is:
	     *
	     *		/dir/dir/dir/.Pnnnnnnnn
	     *
	     * in the target directory.  We create this by replacing the
	     * name of the target file with the D.nnnnnn name from the
	     * work vector, and then overwriting the D. with .P
	     */

	    if (direct) {
		if (p = strrchr(dfile, '/')) {	/* find the last slash */
			p++;
			strcpy(p, fileid);	/* append D.nnnnn name to dir */
			*p++ = '.';
			*p = 'P';		/* replace beginning with .P */
	    		DEBUG(7, "Point file (direct) =%s\n", dfile);
			return;
		}
	    }
	    strcpy(dfile, RemSpool);
	    strcat(dfile, "/");
	    p = dfile + strlen(Dfile);
	    strcat(dfile, fileid);
	    *p = 'P';
	    DEBUG(7, "Point file=%s\n", dfile);
	return;
}


/*
 * execute the conversation between the two machines 
 * after both programs are running.
 * returns:
 *	SUCCESS 	-> ok
 *	FAIL 		-> failed
 */
int
cntrl()
{
	FILE * fp;
	struct stat stbuf;
	char *	p;
	long 	startp;		/* checkpoint restart point */
	long	actualsize;	/* actual file size */
	long	im;
	long	lfilemode;
	mode_t	filemode;
	int	status;
	int	i, narg;
	int	mailopt, ntfyopt;
	int	ret;
	char	tbuf[TBUFSIZE];
	char	rqstr[BUFSIZ];	/* contains the current request message */
	char	msg[BUFSIZ];
	char	filename[MAXFULLNAME], wrktype;
	char	fsize[NAMESIZE];	/* holds file size/checkpoint string */
	char	localname[MAXFULLNAME];	/* real local system name */
	char	Recspool[MAXFULLNAME];	/* spool area for slave uucico */
	static pid_t	pnum;
	extern int uuxqtflag;	/* set if received X. or D. file */

	pnum = getpid();
	Wfile[0] = '\0';
	(void) sprintf(Recspool, "%s/%s", SPOOL, Rmtname);
top:
	(void) strcpy(User, Uucp);
	statfopt = 0;
	*Jobid = '\0';
	DEBUG(4, "*** TOP ***  -  Role=%d, ", Role);
	setline(RESET);
	if (Role == MASTER) {

		/*
		 * get work
		 */
		pfFindFile();
		if ((narg = gtwvec(Wfile, wrkvec, W_MAX)) == 0) {
			acEnd(COMPLETE); /*stop collecting accounting log */
			WMESG(HUP, ""); /* I(master) am done. want me to quit? */
			RMESG(HUP, msg);
			goto process;
		}
		DEBUG(7, "Wfile - %s,", Wfile);
		strncpy(Jobid, BASENAME(Wfile, '/')+2, NAMESIZE);
		Jobid[NAMESIZE-1] = '\0';
		DEBUG(7, "Jobid = %s\n", Jobid);
		wrktype = W_TYPE[0];
		pfFound(Jobid, W_OPTNS, Nstat.t_qtime);
		mailopt = strchr(W_OPTNS, 'm') != NULL;
		statfopt = strchr(W_OPTNS, 'o') != NULL;
		ntfyopt = strchr(W_OPTNS, 'n') != NULL;

		uucpname(localname); /* get real local machine name */
		acDojob(Jobid, localname, W_USER); 
		scRequser(W_USER); /* log requestor user id */

		/*
		 * We used to check for corrupt workfiles here (narg < 5),
		 * but we were doing it wrong, and besides, anlwrk.c is the
		 * appropriate place to do it.
		 */

		(void) sprintf(User, "%s", W_USER);
		if (wrktype == SNDFILE ) {
			(void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Myname,
			    W_FILE1, Rmtname, W_FILE2, User);

			/* log destination node, user and file name */

			scDest(Rmtname,NOTAVAIL,W_FILE2); 

			/* log source node, file owner, file name, mod time and size */

			scSrc(Myname,scOwn(W_FILE1),W_FILE1,scMtime(W_FILE1)
						,scSize(W_FILE1)); 
							
			logent(rqstr, "REQUEST");
			CDEBUG(1, "Request: %s\n", rqstr);
			mf = W_SFILE;
			(void) strcpy(filename, W_FILE1);
			expfile(filename);
			(void) strcpy(Dfile, W_DFILE);
			if ( (fp = fopen(Dfile, "r")) == NULL) {
			    if ( (fp = fopen(filename, "r")) == NULL) {
				/*  cannot read spool or original file */
				unlinkdf(Dfile);
				lnotify(User, rqstr, "can't access");
				(void) sprintf(msg, "CAN'T READ %s %d",
					filename, errno);
				logent(msg, "FAILED");
				CDEBUG(1, "Failed: Can't Read %s\n", filename);
		    		scWrite();	/* log the security violation */
				goto top;
			    } else {
				/* ensure original file is publicly readable */
				if ( !F_READANY(fileno(fp)) ) {
				    /* access denied */
				    logent("DENIED", "ACCESS");
				    unlinkdf(W_DFILE);
				    lnotify(User, rqstr, "access denied");
				    CDEBUG(1, "Failed: Access Denied\n%s", "");
		    		    scWrite();	/* log the security violation */
				    goto top;
				}
			    }
			}

			if (Restart && !(fstat(fileno(fp), &stbuf))) {
				(void) sprintf(fsize, "0x%lx", stbuf.st_size);
				W_FSIZE = fsize; /* set file size in vector */
			}

			/* Check whether remote's ulimit is exceeded */
			if (SizeCheck) {
				if (((stbuf.st_size-1)/512 + 1) > RemUlimit) {
					/* remote ulimit exceeded */
					unlinkdf(Dfile);
					lnotify(User, rqstr, "remote ulimit exceeded");
					logent("DENIED", "REMOTE ULIMIT EXCEEDED");
					CDEBUG(1, "Denied: remote ulimit exceeded %s\n", filename);
					scWrite();
					(void) fclose(fp);
					goto top;
				}
			}
		}

		if (wrktype == RCVFILE) {
			(void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Rmtname,
			    W_FILE1, Myname, W_FILE2, User);

			/* log destination node, user and file name */

			scDest(Myname,NOTAVAIL,W_FILE2); 

			/* log source node, file owner, file name, mod time and size */

			scSrc(Rmtname,NOTAVAIL,W_FILE1,NOTAVAIL,NOTAVAIL); 

			logent(rqstr, "REQUEST");
			CDEBUG(1, "Request: %s\n", rqstr);
			mf = W_RFILE;
			(void) strcpy(filename, W_FILE2);

			/* change Wrkdir to SPOOL/Rmtname in case the file being
			** requested is needed for some remote execution.
			*/

			(void) strcpy(Wrkdir, Recspool);
			expfile(filename);

			/* now change Wrkdir back to what it was 
			** just being paranoid.
			*/

			(void) strcpy(Wrkdir, RemSpool);
			if (chkperm(W_FILE1, filename, strchr(W_OPTNS, 'd'))) {

				/* access denied */
				logent("DENIED", "ACCESS");
				lnotify(User, rqstr, "access denied");
				CDEBUG(1, "Failed: Access Denied--File: %s\n", 
				    filename);
		    		scWrite();	/* log the security violation */
				goto top;
			}

			/*
			 * If we are not going to spool the file in the spool
			 * directory, just use the destination file name.  If we
			 * are not supporting restart, wipe out the target file.
			 * else:
			 *
			 * If restart is enabled, make up the Point file name
			 * as the file to open, else use the TM style name.
			 *
			 * If we run into a spool name of "D.0", this implies
			 * that someone forgot to install the new uucp and
			 * uux commands.  Such jobs will not be checkpointed.
			 */

			    
			if (Restart && (strlen(W_RDFILE) > (size_t) 6)) {
				if (noSpool()) {
				    strcpy(Dfile, filename);	/* use Dest file directly */
				    Pname(W_RDFILE, Dfile, TRUE);
				}
				else
				    Pname(W_RDFILE, Dfile, FALSE);
			}
			else {
			    TMname(Dfile, pnum); /* get TM file name */
			    unlink(Dfile);
			}

			/*
			 * If the spool file exists, it better have the right owner
			 * and permissions!
			 */

			if (Restart && noSpool()) {
				if ((! stat(Dfile, &stbuf)) &&
				    ((stbuf.st_mode != (DFILEMODE|S_IFREG)) ||
				    ((stbuf.st_gid != UUCPGID) ||
				     (stbuf.st_uid != UUCPUID)))) {
				    lnotify(User, rqstr,
					"bad spool file ownership/permissions");
				    logent("BAD DESTFILE OWNER/PERMS", "FAIL");
				    CDEBUG(1, "Failed: bad dest file owner/perms 0%o; fail\n", stbuf.st_mode);
				    goto top;
				}
			}
			if ( ((fp = fopen(Dfile, "a+")) == NULL)
			     || nospace(Dfile)) {

				/* can not create temp */
				if (noSpool())
				    logent("CAN'T CREATE/OPEN DEST FILE", "FAILED");
				else
				    logent("CAN'T CREATE TM FILE", "FAILED");
				CDEBUG(1, "Failed: No Space!\n%s", "");
				unlinkdf(Dfile);
				assert(Ct_CREATE, Dfile, nospace(Dfile),
				    __FILE__, __LINE__);
				cleanup(FAIL);
			}

			/*
			 * Send the W_POINT value to the other side.
			 */

			if (Restart) {
			    if (fstat (fileno(fp), &stbuf)) {
				logent("CAN'T STAT DFILE", "START FROM BEGINNING");
				stbuf.st_size = 0L;
			    }

			    /*
			     * find a good start point.  Take care of simple
			     * underflow and the signed nature of longs.
			     */

			    DEBUG(7, "Dfile length 0x%lx\n", stbuf.st_size);
			    startp = stbuf.st_size - (stbuf.st_size % BUFSIZ);
			    if((stbuf.st_size >= 0) && (startp < 0))
				startp = 0;

			    if(startp)
			    {
				if(startp < 0)
				    sprintf(tbuf,"start=0x%lx", startp);
				else
				    sprintf(tbuf,"start=%ld", startp);
	
				logent(tbuf, "RESTART");
			    }

			    sprintf(fsize, "0x%lx", startp);
			    W_POINT = fsize; /* set start point in vector */
			    if (lseek(fileno(fp), startp, 0) == -1) {
				WMESG(SNDFILE, EM_SEEK);
				logent("CAN'T SEEK", "DENIED");
				CDEBUG(1, "Failed, Can't seek in Dfile\n%s", "");
				unlinkdf(Dfile);
				goto top;
		    	    }
		    	    fp->_cnt = 0;
		    	    fp->_ptr = fp->_base;
			}

			Seqn++;
			chmod(Dfile, DFILEMODE);	/* no peeking! */
			chown(Dfile, UUCPUID, UUCPGID);

		}
		DEBUG(4, "wrktype - %c\n ", wrktype);

		/* Build up the message itself */

		msg[0] = '\0';
		for (i = 1; i < narg; i++) {
			(void) strcat(msg, " ");
			(void) strcat(msg, wrkvec[i]);
		}

		WMESG(wrktype, msg); /* I(master) am sending you our work file */
		RMESG(wrktype, msg); /* I(master) am waiting for your response */
		goto process;
	}

	/*
	 * role is slave
	 */

	RAMESG(msg); /* I(slave) am waiting for our work file */

process:

	DEBUG(4, " PROCESS: msg - %s\n", msg);
	switch (msg[0]) {

	case RQSTCMPT:
		DEBUG(4, "%s\n", "RQSTCMPT:");
		if (msg[1] == 'N') {
			i = atoi(&msg[2]);
			if (i < 0 || i > EM_MAX) 
				i = 0;
			logent(Em_msg[i], "REQUESTED");
		}
		if (Role == MASTER) {
		        notify(mailopt, W_USER, rqstr, Rmtname, &msg[1]);
		}
		pfEndfile("");	/* "" indicates the file transfer completely */
		goto top;

	case HUP:
		DEBUG(4, "%s\n", "HUP:");
		if (msg[1] == 'Y') {
			WMESG(HUP, YES); /* let's quit */
			(*Turnoff)();
			Rdmsg = imsg;
			Wrmsg = omsg;
			Turnoff = turnoff;
			return(0);
		}

		if (msg[1] == 'N') {
			ASSERT(Role == MASTER, Wr_ROLE, "", Role);
			Role = SLAVE;
			scReqsys(Rmtname); /* log requestor system */
			chremdir(Rmtname);
			goto top;
		}

		/*
		 * get work
		 */
		if ( (switchRole() == FALSE) || !iswrk(Wfile) ) {
			DEBUG(5, "SLAVE-switchRole (%s)\n",
			    switchRole() ? "TRUE" : "FALSE");
			WMESG(HUP, YES); /* let's quit */
			RMESG(HUP, msg);
			goto process;
		}

		/* Note that Wfile is the first C. to process at top
		 * set above by iswrk() call
		 */
		if (uuxqtflag) {
			xuuxqt(uuxqtarg);
			uuxqtflag = 0;
		}
		WMESG(HUP, NO); /* don't quit. I(slave) have more to do */
		Role = MASTER;
		uucpname(localname); /* get real local machine name */
		scReqsys(localname); /* log requestor system */
		acInit("xfer");
		goto top;

	case XUUCP:
		/*
		 * slave part
		 * No longer accepted
		 */

		WMESG(XUUCP, NO);
		goto top;

	case SNDFILE:

		/*
		 * MASTER section of SNDFILE
		 */
		DEBUG(4, "%s\n", "SNDFILE:");
		if (msg[1] == 'N')
		   {
		    i = atoi(&msg[2]);
		    if (i < 0 || i > EM_MAX)
			i = 0;
		    logent(Em_msg[i], "REQUEST");
		    notify(mailopt, W_USER, rqstr, Rmtname, &msg[1]);
		    ASSERT(Role == MASTER, Wr_ROLE, "", Role);
		    (void) fclose(fp);
		    /* if remote is out of tmp space, then just hang up */
		    ASSERT(i != 4, Em_msg[4], Rmtname, i);	/* EM_NOTMP */
		    unlinkdf(W_DFILE);
		    scWrite();	/* something is wrong on other side, 
				   log the security violation */
		    Seqn++;
		    goto top;
		   }

		if (msg[1] == 'Y') {

			/*
			 * send file
			 */
			ASSERT(Role == MASTER, Wr_ROLE, "", Role);
			if (fstat(fileno(fp), &stbuf)) /* never fail but .. */
			    stbuf.st_size = 0;  /* for time loop calculation */

			/*
			 * If checkpoint restart is enabled, seek to the 
			 * starting point in the file.  We use hex because
			 * C doesn't support unsigned long directly.
			 */

			if (Restart) {
			    if((startp = strtol(&msg[2], (char **) 0, FLENRADIX))) {
			        CDEBUG(1, "Restart point=0x%lx\n", startp);
				if(startp < 0)
				    sprintf(tbuf,"start=0x%lx", startp);
				else
				    sprintf(tbuf,"start=%ld", startp);
				p = tbuf + strlen(tbuf);
				if (stbuf.st_size < 0)
				    sprintf(p,", length=0x%lx", stbuf.st_size);
				else
				    sprintf(p,", length=%ld", stbuf.st_size);
	
				logent(tbuf, "RESTART");
				errno = 0;
				if (lseek(fileno(fp), startp, 0) == -1) {
				    logent(strerror(errno), "FSEEK ERROR");
				    (void) fclose(fp);
				    (*Turnoff)();
		    		    Seqn++;
				    return(FAIL);
				}
				fp->_cnt = 0;
				fp->_ptr = fp->_base;
			    }
			}
			(void) millitick();	/* start msec timer */
			pfStrtXfer(MCHAR, SNDFILE);
			scStime(); 	/* log start transfer time for security log */

		/* (ret != 0) implies the trammission error occurred.
     	           If checkpoint protocol is available then the next 
		   transfer will restart from the breakpoint of the file, 
		   otherwise from the beginning of the file  */

			ret = (*Wrdata)(fp, Ofn);

		/* the second millitick() returns the duration between 
		   the first and second call. 
   		   writes "PARTIAL FILE to the transfer log indicating  
		   a transmission error. */

			statlog( "->", getfilesize(), millitick(),
					(ret) ? "PARTIAL FILE" : ""  );

			acInc();	/* increment job size in accounting log					 */
			pfEndXfer();
			scEtime();	/* log end transfer time for security log */
		    	Seqn++;
			(void) fclose(fp);
			if (ret != 0) {
				pfEndfile("PARTIAL FILE");
				acEnd(PARTIAL); /*stop collecting accounting log */
				(*Turnoff)();
				return(FAIL);
			}

			/* loop depending on the size of the file */
			/* give an extra try for each megabyte */
			for (im = stbuf.st_size >> 10; im >= 0; --im) {
		    	    if ((ret = rmesg(RQSTCMPT, msg)) == 0)
				break;	/* got message */
			}
			if (ret != 0) {
			    (*Turnoff)();
			     return(FAIL);
			}
			unlinkdf(W_DFILE);
			goto process;
		}

		/* 
		 * SLAVE section of SNDFILE
		 */
		ASSERT(Role == SLAVE, Wr_ROLE, "", Role);

		/*
		 * request to receive file
		 * check permissions
		 */
		i = getargs(msg, wrkvec, W_MAX);

		scRequser(W_USER); /* log requestor user id */

		/* log destination node, user and file name */

		scDest(Myname,NOTAVAIL,W_FILE2); 

		/* log source node, file owner, file name, mod time and size */

		scSrc(Rmtname,NOTAVAIL,W_FILE1,NOTAVAIL,NOTAVAIL); 

		/* Check for bad request */
		if (i < W_MIN) {
			WMESG(SNDFILE, EM_BADUUCP); /* you(remote master) gave me
							bad work file */
			logent("DENIED", "TOO FEW ARGS IN SLAVE SNDFILE");
			goto top;
		}
		/* SLAVE gets the original filesize from sender (MASTER) */
		/* This will be used to check the length of the P. file */
		if (Restart) {
		    if (W_FSIZE && (*W_FSIZE != '\0')) {
			actualsize = strtol(W_FSIZE, (char **) 0, FLENRADIX);
			CDEBUG(7, "Actual File Length %ld\n", actualsize);
		    } else {
			actualsize = -1;
			CDEBUG(7, "Actual File Length Not Provided\n%s", "");
		    }
		}

		mf = W_SFILE;
		(void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Rmtname,
			    W_FILE1, Myname, W_FILE2, W_USER);
		logent(rqstr, "REMOTE REQUESTED");
		DEBUG(4, "msg - %s\n", msg);
		CDEBUG(1, "Remote Requested: %s\n", rqstr);
		(void) strcpy(filename, W_FILE2);
		expfile(filename);
		DEBUG(4, "SLAVE - filename: %s\n", filename);
		if (chkpth(filename, CK_WRITE)
		     || chkperm(W_FILE1, filename, strchr(W_OPTNS, 'd'))) {
			WMESG(SNDFILE, EM_RMTACC); /* you(remote master) can't 
						send data to this file(directory) */
			logent("DENIED", "PERMISSION");
			CDEBUG(1, "Failed: Access Denied\n%s", "");
		    	scWrite(); /* log security violation */	
			goto top;
		}
		(void) sprintf(User, "%s", W_USER);

		DEBUG(4, "chkpth ok Rmtname - %s\n", Rmtname);

		

		if (Restart && (strlen(W_DFILE) > (size_t) 6)) {
			if (noSpool()) {
			    strcpy(Dfile, filename);	/* use Dest file directly */
			    Pname(W_DFILE, Dfile, TRUE);
			    if (! Restart)
				unlink(Dfile);
			}
			else
			    Pname(W_DFILE, Dfile, FALSE);
		}
		else {
		    TMname(Dfile, pnum); /* get TM file name */
		    unlink(Dfile);
		}

		/*
		 * If the spool file exists, it better have the right owner
		 * and permissions!
		 */

		if (Restart && noSpool()) {
			if ((! stat(Dfile, &stbuf)) &&
			    ((stbuf.st_mode != (DFILEMODE|S_IFREG)) ||
			    ((stbuf.st_gid != UUCPGID) ||
			     (stbuf.st_uid != UUCPUID)))) {
			    WMESG(SNDFILE, EM_NOTMP); /* I(slave) see bad perms */
			    logent("BAD DESTFILE OWNER/PERMS", "FAILED");
			    CDEBUG(1, "Failed: bad dest file owner/perms 0%o\n", stbuf.st_mode);
			    goto top;
			}
		}
		if ( ((fp = fopen(Dfile, "a+")) == NULL) || nospace(Dfile) ) {
			WMESG(SNDFILE, EM_NOTMP); /* I(slave) can't create TM file */
			logent("CAN'T OPEN", "DENIED");
			CDEBUG(1, "Failed: Can't Create Temp File\n%s", "");
			unlinkdf(Dfile);
			goto top;
		}
		chmod(Dfile, DFILEMODE);	/* no peeking! */
		chown(Dfile, UUCPUID, UUCPGID);
		if (Restart && (strlen(W_DFILE) > (size_t) 6)) {
		    if(fstat(fileno(fp), &stbuf)) {
			WMESG(SNDFILE, EM_NOTMP);
			logent("CAN'T STAT", "DENIED");
			CDEBUG(1, "Failed: Can't Stat Temp File\n%s", "");
			unlinkdf(Dfile);
			Seqn++;
			goto top;
		    }
		    /*
		     * find a good start point.  Take care of simple underflow
		     * and the signed nature of longs.
		     */

		    DEBUG(7, "Dfile length 0x%lx\n", stbuf.st_size);
		    startp = stbuf.st_size - (stbuf.st_size % BUFSIZ);
		    if((stbuf.st_size >= 0) && (startp < 0))
			startp = 0;

		    if(startp)
		    {
			if(startp < 0)
			    sprintf(tbuf,"start=0x%lx", startp);
			else
			    sprintf(tbuf,"start=%ld", startp);

			logent(tbuf, "RESTART");
		    }

		    sprintf(tbuf, "%s 0x%lx", YES, startp);
		    if (lseek(fileno(fp), startp, 0) == -1) {
			WMESG(SNDFILE, EM_SEEK);
			logent("CAN'T SEEK", "DENIED");
			CDEBUG(1, "Failed, Can't seek in Dfile\n%s", "");
			unlinkdf(Dfile);
			Seqn++;
			goto top;
		    }
		    fp->_cnt = 0;
		    fp->_ptr = fp->_base;
		    CDEBUG(1," restart msg %s\n", tbuf);
		    WMESG(SNDFILE, tbuf);	
		}
		else
		    WMESG(SNDFILE, YES);	/* I(slave) clear to send */
		(void) millitick();	/* start msec timer */
		pfStrtXfer(SCHAR, RCVFILE);
		scStime(); 	/* log start transfer time for security log */
		/* (ret != 0) implies the trammission error occurred.
     	           If checkpoint protocol is available then the next 
		   recieve will restart from the breakpoint of the file, 
		   otherwise from the beginning of the file  */

		setline(RCVFILE);
		ret = (*Rddata)(Ifn, fp);
		setline(SNDFILE);

		/* the second millitick() returns the duration between 
		   the first and second call. 
   		   writes "PARTIAL FILE to the transfer log indicating  
		   a transmission error. */

		statlog( "<-", getfilesize(), millitick(),
				(ret) ? "PARTIAL FILE" : ""  );

		pfEndXfer();
		scEtime();	/* log end transfer time for security log */
		Seqn++;

		if (ret != 0) {
			pfEndfile("PARTIAL FILE");
			(void) fclose(fp);
			if ( ret == EFBIG ) {
				WMESG(RQSTCMPT, EM_ULIMIT);
				logent("FILE EXCEEDS ULIMIT","FAILED");
				CDEBUG(1, "Failed: file size exceeds ulimit%s\n", "");
				goto top;
			}
			(*Turnoff)();
			logent("INPUT FAILURE", "IN SEND/SLAVE MODE");
			return(FAIL);
		}
		if (Restart && (actualsize != -1)) {
		    if (fstat(fileno(fp), &stbuf)) {
			(void) fclose(fp);
			unlinkdf(Dfile);
			(*Turnoff)();
			logent("CAN'T STAT PFILE", "FAILED");
			return(FAIL);
		    }
		    if (stbuf.st_size != actualsize) {
			(void) fclose(fp);
			unlinkdf(Dfile);
			(*Turnoff)();
			logent("RECEIVED SIZE NOT EQUAL TO ACTUAL SIZE", "FAILED");
			CDEBUG(1, "Failed: receive size %ld ", stbuf.st_size);
			CDEBUG(1, "not equal to actual size %ld\n", actualsize);
			return(FAIL);
		    }
		}
		(void) fclose(fp);

		/* copy to user directory */
		ntfyopt = strchr(W_OPTNS, 'n') != NULL;

		/*
		 * See if spool file and target file in the same file system
		 */

		ret = 0;
		if (p = strrchr(Dfile, '/'))
		{
			*p = '\0';
			ret = PREFIX(Dfile, filename);
			*p = '/';
		}
			
		if (noSpool() && ret)
		{
		    /*
		     * if we are not already in the right file, and
		     * it is theoretically in the same file system,
		     * link it there...
		     */

		    if(strcmp (filename, Dfile)) {
			    unlink(filename);
			    if(link(Dfile, filename))
			    {
				logent("FAILED", "MOVE");
				scWrite();
				putinpub(filename, Dfile, BASENAME(W_USER,'!'));
			    }
			    else
	    			DEBUG(7, "linked Point file to %s\n", filename);
			    unlink(Dfile);
		    }
		    else
		    	DEBUG(7, "Point file and %s the same\n", filename);
		    status = 0;			/* all done */
		}
		else
		    status = xmv(Dfile, filename);

		scSize(Dfile);	/* log source file size */
		WMESG(RQSTCMPT, status ? EM_RMTCP : YES);
		if (status == 0) {
			sscanf(W_MODE, "%lo", &lfilemode);
			if (lfilemode <= 0)
				filemode = PUB_FILEMODE;
			else
				filemode = (mode_t)lfilemode;
			if (PREFIX(RemSpool, filename))
			    chmod(filename, DFILEMODE);
			else
			    chmod(filename, (filemode & LEGALMODE) | PUB_FILEMODE);
			arrived(ntfyopt, filename, W_NUSER, Rmtname, User);
		} else {
			logent("FAILED",  "COPY");
			scWrite();	/* log the security violation */
			status = putinpub(filename, Dfile,
			    BASENAME(W_USER, '!'));
			DEBUG(4, "->PUBDIR %d\n", status);
			if (status == 0)
				arrived(ntfyopt, filename, W_NUSER,
				    Rmtname, User);
		}
		pfEndfile("");	/* "" indicates the file transfer completely */
		if ( W_FILE2[1] == '.'  &&
		    (W_FILE2[0] == XQTPRE || W_FILE2[0] == DATAPRE) )
			uuxqtflag = 1;
		goto top;

	case RCVFILE:

		/*
		 * MASTER section of RCVFULE 
		 */
		DEBUG(4, "%s\n", "RCVFILE:");
		if (msg[1] == 'N') {
			i = atoi(&msg[2]);
			if (i < 0 || i > EM_MAX)
				i = 0;
			logent(Em_msg[i], "REQUEST");
		        notify(mailopt, W_USER, rqstr, Rmtname, &msg[1]);
			ASSERT(Role == MASTER, Wr_ROLE, "", Role);
			(void) fclose(fp);
			unlinkdf(Dfile);
		    	scWrite();	/* something is wrong on other side, 
					   log the security violation */
			goto top;
		}

		if (msg[1] == 'Y') {

		/* MASTER gets the original filesize from sender (SLAVE) */
		/* This will be used to check the length of the P. file */
			if (Restart) {
			    *fsize = '\0';
			    sscanf(&msg[2], "%*o %s", fsize);
			    if (*fsize != '\0') {
				actualsize = strtol(fsize, (char **) 0, FLENRADIX);
				CDEBUG(7, "Actual File Length %ld\n", actualsize);
			    } else {
				actualsize = -1;
				CDEBUG(7, "Actual File Length Not Provided\n%s", "");
			    }
			}

			/*
			 * receive file
			 */
			ASSERT(Role == MASTER, Wr_ROLE, "", Role);
			(void) millitick();	/* start msec timer */
			pfStrtXfer(MCHAR, SNDFILE);
			scStime();
		/* (ret != 0) implies the trammission error occurred.
     	           If checkpoint protocol is available then the next 
		   recieve will restart from the breakpoint of the file, 
		   otherwise from the beginning of the file  */

			ret = (*Rddata)(Ifn, fp);

		/* the second millitick() returns the duration between 
		   the first and second call. 
   		   writes "PARTIAL FILE to the transfer log indicating  
		   a transmission error. */

			statlog( "<-", getfilesize(), millitick(),
				(ret) ? "PARTIAL FILE" : ""  );
			pfEndXfer();
			scEtime();
			if (ret != 0) {
				pfEndfile("PARTIAL FILE");
				(void) fclose(fp);
				if ( ret == EFBIG ) {
					WMESG(RQSTCMPT, EM_ULIMIT);
					logent("FILE EXCEEDS ULIMIT","FAILED");
					CDEBUG(1, "Failed: file size exceeds ulimit%s\n", "");
					goto top;
				}
				(*Turnoff)();
				logent("INPUT FAILURE", "IN RECEIVE/MASTER MODE");
				return(FAIL);
			}
			if (Restart && (actualsize != -1)) {
			    if (fstat(fileno(fp), &stbuf)) {
				(void) fclose(fp);
				unlinkdf(Dfile);
				(*Turnoff)();
				logent("CAN'T STAT PFILE", "FAILED");
				return(FAIL);
			    }
			    if (stbuf.st_size != actualsize) {
				(void) fclose(fp);
				unlinkdf(Dfile);
				(*Turnoff)();
				logent("RECEIVED SIZE NOT EQUAL TO ACTUAL SIZE", "FAILED");
				CDEBUG(1, "Failed: receive size %ld ", stbuf.st_size);
				CDEBUG(1, "not equal to actual size %ld\n", actualsize);
				return(FAIL);
			    }
			}
			(void) fclose(fp);

			/*
			 * See if spool file and target file in the same file system
			 */

			ret = 0;
			if (p = strrchr(Dfile, '/'))
			{
				*p = '\0';
				ret = PREFIX(Dfile, filename);
				*p = '/';
			}
				
			if (noSpool() && ret)
			{
			    /*
			     * if we are not already in the right file, and
			     * it is theoretically in the same file system,
			     * link it there...
			     */

			    if(strcmp (filename, Dfile)) {
				    unlink(filename);
				    if(link(Dfile, filename))
				    {
					logent("FAILED", "MOVE");
					scWrite();
					putinpub(filename, Dfile, W_USER);
				    }
				    else
					DEBUG(7, "linked Point file to %s\n", filename);
				    unlink(Dfile);
			    }
			    else
				DEBUG(7, "Point file and %s the same\n", filename);
			    status = 0;			/* all done */
			}
			else
			    status = xmv(Dfile, filename);

			WMESG(RQSTCMPT, status ? EM_RMTCP : YES);
			notify(mailopt, W_USER, rqstr, Rmtname,
			    status ? EM_LOCCP : YES);
			if (status == 0) {
			    sscanf(&msg[2], "%lo", &lfilemode);
			    if (lfilemode <= 0)
				filemode = PUB_FILEMODE;
			    else
				filemode = (mode_t)lfilemode;
			    if (PREFIX(RemSpool, filename))
				chmod(filename, DFILEMODE);
			    else
				chmod(filename, (filemode & LEGALMODE) | PUB_FILEMODE);
			} else {
			    logent("FAILED", "COPY");
		    	    scWrite();	/* log the security violation */
			    putinpub(filename, Dfile, W_USER);
			}
			pfEndfile("");	/* "" indicates the file transfer completely */
		if ( W_FILE2[1] == '.'  &&
		    (W_FILE2[0] == XQTPRE || W_FILE2[0] == DATAPRE) )
			uuxqtflag = 1;
			goto top;
		}

		/*
		 * SLAVE section of RCVFILE
		 * (request to send file)
		 */
		ASSERT(Role == SLAVE, Wr_ROLE, "", Role);

		/* check permissions */
		i = getargs(msg, wrkvec, W_MAX);

		scRequser(W_USER); /* log requestor user id */

		/* log destination node, user and file name */

		scDest(Rmtname,NOTAVAIL,W_FILE2); 

		/* log source node, file owner, file name, mod time and size */

		scSrc(Myname,scOwn(W_FILE1),W_FILE1,scMtime(W_FILE1),scSize(W_FILE1)); 
		/* Check for bad request */
		if (i < 5) {
			WMESG(RCVFILE, EM_BADUUCP); /* you(remote master) gave me
							bad work file */
			logent("DENIED", "TOO FEW ARGS IN SLAVE RCVFILE");
			goto top;
		}

		(void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Myname,
			    W_FILE1, Rmtname, W_FILE2, W_USER);
		logent(rqstr, "REMOTE REQUESTED");
		CDEBUG(1, "Remote Requested: %s\n", rqstr);
		mf = W_RFILE;
		DEBUG(4, "msg - %s\n", msg);
		DEBUG(4, "W_FILE1 - %s\n", W_FILE1);
		(void) strcpy(filename, W_FILE1);
		expfile(filename);
		if (DIRECTORY(filename)) {
			(void) strcat(filename, "/");
			(void) strcat(filename, BASENAME(W_FILE2, '/'));
		}
		(void) sprintf(User, "%s", W_USER);

		if (requestOK() == FALSE) {
			/* remote can't request data from my system */
			WMESG(RCVFILE, EM_RMTACC);
			logent("DENIED", "REQUESTING");
			CDEBUG(1, "Failed: Access Denied\n%s", "");
		    	scWrite();	/* log the security violation */
			goto top;
		}
		DEBUG(4, "requestOK for Loginuser - %s\n", Loginuser);

		if ((fp = fopen(filename, "r")) == NULL) {
			WMESG(RCVFILE, EM_RMTACC); /* you(remote master) can't
							read my file */
			logent("CAN'T OPEN", "DENIED");
			CDEBUG(1, "Failed: Can't Open %s\n", filename);
		    	scWrite();	/* log the security violation */
			goto top;
		}

		if (chkpth(filename, CK_READ) || !F_READANY(fileno(fp))) {
			WMESG(RCVFILE, EM_RMTACC); /* you(remote master) can't 
							retrive my file */
			logent("DENIED", "PERMISSION");
			CDEBUG(1, "Failed: Access Denied\n%s", "");
		    	scWrite();	/* log the security violation */
			fclose(fp);
			goto top;
		}
		DEBUG(4, "chkpth ok Loginuser - %s\n", Loginuser);

		ASSERT(fstat(fileno(fp), &stbuf) == 0, Ct_STAT,
		    filename, errno);

		/* Check whether remote's ulimit is exceeded */
		if (SizeCheck) {
			if (((stbuf.st_size-1)/512 + 1) > RemUlimit) {
				/* remote ulimit exceeded */
				WMESG(RCVFILE, EM_ULIMIT);
				logent("DENIED", "REMOTE ULIMIT EXCEEDED");
				CDEBUG(1, "Denied: remote ulimit exceeded %s\n", filename);
				scWrite();
				(void) fclose(fp);
				goto top;
			}
		}

		/*
		 * ok to send file
		 */
		
		if (Restart && i >= 10) {
		    if (startp = strtol(W_POINT, (char **) 0, FLENRADIX)) {
			CDEBUG(1,"Restart point=0x%lx\n", startp);
			errno = 0;
			if (lseek(fileno(fp), startp, 0) == -1) {
			    WMESG(RCVFILE, EM_SEEK);
			    logent(strerror(errno), "FSEEK ERROR");
			    (void) fclose(fp);
			    goto top;
			}
			fp->_cnt = 0;
			fp->_ptr = fp->_base;
			if(startp < 0)
			    sprintf(tbuf,"start=0x%lx", startp);
			else
			    sprintf(tbuf,"start=%ld", startp);
			p = tbuf + strlen(tbuf);
			if (stbuf.st_size < 0)
			    sprintf(p,", length=0x%lx", stbuf.st_size);
			else
			    sprintf(p,", length=%ld", stbuf.st_size);

			logent(tbuf, "RESTART");
		   }
		}

		if (Restart)
			(void) sprintf(msg, "%s %lo 0x%lx", YES,
				(long) (stbuf.st_mode & LEGALMODE),
				(long) stbuf.st_size);
		else
			(void) sprintf(msg, "%s %lo", YES,
				(long) (stbuf.st_mode & LEGALMODE));
		WMESG(RCVFILE, msg); /* I(slave) send you my file now */
		Seqn++;
		(void) millitick();	/* start msec timer */
		scStime();
		pfStrtXfer(SCHAR, SNDFILE);
		/* (ret != 0) implies the trammission error occurred.
     	           If checkpoint protocol is available then the next 
		   transfer will restart from the breakpoint of the file, 
		   otherwise from the beginning of the file  */

		ret = (*Wrdata)(fp, Ofn);

		/* the second millitick() returns the duration between 
		   the first and second call. 
   		   writes "PARTIAL FILE to the transfer log indicating  
		   a transmission error. */

		statlog( "->", getfilesize(), millitick(),
					(ret) ? "PARTIAL FILE" : ""  );
		pfEndXfer();
		scEtime();

		(void) fclose(fp);
		if (ret != 0) {
			pfEndfile("PARTIAL FILE");
			(*Turnoff)();
			return(FAIL);
		}

		/* loop depending on the size of the file */
		/* give an extra try for each megabyte */
		/* stbuf set in fstat several lines back  */
		for (im = stbuf.st_size >> 10; im >= 0; --im) {
		    if ((ret = rmesg(RQSTCMPT, msg)) == 0)
			break;	/* got message */
		}
		if (ret != 0) {
		    (*Turnoff)();
		     return(FAIL);
		}
		goto process;
	}
	(*Turnoff)();
	return(FAIL);
}



/*
 * read message
 * returns:
 *	0	-> success
 *	FAIL	-> failure
 */
int
rmesg(c, msg)
char *msg, c;
{
	char str[50];

	DEBUG(4, "rmesg - '%c' ", c);
	if ((*Rdmsg)(msg, Ifn) != 0) {
		DEBUG(4, "got %s\n", "FAIL");
		(void) sprintf(str, "expected '%c' got FAIL", c);
		logent(str, "BAD READ");
		return(FAIL);
	}
	if (c != '\0' && msg[0] != c) {
		DEBUG(4, "got %s\n", msg);
		(void) sprintf(str, "expected '%c' got %s", c, msg);
		logent(str, "BAD READ");
		return(FAIL);
	}
	DEBUG(4, "got %s\n", msg);
	return(0);
}


/*
 * write a message
 * returns:
 *	0	-> ok
 *	FAIL	-> ng
 */
int
wmesg(m, s)
char *s, m;
{
	CDEBUG(4, "wmesg '%c'", m);
	CDEBUG(4, "%s\n", s);
	return((*Wrmsg)(m, s, Ofn));
}


/*
 * mail results of command
 * return: 
 *	none
 */
void
notify(mailopt, user, msgin, sys, msgcode)
char *user, *msgin, *sys;
char *msgcode;
{
	int i;
	char str[BUFSIZ];
	char *msg;

	DEBUG(4,"mailopt %d, ", mailopt);
	DEBUG(4,"statfopt %d\n", statfopt);
	if (statfopt == 0 && mailopt == 0 && *msgcode == 'Y')
		return;
	if (*msgcode == 'Y')
		msg = "copy succeeded";
	else {
		i = atoi(msgcode + 1);
		if (i < 1 || i > EM_MAX)
			i = 0;
		msg = Em_msg[i];
	}
	if(statfopt){
		stmesg(msgin, msg);
		return;
	}
	(void) sprintf(str, "REQUEST: %s\n(SYSTEM: %s)  %s\n",
		msgin, sys, msg);
	mailst(user, msg, str, "", "");
	return;
}

/*
 * local notify
 * return:
 *	none
 */
void
lnotify(user, msgin, mesg)
char *user, *msgin, *mesg;
{
	char mbuf[BUFSIZ];

	if(statfopt){
		stmesg(msgin, mesg);
		return;
	}
	(void) sprintf(mbuf, "REQUEST: %s\n(SYSTEM: %s)  %s\n",
		msgin, Myname, mesg);
	mailst(user, mesg, mbuf, "", "");
	return;
}

/*ARGSUSED*/
static void
stmesg(f, m)
char	*f, *m;
{
#ifdef notdef
	FILE	*Cf;
	time_t	clock;
	long	td, th, tm, ts;
#endif
	char msg[BUFSIZ];

	DEBUG(4,"STMES %s\n",mf);
	sprintf(msg, "STMESG - %s", mf);
	logent("DENIED", msg);
#ifdef notdef
	/*
	 * This code is a giant security hole.
	 * No checking is done on what file is
	 * written and chmod'ed.  For now we
	 * just ifdef this out.
	 */
	if((Cf = fopen(mf, "a+")) == NULL){
		chmod(mf, PUB_FILEMODE);
		return;
	}
	(void) time(&clock);
	(void) fprintf(Cf, "uucp job: %s (%s) ", Jobid, timeStamp());
	td = clock - Nstat.t_qtime;
	ts = td%60;
	td /= 60;
	tm = td%60;
	td /= 60;
	th = td;
	(void) fprintf(Cf, "(%ld:%ld:%ld)\n%s\n%s\n\n", th, tm, ts, f, m);
	(void) fclose(Cf);
	chmod(mf, PUB_FILEMODE);
#endif
}

/*
 * converse with the remote machine, agree upon a 
 * protocol (if possible) and start the protocol.
 * return:
 *	SUCCESS	-> successful protocol selection
 *	FAIL	-> can't find common or open failed
 */
int
startup(void)
{
	extern void blptcl();
	extern int fptcl();
	char msg[BUFSIZ], str[BUFSIZ];

	Rdmsg = imsg;
	Wrmsg = omsg;
	Turnoff = turnoff;
	blptcl(str);
	if (Role == MASTER) {
		RMESG(SLTPTCL, msg);
		if ( fptcl(&msg[1], str) == FAIL) {
		    /* no protocol match */
		    WMESG(USEPTCL, NO);
		    return(FAIL);
		} else {
		    /* got protocol match */
		    WMESG(USEPTCL, &msg[1]);
		    return(stptcl(&msg[1]));
		}
	} else {
		WMESG(SLTPTCL, str);
		RMESG(USEPTCL, msg);
		if ( fptcl(&msg[1], str) == FAIL ) {
			return(FAIL);
		} else {
		    return(stptcl(&msg[1]));
		}
	}
}

/*
 * choose a protocol from the input string (str)
 * and return the found letter.
 * Use the MASTER string (valid) for order of selection.
 * return:
 *	'\0'		-> no acceptable protocol
 *	any character	-> the chosen protocol
 */
int
fptcl(str, valid)
char *str, *valid;
{
	char *l;

	DEBUG(9, "Slave protocol list(%s)\n", str);
	DEBUG(9, "Master protocol list(%s)\n", valid);

	for (l = valid; *l != '\0'; l++) {
		if ( strchr(str, *l) != NULL) {
		    *str = *l;
		    *(str+1) = '\0';
		    /* also update string with parms */
		    strcpy(_Protocol, findProto(_Protocol, *str));
		    return(SUCCESS);
		}
	}
	return(FAIL);
}

/*
 * build a string of the letters of the available
 * protocols and return the string (str).  The string consists of protocols
 * that are specified in the Systems and Devices files.  If nothing was
 * specified in those files, then the string is the list of protocols from
 * our Ptble.
 *
 *	str =		place to put the protocol list
 *	length =	size of buffer at str
 *
 * return:
 *	a pointer to string (str)
 */
void
blptcl(str)
char *str;
{
	struct Proto *p;
	char *validPtr;

	/* Build list of valid protocols. */
	for (validPtr = str, p = Ptbl; (*validPtr = p->P_id) != NULLCHAR;
		validPtr++, p++);

	/* Build _Protocol */
	(void) protoString(str);	/* Get desired protocols. */
	return;
}

/*
 * set up the six routines (Rdmg. Wrmsg, Rddata
 * Wrdata, Turnon, Turnoff) for the desired protocol.
 * returns:
 *	SUCCESS 	-> ok
 *	FAIL		-> no find or failed to open
 */
int
stptcl(c)
char *c;
{
	struct Proto *p;

	for (p = Ptbl; p->P_id != '\0'; p++) {
		if (*c == p->P_id) {

			/*
			 * found protocol 
			 * set routine
			 */
			Rdmsg = p->P_rdmsg;
			Wrmsg = p->P_wrmsg;
			Rddata = p->P_rddata;
			Wrdata = p->P_wrdata;
			Turnon = p->P_turnon;
			Turnoff = p->P_turnoff;
			if ((*Turnon)() != 0)
				break;
			CDEBUG(4, "Proto started %c\n", *c);
			pfPtcl(c);
			return(SUCCESS);
		}
	}
	CDEBUG(4, "Proto start-fail %c\n", *c);
	return(FAIL);
}

/*
 * unlink D. file
 * returns:
 *	none
 */
void
unlinkdf(file)
char *file;
{
	if (strlen(file) > (size_t) 6)
		(void) unlink(file);
	return;
}

/*
 * notify receiver of arrived file
 * returns:
 *	none
 */
void
arrived(opt, file, nuser, rmtsys, rmtuser)
char *file, *nuser, *rmtsys, *rmtuser;
{
	char mbuf[200];

	if (!opt)
		return;
	(void) sprintf(mbuf, "%s from %s!%s arrived\n", file, rmtsys, rmtuser);
	mailst(nuser, mbuf, mbuf, "", "");
	return;
}


/*
 * Check to see if there is space for file
 */

#define FREESPACE 50  /* Minimum freespace in blocks to permit transfer */
#define FREENODES 5   /* Minimum number of inodes to permit transfer */

/*ARGSUSED*/
static int
nospace(name)
char *name;
#ifdef NOUSTAT
{return(FALSE);}
#else
{
	struct stat statb;
#ifdef STATFS
	struct statfs statfsb;
#else
	struct ustat ustatb;
#endif

	if( stat(name, &statb) < 0 )
		return(TRUE);
#ifdef	RT
	if( (statb.st_mode|S_IFMT) == S_IFREG ||
	    (statb.st_mode|S_IFMT) == S_IFEXT ||
	    (statb.st_mode&S_IFMT) == S_IF1EXT )
#else
	if( (statb.st_mode&S_IFMT) == S_IFREG )
#endif
	{
#ifdef STATFS
		if( statfs(name, &statfsb)<0 )
#else
		if( ustat(statb.st_dev, &ustatb)<0 )
#endif
			return(TRUE);
#ifdef STATFS
		/*
		 * Use 512-byte blocks, because that's the unit "ustat" tends
		 * to work in.
		 */
		if( ((statfsb.f_bavail*statfsb.f_bsize)/512) < FREESPACE )
#else
		if( ustatb.f_tfree < FREESPACE )
#endif
		{
			logent("FREESPACE IS LOW","REMOTE TRANSFER DENIED - ");
			return(TRUE);
		}
#ifdef STATFS
		/*
		 * The test for "> 0" is there because the @$%#@#@$ NFS
		 * protocol doesn't pass the number of free files over the
		 * wire, so "statfs" on an NFS file system always returns -1.
		 */
		if( statfsb.f_ffree > 0
		    && statfsb.f_ffree < FREENODES )
#else
		if( ustatb.f_tinode < FREENODES )
#endif
		{
			logent("TOO FEW INODES","REMOTE TRANSFER DENIED - ");
			return(TRUE);
		}
	}
	return(FALSE);
}
#endif

#ifdef V7USTAT
int
ustat(dev, ustat)
int	dev;
struct ustat *ustat;
{
	FILE	*dfp, *popen();
	char	*fval, buf[BUFSIZ];

	sprintf(buf, "%s %d %d 2>&1", V7USTAT, major(dev), minor(dev));
	if ((dfp = popen(buf, "r")) == NULL)
		return(-1);
	fval = fgets(buf, sizeof(buf), dfp);
	if (pclose(dfp) != 0
	 || fval == NULL
	 || sscanf(buf, "%d %d", &ustat->f_tfree, &ustat->f_tinode) != 2)
		return(-1);
	return(0);
}
#endif	/* V7USTAT */