4BSD/usr/src/cmd/berknet/netdaemon.c

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

/*
	The daemon program that runs the network.

Usage:
	netdaemon -m mach [-r readfd] [-w writefd] [-d] [-h]
		[-os] [-or] [-ou num]

Must be started by root.
Options:
	-m mach		remote machine is mach (required)
	-d		turn debugging on
	-r num		if simulute w/pipes, read from num
	-w num		if simulate w/pipes, write on num
	-h		use high-speed link (not implemented yet)
	-os		only send
	-or		only receive
	-ou num		only send things with uid = num
	-p num		length of packet
*/

# include "defs.h"
/* take a time, adjust to be in PST, and divide by no of secs in a day */
/* adjust by 10 mins, and day is considered to begin at 3AM */
/* 6*3600 = 21600 +17400 = 39000 */
/* number of seconds in a day, usually 86400L */
# define nsecday 86400L
/* number of days since time began */
# define numdays(S) ((S - 39000L)/nsecday)
/* set my priority to normal */
# define RENICE (nice(-40), nice(20), nice(0))

/* global variables */
extern char **environ;
struct dumpstruc dump;
struct bstruct btable[];
struct daemonparms netd;
struct userinfo status;

/* local variables */
static long length;
static FILE *dir;
/* static char sheader[] = 		"ABCDE"; */
static char tempfile[]= 	TEMPFILE;
static char publogfile[]=  	PUBLOGFILE;
static struct stat statbuf;
static struct direct dirbuf;
int handlekill();
static char frommach;
long linechars();

main(argc,argv)
  char **argv; {
	register int i;
	long ltime,t;
	char buf[100];

	nice(-1);
	signal(SIGTERM,handlekill);
	debugflg = DBV;
	setupdaemon(argc,argv);
	/* now running alone as a daemon */
		/*
		for(i=0; i<15; i++)close(i);
		signal(SIGHUP,SIG_IGN);
		signal(SIGQUIT,SIG_IGN);
		signal(SIGINT,SIG_IGN);
		*/
	senddir[strlen(senddir)-1] = remote;		/* choose dir */
	if(chdir(senddir) < 0){
		perror(senddir);
		exit(EX_OSFILE);
		}
	dir = fopen(senddir,"r");
	if(dir == NULL){
		perror(senddir);
		exit(EX_OSFILE);
		}
	mktemp(tempfile);
	tempfile[strlen(tempfile) - 7] = remote;
	ltime = gettime();
	if(ltime == 0L)
		fprintf(stderr,"The network says 'The clock is set wrong.'\n");
	sprintf(buf,"net restarted to %s %d %s",longname(remote),
		getpid(),ctime(&ltime));
	dump.longtime = ltime;
	dump.lastndays = numdays(ltime);
	addtolog(remote,buf);
	addtopublic(buf);
	fprintf(stderr,buf);
	if(!debugflg)fclose(stderr);
	sendpurge();
	mainloop();
	/* never returns */
}
/* the main loop of the daemon, alternatively rcv then send, if poss.*/
mainloop(){
	register int i;

	for(;;){	/* begin reading file */
		debug("daemon %c %d\n",remote,getpid());
		/* first receive */
		if(netd.dp_sndorcv >= 0){	/* if we can receive */
			i = netrcv();
			if(i == -1)dump.nabnormal++;
		}
		/* now look to send */
		if(netd.dp_sndorcv <= 0)	/* if we can send */
			netsend();
		/* print out statistics if the right time */
		printstat();
		dump.nloop++;
	}
}
	/* this code is a little strange because some machines
	   seem to have trouble having the date set, and time()
	   returns 0 until somebody remembers to set the date */
