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