xref: /csrg-svn/usr.bin/uucp/uucico/cico.c (revision 33559)
113639Ssam #ifndef lint
2*33559Srick static char sccsid[] = "@(#)cico.c	5.15 (Berkeley) 02/24/88";
313639Ssam #endif
413639Ssam 
523590Sbloom #include <signal.h>
613639Ssam #include "uucp.h"
713639Ssam #include <setjmp.h>
817767Sralph #ifdef	USG
913639Ssam #include <termio.h>
10*33559Srick #include <fcntl.h>
1113639Ssam #endif
1217767Sralph #ifndef	USG
1313639Ssam #include <sgtty.h>
1413639Ssam #endif
1517767Sralph #ifdef BSDTCP
1617767Sralph #include <netdb.h>
1717767Sralph #include <netinet/in.h>
1817767Sralph #include <sys/socket.h>
1917767Sralph #endif BSDTCP
2017767Sralph #include <sys/stat.h>
21*33559Srick #ifdef BSD4_2
22*33559Srick #include <sys/time.h>
23*33559Srick #include <fcntl.h>
24*33559Srick #else
25*33559Srick #include <time.h>
26*33559Srick #endif
2717767Sralph #include "uust.h"
2817767Sralph #include "uusub.h"
2913639Ssam 
3023590Sbloom #if defined(VMS) && defined(BSDTCP)
3123590Sbloom #define NOGETPEER
3223590Sbloom #endif
3323590Sbloom 
3423590Sbloom #ifdef BSD2_9
3523590Sbloom #define NOGETPEER
3623590Sbloom #endif
3723590Sbloom 
3817767Sralph jmp_buf Sjbuf;
3917767Sralph jmp_buf Pipebuf;
4013639Ssam 
4117767Sralph /*  call fail text  */
4213639Ssam char *Stattext[] = {
4313639Ssam 	"",
4413639Ssam 	"BAD SYSTEM",
4517767Sralph 	"WRONG TIME TO CALL",
4613639Ssam 	"SYSTEM LOCKED",
4713639Ssam 	"NO DEVICE",
4825703Sbloom 	"CALL FAILED",
4913639Ssam 	"LOGIN FAILED",
5013639Ssam 	"BAD SEQUENCE"
5117767Sralph };
5213639Ssam 
5317767Sralph /*  call fail codes  */
5417767Sralph int Stattype[] = {
5517767Sralph 	0,
5617767Sralph 	0,
5717767Sralph 	SS_WRONGTIME,
5817767Sralph 	0,
5917767Sralph 	SS_NODEVICE,
6017767Sralph 	SS_FAIL,
6117767Sralph 	SS_FAIL,
6217767Sralph 	SS_BADSEQ
6317767Sralph };
6413639Ssam 
6525703Sbloom 				/* Arguments to setdebug():		     */
6625703Sbloom #define DBG_TEMP  0		/*   Create a temporary audit file	     */
6725703Sbloom #define DBG_PERM  1		/*   Create a permanent audit file	     */
6825703Sbloom #define DBG_CLEAN 2		/*   Cleanup, discard temp file		     */
6913639Ssam 
7017767Sralph int ReverseRole = 0;
7117767Sralph int Role = SLAVE;
72*33559Srick int InitialRole = SLAVE;
73*33559Srick long StartTime;
7417767Sralph int onesys = 0;
7518616Sralph int turntime = 30 * 60;	/* 30 minutes expressed in seconds */
7625703Sbloom char *ttyn = NULL;
7717767Sralph extern int LocalOnly;
7825703Sbloom extern int errno;
7918616Sralph extern char MaxGrade, DefMaxGrade;
8018616Sralph extern char Myfullname[];
8117767Sralph 
82*33559Srick long Bytes_Sent, Bytes_Received;
83*33559Srick 
8417767Sralph #ifdef	USG
8513639Ssam struct termio Savettyb;
8613639Ssam #endif
8717767Sralph #ifndef	USG
8813639Ssam struct sgttyb Savettyb;
8913639Ssam #endif
9013639Ssam 
9125703Sbloom /*
9225703Sbloom  *	this program is used  to place a call to a
9313639Ssam  *	remote machine, login, and copy files between the two machines.
9413639Ssam  */
9513639Ssam main(argc, argv)
9625703Sbloom int argc;
9713639Ssam register char *argv[];
9813639Ssam {
9913639Ssam 	register int ret;
10013639Ssam 	int seq;
10113639Ssam 	char wkpre[NAMESIZE], file[NAMESIZE];
10225703Sbloom 	char msg[MAXFULLNAME], *q;
10313639Ssam 	register char *p;
10425703Sbloom 	extern onintr(), timeout(), dbg_signal();
10513639Ssam 	extern char *pskip();
10623725Sbloom 	char rflags[MAXFULLNAME];
10723590Sbloom #ifdef NOGETPEER
10817767Sralph 	u_long Hostnumber = 0;
10923590Sbloom #endif NOGETPEER
11013639Ssam 
11113639Ssam 	strcpy(Progname, "uucico");
11213639Ssam 
11326149Sbloom #ifdef BSD4_2
11426149Sbloom 	sigsetmask(0);	/* in case we inherit blocked signals */
11526149Sbloom #endif BSD4_2
11613639Ssam 	signal(SIGINT, onintr);
11713639Ssam 	signal(SIGHUP, onintr);
11813639Ssam 	signal(SIGQUIT, onintr);
11913639Ssam 	signal(SIGTERM, onintr);
12013639Ssam 	signal(SIGPIPE, onintr);	/* 4.1a tcp-ip stupidity */
12125703Sbloom 	signal(SIGFPE, dbg_signal);
12213639Ssam 	ret = guinfo(getuid(), User, msg);
12313639Ssam 	strcpy(Loginuser, User);
12423590Sbloom 	uucpname(Myname);
12517767Sralph 	ASSERT(ret == 0, "BAD UID", CNULL, ret);
12613639Ssam 
12725703Sbloom 	setbuf (stderr, CNULL);
12825703Sbloom 
12925703Sbloom 	rflags[0] = '\0';
13013639Ssam 	umask(WFMASK);
13113639Ssam 	strcpy(Rmtname, Myname);
13213639Ssam 	Ifn = Ofn = -1;
13313639Ssam 	while(argc>1 && argv[1][0] == '-'){
13413639Ssam 		switch(argv[1][1]){
13513639Ssam 		case 'd':
13613639Ssam 			Spool = &argv[1][2];
13713639Ssam 			break;
13813639Ssam 		case 'g':
13918616Sralph 		case 'p':
14018616Sralph 			MaxGrade = DefMaxGrade = argv[1][2];
14113639Ssam 			break;
14213639Ssam 		case 'r':
14313639Ssam 			Role = atoi(&argv[1][2]);
14413639Ssam 			break;
14517767Sralph 		case 'R':
14617767Sralph 			ReverseRole++;
14717767Sralph 			Role = MASTER;
14817767Sralph 			break;
14913639Ssam 		case 's':
15023590Sbloom 			strncpy(Rmtname, &argv[1][2], MAXBASENAME);
15123590Sbloom 			Rmtname[MAXBASENAME] = '\0';
15213639Ssam 			if (Rmtname[0] != '\0')
15313639Ssam 				onesys = 1;
15413639Ssam 			break;
15513639Ssam 		case 'x':
15613639Ssam 			Debug = atoi(&argv[1][2]);
15713639Ssam 			if (Debug <= 0)
15813639Ssam 				Debug = 1;
15925703Sbloom 			strcat(rflags, argv[1]);
16013639Ssam 			break;
16118616Sralph 		case 't':
16218616Sralph 			turntime = atoi(&argv[1][2])*60;/* minutes to seconds */
16318616Sralph 			break;
16417767Sralph 		case 'L':	/* local calls only */
16517767Sralph 			LocalOnly++;
16617767Sralph 			break;
16723590Sbloom #ifdef NOGETPEER
16817767Sralph 		case 'h':
16917767Sralph 			Hostnumber = inet_addr(&argv[1][2]);
17017767Sralph 			break;
17123590Sbloom #endif NOGETPEER
17213639Ssam 		default:
17317767Sralph 			printf("unknown flag %s (ignored)\n", argv[1]);
17413639Ssam 			break;
17513639Ssam 		}
17613639Ssam 		--argc;  argv++;
17713639Ssam 	}
17813639Ssam 
17917767Sralph 	while (argc > 1) {
18025703Sbloom 		fprintf(stderr, "unknown argument %s (ignored)\n", argv[1]);
18117767Sralph 		--argc; argv++;
18217767Sralph 	}
18317767Sralph 
18427069Sbloom 	if (Debug && Role == MASTER)
18527069Sbloom 		chkdebug();
18627069Sbloom 
18725124Sbloom 	/* Try to run as uucp */
18817767Sralph 	setgid(getegid());
18917767Sralph 	setuid(geteuid());
19017767Sralph #ifdef	TIOCNOTTY
19117767Sralph 	/*
19217767Sralph 	 * detach uucico from controlling terminal
19317767Sralph 	 * to defend against rlogind sending us a SIGKILL (!!!)
19417767Sralph 	 */
19517767Sralph 	if (Role == MASTER && (ret = open("/dev/tty", 2)) >= 0) {
19617767Sralph 		ioctl(ret, TIOCNOTTY, STBNULL);
19717767Sralph 		close(ret);
19817767Sralph 	}
19917767Sralph #endif TIOCNOTTY
20017767Sralph #ifdef BSD4_2
20125517Stef 	if (getpgrp(0) == 0) { /* We have no controlling terminal */
20217767Sralph 		setpgrp(0, getpid());
20317767Sralph 	}
20417767Sralph #endif BSD4_2
20517767Sralph 
20617767Sralph 	ret = subchdir(Spool);
20717767Sralph 	ASSERT(ret >= 0, "CHDIR FAILED", Spool, ret);
20813639Ssam 	strcpy(Wrkdir, Spool);
20913639Ssam 
21025703Sbloom 	if (Debug) {
21125703Sbloom 		setdebug ((Role == SLAVE) ? DBG_TEMP : DBG_PERM);
21225703Sbloom 		if (Debug > 0)
21325703Sbloom 			logent ("Local Enabled", "DEBUG");
21425703Sbloom 	}
21525703Sbloom 
21625703Sbloom 	/*
21725703Sbloom 	 * First time through: If we're the slave, do initial checking.
21825703Sbloom 	 */
21913639Ssam 	if (Role == SLAVE) {
22017767Sralph 		/* check for /etc/nologin */
22125124Sbloom 		if (access(NOLOGIN, 0) == 0) {
22217767Sralph 			logent(NOLOGIN, "UUCICO SHUTDOWN");
22323590Sbloom 			if (Debug > 4)
22417767Sralph 				logent("DEBUGGING", "continuing anyway");
22517767Sralph 			else
22617767Sralph 				cleanup(1);
22717767Sralph 		}
22825703Sbloom 		Ifn = 0;
22925703Sbloom 		Ofn = 1;
23017767Sralph #ifdef	TCPIP
23117767Sralph 		/*
23217767Sralph 		 * Determine if we are on TCPIP
23317767Sralph 		 */
234*33559Srick 		if (isatty(Ifn) == 0) {
23517767Sralph 			IsTcpIp = 1;
23617767Sralph 			DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL);
23723590Sbloom 		} else
23823590Sbloom 			IsTcpIp = 0;
23917767Sralph #endif TCPIP
24013639Ssam 		/* initial handshake */
24113639Ssam 		onesys = 1;
24217767Sralph 		if (!IsTcpIp) {
24317767Sralph #ifdef	USG
24425703Sbloom 			ret = ioctl(Ifn, TCGETA, &Savettyb);
24513639Ssam 			Savettyb.c_cflag = (Savettyb.c_cflag & ~CS8) | CS7;
24613639Ssam 			Savettyb.c_oflag |= OPOST;
24713639Ssam 			Savettyb.c_lflag |= (ISIG|ICANON|ECHO);
24817767Sralph #else !USG
24925703Sbloom 			ret = ioctl(Ifn, TIOCGETP, &Savettyb);
25013639Ssam 			Savettyb.sg_flags |= ECHO;
25113639Ssam 			Savettyb.sg_flags &= ~RAW;
25217767Sralph #endif !USG
25325703Sbloom 			ttyn = ttyname(Ifn);
25413639Ssam 		}
25513639Ssam 		fixmode(Ifn);
25625703Sbloom 
25725703Sbloom 		/*
25825703Sbloom 		 * Initial Message -- tell them we're here, and who we are.
25925703Sbloom 		 */
26018616Sralph 		sprintf(msg, "here=%s", Myfullname);
26117767Sralph 		omsg('S', msg, Ofn);
26213639Ssam 		signal(SIGALRM, timeout);
263*33559Srick 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
26413639Ssam 		if (setjmp(Sjbuf)) {
26513639Ssam 			/* timed out */
26617767Sralph 			if (!IsTcpIp) {
26717767Sralph #ifdef	USG
26825703Sbloom 				ret = ioctl(Ifn, TCSETA, &Savettyb);
26925703Sbloom 
27023590Sbloom #else	!USG
27125703Sbloom 				ret = ioctl(Ifn, TIOCSETP, &Savettyb);
27223590Sbloom #endif !USG
27313639Ssam 			}
27417767Sralph 			cleanup(0);
27513639Ssam 		}
27613639Ssam 		for (;;) {
27713639Ssam 			ret = imsg(msg, Ifn);
27825703Sbloom 			if (ret != SUCCESS) {
27913639Ssam 				alarm(0);
28017767Sralph 				if (!IsTcpIp) {
28117767Sralph #ifdef	USG
28225703Sbloom 					ret = ioctl(Ifn, TCSETA, &Savettyb);
28323590Sbloom #else	!USG
28425703Sbloom 					ret = ioctl(Ifn, TIOCSETP, &Savettyb);
28523590Sbloom #endif !USG
28613639Ssam 				}
28717767Sralph 				cleanup(0);
28813639Ssam 			}
28913639Ssam 			if (msg[0] == 'S')
29013639Ssam 				break;
29113639Ssam 		}
29213639Ssam 		alarm(0);
29313639Ssam 		q = &msg[1];
29413639Ssam 		p = pskip(q);
29523590Sbloom 		strncpy(Rmtname, q, MAXBASENAME);
29623590Sbloom 		Rmtname[MAXBASENAME] = '\0';
29725703Sbloom 
29825703Sbloom 		/*
29925703Sbloom 		 * Now that we know who they are, give the audit file the right
30025703Sbloom 		 * name.
30125703Sbloom 		 */
30225703Sbloom 		setdebug (DBG_PERM);
30313639Ssam 		DEBUG(4, "sys-%s\n", Rmtname);
30423725Sbloom 		/* The versys will also do an alias on the incoming name */
30523725Sbloom 		if (versys(&Rmtname)) {
306*33559Srick #ifdef	NOSTRANGERS
30723725Sbloom 			/* If we don't know them, we won't talk to them... */
308*33559Srick 			assert("Unknown host:", Rmtname, 0);
30923590Sbloom 			omsg('R', "You are unknown to me", Ofn);
31023590Sbloom 			cleanup(0);
31123725Sbloom #endif	NOSTRANGERS
31223590Sbloom 		}
31317767Sralph #ifdef BSDTCP
31417767Sralph 		/* we must make sure they are really who they say they
31517767Sralph 		 * are. We compare the hostnumber with the number in the hosts
31617767Sralph 		 * table for the site they claim to be.
31717767Sralph 		 */
31817767Sralph 		if (IsTcpIp) {
31917767Sralph 			struct hostent *hp;
32017767Sralph 			char *cpnt, *inet_ntoa();
32125703Sbloom 			int fromlen;
32217767Sralph 			struct sockaddr_in from;
32325124Sbloom 			extern char PhoneNumber[];
32417767Sralph 
32523590Sbloom #ifdef	NOGETPEER
32623590Sbloom 			from.sin_addr.s_addr = Hostnumber;
32723590Sbloom 			from.sin_family = AF_INET;
32823590Sbloom #else	!NOGETPEER
32925703Sbloom 			fromlen = sizeof(from);
33025703Sbloom 			if (getpeername(Ifn, &from, &fromlen) < 0) {
33117767Sralph 				logent(Rmtname, "NOT A TCP CONNECTION");
33217767Sralph 				omsg('R', "NOT TCP", Ofn);
33317767Sralph 				cleanup(0);
33417767Sralph 			}
33523590Sbloom #endif	!NOGETPEER
33617767Sralph 			hp = gethostbyaddr(&from.sin_addr,
33717767Sralph 				sizeof (struct in_addr), from.sin_family);
33825703Sbloom 			if (hp == NULL) {
33917767Sralph 				/* security break or just old host table? */
34017767Sralph 				logent(Rmtname, "UNKNOWN IP-HOST Name =");
34117767Sralph 				cpnt = inet_ntoa(from.sin_addr),
34217767Sralph 				logent(cpnt, "UNKNOWN IP-HOST Number =");
34317767Sralph 				sprintf(wkpre, "%s/%s isn't in my host table",
34417767Sralph 					Rmtname, cpnt);
34517767Sralph 				omsg('R' ,wkpre ,Ofn);
34617767Sralph 				cleanup(0);
34717767Sralph 			}
34825703Sbloom 			if (Debug > 99)
34917767Sralph 				logent(Rmtname,"Request from IP-Host name =");
35025124Sbloom 			/*
35125124Sbloom 			 * The following is to determine if the name given us by
35225124Sbloom 			 * the Remote uucico matches any of the names
35317767Sralph 			 * given its network number (remote machine) in our
35417767Sralph 			 * host table.
35525124Sbloom 			 * We could check the aliases, but that won't work in
35625124Sbloom 			 * all cases (like if you are running the domain
35725124Sbloom 			 * server, where you don't get any aliases). The only
35825124Sbloom 			 * reliable way I can think of that works in ALL cases
35925124Sbloom 			 * is too look up the site in L.sys and see if the
36025124Sbloom 			 * sitename matches what we would call him if we
36125124Sbloom 			 * originated the call.
36217767Sralph 			 */
36325124Sbloom 			/* PhoneNumber contains the official network name of the 			   host we are checking. (set in versys.c) */
36425124Sbloom 			if (sncncmp(PhoneNumber, hp->h_name, SYSNSIZE) == 0) {
36517767Sralph 				if (Debug > 99)
36617767Sralph 					logent(q,"Found in host Tables");
36725124Sbloom 			} else {
36825124Sbloom 				logent(hp->h_name, "FORGED HOSTNAME");
36925124Sbloom 				logent(inet_ntoa(from.sin_addr), "ORIGINATED AT");
37025124Sbloom 				logent(PhoneNumber, "SHOULD BE");
37125124Sbloom 				sprintf(wkpre, "You're not who you claim to be: %s !=  %s", hp->h_name, PhoneNumber);
37225124Sbloom 				omsg('R', wkpre, Ofn);
37325124Sbloom 				cleanup(0);
37417767Sralph 			}
37517767Sralph 		}
37617767Sralph #endif	BSDTCP
37717767Sralph 
37825703Sbloom 		if (mlock(Rmtname)) {
37913639Ssam 			omsg('R', "LCK", Ofn);
38013639Ssam 			cleanup(0);
38113639Ssam 		}
38213639Ssam 		else if (callback(Loginuser)) {
38313639Ssam 			signal(SIGINT, SIG_IGN);
38413639Ssam 			signal(SIGHUP, SIG_IGN);
38513639Ssam 			omsg('R', "CB", Ofn);
38613639Ssam 			logent("CALLBACK", "REQUIRED");
38713639Ssam 			/*  set up for call back  */
38817767Sralph 			systat(Rmtname, SS_CALLBACK, "CALLING BACK");
38913639Ssam 			gename(CMDPRE, Rmtname, 'C', file);
39013639Ssam 			close(creat(subfile(file), 0666));
39113639Ssam 			xuucico(Rmtname);
39213639Ssam 			cleanup(0);
39313639Ssam 		}
39413639Ssam 		seq = 0;
39513639Ssam 		while (*p == '-') {
39613639Ssam 			q = pskip(p);
39713639Ssam 			switch(*(++p)) {
39813639Ssam 			case 'x':
39925703Sbloom 				if (Debug == 0) {
40025703Sbloom 					Debug = atoi(++p);
40125703Sbloom 					if (Debug <= 0)
40225703Sbloom 						Debug = 1;
40325703Sbloom 					setdebug(DBG_PERM);
40425703Sbloom 					if (Debug > 0)
40525703Sbloom 						logent("Remote Enabled", "DEBUG");
40625703Sbloom 				} else {
40725703Sbloom 					DEBUG(1, "Remote debug request ignored\n",
40825703Sbloom 					   CNULL);
40925703Sbloom 				}
41013639Ssam 				break;
41113639Ssam 			case 'Q':
41213639Ssam 				seq = atoi(++p);
41313639Ssam 				break;
41418616Sralph 			case 'p':
41518616Sralph 				MaxGrade = DefMaxGrade = *++p;
41618616Sralph 				DEBUG(4, "MaxGrade set to %c\n", MaxGrade);
41718616Sralph 				break;
41823590Sbloom 			case 'v':
41923590Sbloom 				if (strncmp(p, "grade", 5) == 0) {
42023590Sbloom 					p += 6;
42123590Sbloom 					MaxGrade = DefMaxGrade = *p++;
42223590Sbloom 					DEBUG(4, "MaxGrade set to %c\n", MaxGrade);
42323590Sbloom 				}
42423590Sbloom 				break;
42513639Ssam 			default:
42613639Ssam 				break;
42713639Ssam 			}
42813639Ssam 			p = q;
42913639Ssam 		}
43013639Ssam 		if (callok(Rmtname) == SS_BADSEQ) {
43113639Ssam 			logent("BADSEQ", "PREVIOUS");
43213639Ssam 			omsg('R', "BADSEQ", Ofn);
43313639Ssam 			cleanup(0);
43413639Ssam 		}
43517767Sralph #ifdef GNXSEQ
43613639Ssam 		if ((ret = gnxseq(Rmtname)) == seq) {
43713639Ssam 			omsg('R', "OK", Ofn);
43813639Ssam 			cmtseq();
43917767Sralph 		} else {
44017767Sralph #else !GNXSEQ
44117767Sralph 		if (seq == 0)
44217767Sralph 			omsg('R', "OK", Ofn);
44313639Ssam 		else {
44417767Sralph #endif !GNXSEQ
44513639Ssam 			systat(Rmtname, Stattype[7], Stattext[7]);
44623590Sbloom 			logent("BAD SEQ", "FAILED HANDSHAKE");
44717767Sralph #ifdef GNXSEQ
44813639Ssam 			ulkseq();
44917767Sralph #endif GNXSEQ
45013639Ssam 			omsg('R', "BADSEQ", Ofn);
45113639Ssam 			cleanup(0);
45213639Ssam 		}
45313639Ssam 		if (ttyn != NULL)
45413639Ssam 			chmod(ttyn, 0600);
45513639Ssam 	}
45617767Sralph 
45713639Ssam loop:
45817767Sralph 	if(setjmp(Pipebuf)) {	/* come here on SIGPIPE	*/
45917767Sralph 		clsacu();
46017767Sralph 		close(Ofn);
46117767Sralph 		close(Ifn);
46217767Sralph 		Ifn = Ofn = -1;
46317767Sralph 		rmlock(CNULL);
46417767Sralph 		sleep(3);
46517767Sralph 	}
46613639Ssam 	if (!onesys) {
467*33559Srick 		do_connect_accounting();
468*33559Srick 		StartTime = 0;
46913639Ssam 		ret = gnsys(Rmtname, Spool, CMDPRE);
47025703Sbloom 		setdebug(DBG_PERM);
47113639Ssam 		if (ret == FAIL)
47213639Ssam 			cleanup(100);
473*33559Srick 		else if (ret == SUCCESS)
47413639Ssam 			cleanup(0);
47517767Sralph 	} else if (Role == MASTER && callok(Rmtname) != 0) {
47613639Ssam 		logent("SYSTEM STATUS", "CAN NOT CALL");
47713639Ssam 		cleanup(0);
47813639Ssam 	}
47913639Ssam 
48023590Sbloom 	sprintf(wkpre, "%c.%.*s", CMDPRE, SYSNSIZE, Rmtname);
481*33559Srick 	StartTime = 0;
482*33559Srick 	Bytes_Sent = Bytes_Received = 0L;
48313639Ssam 
48417767Sralph 	signal(SIGINT, SIG_IGN);
48517767Sralph 	signal(SIGQUIT, SIG_IGN);
48613639Ssam 	if (Role == MASTER) {
487*33559Srick 		extern char LineType[];
48817767Sralph 		/* check for /etc/nologin */
48925124Sbloom 		if (access(NOLOGIN, 0) == 0) {
49017767Sralph 			logent(NOLOGIN, "UUCICO SHUTDOWN");
49123590Sbloom 			if (Debug > 4)
49217767Sralph 				logent("DEBUGGING", "continuing anyway");
49317767Sralph 			else
49417767Sralph 				cleanup(1);
49517767Sralph 		}
49613639Ssam 		/*  master part */
49713639Ssam 		signal(SIGHUP, SIG_IGN);
49813639Ssam 		if (Ifn != -1 && Role == MASTER) {
49913639Ssam 			write(Ofn, EOTMSG, strlen(EOTMSG));
50013639Ssam 			clsacu();
50113639Ssam 			close(Ofn);
50213639Ssam 			close(Ifn);
50313639Ssam 			Ifn = Ofn = -1;
50413639Ssam 			rmlock(CNULL);
50513639Ssam 			sleep(3);
50613639Ssam 		}
50725124Sbloom 		if (mlock(Rmtname) != SUCCESS) {
508*33559Srick 			DEBUG(1, "LOCKED: call to %s\n", Rmtname);
50917767Sralph 			US_SST(us_s_lock);
51013639Ssam 			goto next;
51113639Ssam 		}
51213639Ssam 		Ofn = Ifn = conn(Rmtname);
513*33559Srick 		sprintf(msg, "call to %s via %s", Rmtname, LineType);
51413639Ssam 		if (Ofn < 0) {
51517767Sralph 			if (Ofn != CF_TIME)
51617767Sralph 				logent(msg, _FAILED);
51717767Sralph 			/* avoid excessive 'wrong time' info */
51823590Sbloom 			if (Stattype[-Ofn] != SS_WRONGTIME){
51917767Sralph 				systat(Rmtname, Stattype[-Ofn], Stattext[-Ofn]);
52017767Sralph 				US_SST(-Ofn);
52117767Sralph 				UB_SST(-Ofn);
52217767Sralph 			}
52313639Ssam 			goto next;
52417767Sralph 		} else {
52513639Ssam 			logent(msg, "SUCCEEDED");
52617767Sralph 			US_SST(us_s_cok);
52717767Sralph 			UB_SST(ub_ok);
52813639Ssam 		}
529*33559Srick 		InitialRole = MASTER;
53017767Sralph #ifdef	TCPIP
53117767Sralph 		/*
53217767Sralph 		 * Determine if we are on TCPIP
53317767Sralph 		 */
53425703Sbloom 		if (isatty(Ifn) == 0) {
53517767Sralph 			IsTcpIp = 1;
53617767Sralph 			DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL);
53723590Sbloom 		} else
53823590Sbloom 			IsTcpIp = 0;
53917767Sralph #endif
54017767Sralph 
54113639Ssam 		if (setjmp(Sjbuf))
54213639Ssam 			goto next;
54313639Ssam 		signal(SIGALRM, timeout);
544*33559Srick 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME*2);
54513639Ssam 		for (;;) {
54613639Ssam 			ret = imsg(msg, Ifn);
547*33559Srick 			if (ret != SUCCESS) {
54813639Ssam 				alarm(0);
549*33559Srick 				DEBUG(4,"\nimsg failed: errno %d\n", errno);
55017767Sralph 				logent("imsg 1", _FAILED);
55117767Sralph 				goto Failure;
55213639Ssam 			}
55313639Ssam 			if (msg[0] == 'S')
55413639Ssam 				break;
55513639Ssam 		}
556*33559Srick 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
55717767Sralph #ifdef GNXSEQ
55813639Ssam 		seq = gnxseq(Rmtname);
55917767Sralph #else !GNXSEQ
56017767Sralph 		seq = 0;
56117767Sralph #endif !GNXSEQ
56218616Sralph 		if (MaxGrade != '\177') {
56323590Sbloom 			DEBUG(2, "Max Grade this transfer is %c\n", MaxGrade);
56425963Sbloom 			sprintf(msg, "%s -Q%d -p%c -vgrade=%c %s",
56525963Sbloom 				Myname, seq, MaxGrade, MaxGrade, rflags);
56625963Sbloom 		} else
56725963Sbloom 			sprintf(msg, "%s -Q%d %s", Myname, seq, rflags);
56813639Ssam 		omsg('S', msg, Ofn);
56913639Ssam 		for (;;) {
57013639Ssam 			ret = imsg(msg, Ifn);
57113639Ssam 			DEBUG(4, "msg-%s\n", msg);
57217767Sralph 			if (ret != SUCCESS) {
57313639Ssam 				alarm(0);
57417767Sralph #ifdef GNXSEQ
57513639Ssam 				ulkseq();
57617767Sralph #endif GNXSEQ
57717767Sralph 				logent("imsg 2", _FAILED);
57817767Sralph 				goto Failure;
57913639Ssam 			}
58013639Ssam 			if (msg[0] == 'R')
58113639Ssam 				break;
58213639Ssam 		}
58313639Ssam 		alarm(0);
58413639Ssam 		if (msg[1] == 'B') {
58513639Ssam 			/* bad sequence */
58623590Sbloom 			logent("BAD SEQ", "FAILED HANDSHAKE");
58717767Sralph 			US_SST(us_s_hand);
58817767Sralph 			systat(Rmtname, SS_BADSEQ, Stattext[SS_BADSEQ]);
58917767Sralph #ifdef GNXSEQ
59013639Ssam 			ulkseq();
59117767Sralph #endif GNXSEQ
59213639Ssam 			goto next;
59313639Ssam 		}
59413639Ssam 		if (strcmp(&msg[1], "OK") != SAME)  {
59523590Sbloom 			logent(&msg[1], "FAILED HANDSHAKE");
59617767Sralph 			US_SST(us_s_hand);
59717767Sralph #ifdef GNXSEQ
59813639Ssam 			ulkseq();
59917767Sralph #endif GNXSEQ
60017767Sralph 			systat(Rmtname, SS_INPROGRESS,
60117767Sralph 				strcmp(&msg[1], "CB") == SAME?
60223590Sbloom 				"AWAITING CALLBACK": "FAILED HANDSHAKE");
60313639Ssam 			goto next;
60413639Ssam 		}
60517767Sralph #ifdef GNXSEQ
60613639Ssam 		cmtseq();
60717767Sralph #endif GNXSEQ
60813639Ssam 	}
60917767Sralph 	DEBUG(1, "Rmtname %s, ", Rmtname);
61013639Ssam 	DEBUG(1, "Role %s,  ", Role ? "MASTER" : "SLAVE");
61113639Ssam 	DEBUG(1, "Ifn - %d, ", Ifn);
61213639Ssam 	DEBUG(1, "Loginuser - %s\n", Loginuser);
61313639Ssam 
61425703Sbloom 	ttyn = ttyname(Ifn);
61525703Sbloom 
616*33559Srick 	alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
61718616Sralph 	if (ret=setjmp(Sjbuf))
61813639Ssam 		goto Failure;
61913639Ssam 	ret = startup(Role);
62013639Ssam 	alarm(0);
62113639Ssam 	if (ret != SUCCESS) {
62217767Sralph 		logent("startup", _FAILED);
62313639Ssam Failure:
62417767Sralph 		US_SST(us_s_start);
62518616Sralph 		systat(Rmtname, SS_FAIL, ret > 0 ? "CONVERSATION FAILED" :
62618616Sralph 			"STARTUP FAILED");
62713639Ssam 		goto next;
62817767Sralph 	} else {
629*33559Srick 		char smsg[BUFSIZ], gmsg[10], pmsg[20], bpsmsg[20];
630*33559Srick 		extern char UsingProtocol;
631*33559Srick 		extern int linebaudrate;
632*33559Srick 		if (ttyn != NULL)
633*33559Srick 			sprintf(bpsmsg, " %s %d bps", &ttyn[5], linebaudrate);
634*33559Srick 		else
635*33559Srick 			bpsmsg[0] = '\0';
636*33559Srick 		if (UsingProtocol != 'g')
637*33559Srick 			sprintf(pmsg, " %c protocol", UsingProtocol);
638*33559Srick 		else
639*33559Srick 			pmsg[0] = '\0';
640*33559Srick 		if (MaxGrade != '\177')
641*33559Srick 			sprintf(gmsg, " grade %c", MaxGrade);
642*33559Srick 		else
643*33559Srick 			gmsg[0] = '\0';
644*33559Srick 		sprintf(smsg, "startup%s%s%s", bpsmsg, pmsg, gmsg);
645*33559Srick 		logent(smsg, "OK");
64617767Sralph 		US_SST(us_s_gress);
647*33559Srick 		StartTime = Now.time;
64813639Ssam 		systat(Rmtname, SS_INPROGRESS, "TALKING");
64913639Ssam 		ret = cntrl(Role, wkpre);
65013639Ssam 		DEBUG(1, "cntrl - %d\n", ret);
65113639Ssam 		signal(SIGINT, SIG_IGN);
65213639Ssam 		signal(SIGHUP, SIG_IGN);
65313639Ssam 		signal(SIGALRM, timeout);
654*33559Srick 		sprintf(smsg, "conversation complete %ld sent %ld received",
655*33559Srick 			Bytes_Sent, Bytes_Received);
65617767Sralph 		if (ret == SUCCESS) {
657*33559Srick 			logent(smsg, "OK");
65817767Sralph 			US_SST(us_s_ok);
65913639Ssam 			rmstat(Rmtname);
66013639Ssam 
66117767Sralph 		} else {
662*33559Srick 			logent(smsg, _FAILED);
66317767Sralph 			US_SST(us_s_cf);
66417767Sralph 			systat(Rmtname, SS_FAIL, "CONVERSATION FAILED");
66513639Ssam 		}
666*33559Srick 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
66713639Ssam 		DEBUG(4, "send OO %d,", ret);
66813639Ssam 		if (!setjmp(Sjbuf)) {
66913639Ssam 			for (;;) {
67013639Ssam 				omsg('O', "OOOOO", Ofn);
67113639Ssam 				ret = imsg(msg, Ifn);
67213639Ssam 				if (ret != 0)
67313639Ssam 					break;
67413639Ssam 				if (msg[0] == 'O')
67513639Ssam 					break;
67613639Ssam 			}
67713639Ssam 		}
67813639Ssam 		alarm(0);
67917767Sralph 		clsacu();
68017767Sralph 		rmlock(CNULL);
681*33559Srick 
68213639Ssam 	}
68313639Ssam next:
68413639Ssam 	if (!onesys) {
68513639Ssam 		goto loop;
68613639Ssam 	}
68713639Ssam 	cleanup(0);
68813639Ssam }
68913639Ssam 
69017767Sralph #ifndef	USG
69113639Ssam struct sgttyb Hupvec;
69213639Ssam #endif
69313639Ssam 
69425703Sbloom /*
69525703Sbloom  *	cleanup and exit with "code" status
69613639Ssam  */
69713639Ssam cleanup(code)
69813639Ssam register int code;
69913639Ssam {
70013639Ssam 	signal(SIGINT, SIG_IGN);
70113639Ssam 	signal(SIGHUP, SIG_IGN);
70213639Ssam 	rmlock(CNULL);
70325703Sbloom 	sleep(5);			/* Wait for any pending output	  */
70413639Ssam 	clsacu();
70513639Ssam 	logcls();
70613639Ssam 	if (Role == SLAVE) {
70717767Sralph 		if (!IsTcpIp) {
70817767Sralph #ifdef USG
70913639Ssam 			Savettyb.c_cflag |= HUPCL;
71023590Sbloom 			(void) ioctl(0, TCSETA, &Savettyb);
71117767Sralph #else !USG
71223590Sbloom 			(void) ioctl(0, TIOCHPCL, STBNULL);
71318616Sralph #ifdef TIOCSDTR
71423590Sbloom 			(void) ioctl(0, TIOCCDTR, STBNULL);
71518616Sralph 			sleep(2);
71623590Sbloom 			(void) ioctl(0, TIOCSDTR, STBNULL);
71718616Sralph #else !TIOCSDTR
71823590Sbloom 			(void) ioctl(0, TIOCGETP, &Hupvec);
71913639Ssam 			Hupvec.sg_ispeed = B0;
72013639Ssam 			Hupvec.sg_ospeed = B0;
72123590Sbloom 			(void) ioctl(0, TIOCSETP, &Hupvec);
72225703Sbloom #endif !TIOCSDTR
72313639Ssam 			sleep(2);
72423590Sbloom 			(void) ioctl(0, TIOCSETP, &Savettyb);
72517767Sralph 			/* make *sure* exclusive access is off */
72623590Sbloom 			(void) ioctl(0, TIOCNXCL, STBNULL);
72717767Sralph #endif !USG
72813639Ssam 		}
72913639Ssam 		if (ttyn != NULL)
73013639Ssam 			chmod(ttyn, 0600);
73113639Ssam 	}
73213639Ssam 	if (Ofn != -1) {
73313639Ssam 		if (Role == MASTER)
73413639Ssam 			write(Ofn, EOTMSG, strlen(EOTMSG));
73513639Ssam 		close(Ifn);
73613639Ssam 		close(Ofn);
73713639Ssam 	}
73818616Sralph #ifdef DIALINOUT
73918616Sralph 	/* reenable logins on dialout */
74018616Sralph 	reenable();
74118616Sralph #endif DIALINOUT
74213639Ssam 	if (code == 0)
74313639Ssam 		xuuxqt();
74417767Sralph 	else
74517767Sralph 		DEBUG(1, "exit code %d\n", code);
74625703Sbloom 	setdebug (DBG_CLEAN);
747*33559Srick 	do_connect_accounting();
74813639Ssam 	exit(code);
74913639Ssam }
75013639Ssam 
751*33559Srick do_connect_accounting()
752*33559Srick {
753*33559Srick 	register FILE *fp;
754*33559Srick 	struct tm *localtime();
755*33559Srick 	register struct tm *tm;
756*33559Srick 	int flags;
757*33559Srick 
758*33559Srick 	if (StartTime == 0)
759*33559Srick 		return;
760*33559Srick 
761*33559Srick #ifdef DO_CONNECT_ACCOUNTING
762*33559Srick 	fp = fopen("/usr/spool/uucp/CONNECT", "a");
763*33559Srick 	ASSERT(fp != NULL, "Can't open CONNECT file", Rmtname, errno);
764*33559Srick 
765*33559Srick 	tm = localtime(&StartTime);
766*33559Srick #ifdef F_SETFL
767*33559Srick 	flags = fcntl(fileno(fp), F_GETFL, 0);
768*33559Srick 	fcntl(fileno(fp), F_SETFL, flags|O_APPEND);
769*33559Srick #endif
770*33559Srick #ifdef USG
771*33559Srick 	fprintf(fp,"%s %d %d%.2d%.2d %.2d%.2d %d %ld %s %ld %ld\n",
772*33559Srick #else /* V7 */
773*33559Srick 	fprintf(fp,"%s %d %d%02d%02d %02d%02d %d %ld %s %ld %ld\n",
774*33559Srick #endif /* V7 */
775*33559Srick 		Rmtname, InitialRole, tm->tm_year, tm->tm_mon + 1,
776*33559Srick 		tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_wday,
777*33559Srick 		(Now.time - StartTime + 59) / 60,
778*33559Srick 		ttyn == NULL ? "ttyp0" : &ttyn[5],
779*33559Srick 			Bytes_Sent, Bytes_Received);
780*33559Srick 	fclose(fp);
781*33559Srick #endif /* DO_CONNECT_ACCOUNTING */
782*33559Srick }
783*33559Srick 
78425703Sbloom /*
78525703Sbloom  *	on interrupt - remove locks and exit
78613639Ssam  */
78713639Ssam 
78813639Ssam onintr(inter)
78913639Ssam register int inter;
79013639Ssam {
791*33559Srick 	char str[BUFSIZ];
79213639Ssam 	signal(inter, SIG_IGN);
79313639Ssam 	sprintf(str, "SIGNAL %d", inter);
79413639Ssam 	logent(str, "CAUGHT");
79517767Sralph 	US_SST(us_s_intr);
79623590Sbloom 	if (*Rmtname && strncmp(Rmtname, Myname, MAXBASENAME))
79717767Sralph 		systat(Rmtname, SS_FAIL, str);
798*33559Srick 	sprintf(str, "conversation complete %ld sent %ld received",
799*33559Srick 		Bytes_Sent, Bytes_Received);
800*33559Srick 	logent(str, _FAILED);
80117767Sralph 	if (inter == SIGPIPE && !onesys)
80217767Sralph 		longjmp(Pipebuf, 1);
80313639Ssam 	cleanup(inter);
80413639Ssam }
80513639Ssam 
80613639Ssam /*
80713639Ssam  * Catch a special signal
80813639Ssam  * (SIGFPE, ugh), and toggle debugging between 0 and 30.
80913639Ssam  * Handy for looking in on long running uucicos.
81013639Ssam  */
81125703Sbloom dbg_signal()
81213639Ssam {
81325703Sbloom 	Debug = (Debug == 0) ? 30 : 0;
81425703Sbloom 	setdebug(DBG_PERM);
81525703Sbloom 	if (Debug > 0)
81625703Sbloom 		logent("Signal Enabled", "DEBUG");
81725703Sbloom }
81817767Sralph 
81925703Sbloom 
82025703Sbloom /*
82125703Sbloom  * Check debugging requests, and open RMTDEBUG audit file if necessary. If an
82225703Sbloom  * audit file is needed, the parm argument indicates how to create the file:
82325703Sbloom  *
82425703Sbloom  *	DBG_TEMP  - Open a temporary file, with filename = RMTDEBUG/pid.
82525703Sbloom  *	DBG_PERM  - Open a permanent audit file, filename = RMTDEBUG/Rmtname.
82625703Sbloom  *		    If a temp file already exists, it is mv'ed to be permanent.
82725703Sbloom  *	DBG_CLEAN - Cleanup; unlink temp files.
82825703Sbloom  *
82925703Sbloom  * Restrictions - this code can only cope with one open debug file at a time.
83025703Sbloom  * Each call creates a new file; if an old one of the same name exists it will
83125703Sbloom  * be overwritten.
83225703Sbloom  */
83325703Sbloom setdebug(parm)
83425703Sbloom int parm;
83525703Sbloom {
83625703Sbloom 	char buf[BUFSIZ];		/* Buffer for building filenames     */
83725703Sbloom 	static char *temp = NULL;	/* Ptr to temporary file name	     */
83825703Sbloom 	static int auditopen = 0;	/* Set to 1 when we open a file	     */
83925703Sbloom 	struct stat stbuf;		/* File status buffer		     */
84025703Sbloom 
84125703Sbloom 	/*
84225703Sbloom 	 * If movement or cleanup of a temp file is indicated, we do it no
84325703Sbloom 	 * matter what.
84425703Sbloom 	 */
84525703Sbloom 	if (temp != CNULL && parm == DBG_PERM) {
84625703Sbloom 		sprintf(buf, "%s/%s", RMTDEBUG, Rmtname);
84725703Sbloom 		unlink(buf);
84825703Sbloom 		if (link(temp, buf) != 0) {
84923590Sbloom 			Debug = 0;
85025703Sbloom 			assert("RMTDEBUG LINK FAIL", temp, errno);
85125703Sbloom 			cleanup(1);
85225703Sbloom 		}
85325703Sbloom 		parm = DBG_CLEAN;
85423590Sbloom 	}
85525703Sbloom 	if (parm == DBG_CLEAN) {
85625703Sbloom 		if (temp != CNULL) {
85725703Sbloom 			unlink(temp);
85825703Sbloom 			free(temp);
85925703Sbloom 			temp = CNULL;
86025703Sbloom 		}
86125703Sbloom 		return;
86225703Sbloom 	}
86325703Sbloom 
86425703Sbloom 	if (Debug == 0)
86525703Sbloom 		return;		/* Gotta be in debug to come here.   */
86625703Sbloom 
86725703Sbloom 	/*
86825703Sbloom 	 * If we haven't opened a file already, we can just return if it's
86925703Sbloom 	 * alright to use the stderr we came in with. We can if:
87025703Sbloom 	 *
87125703Sbloom 	 *	Role == MASTER, and Stderr is a regular file, a TTY or a pipe.
87225703Sbloom 	 *
87325703Sbloom 	 * Caution: Detecting when stderr is a pipe is tricky, because the 4.2
87425703Sbloom 	 * man page for fstat(2) disagrees with reality, and System V leaves it
87525703Sbloom 	 * undefined, which means different implementations act differently.
87625703Sbloom 	 */
87725703Sbloom 	if (!auditopen && Role == MASTER) {
87825703Sbloom 		if (isatty(fileno(stderr)))
87925703Sbloom 			return;
88025703Sbloom 		else if (fstat(fileno(stderr), &stbuf) == 0) {
88125703Sbloom #ifdef USG
88225703Sbloom 			/* Is Regular File or Fifo   */
88325703Sbloom 			if ((stbuf.st_mode & 0060000) == 0)
88425703Sbloom 				return;
88525703Sbloom #else !USG
88617767Sralph #ifdef BSD4_2
88725703Sbloom 					/* Is Regular File */
88825703Sbloom 			if ((stbuf.st_mode & S_IFMT) == S_IFREG ||
88925703Sbloom 			    stbuf.st_mode == 0)		/* Is a pipe */
89025703Sbloom 				return;
89125703Sbloom #else !BSD4_2
89225703Sbloom 					 /* Is Regular File or Pipe  */
89325703Sbloom 			if ((stbuf.st_mode & S_IFMT) == S_IFREG)
89425703Sbloom 				return;
89525703Sbloom #endif BSD4_2
89625703Sbloom #endif USG
89725703Sbloom 		}
89817767Sralph 	}
89913639Ssam 
90025703Sbloom 	/*
90125703Sbloom 	 * We need RMTDEBUG directory to do auditing. If the file doesn't exist,
90225703Sbloom 	 * then we forget about debugging; if it exists but has improper owner-
90325703Sbloom 	 * ship or modes, we gripe about it in ERRLOG.
90425703Sbloom 	 */
90525703Sbloom 	if (stat(RMTDEBUG, &stbuf) != SUCCESS) {
90625703Sbloom 		Debug = 0;
90725703Sbloom 		return;
90825703Sbloom 	}
90925703Sbloom 	if ((geteuid() != stbuf.st_uid) ||	  	/* We must own it    */
91025703Sbloom 	    ((stbuf.st_mode & 0170700) != 040700)) {	/* Directory, rwx    */
91125703Sbloom 		Debug = 0;
91225703Sbloom 		assert("INVALID RMTDEBUG DIRECTORY:", RMTDEBUG, stbuf.st_mode);
91325703Sbloom 		return;
91425703Sbloom 	}
91513639Ssam 
91625703Sbloom 	if (parm == DBG_TEMP) {
91725703Sbloom 		sprintf(buf, "%s/%d", RMTDEBUG, getpid());
91825703Sbloom 		temp = malloc(strlen (buf) + 1);
91925703Sbloom 		if (temp == CNULL) {
92025703Sbloom 			Debug = 0;
92125703Sbloom 			assert("RMTDEBUG MALLOC ERROR:", temp, errno);
92225703Sbloom 			cleanup(1);
92325703Sbloom 		}
92425703Sbloom 		strcpy(temp, buf);
92525703Sbloom 	} else
92625703Sbloom 		sprintf(buf, "%s/%s", RMTDEBUG, Rmtname);
92713639Ssam 
92825703Sbloom 	unlink(buf);
92925703Sbloom 	if (freopen(buf, "w", stderr) != stderr) {
93025703Sbloom 		Debug = 0;
93125703Sbloom 		assert("FAILED RMTDEBUG FILE OPEN:", buf, errno);
93225703Sbloom 		cleanup(1);
93325703Sbloom 	}
93425703Sbloom 	setbuf(stderr, CNULL);
93525703Sbloom 	auditopen = 1;
93613639Ssam }
93713639Ssam 
93823590Sbloom /*
93925703Sbloom  *	catch SIGALRM routine
94013639Ssam  */
94113639Ssam timeout()
94213639Ssam {
94323590Sbloom 	extern int HaveSentHup;
94423590Sbloom 	if (!HaveSentHup) {
94523590Sbloom 		logent(Rmtname, "TIMEOUT");
94623590Sbloom 		if (*Rmtname && strncmp(Rmtname, Myname, MAXBASENAME)) {
94723590Sbloom 			US_SST(us_s_tmot);
94823590Sbloom 			systat(Rmtname, SS_FAIL, "TIMEOUT");
94923590Sbloom 		}
95017767Sralph 	}
95113639Ssam 	longjmp(Sjbuf, 1);
95213639Ssam }
95313639Ssam 
95413639Ssam static char *
95513639Ssam pskip(p)
95613639Ssam register char *p;
95713639Ssam {
95817767Sralph 	while(*p && *p != ' ')
95913639Ssam 		++p;
96023590Sbloom 	while(*p && *p == ' ')
96117767Sralph 		*p++ = 0;
96217767Sralph 	return p;
96313639Ssam }
964