printstat(){
	long thisndays, thistime;
	thistime = gettime();
	thisndays = numdays(thistime);
	if(dump.longtime == 0L){
		dump.longtime = thistime;
		dump.lastndays = thisndays;
		return;
		}
	if(thisndays == dump.lastndays + 1L) dumpit(thistime);
	dump.lastndays = thisndays;
}
/* look for files to send */
netsend(){
	static long lasttime = 0;
	static char nleft = 1;
	long lFileLen,diff;
	double drate;
	register int uid,uidBest;
	char *sdate,*sn,*swait;
	long ot,nt,filesize;
	register int i;
	char stemp[20];
	static char jname[FNS];

	debug("ck send");
	if(stat(senddir,&statbuf) < 0){
		error("%s %s",senddir,sys_errlist[errno]);
		return;
		}
	if(statbuf.st_mtime == lasttime && nleft == 0)return;	/* no need to search */
	lasttime = statbuf.st_mtime;
	fseek(dir,0L,0);
	lFileLen = 10000000L;
	nleft = 0;
	while(fread(&dirbuf,1,sizeof dirbuf,dir) == sizeof dirbuf){
		if(dirbuf.d_ino == 0
		   || dirbuf.d_name[0] != 'c'
		   || dirbuf.d_name[1] != 'f'
		   || dirbuf.d_name[2] != remote
		   || stat(dirbuf.d_name,&statbuf) < 0
		   || statbuf.st_mode == 0)
			continue;
		dirbuf.d_name[0] = 'd';
		if(stat(dirbuf.d_name,&statbuf) < 0 || statbuf.st_mode == 0)
			continue;
		uid = guid(statbuf.st_uid,statbuf.st_gid);
		if(netd.dp_onlyuid != 0 && uid != netd.dp_onlyuid && uid != SUPERUSER
			&& uid != NUID)continue;
		nleft++;
		filesize = getsize(&statbuf);
		if(lFileLen > filesize){
			lFileLen = filesize;
			for(i=0; i<DIRSIZ; i++)
				jname[i] = dirbuf.d_name[i];
			uidBest = uid;
		}
# ifdef MAXSENDQ
		if(nleft > MAXSENDQ)break;
# endif
	}
	if(lFileLen == 10000000L)return;
	strcpy(stemp,jname);
	stemp[0] = 'c';
	sn = SnFromUid(uidBest);
	if(sn == NULL){
		addtolog(remote,"Unknown userid %d\n",uidBest);
			return;
	}
	addtolog(remote,"^S %s %c: %s ",sn,remote,jname+2);
	ot = gettime();
	if(send(jname) == 0)return;
	nt = gettime();
	filesize = getsize(&statbuf);
	unlink(jname);
	unlink(stemp);
	diff = nt - ot;
	if(diff < 1)diff = 1;		/* avoid dividing by zero */
	sdate = ctime(&nt)+4;
	sdate[strlen(sdate) -9] = 0;
	swait = comptime(ot - statbuf.st_mtime);
	jname[3] = jname[2];
# ifndef NOFP
	drate = (double)filesize / (double)diff;
	addtolog(remote,"^T%c(%s, %ldb, %ldsec, %4.1fb/sec, w %s)\n",
		remote,sdate,filesize, diff,drate, swait);
# else
	addtolog(remote,"^T%c(%s, %ldb, %ldsec, w %s)\n",
		remote,sdate,filesize, diff,swait);
# endif
	addtopublic("%s: sent %-8s to %s (%s, %ld b, wait %s)\n",
		sdate,sn,longname(remote),jname+3,filesize,swait);
	dump.nsend++;
	dump.bytetot += filesize;
	dump.elaptot += diff;
	}
send(jname)
	char *jname;
{	/* push those bytes */
	/* returns 0 if send fails, 1 otherwise */
	register int n;
	int i;
	long lsize;
	char mbuf[20], buf[MAXNBUF];
	register char *p;
	register FILE *jfile;

	debug("send %s",jname);
	if(stat(jname,&statbuf) < 0)goto sfail;
	lsize = getsize(&statbuf);
	if(lsize < MINSIZE){		/* all files are at least this long */
		unlink(jname);
		jname[0] = 'c';
		unlink(jname);
		return(1);
		}
	jfile = fopen(jname,"r");
	if(jfile == NULL)goto sfail;
	/*
	strcpy(mbuf,sheader);
	i = strlen(sheader);
	p = (char *)&lsize;
	lsize = fixuplong(lsize);
	mbuf[i] = *p++;
	mbuf[i+1] = *p++;
	mbuf[i+2] = *p++;
	mbuf[i+3] = *p++;
	i = i + 4;
	sendreset();
	*/
	initseqno();
	sprintf(mbuf,"|%08ld|",lsize);
	i = 10;
	if(xwrite(mbuf,i) == WRITEFAIL)goto bwrite;
	while((n=read(fileno(jfile),buf,MAXNBUF)) > 0)
		if(xwrite(buf,n) == WRITEFAIL)goto bwrite;
	fclose(jfile);
	debug("end send");
	return(1);
bwrite:
	dump.nsendfail++;
	fclose(jfile);
	addtolog(remote,"^F%c\n",remote);
	return(0);
sfail:
	error("%s: %s",jname,sys_errlist[errno]);
	dump.nsendfail++;
	return(0);
	}
