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