xref: /csrg-svn/old/berknet/netdaemon.c (revision 8508)
1 static char sccsid[] = "@(#)netdaemon.c	4.4	(Berkeley)	10/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 int 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 int subs;
256 ignoreit() {
257 	signal(SIGALRM,0);
258 }
259 
260 send(jname)
261 	char *jname;
262 {	/* push those bytes */
263 	/* returns 0 if send fails, 1 otherwise */
264 	register int n;
265 	int i;
266 	long lsize;
267 	char mbuf[20], buf[MAXNBUF];
268 	register char *p;
269 	register FILE *jfile;
270 
271 	debug("send %s",jname);
272 	if(stat(jname,&statbuf) < 0)goto sfail;
273 	lsize = getsize(&statbuf);
274 	if(lsize < MINSIZE){		/* all files are at least this long */
275 		unlink(jname);
276 		jname[0] = 'c';
277 		unlink(jname);
278 		return(1);
279 		}
280 	jfile = fopen(jname,"r");
281 	if(jfile == NULL)goto sfail;
282 	/*
283 	strcpy(mbuf,sheader);
284 	i = strlen(sheader);
285 	p = (char *)&lsize;
286 	lsize = fixuplong(lsize);
287 	mbuf[i] = *p++;
288 	mbuf[i+1] = *p++;
289 	mbuf[i+2] = *p++;
290 	mbuf[i+3] = *p++;
291 	i = i + 4;
292 	sendreset();
293 	*/
294 	initseqno();
295 	sprintf(mbuf,"|%08ld|",lsize);
296 	i = 10;
297 	if(xwrite(mbuf,i) == WRITEFAIL)goto bwrite;
298 	while((n=read(fileno(jfile),buf,MAXNBUF)) > 0)
299 		if(xwrite(buf,n) == WRITEFAIL)goto bwrite;
300 	fclose(jfile);
301 	debug("end send");
302 	return(1);
303 bwrite:
304 	dump.nsendfail++;
305 	fclose(jfile);
306 	addtolog(remote,"^F%c\n",remote);
307 	return(0);
308 sfail:
309 	error("%s: %s",jname,sys_errlist[errno]);
310 	dump.nsendfail++;
311 	return(0);
312 	}
313 netrcv(){
314 	/* returns -2 in normal fail, -1 in abnormal fail, >= 0 otherwise */
315 	char sin;
316 	char mgetc(), *s;
317 	register int n;
318 	char c;
319 	int i, dummy, pid;
320 	unsigned rcode;
321 	long otime,olength,diff,rcvfinish,nt;
322 	double r;
323 	char hbuf[20], buf[MAXNBUF];
324 	register FILE *temp;
325 	static struct header hd;
326 
327 	initseqno();
328 	/*
329 	n = nread(hbuf,strlen(sheader));
330 	if(n == BROKENREAD)return(-2);
331 	if(n != strlen(sheader) || strcmp(sheader,hbuf) != 0){
332 		error("wrong head %d %s",n,hbuf);
333 		return(-1);
334 		}
335 	n = nread(&length,4);
336 	length = fixuplong(length);
337 	*/
338 	n = nread(hbuf,10);
339 	if(n == BROKENREAD)return(-2);
340 	if(n != 10){
341 		error("bad length nread %d",n);
342 		return(-1);
343 		}
344 	hbuf[10] = 0;
345 	if(hbuf[0] != '|' || hbuf[9] != '|'){
346 		error("poor format %s",hbuf);
347 		return(-1);
348 		}
349 	hbuf[9] = 0;
350 	length = atol(hbuf+1);
351 	if(length < 0 || length > 100000000L){
352 		error("bad length %ld",length);
353 		return(-1);
354 		}
355 	dump.braw = 4;
356 	olength = length;
357 	otime = gettime();
358 	debug("length = %ld\n",length);
359 
360 /*
361 	begin parsing header
362 
363 	from local to remote (requests)
364 	code	net option	reason
365 	q			normal request
366 	y	-y		simply skips login check (used by netlpr)
367 
368 	from remote to local
369 	code	net option	reason
370 	w	-w		message to be written/mailed back
371 	s	-z		normal response
372 */
373 
374 	i = readhd(&hd);
375 	if(i == -3)goto forw;			/* being forwarded thru us */
376 	if(i != 0)return(i);
377 
378 	strcpy(status.login, hd.hd_snto);
379 	strcpy(status.localname,hd.hd_snfrom);
380 
381 	demask(hd.hd_spasswd);
382 
383 	s = hd.hd_scmdvirt;
384 	while(*s && *s != ' ')s++;
385 	c = *s;
386 	*s = 0;
387 	if(strcmp(hd.hd_scmdvirt,"netlpr") == 0)dump.nnetlpr++;
388 	else if(strcmp(hd.hd_scmdvirt,"netmail") == 0)dump.nnetmail++;
389 	else if(strcmp(hd.hd_scmdvirt,"mail") == 0)dump.nsmail++;
390 	else if(strcmp(hd.hd_scmdvirt,"netcp") == 0)dump.nnetcp++;
391 	else if(strcmp(hd.hd_scmdvirt,"response") == 0)dump.nresp++;
392 	else dump.nnet++;
393 	*s = c;
394 
395 	printhd(&hd);
396 
397 	/* any chars left are data */
398 forw:
399 	sin = 0;
400 	if(length > 0){	/* make a temp input file */
401 		increment(tempfile);
402 		temp = fopen(tempfile,"w");
403 		if(temp == NULL){
404 			error("%s %s",tempfile,sys_errlist[errno]);
405 			return(-1);
406 			}
407 		chmod(tempfile,0600);
408 		if(hd.hd_mchto != local){
409 			fprintf(temp,"%c :%c :",hd.hd_code,hd.hd_mchto);
410 			fflush(temp);
411 		}
412 		/* this is the loop to read in all the data */
413 		while((n = mread(buf,MAXNBUF)) > 0)
414 			if(write(fileno(temp),buf,n) != n){
415 				error("%s %s",tempfile,sys_errlist[errno]);
416 				fclose(temp);
417 				unlink(tempfile);
418 				return(-1);
419 				};
420 		fclose(temp);
421 		if(n == BROKENREAD || length > 0){
422 			unlink(tempfile);
423 			return(-2);
424 			}
425 		sin = 1;
426 		if(hd.hd_mchto != local){
427 			diff = gettime() - otime;
428 			if(diff < 1)diff = 1;	/* avoid dividing by 0 */
429 # ifndef NOFP
430 			r = olength;
431 			r = r/diff;
432 			addtolog(remote,"^P(to %c, %ldb, %ldsec, %4.1fb/sec)\n",
433 				hd.hd_mchto,olength,diff,r);
434 # else NOFP
435 			addtolog(remote,"^P(to %c, %ldb, %ldsec)\n",
436 				hd.hd_mchto,olength,diff);
437 # endif NOFP
438 			dump.npass++;
439 			dump.bytetot += olength;
440 			dump.elaptot += diff;
441 			while((pid = fork()) == -1)sleep(2);
442 			if(pid == 0){
443 				extern char *logfile;
444 				RENICE0();
445 #ifdef CCV7
446 				/* make sure the spawned child has it's own
447 					group process to avoid the nasty
448 					"try again" message
449 				*/
450 				setpgrp();
451 				/*
452 				 * log error messages on unit 2 from subprocess
453 				 * though this may not sync very well
454 				 */
455 #endif CCV7
456 				dup2(open("/logit",1),2);
457 				lseek(2,0,2);
458 				execl(netcmd,"net","-x","-m",longname(hd.hd_mchto),
459 					"-s",tempfile,0);
460 				error("%s: %s",netcmd,sys_errlist[errno]);
461 				exit(EX_UNAVAILABLE);
462 				}
463 			while (pid != wait(&rcode))
464 				error("netdaemon: wait returned with wrong pid\n");
465 			unlink(tempfile);
466 			rcode >>= 8;
467 			if(rcode != 0)
468 				error("%s: pass-thru rcode %d", netcmd, rcode);
469 			debug("passthru to %c code %c rcode %d",
470 				hd.hd_mchto,hd.hd_code,rcode);
471 			return(1);
472 			}
473 		}
474 	if(length > 0){error("file too short"); return(-1); }
475 	rcvfinish = gettime();
476 
477 	while((pid = fork()) == -1)sleep(2);
478 	if(pid > 0){
479 		if (++subs < 10)
480 		{
481 			signal(SIGALRM,ignoreit);
482 			alarm(5);
483 		}
484 		/*
485 		 * Note: we expect the alarm to cause a system
486 		 * call aborted error thus to fall out of the wait()
487 		 * if no processes need to be recovered after 5 seconds
488 		 *
489 		 * This patch is intended to limit the number of
490 		 * processes the network can create at anytime
491 		 * to keep from running out of processes per uid
492 		 */
493 		while( wait(&dummy) != -1)  if (subs) --subs;
494 		return(1);	/* normal return */
495 	}
496 	/* this is a child, who will go ahead and execute the command */
497 	/* running uid=0 at this point */
498 	RENICE0();
499 	/* nice(0 set back to 0 */
500 #ifdef CCV7
501 	/* separate group process */
502 	setpgrp();
503 #endif CCV7
504 
505 
506 	/*
507 	while((pid = fork()) == -1)sleep(2);
508 	if(pid != 0)exit(EX_OK);
509 	*/
510 
511 	/* child process which forks and waits */
512 	mktemp(resfile);
513 	while((pid = fork()) == -1)sleep(2);
514 	if(pid == 0){
515 		/* child */
516 		strcpy(status.loginshell,Bsh);
517 		frommach = hd.hd_mchfrom;
518 		n = check(&hd,(hd.hd_code == 'q'));
519 		if(!n)errormsg(TRUE,&hd,NULL,
520 			"Bad remote login/password '%s'",hd.hd_snto);
521 		temp = fopen(resfile,"w");
522 		if(temp == NULL)
523 			errormsg(TRUE,&hd,NULL,
524 			"Create file %s: %s",resfile,sys_errlist[errno]);
525 		fclose(temp);
526 		chmod(resfile,0600);
527 		mchown(resfile,status.muid,status.mgid);
528 		if(sin)
529 			mchown(tempfile,status.muid,status.mgid);
530 		else tempfile[0] = 0;
531 		setgid(status.mgid);
532 		setuid(status.muid);
533 		/* after this point our gid, uid is the target user's */
534 		excmd(&hd,resfile,tempfile);
535 	}
536 	/* parent */
537 	while (pid != wait(&rcode))
538 			error("netdaemon:  returned wrong pid\n");
539 
540 	rcode = (((rcode&077400) >>8) &0177);
541 	/*
542 	fclose(stdin);
543 	fclose(stdout);
544 	fclose(stderr);
545 	*/
546 	if(sin)unlink(tempfile);
547 	/*
548 	   now send something back to the sender
549 	   unless this was a response (file or message)
550 	*/
551 	if((hd.hd_code == 'q' || hd.hd_code == 'y')
552 	&& (hd.hd_srespfile[0] || !hd.hd_fnonotify))
553 		sndresponse(&hd,rcode);
554 	unlink(resfile);
555 	s = ctime(&rcvfinish);
556 	s += 4;
557 	s[strlen(s) -8] = 0;
558 	diff = rcvfinish - otime;
559 	if(diff < 1)diff = 1;		/* avoid dividing by zero */
560 	dump.bytetot += olength;
561 	dump.elaptot += diff;
562 	sprintf(buf,"%s rcv  %c:%-8s (%s)",
563 		s,hd.hd_mchfrom,hd.hd_snfrom,hd.hd_snto);
564 	addtolog(remote,"%s C: %s\n",buf,hd.hd_scmdvirt);
565 	addtopublic("%s R: %d C: %s\n",buf,rcode,hd.hd_scmdvirt);
566 	nt = rcvfinish - hd.hd_ltimesent;
567 	buf[0] = 0;
568 	if(nt > 0L)sprintf(buf," took (%s)",comptime(nt));
569 # ifndef NOFP
570 	r = olength;
571 	r = r/diff;
572 	addtolog(remote,"\t\tR: %d%s %ldb %ldsec %4.1fb/sec\n",
573 		rcode,buf,olength,diff,r);
574 	r = dump.braw;
575 	r = r/diff;
576 	addtolog(remote,"\t\t%4.1frb/sec %4.1f%% use\n",r,(r/linechars())*100L);
577 # else NOFP
578 	addtolog(remote,"\t\tR: %d%s %ldb %ldsec\n",
579 		rcode,buf,olength,diff);
580 # endif NOFP
581 	exit(EX_OK);
582 	/*UNREACHED*/
583 	}
584 long linechars(){
585 	if(netd.dp_inspeed == 13)return(960L);
586 	else return(120L);
587 	}
588 /*
589 	execute the user's command
590 	this procedure is executed with uid, gid of the user
591 */
592 excmd(phd,tempresfile,tempinfile)
593 	register struct header *phd;
594 	char *tempresfile, *tempinfile;
595 {
596 	FILE *fd;
597 	int i, uid;
598 	register char *s, c;
599 
600 	uid = getuid();
601 	uid = uidmask(uid);
602 	status.muid = uidmask(status.muid);
603 	if(uid != status.muid)error("setuid fails");
604 	debug("uid: %u, gid: %u\n",uid,status.mgid);
605 	/* check for allowed root commands, for security reasons */
606 	if(uid == SUPERUSER){
607 		s = phd->hd_scmdact;
608 		while(*s && *s != ' ')s++;
609 		c = *s;
610 		*s = 0;
611 		/* these are the only commands root may execute */
612 		if(strcmp(phd->hd_scmdact,"cat")            	!= 0
613 		&& strcmp(phd->hd_scmdact,MWRITECMD)        	!= 0
614 		&& strcmp(phd->hd_scmdact,"/bin/cat")       	!= 0
615 		&& strcmp(phd->hd_scmdact,"netrm")          	!= 0
616 		&& strcmp(phd->hd_scmdact,"/usr/lib/tq")    	!= 0
617 		&& strcmp(phd->hd_scmdact,"/usr/cc/lib/tq") 	!= 0
618 		&& strcmp(phd->hd_scmdact,"/usr/lib/rtrrm") 	!= 0
619 		&& strcmp(phd->hd_scmdact,"/usr/cc/lib/rtrrm")	!= 0
620 		&& strcmp(phd->hd_scmdact,"lpr")            	!= 0)
621 			errormsg(TRUE,phd,tempresfile,
622 				"Not allowed to execute '%s' as root",
623 				phd->hd_scmdact);
624 		*s = c;
625 		}
626 	if(chdir(status.dir) < 0)
627 		errormsg(TRUE,phd,tempresfile,
628 			"chdir %s: %s",status.dir,sys_errlist[errno]);
629 	setenv(status.dir);	/* set up v7 environment */
630 	if(tempinfile[0])mreopen(TRUE,phd,tempresfile,tempinfile,"r",stdin);
631 	else if(phd->hd_sinfile[0])mreopen(TRUE,phd,tempresfile,phd->hd_sinfile,"r",stdin);
632 	else mreopen(TRUE,phd,tempresfile,"/dev/null","r",stdin);
633 	if(phd->hd_code == 's' && phd->hd_soutfile[0]){
634 		if(stat(phd->hd_soutfile,&statbuf) < 0
635 		   || getsize(&statbuf) != 0)
636 			errormsg(FALSE,phd,tempresfile,"Bad result file '%s'",phd->hd_soutfile);
637 		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
638 		}
639 	else if(phd->hd_soutfile[0]){
640 		fd = fopen(phd->hd_soutfile,"w");
641 		if(fd == NULL)
642 			errormsg(TRUE,phd,tempresfile,"Open file %s: %s",
643 				phd->hd_soutfile,sys_errlist[errno]);
644 		fclose(fd);
645 		mreopen(TRUE,phd,tempresfile,phd->hd_soutfile,"w",stdout);
646 		}
647 	else mreopen(TRUE,phd,tempresfile,tempresfile,"a",stdout);
648 	debug("exec '%s'\n",phd->hd_scmdact);
649 	if(debugflg == 0){
650 		/* cheat */
651 		close(2);
652 		dup(1);
653 		/*
654 		mreopen(TRUE,phd,tempresfile,tempresfile,"a",stderr);
655 		*/
656 		}
657 	for(i=3;i<15;i++)close(i);
658 	if(strcmp(phd->hd_scmdact,"cat") == 0
659 	|| strcmp(phd->hd_scmdact,"/bin/cat") == 0)excat();
660 	do {
661 		mexecl(status.loginshell,"sh","-c",phd->hd_scmdact,0);
662 		sleep(2);
663 		} while(errno == ETXTBSY);
664 	perror(status.loginshell);
665 	exit(EX_UNAVAILABLE);
666 }
667 /*
668 	send back a response
669 
670 	if errormsg was called the resfile should be unlinked,
671 	to avoid two messages being sent there
672 */
673 sndresponse(phd,rcode)
674 unsigned rcode;
675 struct header *phd;
676 {
677 	char cmdstr[BUFSIZ], buf[BUFSIZ];
678 	int dummy;
679 	long maxfile = MAXFILELARGE;
680 	/* send response back if a response file
681 	was given or if mail/write is allowed */
682 	if(stat(resfile,&statbuf) < 0){
683 		error("%s %s",resfile,sys_errlist[errno]);
684 		return;
685 		}
686 	if(getsize(&statbuf) >= maxfile){
687 		errormsg(TRUE,phd,"Result file too large - not sent");
688 		return;
689 		}
690 	if(getsize(&statbuf) == 0){
691 		/* response file specified, no output generated */
692 		if(phd->hd_srespfile[0] != 0)return;
693 		/* quiet option - no output and a rcode of 0 */
694 		if(rcode == 0 && phd->hd_fquiet)return;
695 	}
696 	/* use both old and new mwrite parm lists */
697 
698 	if(phd->hd_srespfile[0])
699 		sprintf(cmdstr,"-o %s cat",phd->hd_srespfile);
700 	else sprintf(cmdstr,
701 "%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -c \"'%s'\" -y %s -e %ld -r %d",
702 	MWRITECMD, phd->hd_snfrom,phd->hd_sttyname,phd->hd_lttytime,
703 	phd->hd_mchto,phd->hd_snto, phd->hd_scmdvirt,phd->hd_ltimesent-TIMEBASE,
704 	phd->hd_addrfrom, phd->hd_addrto, phd->hd_lttytime,
705 	phd->hd_scmdvirt, phd->hd_sttyname, phd->hd_ltimesent-TIMEBASE, rcode);
706 
707 	sprintf(buf,"%s -m%c -z -b -l %s -s %s -c response %s",
708 		netcmd,phd->hd_mchfrom,phd->hd_snfrom,resfile,cmdstr);
709 	dummy = system(buf);		/* execute command buf */
710 }
711 
712 /*
713 
714 	excat
715 	does nothing more than copy standard input to standard
716 	output, like the cat command, but reports write errors.
717 	Uses getc and putc rather than fwrite and fread because
718 	the latter call getc and putc.
719 */
720 excat(){
721 	register int n;
722 	char buf[BUFSIZ];
723 
724 	errno = 0;
725 	while((n = read(0,buf,BUFSIZ)) > 0){
726 		if(write(1,buf,n) != n){
727 			perror("filecat: stdout");
728 			exit(EX_OSFILE);
729 			}
730 		}
731 	if(errno){
732 		perror("filecat: stdin");
733 		exit(EX_OSFILE);
734 	}
735 	exit(EX_OK);
736 }
737 /* returns errors for netrcv() */
738 static readhd(phd)
739 register struct header *phd;
740 {
741 	char cflag, sbuf[BUFSIZ], parmlist[PARMLIST], *cptr;
742 	int i, code;
743 	code = mgetc();
744 	phd->hd_mchto = mgetc();
745 	if(code != 'q' && code != 'y' && code != 'w' && code != 's'){
746 		error("bad code");
747 		return(-1);
748 		}
749 	phd->hd_code = code;
750 	for(i = 0; i < MAXINX; i++)
751 		if(phd->hd_mchto == inxtoch(i)) break;
752 	if(i >= MAXINX){
753 		error("bad phd->hd_mchto");
754 		return(-1);
755 		}
756 	if(phd->hd_mchto != local)return(-3);	/* being forwarded through us */
757 	phd->hd_mchfrom = mgetc();
758 	phd->hd_vmajor = mgetc();
759 	phd->hd_vminor = mgetc();
760 	i = 0;
761 	i += mgets(phd->hd_snto,NS);
762 	i += mgets(phd->hd_spasswd,20);
763 	i += mgets(phd->hd_sinfile,FNS);
764 	i += mgets(phd->hd_soutfile,FNS);
765 	i += mgets(phd->hd_srespfile,FNS);
766 	i += mgets(phd->hd_snfrom,NS);
767 
768 	/* addrfrom is the person who sent this to us,
769 	   addrto is the person who received the command, i.e.
770 	   addrto is on this machine */
771 	if(phd->hd_snfrom[0] == 0)strcpy(phd->hd_snfrom,"root");
772 	sprintf(phd->hd_addrfrom,  "%s:%s",longname(phd->hd_mchfrom),phd->hd_snfrom);
773 	sprintf(phd->hd_addrto,    "%s:%s",longname(phd->hd_mchto),phd->hd_snto);
774 
775 	i += mgets(phd->hd_sttyname,20);
776 	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
777 	cflag = mgetc();
778 	if(!phd->hd_mchfrom || !phd->hd_code || !cflag || !phd->hd_vmajor || !phd->hd_vminor){
779 		error("mgetc fails");
780 		return(-1);
781 		}
782 
783 	cflag -= 'a';
784 	phd->hd_fnonotify = (cflag & F_NONOTIFY);
785 	phd->hd_fquiet = (cflag & F_QUIET);
786 
787 	phd->hd_vmajor -= 'a';
788 	phd->hd_vminor -= 'a';
789 
790 	i += mgets(sbuf,BUFSIZ);
791 	phd->hd_lttytime = 0;
792 	sscanf(sbuf,"%lo",&phd->hd_lttytime);
793 
794 	i += mgets(parmlist,PARMLIST);
795 #ifdef CRN
796 	cptr = parmlist;
797 	while( *cptr != '(' )
798 		cptr++;
799 	*cptr = '\0';
800 	strcpy( phd->hd_ijobno, parmlist );
801 	*cptr = '(';
802 #else CRN
803 	strcpy( phd->hd_ijobno, "XYZZ" );
804 #endif CRN
805 	/* keep variable parameter list in crn slot */
806 	parseparmlist(parmlist);
807 
808 	i += mgets(sbuf,BUFSIZ);		/* time sent */
809 	sscanf(sbuf,"%ld",&phd->hd_ltimesent);
810 	phd->hd_ltimesent += TIMEBASE;
811 	i += mgetcmd(phd->hd_scmdact);
812 	i += mgetcmd(phd->hd_scmdvirt);
813 	if(i != 0){error("mgets fails"); return(-1);}
814 	if(phd->hd_scmdvirt[0] == 0)strcpy(phd->hd_scmdvirt,phd->hd_scmdact);
815 	return(0);
816 }
817 /*
818    check() -- verify login name and password
819    phd    = login,passwd
820    fverify  = 1 if password must check
821    Returns 1 if password is ok, 0 if not.
822 */
823 check(phd,fverify)	/* 1 if OK, 0 if not */
824 register struct header *phd;
825 int fverify;
826 {
827 	char *sencpasswd, *u, *nullstr = "";
828 	struct passwd *pwd;
829 #ifdef CRN
830 	struct gecos *gcos;
831 #endif CRN
832 	if(phd->hd_snto[0] == 0)return(!fverify);
833 	debug("check: phd->hd_snto = %s\n", phd->hd_snto );
834 	if(!goodacctname(phd->hd_snto))return(!fverify);
835 	pwd = getpwnam(phd->hd_snto);
836 	debug("got pwd=%d, pwd->pw_passwd = %s\n",pwd, pwd->pw_passwd);
837 	if(pwd == NULL)return(!fverify);
838 	if(*phd->hd_spasswd)sencpasswd = crypt(phd->hd_spasswd,pwd->pw_passwd);
839 	else sencpasswd = nullstr;
840 	debug("check: passwd(rcvd)=%s, passwd(file) = %s, passwd(encrypt)=%s\n", phd->hd_spasswd, pwd->pw_passwd, sencpasswd );
841 
842 	status.muid = guid(pwd->pw_uid,pwd->pw_gid);
843 	status.mgid = pwd->pw_gid;
844 #ifdef CRN
845 	if( (gcos=pwgecos( pwd->pw_gecos )) == NULL )
846 		strcpy( status.jobno, MAGICCRN );
847 	else
848 		strcpy( status.jobno, gcos->gc_crn );
849 #else CRN
850 	strcpy( status.jobno, "XYZZ");
851 #endif CRN
852 	strcpy(status.dir,pwd->pw_dir);
853 	strcpy(status.loginshell,pwd->pw_shell);
854 	u = status.loginshell;
855 	if(u[0] == 0 || strcmp("/bin/sbash",u) == 0)strcpy(u,Bsh);
856 
857 	getpwdf(pwd);
858 	/* ignore network passwd */
859 	/* acct is not a pair, acct is not "network", passwd is incorrect,
860 	and verification is requested => passwd not ok */
861 	if(!facctpaircheck(phd) && strcmp(phd->hd_snto,"network") != 0
862 	&& strcmp(pwd->pw_passwd,sencpasswd) != 0 && fverify)
863 		return(0);
864 	return(1);	/* otherwise passwd ok */
865 	}
866 mread(b,n)
867   register int n; {
868 	if(length <= 0)return(0);
869 	if(length < n)n = length;
870 	n = nread(b,n);
871 	if(n != BROKENREAD)length -= n;
872 	return(n);
873 	}
874 char mgetc(){			/* returns 0 if fail */
875 	register char c;
876 	register int n;
877 	char buf[3];
878 	if((n=nread(buf,3)) == BROKENREAD)return(0);
879 	if(n != 3){error("bad read %d",n); return(0); }
880 	c = buf[0];
881 	if(buf[1] != ' ' && buf[1] != ':'){error("Bad char %c",buf[1]); return(0); }
882 	length -= 3;
883 	if(length < 0){error("length wrong2 %ld",length); return(0); }
884 	return(c);
885 	}
886 /* read in string over the network wire */
887 /* put string in s, max length is maxlen */
888 mgets(s,maxlen)			/* returns 0 if ok, 1 if not */
889   int maxlen;
890   register char *s; {
891 	register char *q;
892 	register int n;
893 	char c;
894 	q = s;
895 	for(;;) {
896 		if((n=nread(&c,1)) == BROKENREAD){
897 			*s = 0;
898 			error("mgets %s",s);
899 			return(1);
900 			}
901 		if(n == 0)break;
902 		if(c == '\\'){
903 			if((n=nread(&c,1)) == BROKENREAD){
904 				*s = 0;
905 				error("mgets %s",s);
906 				return(1);
907 				}
908 			if(n == 0)break;
909 			}
910 		if(c == ' ')break;
911 		if(maxlen-- > 0) *s++ = c;
912 		}
913 	*s = 0;
914 	if(nread(&c,1) == BROKENREAD){
915 		error("mgets %s",s);
916 		return(1);
917 		}
918 	length -= (s - q + 2);
919 	if(length < 0){error("length wrong1 %ld %s",length,q); return(-1); }
920 	if(maxlen < 0)
921 		error("mgets - string too long");
922 	return(0);
923 	}
924 mgetcmd(s)			/* returns 0 if succeed, 1 otherwise */
925   char *s; {
926 	int i,n;
927 	char c;
928 	i = 0;
929 	for(;;){
930 		if((n=nread(&c,1)) == BROKENREAD){
931 			s[i] = 0;
932 			error("mgetcmd %s",s);
933 			return(1);
934 			}
935 		if(n <= 0 || c == '\n')break;
936 		if(c == '\\'){
937 			if(nread(&c,1) == BROKENREAD){
938 				s[i] = 0;
939 				error("mgetcmd %s",s);
940 				return(1);
941 				}
942 			length--;
943 			}
944 		s[i++] = c;
945 		length--;
946 		}
947 	s[i] = 0;
948 	length--;
949 	return(0);
950 	}
951 increment(s)
952  char *s; {
953 	int i;
954 	char *p;
955 	i = strlen(s) - 1;
956 	while(s[i] == '9')i--;
957 	if(s[i] < '0' || s[i] > '9'){
958 		p = s+i+1;
959 		while(*p)*p++ = '0';
960 		return;
961 		}
962 	(s[i])++;
963 	i++;
964 	while(s[i])s[i++] = '0';
965 	return;
966 	}
967 /* gather 24-hour stats and  mail to STATADDR */
968 /* should also gather stats on # error msgs */
969 dumpit(currt)
970   long currt; {
971 	register struct dumpstruc *p = &dump;
972 	register int ntot;
973 	long elapt;
974 	double cputime,utime,stime,bs,rawbs;
975 	char *sstartt;
976 	FILE *fdm;
977 	char froma[30];
978 	struct tms tbf;
979 
980 	/* if STATADDR is a file, the mail program this call will
981 	   ultimately execute must be able to deal with it,
982 	   and the remote mail program must be able to write on the
983 	   file, i.e. mode 666 */
984 	sprintf(froma,"%s=>",longname(local));
985 	strcat(froma,longname(remote));
986 	fdm = mailopen(STATADDR,froma,1,0);
987 	if(fdm == NULL)return;
988 
989 	/* calculate times */
990 	elapt = currt - dump.longtime;
991 	ntot = p->nnetcp + p->nnetmail + p->nsmail + p->nnetlpr
992 		+ p->nresp + p->nnet;
993 	sstartt = ctime(&dump.longtime) + 4;
994 	sstartt[strlen(sstartt) - 9] = 0;
995 
996 	times(&tbf);
997 # ifndef NOFP
998 	utime = tbf.tms_utime + tbf.tms_cutime;
999 	stime = tbf.tms_stime + tbf.tms_cstime;
1000 	cputime = utime + stime;
1001 	if(elapt > 0)cputime = (cputime/elapt) * 100.0;
1002 	else cputime = 0.0;
1003 	utime = utime/60.0;
1004 	stime = stime/60.0;
1005 	cputime = cputime/60.0;
1006 	bs = p->bytetot;
1007 	if(p->elaptot > 0)bs = bs /p->elaptot;
1008 	else bs = 0.0;
1009 # endif NOFP
1010 
1011 	/* print out the statistics */
1012 	fprintf(fdm,"Subject: %s, %s, time %s\n",
1013 		froma,sstartt, comptime(elapt));
1014 	fprintf(fdm,"Command summary:\n");
1015 	fprintf(fdm,"\t# sent %d\t# pass_thru %d\t# rcv %d:\t# netcp %d\n",
1016 		p->nsend,p->npass,ntot,p->nnetcp);
1017 	fprintf(fdm,"\t# netlpr %d\t# netmail %d\t# sendbmail %d\t# resp %d\n",
1018 		p->nnetlpr,p->nnetmail,p->nsmail,p->nresp);
1019 	fprintf(fdm,"Protocol summary:\n");
1020 	fprintf(fdm,"\t# pk_sent %d\t# pk_rcv %d\t# b_sent %ld\t# b_rcv %ld\n",
1021 		p->npacksent,p->npackrcv,p->nbytesent, p->nbytercv);
1022 	fprintf(fdm,
1023 		"\t# send_fails %d\t# retrans %d\t# abn %d\t\t# cksum_errs %d\n",
1024 		p->nsendfail,p->nretrans, p->nabnormal,p->ncksum);
1025 # ifndef NOFP
1026 	fprintf(fdm,"Load:\tuser %4.1f\tsys %4.1f\tpct %5.2f\trate %6.1f\n",
1027 		utime,stime,cputime,bs);
1028 	rawbs = p->brawtot*100L;
1029 	rawbs = rawbs / linechars();
1030 	fprintf(fdm,"\trawbytes %ld\tuse %4.1f\n", p->brawtot,rawbs);
1031 # endif NOFP
1032 	mailclose(fdm);
1033 
1034 	/* reset counters */
1035 	p->nbytesent = p->nbytercv = p->elaptot = p->bytetot = 0L;
1036 	p->nretrans = p->nloop = p->nabnormal = p->ncksum = 0;
1037 	p->npacksent = p->npackrcv = p->nnetcp = p->nnetmail = 0;
1038 	p->nsmail = p->nnetlpr = p->nnet = p->npass = 0;
1039 	p->nsend = p->nsendfail = 0;
1040 	dump.longtime = currt;
1041 	}
1042 /* returns 1 if n is ok, 0 if not */
1043 goodacctname(n)
1044   char *n; {
1045 	int i;
1046 	i = -1;
1047 	while(btable[++i].bname)
1048 		if(strcmp(btable[i].bname,n) == 0 &&
1049 			local == btable[i].bmach)return(0);
1050 	return(1);
1051 	}
1052 demask(s)
1053   register char *s; {
1054 /*
1055 	static char buf[20];
1056 	char skey[30];
1057 	makeuukey(skey,status.login,local);
1058 	strcpy(s,nbsdecrypt(s,skey,buf));
1059 */
1060 	while(*s){
1061 		*s &= 0177;		/* strip quote bites */
1062 		*s++ ^= 040;		/* invert upper-lower */
1063 		}
1064 	}
1065 /*VARARGS0*/
1066 mreopen(fsendtofmach,phd,sfn,a,b,c){
1067 /* simply handles errors by giving error msg */
1068 	if(freopen(a,b,c) == NULL)
1069 		errormsg(fsendtofmach,phd,sfn,"%s: %s",a,sys_errlist[errno]);
1070 }
1071 /*
1072 	addtopub(string, args)
1073 
1074 	add a message to the public logfile /usr/net/logfile.
1075 	note that the file must be writeable by everyone
1076 	if error messages from the netrcv subroutine
1077 	such as chdir errors are to be noticed.
1078 */
1079 /*VARARGS0*/
1080 addtopublic(s,a,b,c,d,e,f,g,h,i,j,k,l,m,n)
1081 char *s;
1082 {
1083 	static FILE *log = NULL;
1084 	if(log == NULL){
1085 		if(stat(publogfile,&statbuf) < 0)return;
1086 		log = fopen(publogfile,"a");
1087 		if(log == NULL)return;
1088 		}
1089 	fseek(log,0L,2);
1090 	fprintf(log,s,a,b,c,d,e,f,g,h,i,j,k,l,m,n);
1091 	fflush(log);
1092 	}
1093 /* set up a dummy environment for v7 /bin/sh */
1094 setenv(home)
1095   char *home; {
1096 	static char *env[3],benv[2][50];
1097 	env[0] = benv[0];
1098 	env[1] = benv[1];
1099 #ifdef CCV7
1100 	strcpy( env[0], "PATH=:.:/usr/cc/bin:/usr/ucb/bin" );
1101 #else CCV7
1102 	strcpy(env[0],"PATH=:/bin:/usr/bin");
1103 #endif CCV7
1104 	sprintf(env[1],"HOME=%s",home);
1105 	env[2] = 0;
1106 	environ = env;
1107 	}
1108 /*
1109 	errormsg(fsendtofmach,phd,sfn,"string",arg(s))
1110 
1111 	Sends error message to user.
1112 	If fsendtofmach=TRUE, send to phd->hd_mchfrom, otherwise
1113 	send to phd->hd_mchto.
1114 	Also, if error occured during return of a "response",
1115 	send to local machine.
1116 
1117 	Note that errormsg can be called by the netrcv subroutine
1118 	after the setuid() call to the specific user, so the
1119 	user must be able to get off an error msg back to him,
1120 	and to write in the two log files.
1121 	Can't use -w,-x,-y,-z for the net cmd because must be root for those.
1122 
1123 	If sfn != NULL, then unlink sfn before exiting.
1124 */
1125 /*VARARGS0*/
1126 errormsg(fsendtofmach,phd,sfn,s,a,b,c,d,e,f,g,h)
1127 char fsendtofmach;
1128 struct header *phd;
1129 char *sfn,*s;
1130 {
1131 	int rcode;
1132 	char errstr[BUFSIZ], cmdstr[BUFSIZ], rcmd[BUFSIZ];
1133 	char toadd[FNS], fromadd[FNS], mchto, mchfrom;
1134 	char snto[FNS], snfrom[FNS];
1135 
1136 	if(phd->hd_sttyname[0] == 0)strcpy(phd->hd_sttyname,"/dev/ttyx");
1137 	/* will send to toadd, from fromadd */
1138 	if(!fsendtofmach || strcmp(phd->hd_scmdvirt,"response") == 0){
1139 		/* send to tomach mach, thus send to toaddr. */
1140 		/* if this is an error during a response, send to local mach. */
1141 		strcpy(toadd,  phd->hd_addrto);
1142 		strcpy(fromadd,phd->hd_addrfrom);
1143 	}
1144 	else {		/* send to remote mach, thus send back to addrfrom*/
1145 		strcpy(toadd,  phd->hd_addrfrom);
1146 		strcpy(fromadd,phd->hd_addrto);
1147 	}
1148 	sprintf(errstr,"Error: ");
1149 	sprintf(cmdstr,s,a,b,c,d,e,f,g,h);
1150 	strcat(errstr,cmdstr);
1151 	strcat(errstr,"\n");
1152 	addtolog(remote,errstr);
1153 	addtopublic(errstr);
1154 
1155 	mchto =   MchSFromAddr(snto,toadd);
1156 	mchfrom = MchSFromAddr(snfrom,fromadd);
1157 
1158 	sprintf(rcmd,
1159 "%s %s %s %lo %c %s \"'%s'\" %ld -t %s -f %s -x %ld -y %s -c \"'%s'\" -e %ld",
1160 	MWRITECMD, snto, phd->hd_sttyname, phd->hd_lttytime,
1161 	local, snfrom,phd->hd_scmdvirt, phd->hd_ltimesent-TIMEBASE,
1162 	toadd, fromadd, phd->hd_lttytime, phd->hd_sttyname, phd->hd_scmdvirt,
1163 	phd->hd_ltimesent-TIMEBASE);
1164 
1165 	if(mchto == local)
1166 		sprintf(cmdstr, "echo \"%s\" | %s", errstr,rcmd);
1167 	else
1168 		sprintf(cmdstr,
1169 		"echo \"%s\" | %s -m%c -b -c errormessage -l network - %s",
1170 			errstr,netcmd,mchto,rcmd);
1171 	rcode = system(cmdstr);
1172 	debug( "errormsg: cmdstr = %s\n", cmdstr );
1173 	debug( "errormsg: rcode = %d\n", rcode );
1174 	if(sfn != NULL)unlink(sfn);
1175 	exit(EX_USAGE);
1176 	}
1177 handlekill(){	/* SIGTERM signal */
1178 	long t;
1179 	/*
1180 	t = gettime();
1181 	dumpit(t);
1182 	*/
1183 # ifdef NETLDISC
1184 	/* turn off net line discipline if possible */
1185 	netd.dp_linedis = 0;
1186 	ioctl(netd.dp_linefd,TIOCSETD,&netd.dp_linedis);
1187 	close(netd.dp_linefd);
1188 	printf("Network line discipline turned off.\n");
1189 # endif NETLDISC
1190 	exit(EX_OK);	/* kill myself */
1191 	}
1192 
1193 /* check a request to see if it is an acct pair */
1194 /* returns 1 if it is, 0 if not */
1195 static facctpaircheck(phd)
1196 register struct header *phd;
1197 {
1198 	return(0);
1199 }
1200 
1201