netrcv(){
	/* returns -2 in normal fail, -1 in abnormal fail, >= 0 otherwise */
	char sin;
	char mgetc(), *s;
	register int n;
	char c;
	int i, dummy, pid;
	unsigned rcode;
	long otime,olength,diff,rcvfinish,nt;
	double r;
	char hbuf[20], buf[MAXNBUF];
	register FILE *temp;
	static struct header hd;

	initseqno();
	/*
	n = nread(hbuf,strlen(sheader));
	if(n == BROKENREAD)return(-2);
	if(n != strlen(sheader) || strcmp(sheader,hbuf) != 0){
		error("wrong head %d %s",n,hbuf);
		return(-1);
		}
	n = nread(&length,4);
	length = fixuplong(length);
	*/
	n = nread(hbuf,10);
	if(n == BROKENREAD)return(-2);
	if(n != 10){
		error("bad length nread %d",n);
		return(-1);
		}
	hbuf[10] = 0;
	if(hbuf[0] != '|' || hbuf[9] != '|'){
		error("poor format %s",hbuf);
		return(-1);
		}
	hbuf[9] = 0;
	length = atol(hbuf+1);
	if(length < 0 || length > 100000000L){
		error("bad length %ld",length);
		return(-1);
		}
	dump.braw = 4;
	olength = length;
	otime = gettime();
	debug("length = %ld\n",length);

/* 
	begin parsing header

	from local to remote (requests)
	code	net option	reason
	q			normal request
	y	-y		simply skips login check (used by netlpr)

	from remote to local
	code	net option	reason
	w	-w		message to be written/mailed back
	s	-z		normal response
*/

	i = readhd(&hd);
	if(i == -3)goto forw;			/* being forwarded thru us */
	if(i != 0)return(i);

	strcpy(status.login, hd.hd_snto);
	strcpy(status.localname,hd.hd_snfrom);

	demask(hd.hd_spasswd);

	s = hd.hd_scmdvirt;
	while(*s && *s != ' ')s++;
	c = *s;
	*s = 0;
	if(strcmp(hd.hd_scmdvirt,"netlpr") == 0)dump.nnetlpr++;
	else if(strcmp(hd.hd_scmdvirt,"netmail") == 0)dump.nnetmail++;
	else if(strcmp(hd.hd_scmdvirt,"mail") == 0)dump.nsmail++;
	else if(strcmp(hd.hd_scmdvirt,"netcp") == 0)dump.nnetcp++;
	else if(strcmp(hd.hd_scmdvirt,"response") == 0)dump.nresp++;
	else dump.nnet++;
	*s = c;

	printhd(&hd);

	/* any chars left are data */
forw:
	sin = 0;
	if(length > 0){	/* make a temp input file */
		increment(tempfile);
		temp = fopen(tempfile,"w");
		if(temp == NULL){
			error("%s %s",tempfile,sys_errlist[errno]);
			return(-1);
			}
		chmod(tempfile,0600);
		if(hd.hd_mchto != local){
			fprintf(temp,"%c :%c :",hd.hd_code,hd.hd_mchto);
			fflush(temp);
		}
		/* this is the loop to read in all the data */
		while((n = mread(buf,MAXNBUF)) > 0)
			if(write(fileno(temp),buf,n) != n){
				error("%s %s",tempfile,sys_errlist[errno]);
				fclose(temp);
				unlink(tempfile);
				return(-1);
				};
		fclose(temp);
		if(n == BROKENREAD || length > 0){
			unlink(tempfile);
			return(-2);
			}
		sin = 1;
		if(hd.hd_mchto != local){
			diff = gettime() - otime;
			if(diff < 1)diff = 1;	/* avoid dividing by 0 */
# ifndef NOFP
			r = olength;
			r = r/diff;
			addtolog(remote,"^P(to %c, %ldb, %ldsec, %4.1fb/sec)\n",
				hd.hd_mchto,olength,diff,r);
# else
			addtolog(remote,"^P(to %c, %ldb, %ldsec)\n",
				hd.hd_mchto,olength,diff);
# endif
			dump.npass++;
			dump.bytetot += olength;
			dump.elaptot += diff;
			while((pid = fork()) == -1)sleep(2);
			if(pid == 0){
				RENICE;
				execl(netcmd,"net","-x","-m",longname(hd.hd_mchto),
					"-s",tempfile,0);
				error("%s: %s",netcmd,sys_errlist[errno]);
				exit(EX_UNAVAILABLE);
				}
			wait(&rcode);
			unlink(tempfile);
			rcode >>= 8;
			if(rcode != 0)
				error("pass-thru rcode %d");
			debug("passthru to %c code %c rcode %d",
				hd.hd_mchto,hd.hd_code,rcode);
			return(1);
			}
		}
	if(length > 0){error("file too short"); return(-1); }
	rcvfinish = gettime();

	while((pid = fork()) == -1)sleep(2);
	if(pid > 0){
		wait(&dummy);
		return(1);	/* normal return */
	}
	RENICE;
	while((pid = fork()) == -1)sleep(2);
	if(pid != 0)exit(EX_OK);

	/* child process which forks and waits */
	mktemp(resfile);
	while((pid = fork()) == -1)sleep(2);
	if(pid == 0){
		/* child */
		strcpy(status.loginshell,Bsh);
		frommach = hd.hd_mchfrom;
		n = check(&hd,(hd.hd_code == 'q'));
		if(!n)errormsg(TRUE,&hd,NULL,
			"Bad remote login/password '%s'",hd.hd_snto);
		temp = fopen(resfile,"w");
		if(temp == NULL)
			errormsg(TRUE,&hd,NULL,
			"Create file %s: %s",resfile,sys_errlist[errno]);
		fclose(temp);
		chmod(resfile,0600);
		mchown(resfile,status.muid,status.mgid);
		if(sin)
			mchown(tempfile,status.muid,status.mgid);
		else tempfile[0] = 0;
		setgid(status.mgid);
		setuid(status.muid);
		/* after this point our gid, uid is the target user's */
		excmd(&hd,resfile,tempfile);
	}
	/* parent */
	wait(&rcode);
	rcode = (((rcode&077400) >>8) &0177);
	/*
	fclose(stdin);
	fclose(stdout);
	fclose(stderr);
	*/
	if(sin)unlink(tempfile);
	/* 
	   now send something back to the sender 
	   unless this was a response (file or message)
	*/
	if((hd.hd_code == 'q' || hd.hd_code == 'y')
	&& (hd.hd_srespfile[0] || !hd.hd_fnonotify))
		sndresponse(&hd,rcode);
	unlink(resfile);
	s = ctime(&rcvfinish);
	s += 4;
	s[strlen(s) -8] = 0;
	diff = rcvfinish - otime;
	if(diff < 1)diff = 1;		/* avoid dividing by zero */
	dump.bytetot += olength;
	dump.elaptot += diff;
	sprintf(buf,"%s rcv  %c:%-8s (%s)",
		s,hd.hd_mchfrom,hd.hd_snfrom,hd.hd_snto);
	addtolog(remote,"%s C: %s\n",buf,hd.hd_scmdvirt);
	addtopublic("%s R: %d C: %s\n",buf,rcode,hd.hd_scmdvirt);
	nt = rcvfinish - hd.hd_ltimesent;
	buf[0] = 0;
	if(nt > 0L)sprintf(buf," took (%s)",comptime(nt));
# ifndef NOFP
	r = olength;
	r = r/diff;
	addtolog(remote,"\t\tR: %d%s %ldb %ldsec %4.1fb/sec\n",
		rcode,buf,olength,diff,r);
	r = dump.braw;
	r = r/diff;
	addtolog(remote,"\t\t%4.1frb/sec %4.1f%% use\n",r,(r/linechars())*100L);
# else
	addtolog(remote,"\t\tR: %d%s %ldb %ldsec\n",
		rcode,buf,olength,diff);
# endif
	exit(EX_OK);
	/*UNREACHED*/
	}
