xref: /csrg-svn/old/berknet/netdaemon.c (revision 8220)
1 static char sccsid[] = "@(#)netdaemon.c	4.2	(Berkeley)	09/12/82";
2 
3 /* sccs id variable */
4 static char *netdaemon_sid = "@(#)netdaemon.c	1.10";
5 
6 /*
7 
8 	The daemon program that runs the network.
9 
10 Usage:
11 	netdaemon -m mach [-r readfd] [-w writefd] [-d] [-h]
12 		[-os] [-or] [-ou num] [-p len] [-8] [-l]
13 
14 Must be started by root.
15 Options:
16 	-d		turn debugging on
17 	-h		use high-speed link (not implemented yet)
18 	-l		don't use net line discipline, even if available
19 	-m mach		remote machine is mach (required)
20 	-os		only send
21 	-or		only receive
22 	-ou num		only send things with uid = num
23 	-p num		length of packet
24 	-r num		if simulute w/pipes, read from num
25 	-w num		if simulate w/pipes, write on num
26 */
27 
28 # include "defs.h"
29 /* take a time, adjust to be in PST, and divide by no of secs in a day */
30 /* adjust by 10 mins, and day is considered to begin at 3AM */
31 /* (6*3600 = 21600) + 17400 = 39000 */
32 /* number of seconds in a day, usually 86400L */
33 # define nsecday 86400L
34 /* number of days since time began */
35 # define numdays(S) ((S - 39000L)/nsecday)
36 /* set my priority to normal */
37 # define RENICE0() { if (getuid() == 0) { nice(-40); nice(20); nice(0); } }
38 
39 /* global variables */
40 extern char **environ;
41 struct dumpstruc dump;
42 struct bstruct btable[];
43 struct daemonparms netd;
44 struct userinfo status;
45 
46 /* local variables */
47 static long length;
48 static DIR *dir;
49 /* static char sheader[] = 		"ABCDE"; */
50 static char tempfile[]= 	TEMPFILE;
51 static char publogfile[]=  	PUBLOGFILE;
52 static struct stat statbuf;
53 int handlekill();
54 static char frommach;
55 long linechars();
56 
57 main(argc,argv)
58   char **argv; {
59 	register int i;
60 	long ltime,t;
61 	char buf[100];
62 
63 	nice(-1);
64 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
65 		signal(SIGHUP, handlekill);
66 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
67 		signal(SIGQUIT, handlekill);
68 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
69 		signal(SIGINT, handlekill);
70 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
71 		signal(SIGTERM, handlekill);
72 	debugflg = DBV;
73 	setupdaemon(argc,argv);
74 	/* now running alone as a daemon */
75 		/*
76 		for(i=0; i<15; i++)close(i);
77 		signal(SIGHUP,SIG_IGN);
78 		signal(SIGQUIT,SIG_IGN);
79 		signal(SIGINT,SIG_IGN);
80 		*/
81 	/* set the umask to a reasonable value */
82 	umask( 022 );
83 	senddir[strlen(senddir)-1] = remote;		/* choose dir */
84 	if(chdir(senddir) < 0){
85 		perror(senddir);
86 		exit(EX_OSFILE);
87 		}
88 	dir = opendir(senddir);
89 	if(dir == NULL){
90 		perror(senddir);
91 		exit(EX_OSFILE);
92 		}
93 	mktemp(tempfile);
94 	tempfile[strlen(tempfile) - 7] = remote;
95 	ltime = gettime();
96 	if(ltime == 0L)
97 		fprintf(stderr,"The network says 'The clock is set wrong.'\n");
98 	sprintf(buf,"net restarted to %s %d %s",longname(remote),
99 		getpid(),ctime(&ltime));
100 	dump.longtime = ltime;
101 	dump.lastndays = numdays(ltime);
102 	addtolog(remote,buf);
103 	addtopublic(buf);
104 	fprintf(stderr,buf);
105 	if(!debugflg)fclose(stderr);
106 	sendpurge();
107 	mainloop();
108 	/* never returns */
109 }
110 /* the main loop of the daemon, alternatively rcv then send, if poss.*/
111 mainloop(){
112 	register int i;
113 
114 	for(;;){	/* begin reading file */
115 		debug("daemon %c %d\n",remote,getpid());
116 		/* first receive */
117 		if(netd.dp_sndorcv >= 0){	/* if we can receive */
118 			i = netrcv();
119 			if(i == -1)dump.nabnormal++;
120 		}
121 		/* now look to send */
122 		if(netd.dp_sndorcv <= 0)	/* if we can send */
123 			netsend();
124 		/* print out statistics if the right time */
125 		printstat();
126 		dump.nloop++;
127 	}
128 }
129 	/* this code is a little strange because some machines
130 	   seem to have trouble having the date set, and time()
131 	   returns 0 until somebody remembers to set the date */
132 printstat(){
133 	long thisndays, thistime;
134 	thistime = gettime();
135 	thisndays = numdays(thistime);
136 	if(dump.longtime == 0L){
137 		dump.longtime = thistime;
138 		dump.lastndays = thisndays;
139 		return;
140 		}
141 	if(thisndays == dump.lastndays + 1L) dumpit(thistime);
142 	dump.lastndays = thisndays;
143 }
144 /* look for files to send */
145 netsend(){
146 	static long lasttime = 0;
147 	static char nleft = 1;
148 	long lFileLen,diff;
149 	double drate;
150 	register unsigned uid,uidBest;
151 	char *sdate,*sn,*swait;
152 	long ot,nt,filesize;
153 	register int i;
154 	char stemp[20];
155 	static char jname[FNS];
156 	register struct direct *dp;
157 
158 	debug("ck send");
159 	if(stat(senddir,&statbuf) < 0){
160 		error("%s %s",senddir,sys_errlist[errno]);
161 		return;
162 		}
163 	if(statbuf.st_mtime == lasttime && nleft == 0)return;	/* no need to search */
164 	lasttime = statbuf.st_mtime;
165 	rewinddir(dir);
166 	lFileLen = 10000000L;
167 	nleft = 0;
168 	while((dp = readdir(dir)) != NULL){
169 		if(dp->d_name[0] != 'c'
170 		   || dp->d_name[1] != 'f'
171 		   || dp->d_name[2] != remote
172 		   || stat(dp->d_name,&statbuf) < 0
173 		   || statbuf.st_mode == 0)
174 			continue;
175 		dp->d_name[0] = 'd';
176 		if(stat(dp->d_name,&statbuf) < 0 || statbuf.st_mode == 0)
177 			continue;
178 		uid = guid(statbuf.st_uid,statbuf.st_gid);
179 		if(netd.dp_onlyuid != 0 && uid != netd.dp_onlyuid && uid != SUPERUSER
180 			&& uid != NUID)continue;
181 		nleft++;
182 		filesize = getsize(&statbuf);
183 #ifndef DONTHOLDBIG
184 		if( (filesize > MAXDAYFILE) && day() ) {
185 			if( !debugflg )
186 				continue;
187 			else
188 				debug("sending large file %s\n", dp->d_name );
189 		}
190 #endif DONTHOLDBIG
191 		if(lFileLen > filesize){
192 			lFileLen = filesize;
193 			strcpy(jname,dp->d_name);
194 			uidBest = uid;
195 		}
196 # ifdef MAXSENDQ
197 		if(nleft > MAXSENDQ)break;
198 # endif MAXSENDQ
199 	}
200 	if(lFileLen == 10000000L)return;
201 	strcpy(stemp,jname);
202 	stemp[0] = 'c';
203 	sn = SnFromUid(uidBest);
204 	if(sn == NULL){
205 		addtolog(remote,"Unknown userid %d\n",uidBest);
206 		addtolog(remote,"Removing %s\n",stemp);
207 		unlink(stemp);
208 		return;
209 	}
210 	addtolog(remote,"^S %s %c: %s ",sn,remote,jname+2);
211 	ot = gettime();
212 	if(send(jname) == 0)return;
213 	nt = gettime();
214 	filesize = getsize(&statbuf);
215 	unlink(jname);
216 	unlink(stemp);
217 	diff = nt - ot;
218 	if(diff < 1)diff = 1;		/* avoid dividing by zero */
219 	sdate = ctime(&nt)+4;
220 	sdate[strlen(sdate) -9] = 0;
221 	swait = comptime(ot - statbuf.st_mtime);
222 	jname[3] = jname[2];
223 # ifndef NOFP
224 	drate = (double)filesize / (double)diff;
225 	addtolog(remote,"^T%c(%s, %ldb, %ldsec, %4.1fb/sec, w %s)\n",
226 		remote,sdate,filesize, diff,drate, swait);
227 # else NOFP
228 	addtolog(remote,"^T%c(%s, %ldb, %ldsec, w %s)\n",
229 		remote,sdate,filesize, diff,swait);
230 # endif NOFP
231 	addtopublic("%s: sent %-8s to %s (%s, %ld b, wait %s)\n",
232 		sdate,sn,longname(remote),jname+3,filesize,swait);
233 	dump.nsend++;
234 	dump.bytetot += filesize;
235 	dump.elaptot += diff;
236 	}
237 
238 /*
239    day() returns 1 if the time is between 6AM and 12PM
240 */
241 day()
242 {
243 	int hour;
244 	long t;
245 	char *ctime();
246 
247 	time( &t );
248 	sscanf( ctime( &t ), "%*s%*s%*s%2d", &hour );
249 	if( (hour>=0) && (hour<6) )
250 		return( 0 );		/* night */
251 	else
252 		return( 1 );		/* day */
253 }
254 
255 send(jname)
256 	char *jname;
257 {	/* push those bytes */
258 	/* returns 0 if send fails, 1 otherwise */
259 	register int n;
260 	int i;
261 	long lsize;
262 	char mbuf[20], buf[MAXNBUF];
263 	register char *p;
264 	register FILE *jfile;
265 
266 	debug("send %s",jname);
267 	if(stat(jname,&statbuf) < 0)goto sfail;
268 	lsize = getsize(&statbuf);
269 	if(lsize < MINSIZE){		/* all files are at least this long */
270 		unlink(jname);
271 		jname[0] = 'c';
272 		unlink(jname);
273 		return(1);
274 		}
275 	jfile = fopen(jname,"r");
276 	if(jfile == NULL)goto sfail;
277 	/*
278 	strcpy(mbuf,sheader);
279 	i = strlen(sheader);
280 	p = (char *)&lsize;
281 	lsize = fixuplong(lsize);
282 	mbuf[i] = *p++;
283 	mbuf[i+1] = *p++;
284 	mbuf[i+2] = *p++;
285 	mbuf[i+3] = *p++;
286 	i = i + 4;
287 	sendreset();
288 	*/
289 	initseqno();
290 	sprintf(mbuf,"|%08ld|",lsize);
291 	i = 10;
292 	if(xwrite(mbuf,i) == WRITEFAIL)goto bwrite;
293 	while((n=read(fileno(jfile),buf,MAXNBUF)) > 0)
294 		if(xwrite(buf,n) == WRITEFAIL)goto bwrite;
295 	fclose(jfile);
296 	debug("end send");
297 	return(1);
298 bwrite:
299 	dump.nsendfail++;
300 	fclose(jfile);
301 	addtolog(remote,"^F%c\n",remote);
302 	return(0);
303 sfail:
304 	error("%s: %s",jname,sys_errlist[errno]);
305 	dump.nsendfail++;
306 	return(0);
307 	}
308 netrcv(){
309 	/* returns -2 in normal fail, -1 in abnormal fail, >= 0 otherwise */
310 	char sin;
311 	char mgetc(), *s;
312 	register int n;
313 	char c;
314 	int i, dummy, pid;
315 	unsigned rcode;
316 	long otime,olength,diff,rcvfinish,nt;
317 	double r;
318 	char hbuf[20], buf[MAXNBUF];
319 	register FILE *temp;
320 	static struct header hd;
321 
322 	initseqno();
323 	/*
324 	n = nread(hbuf,strlen(sheader));
325 	if(n == BROKENREAD)return(-2);
326 	if(n != strlen(sheader) || strcmp(sheader,hbuf) != 0){
327 		error("wrong head %d %s",n,hbuf);
328 		return(-1);
329 		}
330 	n = nread(&length,4);
331 	length = fixuplong(length);
332 	*/
333 	n = nread(hbuf,10);
334 	if(n == BROKENREAD)return(-2);
335 	if(n != 10){
336 		error("bad length nread %d",n);
337 		return(-1);
338 		}
339 	hbuf[10] = 0;
340 	if(hbuf[0] != '|' || hbuf[9] != '|'){
341 		error("poor format %s",hbuf);
342 		return(-1);
343 		}
344 	hbuf[9] = 0;
345 	length = atol(hbuf+1);
346 	if(length < 0 || length > 100000000L){
347 		error("bad length %ld",length);
348 		return(-1);
349 		}
350 	dump.braw = 4;
351 	olength = length;
352 	otime = gettime();
353 	debug("length = %ld\n",length);
354 
355 /*
356 	begin parsing header
357 
358 	from local to remote (requests)
359 	code	net option	reason
360 	q			normal request
361 	y	-y		simply skips login check (used by netlpr)
362 
363 	from remote to local
364 	code	net option	reason
365 	w	-w		message to be written/mailed back
366 	s	-z		normal response
367 */
368 
369 	i = readhd(&hd);
370 	if(i == -3)goto forw;			/* being forwarded thru us */
371 	if(i != 0)return(i);
372 
373 	strcpy(status.login, hd.hd_snto);
374 	strcpy(status.localname,hd.hd_snfrom);
375 
376 	demask(hd.hd_spasswd);
377 
378 	s = hd.hd_scmdvirt;
379 	while(*s && *s != ' ')s++;
380 	c = *s;
381 	*s = 0;
382 	if(strcmp(hd.hd_scmdvirt,"netlpr") == 0)dump.nnetlpr++;
383 	else if(strcmp(hd.hd_scmdvirt,"netmail") == 0)dump.nnetmail++;
384 	else if(strcmp(hd.hd_scmdvirt,"mail") == 0)dump.nsmail++;
385 	else if(strcmp(hd.hd_scmdvirt,"netcp") == 0)dump.nnetcp++;
386 	else if(strcmp(hd.hd_scmdvirt,"response") == 0)dump.nresp++;
387 	else dump.nnet++;
388 	*s = c;
389 
390 	printhd(&hd);
391 
392 	/* any chars left are data */
393 forw:
394 	sin = 0;
395 	if(length > 0){	/* make a temp input file */
396 		increment(tempfile);
397 		temp = fopen(tempfile,"w");
398 		if(temp == NULL){
399 			error("%s %s",tempfile,sys_errlist[errno]);
400 			return(-1);
401 			}
402 		chmod(tempfile,0600);
403 		if(hd.hd_mchto != local){
404 			fprintf(temp,"%c :%c :",hd.hd_code,hd.hd_mchto);
405 			fflush(temp);
406 		}
407 		/* this is the loop to read in all the data */
408 		while((n = mread(buf,MAXNBUF)) > 0)
409 			if(write(fileno(temp),buf,n) != n){
410 				error("%s %s",tempfile,sys_errlist[errno]);
411 				fclose(temp);
412 				unlink(tempfile);
413 				return(-1);
414 				};
415 		fclose(temp);
416 		if(n == BROKENREAD || length > 0){
417 			unlink(tempfile);
418 			return(-2);
419 			}
420 		sin = 1;
421 		if(hd.hd_mchto != local){
422 			diff = gettime() - otime;
423 			if(diff < 1)diff = 1;	/* avoid dividing by 0 */
424 # ifndef NOFP
425 			r = olength;
426 			r = r/diff;
427 			addtolog(remote,"^P(to %c, %ldb, %ldsec, %4.1fb/sec)\n",
428 				hd.hd_mchto,olength,diff,r);
429 # else NOFP
430 			addtolog(remote,"^P(to %c, %ldb, %ldsec)\n",
431 				hd.hd_mchto,olength,diff);
432 # endif NOFP
433 			dump.npass++;
434 			dump.bytetot += olength;
435 			dump.elaptot += diff;
436 			while((pid = fork()) == -1)sleep(2);
437 			if(pid == 0){
438 # ifndef V6
439 				RENICE0();
440 # endif V6
441 #ifdef CCV7
442 				/* make sure the spawned child has it's own
443 					group process to avoid the nasty
444 					"try again" message
445 				*/
446 				setpgrp();
447 #endif CCV7
448 				execl(netcmd,"net","-x","-m",longname(hd.hd_mchto),
449 					"-s",tempfile,0);
450 				error("%s: %s",netcmd,sys_errlist[errno]);
451 				exit(EX_UNAVAILABLE);
452 				}
453 			wait(&rcode);
454 			unlink(tempfile);
455 			rcode >>= 8;
456 			if(rcode != 0)
457 				error("pass-thru rcode %d");
458 			debug("passthru to %c code %c rcode %d",
459 				hd.hd_mchto,hd.hd_code,rcode);
460 			return(1);
461 			}
462 		}
463 	if(length > 0){error("file too short"); return(-1); }
464 	rcvfinish = gettime();
465 
466 	while((pid = fork()) == -1)sleep(2);
467 	if(pid > 0){
468 		wait(&dummy);
469 		return(1);	/* normal return */
470 	}
471 	/* this is a child, who will go ahead and execute the command */
472 	/* running uid=0 at this point */
473 # ifndef V6
474 	RENICE0();
475 # endif V6
476 	/* nice(0 set back to 0 */
477 #ifdef CCV7
478 	/* separate group process */
479 	setpgrp();
480 #endif CCV7
481 
482 	while((pid = fork()) == -1)sleep(2);
483 	if(pid != 0)exit(EX_OK);
484 
485 	/* child process which forks and waits */
486 	mktemp(resfile);
487 	while((pid = fork()) == -1)sleep(2);
488 	if(pid == 0){
489 		/* child */
490 		strcpy(status.loginshell,Bsh);
491 		frommach = hd.hd_mchfrom;
492 		n = check(&hd,(hd.hd_code == 'q'));
493 		if(!n)errormsg(TRUE,&hd,NULL,
494 			"Bad remote login/password '%s'",hd.hd_snto);
495 		temp = fopen(resfile,"w");
496 		if(temp == NULL)
497 			errormsg(TRUE,&hd,NULL,
498 			"Create file %s: %s",resfile,sys_errlist[errno]);
499 		fclose(temp);
500 		chmod(resfile,0600);
501 		mchown(resfile,status.muid,status.mgid);
502 		if(sin)
503 			mchown(tempfile,status.muid,status.mgid);
504 		else tempfile[0] = 0;
505 		setgid(status.mgid);
506 		setuid(status.muid);
507 		/* after this point our gid, uid is the target user's */
508 		excmd(&hd,resfile,tempfile);
509 	}
510 	/* parent */
511 	wait(&rcode);
512 	rcode = (((rcode&077400) >>8) &0177);
513 	/*
514 	fclose(stdin);
515 	fclose(stdout);
516 	fclose(stderr);
517 	*/
518 	if(sin)unlink(tempfile);
519 	/*
520 	   now send something back to the sender
521 	   unless this was a response (file or message)
522 	*/
523 	if((hd.hd_code == 'q' || hd.hd_code == 'y')
524 	&& (hd.hd_srespfile[0] || !hd.hd_fnonotify))
525 		sndresponse(&hd,rcode);
526 	unlink(resfile);
527 	s = ctime(&rcvfinish);
528 	s += 4;
529 	s[strlen(s) -8] = 0;
530 	diff = rcvfinish - otime;
531 	if(diff < 1)diff = 1;		/* avoid dividing by zero */
532 	dump.bytetot += olength;
533 	dump.elaptot += diff;
534 	sprintf(buf,"%s rcv  %c:%-8s (%s)",
535 		s,hd.hd_mchfrom,hd.hd_snfrom,hd.hd_snto);
536 	addtolog(remote,"%s C: %s\n",buf,hd.hd_scmdvirt);
537 	addtopublic("%s R: %d C: %s\n",buf,rcode,hd.hd_scmdvirt);
538 	nt = rcvfinish - hd.hd_ltimesent;
539 	buf[0] = 0;
540 	if(nt > 0L)sprintf(buf," took (%s)",comptime(nt));
541 # ifndef NOFP
542 	r = olength;
543 	r = r/diff;
544 	addtolog(remote,"\t\tR: %d%s %ldb %ldsec %4.1fb/sec\n",
545 		rcode,buf,olength,diff,r);
546 	r = dump.braw;
547 	r = r/diff;
548 	addtolog(remote,"\t\t%4.1frb/sec %4.1f%% use\n",r,(r/linechars())*100L);
549 # else NOFP
550 	addtolog(remote,"\t\tR: %d%s %ldb %ldsec\n",
551 		rcode,buf,olength,diff);
552 # endif NOFP
553 	exit(EX_OK);
554 	/*UNREACHED*/
555 	}
556 long linechars(){
557 	if(netd.dp_inspeed == 13)return(960L);
558 	else return(120L);
559 	}
560 /*
561 	execute the user's command
562 	this procedure is executed with uid, gid of the user
563 */
564 excmd(phd,tempresfile,tempinfile)
565 	register struct header *phd;
566 	char *tempresfile, *tempinfile;
567 {
568 	FILE *fd;
569 	int i, uid;
570 	register char *s, c;
571 
572 	uid = getuid();
573 	uid = uidmask(uid);
574 	status.muid = uidmask(status.muid);
575 	if(uid != status.muid)error("setuid fails");
576 	debug("uid: %u, gid: %u\n",uid,status.mgid);
577 	/* check for allowed root commands, for security reasons */
578 	if(uid == SUPERUSER){
579 		s = phd->hd_scmdact;
580 		while(*s && *s != ' ')s++;
581 		c = *s;
582 		*s = 0;
583 		/* these are the only commands root may execute */
584 		if(strcmp(phd->hd_scmdact,"cat")            	!= 0
585 		&& strcmp(phd->hd_scmdact,MWRITECMD)        	!= 0
586 		&& strcmp(phd->hd_scmdact,"/bin/cat")       	!= 0
587 		&& strcmp(phd->hd_scmdact,"netrm")          	!= 0
588 		&& strcmp(phd->hd_scmdact,"/usr/lib/tq")    	!= 0
589 		&& strcmp(phd->hd_scmdact,"/usr/cc/lib/tq") 	!= 0
590 		&& strcmp(phd->hd_scmdact,"/usr/lib/rtrrm") 	!= 0
591 		&& strcmp(phd->hd_scmdact,"/usr/cc/lib/rtrrm")	!= 0
592 		&& strcmp(phd->hd_scmdact,"lpr")            	!= 0)
593 			errormsg(TRUE,phd,tempresfile,
594 				"Not allowed to execute '%s' as root",
595 				phd->hd_scmdact);
596 		*s = c;
597 		}
598 	if(chdir(status.dir) < 0)
599 		errormsg(TRUE,phd,tempresfile,
600 			"chdir %s: %s",status.dir,sys_errlist[errno]);
601 	setenv(status.dir);	/* set up v7 environment */
602 	if(tempinfile[0])mreopen(TRUE,phd,tempresfile,tempinfile,"r",stdin);
603 	else if(phd->hd_sinfile[0])mreopen(TRUE,phd,tempresfile,phd->hd_sinfile,"r",stdin);
604 	else mreopen(TRUE,phd,tempresfile,"/dev/null","r",stdin);
605 	if(phd->hd_code == 's' && phd->hd_soutfile[0]){
606 		if(stat(phd->hd_soutfile,&statbuf) < 0
607 		   || getsize(&statbuf) != 0)
608 			errormsg(FALSE,phd,tempresfile,"Bad result file '%s'",phd->hd_soutfile);
609 		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
610 		}
611 	else if(phd->hd_soutfile[0]){
612 		fd = fopen(phd->hd_soutfile,"w");
613 		if(fd == NULL)
614 			errormsg(TRUE,phd,tempresfile,"Open file %s: %s",
615 				phd->hd_soutfile,sys_errlist[errno]);
616 		fclose(fd);
617 		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
618 		}
619 	else mreopen(TRUE,phd,tempresfile,tempresfile,"a",stdout);
620 	debug("exec '%s'\n",phd->hd_scmdact);
621 	if(debugflg == 0){
622 		/* cheat */
623 		close(2);
624 		dup(1);
625 		/*
626 		mreopen(TRUE,phd,tempresfile,tempresfile,"a",stderr);
627 		*/
628 		}
629 	for(i=3;i<15;i++)close(i);
630 	if(strcmp(phd->hd_scmdact,"cat") == 0
631 	|| strcmp(phd->hd_scmdact,"/bin/cat") == 0)excat();
632 	do {
633 		mexecl(status.loginshell,"sh","-c",phd->hd_scmdact,0);
634 		sleep(2);
635 		} while(errno == ETXTBSY);
636 	perror(status.loginshell);
637 	exit(EX_UNAVAILABLE);
638 }
639 /*
640 	send back a response
641 
642 	if errormsg was called the resfile should be unlinked,
643 	to avoid two messages being sent there
644 */
645 sndresponse(phd,rcode)
646 unsigned rcode;
647 struct header *phd;
648 {
649 	char cmdstr[BUFSIZ], buf[BUFSIZ];
650 	int dummy;
651 	long maxfile = MAXFILELARGE;
652 	/* send response back if a response file
653 	was given or if mail/write is allowed */
654 	if(stat(resfile,&statbuf) < 0){
655 		error("%s %s",resfile,sys_errlist[errno]);
656 		return;
657 		}
658 	if(getsize(&statbuf) >= maxfile){
659 		errormsg(TRUE,phd,"Result file too large - not sent");
660 		return;
661 		}
662 	if(getsize(&statbuf) == 0){
663 		/* response file specified, no output generated */
664 		if(phd->hd_srespfile[0] != 0)return;
665 		/* quiet option - no output and a rcode of 0 */
666 		if(rcode == 0 && phd->hd_fquiet)return;
667 	}
668 	/* use both old and new mwrite parm lists */
669 
670 	if(phd->hd_srespfile[0])
671 		sprintf(cmdstr,"-o %s cat",phd->hd_srespfile);
672 	else sprintf(cmdstr,
673 "%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -c \"'%s'\" -y %s -e %ld -r %d",
674 	MWRITECMD, phd->hd_snfrom,phd->hd_sttyname,phd->hd_lttytime,
675 	phd->hd_mchto,phd->hd_snto, phd->hd_scmdvirt,phd->hd_ltimesent-TIMEBASE,
676 	phd->hd_addrfrom, phd->hd_addrto, phd->hd_lttytime,
677 	phd->hd_scmdvirt, phd->hd_sttyname, phd->hd_ltimesent-TIMEBASE, rcode);
678 
679 	sprintf(buf,"%s -m%c -z -b -l %s -s %s -c response %s",
680 		netcmd,phd->hd_mchfrom,phd->hd_snfrom,resfile,cmdstr);
681 	dummy = system(buf);		/* execute command buf */
682 }
683 
684 /*
685 
686 	excat
687 	does nothing more than copy standard input to standard
688 	output, like the cat command, but reports write errors.
689 	Uses getc and putc rather than fwrite and fread because
690 	the latter call getc and putc.
691 */
692 excat(){
693 	register int n;
694 	char buf[BUFSIZ];
695 
696 	errno = 0;
697 	while((n = read(0,buf,BUFSIZ)) > 0){
698 		if(write(1,buf,n) != n){
699 			perror("filecat: stdout");
700 			exit(EX_OSFILE);
701 			}
702 		}
703 	if(errno){
704 		perror("filecat: stdin");
705 		exit(EX_OSFILE);
706 	}
707 	exit(EX_OK);
708 }
709 /* returns errors for netrcv() */
710 static readhd(phd)
711 register struct header *phd;
712 {
713 	char cflag, sbuf[BUFSIZ], parmlist[PARMLIST], *cptr;
714 	int i, code;
715 	code = mgetc();
716 	phd->hd_mchto = mgetc();
717 	if(code != 'q' && code != 'y' && code != 'w' && code != 's'){
718 		error("bad code");
719 		return(-1);
720 		}
721 	phd->hd_code = code;
722 	for(i = 0; i < MAXINX; i++)
723 		if(phd->hd_mchto == inxtoch(i)) break;
724 	if(i >= MAXINX){
725 		error("bad phd->hd_mchto");
726 		return(-1);
727 		}
728 	if(phd->hd_mchto != local)return(-3);	/* being forwarded through us */
729 	phd->hd_mchfrom = mgetc();
730 	phd->hd_vmajor = mgetc();
731 	phd->hd_vminor = mgetc();
732 	i = 0;
733 	i += mgets(phd->hd_snto,NS);
734 	i += mgets(phd->hd_spasswd,20);
735 	i += mgets(phd->hd_sinfile,FNS);
736 	i += mgets(phd->hd_soutfile,FNS);
737 	i += mgets(phd->hd_srespfile,FNS);
738 	i += mgets(phd->hd_snfrom,NS);
739 
740 	/* addrfrom is the person who sent this to us,
741 	   addrto is the person who received the command, i.e.
742 	   addrto is on this machine */
743 	if(phd->hd_snfrom[0] == 0)strcpy(phd->hd_snfrom,"root");
744 	sprintf(phd->hd_addrfrom,  "%s:%s",longname(phd->hd_mchfrom),phd->hd_snfrom);
745 	sprintf(phd->hd_addrto,    "%s:%s",longname(phd->hd_mchto),phd->hd_snto);
746 
747 	i += mgets(phd->hd_sttyname,20);
748 	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
749 	cflag = mgetc();
750 	if(!phd->hd_mchfrom || !phd->hd_code || !cflag || !phd->hd_vmajor || !phd->hd_vminor){
751 		error("mgetc fails");
752 		return(-1);
753 		}
754 
755 	cflag -= 'a';
756 	phd->hd_fnonotify = (cflag & F_NONOTIFY);
757 	phd->hd_fquiet = (cflag & F_QUIET);
758 
759 	phd->hd_vmajor -= 'a';
760 	phd->hd_vminor -= 'a';
761 
762 	i += mgets(sbuf,BUFSIZ);
763 	phd->hd_lttytime = 0;
764 	sscanf(sbuf,"%lo",&phd->hd_lttytime);
765 
766 	i += mgets(parmlist,PARMLIST);
767 #ifdef CRN
768 	cptr = parmlist;
769 	while( *cptr != '(' )
770 		cptr++;
771 	*cptr = '\0';
772 	strcpy( phd->hd_ijobno, parmlist );
773 	*cptr = '(';
774 #else CRN
775 	strcpy( phd->hd_ijobno, "XYZZ" );
776 #endif CRN
777 	/* keep variable parameter list in crn slot */
778 	parseparmlist(parmlist);
779 
780 	i += mgets(sbuf,BUFSIZ);		/* time sent */
781 	sscanf(sbuf,"%ld",&phd->hd_ltimesent);
782 	phd->hd_ltimesent += TIMEBASE;
783 	i += mgetcmd(phd->hd_scmdact);
784 	i += mgetcmd(phd->hd_scmdvirt);
785 	if(i != 0){error("mgets fails"); return(-1);}
786 	if(phd->hd_scmdvirt[0] == 0)strcpy(phd->hd_scmdvirt,phd->hd_scmdact);
787 	return(0);
788 }
789 /*
790    check() -- verify login name and password
791    phd    = login,passwd
792    fverify  = 1 if password must check
793    Returns 1 if password is ok, 0 if not.
794 */
795 check(phd,fverify)	/* 1 if OK, 0 if not */
796 register struct header *phd;
797 int fverify;
798 {
799 	char *sencpasswd, *u, *nullstr = "";
800 	struct passwd *pwd;
801 #ifdef CRN
802 	struct gecos *gcos;
803 #endif CRN
804 	if(phd->hd_snto[0] == 0)return(!fverify);
805 	debug("check: phd->hd_snto = %s\n", phd->hd_snto );
806 	if(!goodacctname(phd->hd_snto))return(!fverify);
807 	pwd = getpwnam(phd->hd_snto);
808 	debug("got pwd=%d, pwd->pw_passwd = %s\n",pwd, pwd->pw_passwd);
809 	if(pwd == NULL)return(!fverify);
810 	if(*phd->hd_spasswd)sencpasswd = crypt(phd->hd_spasswd,pwd->pw_passwd);
811 	else sencpasswd = nullstr;
812 	debug("check: passwd(rcvd)=%s, passwd(file) = %s, passwd(encrypt)=%s\n", phd->hd_spasswd, pwd->pw_passwd, sencpasswd );
813 
814 	status.muid = guid(pwd->pw_uid,pwd->pw_gid);
815 	status.mgid = pwd->pw_gid;
816 #ifdef CRN
817 	if( (gcos=pwgecos( pwd->pw_gecos )) == NULL )
818 		strcpy( status.jobno, MAGICCRN );
819 	else
820 		strcpy( status.jobno, gcos->gc_crn );
821 #else CRN
822 	strcpy( status.jobno, "XYZZ");
823 #endif CRN
824 	strcpy(status.dir,pwd->pw_dir);
825 	strcpy(status.loginshell,pwd->pw_shell);
826 	u = status.loginshell;
827 	if(u[0] == 0 || strcmp("/bin/sbash",u) == 0)strcpy(u,Bsh);
828 
829 	getpwdf(pwd);
830 	/* ignore network passwd */
831 	/* acct is not a pair, acct is not "network", passwd is incorrect,
832 	and verification is requested => passwd not ok */
833 	if(!facctpaircheck(phd) && strcmp(phd->hd_snto,"network") != 0
834 	&& strcmp(pwd->pw_passwd,sencpasswd) != 0 && fverify)
835 		return(0);
836 	return(1);	/* otherwise passwd ok */
837 	}
838 mread(b,n)
839   register int n; {
840 	if(length <= 0)return(0);
841 	if(length < n)n = length;
842 	n = nread(b,n);
843 	if(n != BROKENREAD)length -= n;
844 	return(n);
845 	}
846 char mgetc(){			/* returns 0 if fail */
847 	register char c;
848 	register int n;
849 	char buf[3];
850 	if((n=nread(buf,3)) == BROKENREAD)return(0);
851 	if(n != 3){error("bad read %d",n); return(0); }
852 	c = buf[0];
853 	if(buf[1] != ' ' && buf[1] != ':'){error("Bad char %c",buf[1]); return(0); }
854 	length -= 3;
855 	if(length < 0){error("length wrong2 %ld",length); return(0); }
856 	return(c);
857 	}
858 /* read in string over the network wire */
859 /* put string in s, max length is maxlen */
860 mgets(s,maxlen)			/* returns 0 if ok, 1 if not */
861   int maxlen;
862   register char *s; {
863 	register char *q;
864 	register int n;
865 	char c;
866 	q = s;
867 	for(;;) {
868 		if((n=nread(&c,1)) == BROKENREAD){
869 			*s = 0;
870 			error("mgets %s",s);
871 			return(1);
872 			}
873 		if(n == 0)break;
874 		if(c == '\\'){
875 			if((n=nread(&c,1)) == BROKENREAD){
876 				*s = 0;
877 				error("mgets %s",s);
878 				return(1);
879 				}
880 			if(n == 0)break;
881 			}
882 		if(c == ' ')break;
883 		if(maxlen-- > 0) *s++ = c;
884 		}
885 	*s = 0;
886 	if(nread(&c,1) == BROKENREAD){
887 		error("mgets %s",s);
888 		return(1);
889 		}
890 	length -= (s - q + 2);
891 	if(length < 0){error("length wrong1 %ld %s",length,q); return(-1); }
892 	if(maxlen < 0)
893 		error("mgets - string too long");
894 	return(0);
895 	}
896 mgetcmd(s)			/* returns 0 if succeed, 1 otherwise */
897   char *s; {
898 	int i,n;
899 	char c;
900 	i = 0;
901 	for(;;){
902 		if((n=nread(&c,1)) == BROKENREAD){
903 			s[i] = 0;
904 			error("mgetcmd %s",s);
905 			return(1);
906 			}
907 		if(n <= 0 || c == '\n')break;
908 		if(c == '\\'){
909 			if(nread(&c,1) == BROKENREAD){
910 				s[i] = 0;
911 				error("mgetcmd %s",s);
912 				return(1);
913 				}
914 			length--;
915 			}
916 		s[i++] = c;
917 		length--;
918 		}
919 	s[i] = 0;
920 	length--;
921 	return(0);
922 	}
923 increment(s)
924  char *s; {
925 	int i;
926 	char *p;
927 	i = strlen(s) - 1;
928 	while(s[i] == '9')i--;
929 	if(s[i] < '0' || s[i] > '9'){
930 		p = s+i+1;
931 		while(*p)*p++ = '0';
932 		return;
933 		}
934 	(s[i])++;
935 	i++;
936 	while(s[i])s[i++] = '0';
937 	return;
938 	}
939 /* gather 24-hour stats and  mail to STATADDR */
940 /* should also gather stats on # error msgs */
941 dumpit(currt)
942   long currt; {
943 	register struct dumpstruc *p = &dump;
944 	register int ntot;
945 	long elapt;
946 	double cputime,utime,stime,bs,rawbs;
947 	char *sstartt;
948 	FILE *fdm;
949 	char froma[30];
950 	struct tms tbf;
951 
952 	/* if STATADDR is a file, the mail program this call will
953 	   ultimately execute must be able to deal with it,
954 	   and the remote mail program must be able to write on the
955 	   file, i.e. mode 666 */
956 	sprintf(froma,"%s=>",longname(local));
957 	strcat(froma,longname(remote));
958 	fdm = mailopen(STATADDR,froma,1,0);
959 	if(fdm == NULL)return;
960 
961 	/* calculate times */
962 	elapt = currt - dump.longtime;
963 	ntot = p->nnetcp + p->nnetmail + p->nsmail + p->nnetlpr
964 		+ p->nresp + p->nnet;
965 	sstartt = ctime(&dump.longtime) + 4;
966 	sstartt[strlen(sstartt) - 9] = 0;
967 
968 	times(&tbf);
969 # ifndef NOFP
970 	utime = tbf.tms_utime + tbf.tms_cutime;
971 	stime = tbf.tms_stime + tbf.tms_cstime;
972 	cputime = utime + stime;
973 	if(elapt > 0)cputime = (cputime/elapt) * 100.0;
974 	else cputime = 0.0;
975 	utime = utime/60.0;
976 	stime = stime/60.0;
977 	cputime = cputime/60.0;
978 	bs = p->bytetot;
979 	if(p->elaptot > 0)bs = bs /p->elaptot;
980 	else bs = 0.0;
981 # endif NOFP
982 
983 	/* print out the statistics */
984 	fprintf(fdm,"Subject: %s, %s, time %s\n",
985 		froma,sstartt, comptime(elapt));
986 	fprintf(fdm,"Command summary:\n");
987 	fprintf(fdm,"\t# sent %d\t# pass_thru %d\t# rcv %d:\t# netcp %d\n",
988 		p->nsend,p->npass,ntot,p->nnetcp);
989 	fprintf(fdm,"\t# netlpr %d\t# netmail %d\t# sendbmail %d\t# resp %d\n",
990 		p->nnetlpr,p->nnetmail,p->nsmail,p->nresp);
991 	fprintf(fdm,"Protocol summary:\n");
992 	fprintf(fdm,"\t# pk_sent %d\t# pk_rcv %d\t# b_sent %ld\t# b_rcv %ld\n",
993 		p->npacksent,p->npackrcv,p->nbytesent, p->nbytercv);
994 	fprintf(fdm,
995 		"\t# send_fails %d\t# retrans %d\t# abn %d\t\t# cksum_errs %d\n",
996 		p->nsendfail,p->nretrans, p->nabnormal,p->ncksum);
997 # ifndef NOFP
998 	fprintf(fdm,"Load:\tuser %4.1f\tsys %4.1f\tpct %5.2f\trate %6.1f\n",
999 		utime,stime,cputime,bs);
1000 	rawbs = p->brawtot*100L;
1001 	rawbs = rawbs / linechars();
1002 	fprintf(fdm,"\trawbytes %ld\tuse %4.1f\n", p->brawtot,rawbs);
1003 # endif NOFP
1004 	mailclose(fdm);
1005 
1006 	/* reset counters */
1007 	p->nbytesent = p->nbytercv = p->elaptot = p->bytetot = 0L;
1008 	p->nretrans = p->nloop = p->nabnormal = p->ncksum = 0;
1009 	p->npacksent = p->npackrcv = p->nnetcp = p->nnetmail = 0;
1010 	p->nsmail = p->nnetlpr = p->nnet = p->npass = 0;
1011 	p->nsend = p->nsendfail = 0;
1012 	dump.longtime = currt;
1013 	}
1014 /* returns 1 if n is ok, 0 if not */
1015 goodacctname(n)
1016   char *n; {
1017 	int i;
1018 	i = -1;
1019 	while(btable[++i].bname)
1020 		if(strcmp(btable[i].bname,n) == 0 &&
1021 			local == btable[i].bmach)return(0);
1022 	return(1);
1023 	}
1024 demask(s)
1025   register char *s; {
1026 /*
1027 	static char buf[20];
1028 	char skey[30];
1029 	makeuukey(skey,status.login,local);
1030 	strcpy(s,nbsdecrypt(s,skey,buf));
1031 */
1032 	while(*s){
1033 		*s &= 0177;		/* strip quote bites */
1034 		*s++ ^= 040;		/* invert upper-lower */
1035 		}
1036 	}
1037 /*VARARGS0*/
1038 mreopen(fsendtofmach,phd,sfn,a,b,c){
1039 /* simply handles errors by giving error msg */
1040 	if(freopen(a,b,c) == NULL)
1041 		errormsg(fsendtofmach,phd,sfn,"%s: %s",a,sys_errlist[errno]);
1042 }
1043 /*
1044 	addtopub(string, args)
1045 
1046 	add a message to the public logfile /usr/net/logfile.
1047 	note that the file must be writeable by everyone
1048 	if error messages from the netrcv subroutine
1049 	such as chdir errors are to be noticed.
1050 */
1051 /*VARARGS0*/
1052 addtopublic(s,a,b,c,d,e,f,g,h,i,j,k,l,m,n)
1053 char *s;
1054 {
1055 	static FILE *log = NULL;
1056 	if(log == NULL){
1057 		if(stat(publogfile,&statbuf) < 0)return;
1058 		log = fopen(publogfile,"a");
1059 		if(log == NULL)return;
1060 		}
1061 	fseek(log,0L,2);
1062 	fprintf(log,s,a,b,c,d,e,f,g,h,i,j,k,l,m,n);
1063 	fflush(log);
1064 	}
1065 /* set up a dummy environment for v7 /bin/sh */
1066 setenv(home)
1067   char *home; {
1068 	static char *env[3],benv[2][50];
1069 	env[0] = benv[0];
1070 	env[1] = benv[1];
1071 #ifdef CCV7
1072 	strcpy( env[0], "PATH=:.:/usr/cc/bin:/usr/ucb/bin" );
1073 #else CCV7
1074 	strcpy(env[0],"PATH=:/bin:/usr/bin");
1075 #endif CCV7
1076 	sprintf(env[1],"HOME=%s",home);
1077 	env[2] = 0;
1078 	environ = env;
1079 	}
1080 /*
1081 	errormsg(fsendtofmach,phd,sfn,"string",arg(s))
1082 
1083 	Sends error message to user.
1084 	If fsendtofmach=TRUE, send to phd->hd_mchfrom, otherwise
1085 	send to phd->hd_mchto.
1086 	Also, if error occured during return of a "response",
1087 	send to local machine.
1088 
1089 	Note that errormsg can be called by the netrcv subroutine
1090 	after the setuid() call to the specific user, so the
1091 	user must be able to get off an error msg back to him,
1092 	and to write in the two log files.
1093 	Can't use -w,-x,-y,-z for the net cmd because must be root for those.
1094 
1095 	If sfn != NULL, then unlink sfn before exiting.
1096 */
1097 /*VARARGS0*/
1098 errormsg(fsendtofmach,phd,sfn,s,a,b,c,d,e,f,g,h)
1099 char fsendtofmach;
1100 struct header *phd;
1101 char *sfn,*s;
1102 {
1103 	int rcode;
1104 	char errstr[BUFSIZ], cmdstr[BUFSIZ], rcmd[BUFSIZ];
1105 	char toadd[FNS], fromadd[FNS], mchto, mchfrom;
1106 	char snto[FNS], snfrom[FNS];
1107 
1108 	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
1109 	/* will send to toadd, from fromadd */
1110 	if(!fsendtofmach || strcmp(phd->hd_scmdvirt,"response") == 0){
1111 		/* send to tomach mach, thus send to toaddr. */
1112 		/* if this is an error during a response, send to local mach. */
1113 		strcpy(toadd,  phd->hd_addrto);
1114 		strcpy(fromadd,phd->hd_addrfrom);
1115 	}
1116 	else {		/* send to remote mach, thus send back to addrfrom*/
1117 		strcpy(toadd,  phd->hd_addrfrom);
1118 		strcpy(fromadd,phd->hd_addrto);
1119 	}
1120 	sprintf(errstr,"Error: ");
1121 	sprintf(cmdstr,s,a,b,c,d,e,f,g,h);
1122 	strcat(errstr,cmdstr);
1123 	strcat(errstr,"\n");
1124 	addtolog(remote,errstr);
1125 	addtopublic(errstr);
1126 
1127 	mchto =   MchSFromAddr(snto,toadd);
1128 	mchfrom = MchSFromAddr(snfrom,fromadd);
1129 
1130 	sprintf(rcmd,
1131 "%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -y %s -c \"'%s'\" -e %ld",
1132 	MWRITECMD, snto, phd->hd_sttyname, phd->hd_lttytime,
1133 	local, snfrom,phd->hd_scmdvirt, phd->hd_ltimesent-TIMEBASE,
1134 	toadd, fromadd, phd->hd_lttytime, phd->hd_sttyname, phd->hd_scmdvirt,
1135 	phd->hd_ltimesent-TIMEBASE);
1136 
1137 	if(mchto == local)
1138 		sprintf(cmdstr, "echo \"%s\" | %s", errstr,rcmd);
1139 	else
1140 		sprintf(cmdstr,
1141 		"echo \"%s\" | %s -m%c -b -c errormessage -l network - %s",
1142 			errstr,netcmd,mchto,rcmd);
1143 	rcode = system(cmdstr);
1144 	debug( "errormsg: cmdstr = %s\n", cmdstr );
1145 	debug( "errormsg: rcode = %d\n", rcode );
1146 	if(sfn != NULL)unlink(sfn);
1147 	exit(EX_USAGE);
1148 	}
1149 handlekill(){	/* SIGTERM signal */
1150 	long t;
1151 	/*
1152 	t = gettime();
1153 	dumpit(t);
1154 	*/
1155 # ifdef NETLDISC
1156 	/* turn off net line discipline if possible */
1157 	netd.dp_linedis = 0;
1158 	ioctl(netd.dp_linefd,TIOCSETD,&netd.dp_linedis);
1159 	close(netd.dp_linefd);
1160 	printf("Network line discipline turned off.\n");
1161 # endif NETLDISC
1162 	exit(EX_OK);	/* kill myself */
1163 	}
1164 
1165 /* check a request to see if it is an acct pair */
1166 /* returns 1 if it is, 0 if not */
1167 static facctpaircheck(phd)
1168 register struct header *phd;
1169 {
1170 	return(0);
1171 }
1172 
1173