long linechars(){
	if(netd.dp_inspeed == 13)return(960L);
	else return(120L);
	}
/* 
	execute the user's command
	this procedure is executed with uid, gid of the user
*/
excmd(phd,tempresfile,tempinfile)
	register struct header *phd;
	char *tempresfile, *tempinfile;
{
	FILE *fd;
	int i, uid;
	register char *s, c;

	uid = getuid();
	uid = uidmask(uid);
	status.muid = uidmask(status.muid);
	if(uid != status.muid)error("setuid fails");
	debug("uid: %u, gid: %u\n",uid,status.mgid);
	/* check for allowed root commands, for security reasons */
	if(uid == SUPERUSER){
		s = phd->hd_scmdact;
		while(*s && *s != ' ')s++;
		c = *s;
		*s = 0;
		/* these are the only commands root may execute */
		if(strcmp(phd->hd_scmdact,"cat")            != 0
		&& strcmp(phd->hd_scmdact,"/bin/cat")       != 0
		&& strcmp(phd->hd_scmdact,MWRITECMD)        != 0
		&& strcmp(phd->hd_scmdact,"netrm")          != 0
		&& strcmp(phd->hd_scmdact,"/usr/lib/tq")    != 0
		&& strcmp(phd->hd_scmdact,"/usr/lib/rtrrm") != 0
		&& strcmp(phd->hd_scmdact,"lpr")            != 0)
			errormsg(TRUE,phd,tempresfile,
				"Not allowed to execute '%s' as root",
				phd->hd_scmdact);
		*s = c;
		}
	if(chdir(status.dir) < 0)
		errormsg(TRUE,phd,tempresfile,
			"chdir %s: %s",status.dir,sys_errlist[errno]);
	setenv(status.dir);	/* set up v7 environment */
	if(tempinfile[0])mreopen(TRUE,phd,tempresfile,tempinfile,"r",stdin);
	else if(phd->hd_sinfile[0])mreopen(TRUE,phd,tempresfile,phd->hd_sinfile,"r",stdin);
	else mreopen(TRUE,phd,tempresfile,"/dev/null","r",stdin);
	if(phd->hd_code == 's' && phd->hd_soutfile[0]){
		if(stat(phd->hd_soutfile,&statbuf) < 0
		   || getsize(&statbuf) != 0)
			errormsg(FALSE,phd,tempresfile,"Bad result file '%s'",phd->hd_soutfile);
		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
		}
	else if(phd->hd_soutfile[0]){
		fd = fopen(phd->hd_soutfile,"w");
		if(fd == NULL)
			errormsg(TRUE,phd,tempresfile,"Open file %s: %s",
				phd->hd_soutfile,sys_errlist[errno]);
		fclose(fd);
		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
		}
	else mreopen(TRUE,phd,tempresfile,tempresfile,"a",stdout);
	debug("exec '%s'\n",phd->hd_scmdact);
	if(debugflg == 0){
		/* cheat */
		close(2);
		dup(1);
		/*
		mreopen(TRUE,phd,tempresfile,tempresfile,"a",stderr);
		*/
		}
	for(i=3;i<15;i++)close(i);
	if(strcmp(phd->hd_scmdact,"cat") == 0
	|| strcmp(phd->hd_scmdact,"/bin/cat") == 0)excat();
	do {
		mexecl(status.loginshell,"sh","-c",phd->hd_scmdact,0);
		sleep(2);
		} while(errno == ETXTBSY);
	perror(status.loginshell);
	exit(EX_UNAVAILABLE);
}
/* 
	send back a response

	if errormsg was called the resfile should be unlinked,
	to avoid two messages being sent there
*/
sndresponse(phd,rcode)
unsigned rcode;
struct header *phd;
{
	char cmdstr[BUFSIZ], buf[BUFSIZ];
	int dummy;
	long maxfile = MAXFILE;
	/* send response back if a response file
	was given or if mail/write is allowed */
	if(stat(resfile,&statbuf) < 0){
		error("%s %s",resfile,sys_errlist[errno]);
		return;
		}
	/* allow larger files between the Ingres machines */
	if(machtype[local  - 'a'] == M_INGRES
	&& machtype[remote - 'a'] == M_INGRES)
		maxfile = MAXFILELARGE;
	if(getsize(&statbuf) >= maxfile){
		errormsg(TRUE,phd,"Result file too large - not sent");
		return;
		}
	if(getsize(&statbuf) == 0){
		/* response file specified, no output generated */
		if(phd->hd_srespfile[0] != 0)return;
		/* quiet option - no output and a rcode of 0 */
		if(rcode == 0 && phd->hd_fquiet)return;
	}
	/* use both old and new mwrite parm lists */

	if(phd->hd_srespfile[0])
		sprintf(cmdstr,"-o %s cat",phd->hd_srespfile);
	else sprintf(cmdstr,
	"%s -t %s -f %s -x %ld -c \"'%s'\" -y %s -e %ld -r %d",
	MWRITECMD, phd->hd_addrfrom, phd->hd_addrto, phd->hd_lttytime,
	phd->hd_scmdvirt, phd->hd_sttyname, phd->hd_ltimesent-TIMEBASE, rcode);

	sprintf(buf,"%s -m%c -z -b -l %s -s %s -c response %s",
		netcmd,phd->hd_mchfrom,phd->hd_snfrom,resfile,cmdstr);
	dummy = system(buf);		/* execute command buf */
}
	
/*

	excat
	does nothing more than copy standard input to standard
	output, like the cat command, but reports write errors.
	Uses getc and putc rather than fwrite and fread because
	the latter call getc and putc.
*/
excat(){
	register int n;
	char buf[BUFSIZ];

	errno = 0;
	while((n = read(0,buf,BUFSIZ)) > 0){
		if(write(1,buf,n) != n){
			perror("filecat: stdout");
			exit(EX_OSFILE);
			}
		}
	if(errno){
		perror("filecat: stdin");
		exit(EX_OSFILE);
	}
	exit(EX_OK);
}
/* returns errors for netrcv() */
static readhd(phd)
register struct header *phd;
{
	char cflag, sbuf[BUFSIZ], parmlist[PARMLIST];
	int i = 0;
	char code;
	code = mgetc();
	phd->hd_mchto = mgetc();
	if(code != 'q' && code != 'y' && code != 'w' && code != 's'){
		error("bad code");
		return(-1);
		}
	phd->hd_code = code;
	if(phd->hd_mchto < 'a' || 'z' < phd->hd_mchto){
		error("bad phd->hd_mchto");
		return(-1);
		}
	if(phd->hd_mchto != local)return(-3);	/* being forwarded through us */
	phd->hd_mchfrom = mgetc();
	phd->hd_vmajor = mgetc();
	phd->hd_vminor = mgetc();
	i += mgets(phd->hd_snto,NS);
	i += mgets(phd->hd_spasswd,20);
	i += mgets(phd->hd_sinfile,FNS);
	i += mgets(phd->hd_soutfile,FNS);
	i += mgets(phd->hd_srespfile,FNS);
	i += mgets(phd->hd_snfrom,NS);

	/* addrfrom is the person who sent this to us,
	   addrto is the person who received the command, i.e.
	   addrto is on this machine */
	if(phd->hd_snfrom[0] == 0)strcpy(phd->hd_snfrom,"root");
	sprintf(phd->hd_addrfrom,  "%s:%s",longname(phd->hd_mchfrom),phd->hd_snfrom);
	sprintf(phd->hd_addrto,    "%s:%s",longname(phd->hd_mchto),phd->hd_snto);

	i += mgets(phd->hd_sttyname,20);
	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
	cflag = mgetc();
	if(!phd->hd_mchfrom || !phd->hd_code || !cflag || !phd->hd_vmajor || !phd->hd_vminor){
		error("mgetc fails");
		return(-1);
		}

	cflag -= 'a';
	phd->hd_fnonotify = (cflag & F_NONOTIFY);
	phd->hd_fquiet = (cflag & F_QUIET);

	phd->hd_vmajor -= 'a';
	phd->hd_vminor -= 'a';

	i += mgets(sbuf,BUFSIZ);
	phd->hd_lttytime = 0;
	sscanf(sbuf,"%lo",&phd->hd_lttytime);

	i += mgets(parmlist,PARMLIST);
	phd->hd_ijobno = atoi(parmlist);
	/* keep variable parameter list in jobno slot */
	parseparmlist(parmlist);

	i += mgets(sbuf,BUFSIZ);		/* time sent */
	sscanf(sbuf,"%ld",&phd->hd_ltimesent);
	phd->hd_ltimesent += TIMEBASE;
	i += mgetcmd(phd->hd_scmdact);
	i += mgetcmd(phd->hd_scmdvirt);
	if(i != 0){error("mgets fails"); return(-1);}
	if(phd->hd_scmdvirt[0] == 0)strcpy(phd->hd_scmdvirt,phd->hd_scmdact);
	return(0);
}
/* 
   check() -- verify login name and password
   phd    = login,passwd
   fverify  = 1 if password must check
   Returns 1 if password is ok, 0 if not.
*/
check(phd,fverify)	/* 1 if OK, 0 if not */
register struct header *phd;
int fverify;
{
	char *sencpasswd, *u, *nullstr = "";
	struct passwd *pwd;
	if(phd->hd_snto[0] == 0)return(!fverify);
	if(!goodacctname(phd->hd_snto))return(!fverify);
	pwd = getpwnam(phd->hd_snto);
	if(pwd == NULL)return(!fverify);

	if(machtype[local-'a'] == M_CC && machtype[frommach-'a'] == M_CC)
		sencpasswd = phd->hd_spasswd;
	else if(*phd->hd_spasswd)sencpasswd = crypt(phd->hd_spasswd,pwd->pw_passwd);
	else sencpasswd = nullstr;

	status.muid = guid(pwd->pw_uid,pwd->pw_gid);
	status.mgid = pwd->pw_gid;
	if(isdigit(pwd->pw_gecos[0]))status.jobno = atoi(pwd->pw_gecos);
	else status.jobno = 32767;
	strcpy(status.dir,pwd->pw_dir);
	strcpy(status.loginshell,pwd->pw_shell);
	u = status.loginshell;
	if(u[0] == 0 || strcmp("/bin/sbash",u) == 0)strcpy(u,Bsh);

	getpwdf(pwd);
	/* ignore network passwd */
	/* acct is not a pair, acct is not "network", passwd is incorrect,
	and verification is requested => passwd not ok */
	if(!facctpaircheck(phd) && strcmp(phd->hd_snto,"network") != 0
	&& strcmp(pwd->pw_passwd,sencpasswd) != 0 && fverify)
		return(0);
	return(1);	/* otherwise passwd ok */
	}
mread(b,n)
  register int n; {
	if(length <= 0)return(0);
	if(length < n)n = length;
	n = nread(b,n);
	if(n != BROKENREAD)length -= n;
	return(n);
	}
char mgetc(){			/* returns 0 if fail */
	register char c;
	register int n;
	char buf[3];
	if((n=nread(buf,3)) == BROKENREAD)return(0);
	if(n != 3){error("bad read %d",n); return(0); }
	c = buf[0];
	if(buf[1] != ' ' && buf[1] != ':'){error("Bad char %c",buf[1]); return(0); }
	length -= 3;
	if(length < 0){error("length wrong2 %ld",length); return(0); }
	return(c);
	}
/* read in string over the network wire */
/* put string in s, max length is maxlen */
mgets(s,maxlen)			/* returns 0 if ok, 1 if not */
  int maxlen;
  register char *s; {
	register char *q;
	register int n;
	char c;
	q = s;
	for(;;) {
		if((n=nread(&c,1)) == BROKENREAD){
			*s = 0;
			error("mgets %s",s);
			return(1);
			}
		if(n == 0)break;
		if(c == '\\'){
			if((n=nread(&c,1)) == BROKENREAD){
				*s = 0;
				error("mgets %s",s);
				return(1);
				}
			if(n == 0)break;
			}
		if(c == ' ')break;
		if(maxlen-- > 0) *s++ = c;
		}
	*s = 0;
	if(nread(&c,1) == BROKENREAD){
		error("mgets %s",s);
		return(1);
		}
	length -= (s - q + 2);
	if(length < 0){error("length wrong1 %ld %s",length,q); return(-1); }
	if(maxlen < 0)
		error("mgets - string too long");
	return(0);
	}
mgetcmd(s)			/* returns 0 if succeed, 1 otherwise */
  char *s; {
	int i,n;
	char c;
	i = 0;
	for(;;){
		if((n=nread(&c,1)) == BROKENREAD){
			s[i] = 0;
			error("mgetcmd %s",s);
			return(1);
			}
		if(n <= 0 || c == '\n')break;
		if(c == '\\'){
			if(nread(&c,1) == BROKENREAD){
				s[i] = 0;
				error("mgetcmd %s",s);
				return(1);
				}
			length--;
			}
		s[i++] = c;
		length--;
		}
	s[i] = 0;
	length--;
	return(0);
	}
increment(s)
 char *s; {
	int i;
	char *p;
	i = strlen(s) - 1;
	while(s[i] == '9')i--;
	if(s[i] < '0' || s[i] > '9'){
		p = s+i+1;
		while(*p)*p++ = '0';
		return;
		}
	(s[i])++;
	i++;
	while(s[i])s[i++] = '0';
	return;
	}
/* gather 24-hour stats and  mail to STATADDR */
/* should also gather stats on # error msgs */
dumpit(currt)
  long currt; {
	register struct dumpstruc *p = &dump;
	register int ntot;
	long elapt;
	double cputime,utime,stime,bs,rawbs;
	char *sstartt;
	FILE *fdm;
	char froma[30];
	struct tms tbf;

	/* if STATADDR is a file, the mail program this call will
	   ultimately execute must be able to deal with it,
	   and the remote mail program must be able to write on the
	   file, i.e. mode 666 */
	sprintf(froma,"%s=>",longname(local));
	strcat(froma,longname(remote));
	fdm = mailopen(STATADDR,froma,1,0);
	if(fdm == NULL)return;

	/* calculate times */
	elapt = currt - dump.longtime;
	ntot = p->nnetcp + p->nnetmail + p->nsmail + p->nnetlpr
		+ p->nresp + p->nnet;
	sstartt = ctime(&dump.longtime) + 4;
	sstartt[strlen(sstartt) - 9] = 0;

	times(&tbf);
# ifndef NOFP
	utime = tbf.tms_utime + tbf.tms_cutime;
	stime = tbf.tms_stime + tbf.tms_cstime;
	cputime = utime + stime;

	if(elapt > 0)cputime = (cputime/elapt) * 100.0;
	else cputime = 0.0;
	utime = utime/60.0;
	stime = stime/60.0;
	cputime = cputime/60.0;
	bs = p->bytetot;
	if(p->elaptot > 0)bs = bs /p->elaptot;
	else bs = 0.0;
# endif

	/* print out the statistics */
	fprintf(fdm,"Subject: %s, %s, time %s\n",
		froma,sstartt, comptime(elapt));
	fprintf(fdm,"Command summary:\n");
	fprintf(fdm,"\t# sent %d\t# pass_thru %d\t# rcv %d:\t# netcp %d\n",
		p->nsend,p->npass,ntot,p->nnetcp);
	fprintf(fdm,"\t# netlpr %d\t# netmail %d\t# sendbmail %d\t# resp %d\n",
		p->nnetlpr,p->nnetmail,p->nsmail,p->nresp);
	fprintf(fdm,"Protocol summary:\n");
	fprintf(fdm,"\t# pk_sent %d\t# pk_rcv %d\t# b_sent %ld\t# b_rcv %ld\n",
		p->npacksent,p->npackrcv,p->nbytesent, p->nbytercv);
	fprintf(fdm,
		"\t# send_fails %d\t# retrans %d\t# abn %d\t\t# cksum_errs %d\n",
		p->nsendfail,p->nretrans, p->nabnormal,p->ncksum);
# ifndef NOFP
	fprintf(fdm,"Load:\tuser %4.1f\tsys %4.1f\tpct %5.2f\trate %6.1f\n",
		utime,stime,cputime,bs);
	rawbs = p->brawtot*100L;
	rawbs = rawbs / linechars();
	fprintf(fdm,"\trawbytes %ld\tuse %4.1f\n", p->brawtot,rawbs);
# endif
	mailclose(fdm);

	/* reset counters */
	p->nbytesent = p->nbytercv = p->elaptot = p->bytetot = 0L;
	p->nretrans = p->nloop = p->nabnormal = p->ncksum = 0;
	p->npacksent = p->npackrcv = p->nnetcp = p->nnetmail = 0;
	p->nsmail = p->nnetlpr = p->nnet = p->npass = 0;
	p->nsend = p->nsendfail = 0;
	dump.longtime = currt;
	}
/* returns 1 if n is ok, 0 if not */
goodacctname(n)
  char *n; {
	int i;
	i = -1;
	while(btable[++i].bname)
		if(strcmp(btable[i].bname,n) == 0 &&
			local == btable[i].bmach)return(0);
	return(1);
	}
demask(s)
  register char *s; {
/*
	static char buf[20];
	char skey[30];
	makeuukey(skey,status.login,local);
	strcpy(s,nbsdecrypt(s,skey,buf));
*/
	while(*s){
		*s &= 0177;		/* strip quote bites */
		*s++ ^= 040;		/* invert upper-lower */
		}
	}
/*VARARGS0*/
mreopen(fsendtofmach,phd,sfn,a,b,c){
/* simply handles errors by giving error msg */
	if(freopen(a,b,c) == NULL)
		errormsg(fsendtofmach,phd,sfn,"%s: %s",a,sys_errlist[errno]);
}
/* 
	addtopub(string, args)

	add a message to the public logfile /usr/net/logfile.
	note that the file must be writeable by everyone
	if error messages from the netrcv subroutine
	such as chdir errors are to be noticed.
*/
/*VARARGS0*/
addtopublic(s,a,b,c,d,e,f,g,h,i,j,k,l,m,n)
char *s;
{
	static FILE *log = NULL;
	if(log == NULL){
		if(stat(publogfile,&statbuf) < 0)return;
		log = fopen(publogfile,"a");
		if(log == NULL)return;
		}
	fseek(log,0L,2);
	fprintf(log,s,a,b,c,d,e,f,g,h,i,j,k,l,m,n);
	fflush(log);
	}
/* set up a dummy environment for v7 /bin/sh */
setenv(home)
  char *home; {
	static char *env[3],benv[2][50];
	env[0] = benv[0];
	env[1] = benv[1];
	strcpy(env[0],"PATH=:/bin:/usr/bin");
	sprintf(env[1],"HOME=%s",home);
	env[2] = 0;
	environ = env;
	}
/* 
	errormsg(fsendtofmach,phd,sfn,"string",arg(s))
	
	Sends error message to user.
	If fsendtofmach=TRUE, send to phd->hd_mchfrom, otherwise
	send to phd->hd_mchto.
	Also, if error occured during return of a "response",
	send to local machine.

	Note that errormsg can be called by the netrcv subroutine
	after the setuid() call to the specific user, so the 
	user must be able to get off an error msg back to him,
	and to write in the two log files.
	Can't use -w,-x,-y,-z for the net cmd because must be root for those.

	If sfn != NULL, then unlink sfn before exiting.
*/
/*VARARGS0*/
errormsg(fsendtofmach,phd,sfn,s,a,b,c,d,e,f,g,h)
char fsendtofmach;				
struct header *phd;
char *sfn,*s;
{
	int rcode;
	char errstr[BUFSIZ], cmdstr[BUFSIZ], rcmd[BUFSIZ];
	char toadd[FNS], fromadd[FNS], mchto, mchfrom;
	char snto[FNS], snfrom[FNS];

	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
	/* will send to toadd, from fromadd */
	if(!fsendtofmach || strcmp(phd->hd_scmdvirt,"response") == 0){
		/* send to tomach mach, thus send to toaddr. */
		/* if this is an error during a response, send to local mach. */
		strcpy(toadd,  phd->hd_addrto);
		strcpy(fromadd,phd->hd_addrfrom);
	}
	else {		/* send to remote mach, thus send back to addrfrom*/
		strcpy(toadd,  phd->hd_addrfrom);
		strcpy(fromadd,phd->hd_addrto);
	}
	sprintf(errstr,"Error: ");
	sprintf(cmdstr,s,a,b,c,d,e,f,g,h);
	strcat(errstr,cmdstr);
	strcat(errstr,"\n");
	addtolog(remote,errstr);
	addtopublic(errstr);

	mchto =   MchSFromAddr(snto,toadd); 
	mchfrom = MchSFromAddr(snfrom,fromadd);

	sprintf(rcmd,
"%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -y %s -c \"'%s'\" -e %ld",
	MWRITECMD, snto, phd->hd_sttyname, phd->hd_lttytime, 
	local, snfrom,phd->hd_scmdvirt, phd->hd_ltimesent-TIMEBASE,
	toadd, fromadd, phd->hd_lttytime, phd->hd_sttyname, phd->hd_scmdvirt,
	phd->hd_ltimesent-TIMEBASE);

	if(mchto == local)
		sprintf(cmdstr, "echo \"%s\" | %s", errstr,rcmd);
	else 
		sprintf(cmdstr,
		"echo \"%s\" | %s -m%c -b -c errormessage -l network - %s",
			errstr,netcmd,mchto,rcmd);
	rcode = system(cmdstr);
	if(sfn != NULL)unlink(sfn);
	exit(EX_USAGE);
	}
handlekill(){	/* SIGTERM signal */
	long t;
	/*
	t = gettime();
	dumpit(t);
	*/
	exit(EX_OK);	/* kill myself */
	}

/* check a request to see if it is an acct pair */
/* returns 1 if it is, 0 if not */
static facctpaircheck(phd)
register struct header *phd;
{
	return(0);
}