xref: /original-bsd/usr.bin/uucp/uucico/cico.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1985, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)cico.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 #include <signal.h>
13 #include "uucp.h"
14 #include <setjmp.h>
15 #ifdef	USG
16 #include <termio.h>
17 #include <fcntl.h>
18 #endif
19 #ifndef	USG
20 #include <sgtty.h>
21 #endif
22 #ifdef BSDTCP
23 #include <netdb.h>
24 #include <netinet/in.h>
25 #include <sys/socket.h>
26 #endif BSDTCP
27 #include <sys/stat.h>
28 #ifdef BSD4_2
29 #include <sys/time.h>
30 #include <fcntl.h>
31 #else
32 #include <time.h>
33 #endif
34 #include "uust.h"
35 #include "uusub.h"
36 #include "pathnames.h"
37 
38 #if defined(VMS) && defined(BSDTCP)
39 #define NOGETPEER
40 #endif
41 
42 jmp_buf Sjbuf;
43 jmp_buf Pipebuf;
44 
45 /*  call fail text  */
46 char *Stattext[] = {
47 	"",
48 	"BAD SYSTEM",
49 	"WRONG TIME TO CALL",
50 	"SYSTEM LOCKED",
51 	"NO DEVICE",
52 	"CALL FAILED",
53 	"LOGIN FAILED",
54 	"BAD SEQUENCE"
55 };
56 
57 /*  call fail codes  */
58 int Stattype[] = {
59 	0,
60 	0,
61 	SS_WRONGTIME,
62 	0,
63 	SS_NODEVICE,
64 	SS_FAIL,
65 	SS_FAIL,
66 	SS_BADSEQ
67 };
68 
69 				/* Arguments to setdebug():		     */
70 #define DBG_TEMP  0		/*   Create a temporary audit file	     */
71 #define DBG_PERM  1		/*   Create a permanent audit file	     */
72 #define DBG_CLEAN 2		/*   Cleanup, discard temp file		     */
73 
74 int ReverseRole = 0;
75 int Role = SLAVE;
76 int InitialRole = SLAVE;
77 long StartTime;
78 int onesys = 0;
79 int turntime = 30 * 60;	/* 30 minutes expressed in seconds */
80 char *ttyn = NULL;
81 extern int LocalOnly;
82 extern int errno;
83 extern char MaxGrade, DefMaxGrade;
84 extern char Myfullname[];
85 
86 long Bytes_Sent, Bytes_Received;
87 
88 #ifdef	USG
89 struct termio Savettyb;
90 #endif
91 #ifndef	USG
92 struct sgttyb Savettyb;
93 #endif
94 
95 #define SETPROCTITLE
96 #ifdef SETPROCTITLE
97 char	**Argv = NULL;		/* pointer to argument vector */
98 char	*LastArgv = NULL;	/* end of argv */
99 #endif SETPROCTITLE
100 
101 /*
102  *	this program is used  to place a call to a
103  *	remote machine, login, and copy files between the two machines.
104  */
105 main(argc, argv, envp)
106 int argc;
107 char **argv;
108 char **envp;
109 {
110 	register int ret;
111 	int seq;
112 	char wkpre[NAMESIZE], file[NAMESIZE];
113 	char msg[MAXFULLNAME], *q;
114 	register char *p;
115 	static void onintr(), timeout(), dbg_signal();
116 	static char *pskip();
117 	extern char *optarg;
118 	extern int optind;
119 	char rflags[MAXFULLNAME];
120 #ifdef NOGETPEER
121 	u_long Hostnumber = 0;
122 #endif NOGETPEER
123 
124 	strcpy(Progname, "uucico");
125 
126 #ifdef BSD4_2
127 	sigsetmask(0L);	/* in case we inherit blocked signals */
128 #endif BSD4_2
129 	signal(SIGINT, onintr);
130 	signal(SIGHUP, onintr);
131 	signal(SIGQUIT, onintr);
132 	signal(SIGTERM, onintr);
133 	signal(SIGPIPE, onintr);	/* 4.1a tcp-ip stupidity */
134 	signal(SIGUSR1, dbg_signal);
135 	ret = guinfo(getuid(), User, msg);
136 	strcpy(Loginuser, User);
137 	uucpname(Myname);
138 	if (ret == FAIL) {
139 		syslog(LOG_ERR, "can't get uid");
140 		cleanup(FAIL);
141 	}
142 
143 	setbuf (stderr, CNULL);
144 
145 	rflags[0] = '\0';
146 	umask(WFMASK);
147 	strcpy(Rmtname, Myname);
148 	Ifn = Ofn = -1;
149 	while ((ret = getopt(argc, argv, "RLd:g:p:r:s:x:t:")) != EOF)
150 		switch(ret){
151 		case 'd':
152 			Spool = optarg;
153 			break;
154 		case 'g':
155 		case 'p':
156 			MaxGrade = DefMaxGrade = *optarg;
157 			break;
158 		case 'r':
159 			Role = atoi(optarg);
160 			break;
161 		case 'R':
162 			ReverseRole++;
163 			Role = MASTER;
164 			break;
165 		case 's':
166 			strncpy(Rmtname, optarg, MAXBASENAME);
167 			Rmtname[MAXBASENAME] = '\0';
168 			if (Rmtname[0] != '\0')
169 				onesys = 1;
170 			break;
171 		case 'x':
172 			Debug = atoi(optarg);
173 			if (Debug <= 0)
174 				Debug = 1;
175 			strcat(rflags, argv[optind-1]);
176 			break;
177 		case 't':
178 			turntime = atoi(optarg)*60;/* minutes to seconds */
179 			break;
180 		case 'L':	/* local calls only */
181 			LocalOnly++;
182 			break;
183 #ifdef NOGETPEER
184 		case 'h':
185 			Hostnumber = inet_addr(&argv[1][2]);
186 			break;
187 #endif NOGETPEER
188 		case '?':
189 		default:
190 			fprintf(stderr, "unknown flag %s (ignored)\n",
191 				argv[optind-1]);
192 			break;
193 		}
194 
195 	while (optind < argc)
196 		fprintf(stderr, "unknown argument %s (ignored)\n",
197 			argv[optind++]);
198 
199 	if (Debug && Role == MASTER)
200 		chkdebug();
201 
202 #ifdef SETPROCTITLE
203 	/*
204 	 *  Save start and extent of argv for setproctitle.
205 	 */
206 
207 	Argv = argv;
208 	LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
209 #endif SETPROCTITLE
210 
211 	/* Try to run as uucp */
212 	setgid(getegid());
213 	setuid(geteuid());
214 #ifdef	TIOCNOTTY
215 	/*
216 	 * detach uucico from controlling terminal
217 	 * to defend against rlogind sending us a SIGKILL (!!!)
218 	 */
219 	if (Role == MASTER && (ret = open(_PATH_TTY, 2)) >= 0) {
220 		ioctl(ret, TIOCNOTTY, STBNULL);
221 		close(ret);
222 	}
223 #endif TIOCNOTTY
224 #ifdef BSD4_2
225 	if (getpgrp(0) == 0) { /* We have no controlling terminal */
226 		setpgrp(0, getpid());
227 	}
228 #ifdef USE_SYSLOG
229 #ifdef BSD4_3
230 	openlog("uucico", LOG_PID, LOG_UUCP);
231 #else /* !BSD4_3 */
232 	openlog("uucico", LOG_PID);
233 #endif /* !BSD4_3 */
234 #endif /* USE_SYSLOG */
235 #endif BSD4_2
236 
237 #ifdef BSD4_3
238 	unsetenv("TZ");		/* We don't want him resetting our time zone */
239 #endif /* !BSD4_3 */
240 
241 	if (subchdir(Spool) < 0) {
242 		syslog(LOG_ERR, "chdir(%s) failed: %m", Spool);
243 		cleanup(FAIL);
244 	}
245 
246 	strcpy(Wrkdir, Spool);
247 
248 	if (Debug) {
249 		setdebug ((Role == SLAVE) ? DBG_TEMP : DBG_PERM);
250 		if (Debug > 0)
251 			logent ("Local Enabled", "DEBUG");
252 	}
253 
254 	/*
255 	 * First time through: If we're the slave, do initial checking.
256 	 */
257 	if (Role == SLAVE) {
258 		/* check for /etc/nologin */
259 		if (access(NOLOGIN, 0) == 0) {
260 			logent(NOLOGIN, "UUCICO SHUTDOWN");
261 			if (Debug > 4)
262 				logent("DEBUGGING", "continuing anyway");
263 			else
264 				cleanup(1);
265 		}
266 		Ifn = 0;
267 		Ofn = 1;
268 #ifdef	TCPIP
269 		/*
270 		 * Determine if we are on TCPIP
271 		 */
272 		if (isatty(Ifn) == 0) {
273 			IsTcpIp = 1;
274 			DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL);
275 		} else
276 			IsTcpIp = 0;
277 #endif TCPIP
278 		/* initial handshake */
279 		onesys = 1;
280 		if (!IsTcpIp) {
281 #ifdef	USG
282 			ret = ioctl(Ifn, TCGETA, &Savettyb);
283 			Savettyb.c_cflag = (Savettyb.c_cflag & ~CS8) | CS7;
284 			Savettyb.c_oflag |= OPOST;
285 			Savettyb.c_lflag |= (ISIG|ICANON|ECHO);
286 #else !USG
287 			ret = ioctl(Ifn, TIOCGETP, &Savettyb);
288 			Savettyb.sg_flags |= ECHO;
289 			Savettyb.sg_flags &= ~RAW;
290 #endif !USG
291 			ttyn = ttyname(Ifn);
292 		}
293 		fixmode(Ifn);
294 
295 		/*
296 		 * Initial Message -- tell them we're here, and who we are.
297 		 */
298 		sprintf(msg, "here=%s", Myfullname);
299 		omsg('S', msg, Ofn);
300 		signal(SIGALRM, timeout);
301 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
302 		if (setjmp(Sjbuf)) {
303 			/* timed out */
304 			if (!IsTcpIp) {
305 #ifdef	USG
306 				ret = ioctl(Ifn, TCSETA, &Savettyb);
307 
308 #else	!USG
309 				ret = ioctl(Ifn, TIOCSETP, &Savettyb);
310 #endif !USG
311 			}
312 			cleanup(0);
313 		}
314 		for (;;) {
315 			ret = imsg(msg, Ifn);
316 			if (ret != SUCCESS) {
317 				alarm(0);
318 				if (!IsTcpIp) {
319 #ifdef	USG
320 					ret = ioctl(Ifn, TCSETA, &Savettyb);
321 #else	!USG
322 					ret = ioctl(Ifn, TIOCSETP, &Savettyb);
323 #endif !USG
324 				}
325 				cleanup(0);
326 			}
327 			if (msg[0] == 'S')
328 				break;
329 		}
330 		alarm(0);
331 		q = &msg[1];
332 		p = pskip(q);
333 		strncpy(Rmtname, q, MAXBASENAME);
334 		Rmtname[MAXBASENAME] = '\0';
335 
336 		/*
337 		 * Now that we know who they are, give the audit file the right
338 		 * name.
339 		 */
340 		setdebug (DBG_PERM);
341 		DEBUG(4, "sys-%s\n", Rmtname);
342 		/* The versys will also do an alias on the incoming name */
343 		if (versys(&Rmtname)) {
344 #ifdef	NOSTRANGERS
345 			/* If we don't know them, we won't talk to them... */
346 			syslog(LOG_WARNING, "Unknown host: %s", Rmtname);
347 			omsg('R', "You are unknown to me", Ofn);
348 			cleanup(0);
349 #endif	NOSTRANGERS
350 		}
351 #ifdef BSDTCP
352 		/* we must make sure they are really who they say they
353 		 * are. We compare the hostnumber with the number in the hosts
354 		 * table for the site they claim to be.
355 		 */
356 		if (IsTcpIp) {
357 			struct hostent *hp;
358 			char *cpnt, *inet_ntoa();
359 			int fromlen;
360 			struct sockaddr_in from;
361 			extern char PhoneNumber[];
362 
363 #ifdef	NOGETPEER
364 			from.sin_addr.s_addr = Hostnumber;
365 			from.sin_family = AF_INET;
366 #else	!NOGETPEER
367 			fromlen = sizeof(from);
368 			if (getpeername(Ifn,
369 			    (struct sockaddr *)&from, &fromlen) < 0) {
370 				logent(Rmtname, "NOT A TCP CONNECTION");
371 				omsg('R', "NOT TCP", Ofn);
372 				cleanup(0);
373 			}
374 #endif	!NOGETPEER
375 			hp = gethostbyaddr((char *)&from.sin_addr,
376 				sizeof (struct in_addr), from.sin_family);
377 			if (hp == NULL) {
378 				/* security break or just old host table? */
379 				logent(Rmtname, "UNKNOWN IP-HOST Name =");
380 				cpnt = inet_ntoa(from.sin_addr),
381 				logent(cpnt, "UNKNOWN IP-HOST Number =");
382 				sprintf(wkpre, "%s/%s isn't in my host table",
383 					Rmtname, cpnt);
384 				omsg('R' ,wkpre ,Ofn);
385 				cleanup(0);
386 			}
387 			if (Debug > 99)
388 				logent(Rmtname,"Request from IP-Host name =");
389 			/*
390 			 * The following is to determine if the name given us by
391 			 * the Remote uucico matches any of the names
392 			 * given its network number (remote machine) in our
393 			 * host table.
394 			 * We could check the aliases, but that won't work in
395 			 * all cases (like if you are running the domain
396 			 * server, where you don't get any aliases). The only
397 			 * reliable way I can think of that works in ALL cases
398 			 * is too look up the site in L.sys and see if the
399 			 * sitename matches what we would call him if we
400 			 * originated the call.
401 			 */
402 			/* PhoneNumber contains the official network name of the 			   host we are checking. (set in versys.c) */
403 			if (sncncmp(PhoneNumber, hp->h_name, SYSNSIZE) == 0) {
404 				if (Debug > 99)
405 					logent(q,"Found in host Tables");
406 			} else {
407 				logent(hp->h_name, "FORGED HOSTNAME");
408 				logent(inet_ntoa(from.sin_addr), "ORIGINATED AT");
409 				logent(PhoneNumber, "SHOULD BE");
410 				sprintf(wkpre, "You're not who you claim to be: %s !=  %s", hp->h_name, PhoneNumber);
411 				omsg('R', wkpre, Ofn);
412 				cleanup(0);
413 			}
414 		}
415 #endif	BSDTCP
416 
417 		if (mlock(Rmtname)) {
418 			omsg('R', "LCK", Ofn);
419 			cleanup(0);
420 		}
421 		else if (callback(Loginuser)) {
422 			signal(SIGINT, SIG_IGN);
423 			signal(SIGHUP, SIG_IGN);
424 			omsg('R', "CB", Ofn);
425 			logent("CALLBACK", "REQUIRED");
426 			/*  set up for call back  */
427 			systat(Rmtname, SS_CALLBACK, "CALLING BACK");
428 			gename(CMDPRE, Rmtname, 'C', file);
429 			close(creat(subfile(file), 0666));
430 			xuucico(Rmtname);
431 			cleanup(0);
432 		}
433 		seq = 0;
434 		while (*p == '-') {
435 			q = pskip(p);
436 			switch(*(++p)) {
437 			case 'x':
438 				if (Debug == 0) {
439 					Debug = atoi(++p);
440 					if (Debug <= 0)
441 						Debug = 1;
442 					setdebug(DBG_PERM);
443 					if (Debug > 0)
444 						logent("Remote Enabled", "DEBUG");
445 				} else {
446 					DEBUG(1, "Remote debug request ignored\n",
447 					   CNULL);
448 				}
449 				break;
450 			case 'Q':
451 				seq = atoi(++p);
452 				break;
453 			case 'p':
454 				MaxGrade = DefMaxGrade = *++p;
455 				DEBUG(4, "MaxGrade set to %c\n", MaxGrade);
456 				break;
457 			case 'v':
458 				if (strncmp(p, "grade", 5) == 0) {
459 					p += 6;
460 					MaxGrade = DefMaxGrade = *p++;
461 					DEBUG(4, "MaxGrade set to %c\n", MaxGrade);
462 				}
463 				break;
464 			default:
465 				break;
466 			}
467 			p = q;
468 		}
469 		setproctitle("%s: startup", Rmtname);
470 		if (callok(Rmtname) == SS_BADSEQ) {
471 			logent("BADSEQ", "PREVIOUS");
472 			omsg('R', "BADSEQ", Ofn);
473 			cleanup(0);
474 		}
475 #ifdef GNXSEQ
476 		if ((ret = gnxseq(Rmtname)) == seq) {
477 			omsg('R', "OK", Ofn);
478 			cmtseq();
479 		} else {
480 #else !GNXSEQ
481 		if (seq == 0)
482 			omsg('R', "OK", Ofn);
483 		else {
484 #endif !GNXSEQ
485 			systat(Rmtname, Stattype[7], Stattext[7]);
486 			logent("BAD SEQ", "FAILED HANDSHAKE");
487 #ifdef GNXSEQ
488 			ulkseq();
489 #endif GNXSEQ
490 			omsg('R', "BADSEQ", Ofn);
491 			cleanup(0);
492 		}
493 		if (ttyn != NULL)
494 			chmod(ttyn, 0600);
495 	}
496 
497 loop:
498 	if(setjmp(Pipebuf)) {	/* come here on SIGPIPE	*/
499 		clsacu();
500 		logcls();
501 		close(Ofn);
502 		close(Ifn);
503 		Ifn = Ofn = -1;
504 		rmlock(CNULL);
505 		sleep(3);
506 	}
507 	if (!onesys) {
508 		do_connect_accounting();
509 #ifdef DIALINOUT
510 		/* reenable logins on dialout */
511 		reenable();
512 #endif DIALINOUT
513 		StartTime = 0;
514 		setproctitle("looking for work");
515 		ret = gnsys(Rmtname, Spool, CMDPRE);
516 		setproctitle("%s: startup", Rmtname);
517 		setdebug(DBG_PERM);
518 		if (ret == FAIL)
519 			cleanup(100);
520 		else if (ret == SUCCESS)
521 			cleanup(0);
522 		logcls();
523 	} else if (Role == MASTER && callok(Rmtname) != 0) {
524 		logent("SYSTEM STATUS", "CAN NOT CALL");
525 		cleanup(0);
526 	}
527 
528 	sprintf(wkpre, "%c.%.*s", CMDPRE, SYSNSIZE, Rmtname);
529 	StartTime = 0;
530 	Bytes_Sent = Bytes_Received = 0L;
531 
532 	signal(SIGINT, SIG_IGN);
533 	signal(SIGQUIT, SIG_IGN);
534 	if (Role == MASTER) {
535 		extern char LineType[];
536 		/* check for /etc/nologin */
537 		if (access(NOLOGIN, 0) == 0) {
538 			logent(NOLOGIN, "UUCICO SHUTDOWN");
539 			if (Debug > 4)
540 				logent("DEBUGGING", "continuing anyway");
541 			else
542 				cleanup(1);
543 		}
544 		/*  master part */
545 		signal(SIGHUP, SIG_IGN);
546 		if (Ifn != -1 && Role == MASTER) {
547 			write(Ofn, EOTMSG, strlen(EOTMSG));
548 			clsacu();
549 			close(Ofn);
550 			close(Ifn);
551 			Ifn = Ofn = -1;
552 			rmlock(CNULL);
553 			sleep(3);
554 		}
555 		if (mlock(Rmtname) != SUCCESS) {
556 			DEBUG(1, "LOCKED: call to %s\n", Rmtname);
557 			US_SST(us_s_lock);
558 			goto next;
559 		}
560 		setproctitle("%s: starting call", Rmtname);
561 		Ofn = Ifn = conn(Rmtname);
562 		sprintf(msg, "(call to %s via %s)", Rmtname, LineType);
563 		if (Ofn < 0) {
564 			if (Ofn != CF_TIME)
565 				logent(msg, _FAILED);
566 			/* avoid excessive 'wrong time' info */
567 			if (Stattype[-Ofn] != SS_WRONGTIME){
568 				systat(Rmtname, Stattype[-Ofn], Stattext[-Ofn]);
569 				US_SST(-Ofn);
570 				UB_SST(-Ofn);
571 			}
572 			goto next;
573 		} else {
574 			logent(msg, "SUCCEEDED");
575 			US_SST(us_s_cok);
576 			UB_SST(ub_ok);
577 		}
578 		InitialRole = MASTER;
579 #ifdef	TCPIP
580 		/*
581 		 * Determine if we are on TCPIP
582 		 */
583 		if (isatty(Ifn) == 0) {
584 			IsTcpIp = 1;
585 			DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL);
586 		} else
587 			IsTcpIp = 0;
588 #endif
589 
590 		if (setjmp(Sjbuf))
591 			goto next;
592 		signal(SIGALRM, timeout);
593 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME*2);
594 		for (;;) {
595 			ret = imsg(msg, Ifn);
596 			if (ret != SUCCESS) {
597 				alarm(0);
598 				DEBUG(4,"\nimsg failed: errno %d\n", errno);
599 				logent("imsg 1", _FAILED);
600 				goto Failure;
601 			}
602 			if (msg[0] == 'S')
603 				break;
604 		}
605 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
606 #ifdef GNXSEQ
607 		seq = gnxseq(Rmtname);
608 #else !GNXSEQ
609 		seq = 0;
610 #endif !GNXSEQ
611 		if (MaxGrade != '\177') {
612 			DEBUG(2, "Max Grade this transfer is %c\n", MaxGrade);
613 			sprintf(msg, "%s -Q%d -p%c -vgrade=%c %s",
614 				Myname, seq, MaxGrade, MaxGrade, rflags);
615 		} else
616 			sprintf(msg, "%s -Q%d %s", Myname, seq, rflags);
617 		omsg('S', msg, Ofn);
618 		for (;;) {
619 			ret = imsg(msg, Ifn);
620 			DEBUG(4, "msg-%s\n", msg);
621 			if (ret != SUCCESS) {
622 				alarm(0);
623 #ifdef GNXSEQ
624 				ulkseq();
625 #endif GNXSEQ
626 				logent("imsg 2", _FAILED);
627 				goto Failure;
628 			}
629 			if (msg[0] == 'R')
630 				break;
631 		}
632 		alarm(0);
633 		if (msg[1] == 'B') {
634 			/* bad sequence */
635 			logent("BAD SEQ", "FAILED HANDSHAKE");
636 			US_SST(us_s_hand);
637 			systat(Rmtname, SS_BADSEQ, Stattext[SS_BADSEQ]);
638 #ifdef GNXSEQ
639 			ulkseq();
640 #endif GNXSEQ
641 			goto next;
642 		}
643 		if (strcmp(&msg[1], "OK") != SAME)  {
644 			logent(&msg[1], "FAILED HANDSHAKE");
645 			US_SST(us_s_hand);
646 #ifdef GNXSEQ
647 			ulkseq();
648 #endif GNXSEQ
649 			systat(Rmtname, SS_INPROGRESS,
650 				strcmp(&msg[1], "CB") == SAME?
651 				"AWAITING CALLBACK": "FAILED HANDSHAKE");
652 			goto next;
653 		}
654 #ifdef GNXSEQ
655 		cmtseq();
656 #endif GNXSEQ
657 	}
658 	DEBUG(1, "Rmtname %s, ", Rmtname);
659 	DEBUG(1, "Role %s,  ", Role ? "MASTER" : "SLAVE");
660 	DEBUG(1, "Ifn - %d, ", Ifn);
661 	DEBUG(1, "Loginuser - %s\n", Loginuser);
662 	setproctitle("%s: %s", Rmtname, Role ? "MASTER" : "SLAVE");
663 
664 	ttyn = ttyname(Ifn);
665 
666 	alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
667 	if (ret=setjmp(Sjbuf))
668 		goto Failure;
669 	ret = startup(Role);
670 	alarm(0);
671 	if (ret != SUCCESS) {
672 		logent("(startup)", _FAILED);
673 Failure:
674 		US_SST(us_s_start);
675 		systat(Rmtname, SS_FAIL, ret > 0 ? "CONVERSATION FAILED" :
676 			"STARTUP FAILED");
677 		goto next;
678 	} else {
679 		char smsg[BUFSIZ], gmsg[10], pmsg[20], bpsmsg[20];
680 		extern char UsingProtocol;
681 		extern int linebaudrate;
682 		if (ttyn != NULL)
683 			sprintf(bpsmsg, " %s %d bps", &ttyn[5], linebaudrate);
684 		else
685 			bpsmsg[0] = '\0';
686 		if (UsingProtocol != 'g')
687 			sprintf(pmsg, " %c protocol", UsingProtocol);
688 		else
689 			pmsg[0] = '\0';
690 		if (MaxGrade != '\177')
691 			sprintf(gmsg, " grade %c", MaxGrade);
692 		else
693 			gmsg[0] = '\0';
694 		sprintf(smsg, "(startup%s%s%s)", bpsmsg, pmsg, gmsg);
695 		logent(smsg, "OK");
696 		US_SST(us_s_gress);
697 		StartTime = Now.time;
698 		systat(Rmtname, SS_INPROGRESS, "TALKING");
699 		ret = cntrl(Role, wkpre);
700 		DEBUG(1, "cntrl - %d\n", ret);
701 		signal(SIGINT, SIG_IGN);
702 		signal(SIGHUP, SIG_IGN);
703 		signal(SIGALRM, timeout);
704 		sprintf(smsg, "(conversation complete %ld sent %ld received)",
705 			Bytes_Sent, Bytes_Received);
706 		if (ret == SUCCESS) {
707 			logent(smsg, "OK");
708 			US_SST(us_s_ok);
709 			rmstat(Rmtname);
710 
711 		} else {
712 			logent(smsg, _FAILED);
713 			US_SST(us_s_cf);
714 			systat(Rmtname, SS_FAIL, "CONVERSATION FAILED");
715 		}
716 		alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME);
717 		DEBUG(4, "send OO %d,", ret);
718 		if (!setjmp(Sjbuf)) {
719 			for (;;) {
720 				omsg('O', "OOOOO", Ofn);
721 				ret = imsg(msg, Ifn);
722 				if (ret != 0)
723 					break;
724 				if (msg[0] == 'O')
725 					break;
726 			}
727 		}
728 		alarm(0);
729 		clsacu();
730 		rmlock(CNULL);
731 
732 	}
733 next:
734 	if (!onesys) {
735 		goto loop;
736 	}
737 	cleanup(0);
738 }
739 
740 #ifndef	USG
741 struct sgttyb Hupvec;
742 #endif
743 
744 /*
745  *	cleanup and exit with "code" status
746  */
747 cleanup(code)
748 register int code;
749 {
750 	signal(SIGINT, SIG_IGN);
751 	signal(SIGHUP, SIG_IGN);
752 	rmlock(CNULL);
753 	sleep(5);			/* Wait for any pending output	  */
754 	clsacu();
755 	logcls();
756 	if (Role == SLAVE) {
757 		if (!IsTcpIp) {
758 #ifdef USG
759 			Savettyb.c_cflag |= HUPCL;
760 			(void) ioctl(0, TCSETA, &Savettyb);
761 #else !USG
762 			(void) ioctl(0, TIOCHPCL, STBNULL);
763 #ifdef TIOCSDTR
764 			(void) ioctl(0, TIOCCDTR, STBNULL);
765 			sleep(2);
766 			(void) ioctl(0, TIOCSDTR, STBNULL);
767 #else !TIOCSDTR
768 			(void) ioctl(0, TIOCGETP, &Hupvec);
769 			Hupvec.sg_ispeed = B0;
770 			Hupvec.sg_ospeed = B0;
771 			(void) ioctl(0, TIOCSETP, &Hupvec);
772 #endif !TIOCSDTR
773 			sleep(2);
774 			(void) ioctl(0, TIOCSETP, &Savettyb);
775 			/* make *sure* exclusive access is off */
776 			(void) ioctl(0, TIOCNXCL, STBNULL);
777 #endif !USG
778 		}
779 		if (ttyn != NULL)
780 			chmod(ttyn, 0600);
781 	}
782 	if (Ofn != -1) {
783 		if (Role == MASTER)
784 			write(Ofn, EOTMSG, strlen(EOTMSG));
785 		close(Ifn);
786 		close(Ofn);
787 	}
788 #ifdef DIALINOUT
789 	/* reenable logins on dialout */
790 	reenable();
791 #endif DIALINOUT
792 	if (code == 0)
793 		xuuxqt();
794 	else
795 		DEBUG(1, "exit code %d\n", code);
796 	setdebug (DBG_CLEAN);
797 	do_connect_accounting();
798 	exit(code);
799 }
800 
801 do_connect_accounting()
802 {
803 #ifdef DO_CONNECT_ACCOUNTING
804 	register FILE *fp;
805 	struct tm *localtime();
806 	register struct tm *tm;
807 	int flags;
808 
809 	if (StartTime == 0)
810 		return;
811 
812 	fp = fopen(DO_CONNECT_ACCOUNTING, "a");
813 	if (fp == NULL) {
814 		syslog(LOG_ALERT, "fopen(%s) failed: %m",DO_CONNECT_ACCOUNTING);
815 		cleanup(FAIL);
816 	}
817 
818 	tm = localtime(&StartTime);
819 #ifdef F_SETFL
820 	flags = fcntl(fileno(fp), F_GETFL, 0);
821 	fcntl(fileno(fp), F_SETFL, flags|O_APPEND);
822 #endif
823 #ifdef USG
824 	fprintf(fp,"%s %d %d%.2d%.2d %.2d%.2d %d %ld %s %ld %ld\n",
825 #else /* V7 */
826 	fprintf(fp,"%s %d %d%02d%02d %02d%02d %d %ld %s %ld %ld\n",
827 #endif /* V7 */
828 		Rmtname, InitialRole, tm->tm_year, tm->tm_mon + 1,
829 		tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_wday,
830 		(Now.time - StartTime + 59) / 60,
831 		ttyn == NULL ? "ttyp0" : &ttyn[5],
832 			Bytes_Sent, Bytes_Received);
833 	fclose(fp);
834 #endif /* DO_CONNECT_ACCOUNTING */
835 }
836 
837 /*
838  *	on interrupt - remove locks and exit
839  */
840 
841 static void
842 onintr(inter)
843 register int inter;
844 {
845 	char str[BUFSIZ];
846 	signal(inter, SIG_IGN);
847 	sprintf(str, "(SIGNAL %d)", inter);
848 	logent(str, "CAUGHT");
849 	US_SST(us_s_intr);
850 	if (*Rmtname && strncmp(Rmtname, Myname, MAXBASENAME))
851 		systat(Rmtname, SS_FAIL, str);
852 	sprintf(str, "(conversation complete %ld sent %ld received)",
853 		Bytes_Sent, Bytes_Received);
854 	logent(str, _FAILED);
855 	if (inter == SIGPIPE && !onesys)
856 		longjmp(Pipebuf, 1);
857 	cleanup(inter);
858 }
859 
860 /*
861  * Catch a special signal
862  * (SIGUSR1), and toggle debugging between 0 and 30.
863  * Handy for looking in on long running uucicos.
864  */
865 static void
866 dbg_signal()
867 {
868 	Debug = (Debug == 0) ? 30 : 0;
869 	setdebug(DBG_PERM);
870 	if (Debug > 0)
871 		logent("Signal Enabled", "DEBUG");
872 }
873 
874 
875 /*
876  * Check debugging requests, and open RMTDEBUG audit file if necessary. If an
877  * audit file is needed, the parm argument indicates how to create the file:
878  *
879  *	DBG_TEMP  - Open a temporary file, with filename = RMTDEBUG/pid.
880  *	DBG_PERM  - Open a permanent audit file, filename = RMTDEBUG/Rmtname.
881  *		    If a temp file already exists, it is mv'ed to be permanent.
882  *	DBG_CLEAN - Cleanup; unlink temp files.
883  *
884  * Restrictions - this code can only cope with one open debug file at a time.
885  * Each call creates a new file; if an old one of the same name exists it will
886  * be overwritten.
887  */
888 setdebug(parm)
889 int parm;
890 {
891 	char buf[BUFSIZ];		/* Buffer for building filenames     */
892 	static char *temp = NULL;	/* Ptr to temporary file name	     */
893 	static int auditopen = 0;	/* Set to 1 when we open a file	     */
894 	struct stat stbuf;		/* File status buffer		     */
895 
896 	/*
897 	 * If movement or cleanup of a temp file is indicated, we do it no
898 	 * matter what.
899 	 */
900 	if (temp != CNULL && parm == DBG_PERM) {
901 		sprintf(buf, "%s/%s", RMTDEBUG, Rmtname);
902 		unlink(buf);
903 		if (link(temp, buf) != 0) {
904 			Debug = 0;
905 			syslog(LOG_ERR, "RMTDEBUG link(%s,%s) failed: %m",
906 				temp, buf);
907 			cleanup(FAIL);
908 		}
909 		parm = DBG_CLEAN;
910 	}
911 	if (parm == DBG_CLEAN) {
912 		if (temp != CNULL) {
913 			unlink(temp);
914 			free(temp);
915 			temp = CNULL;
916 		}
917 		return;
918 	}
919 
920 	if (Debug == 0)
921 		return;		/* Gotta be in debug to come here.   */
922 
923 	/*
924 	 * If we haven't opened a file already, we can just return if it's
925 	 * alright to use the stderr we came in with. We can if:
926 	 *
927 	 *	Role == MASTER, and Stderr is a regular file, a TTY or a pipe.
928 	 *
929 	 * Caution: Detecting when stderr is a pipe is tricky, because the 4.2
930 	 * man page for fstat(2) disagrees with reality, and System V leaves it
931 	 * undefined, which means different implementations act differently.
932 	 */
933 	if (!auditopen && Role == MASTER) {
934 		if (isatty(fileno(stderr)))
935 			return;
936 		else if (fstat(fileno(stderr), &stbuf) == 0) {
937 #ifdef USG
938 			/* Is Regular File or Fifo   */
939 			if ((stbuf.st_mode & 0060000) == 0)
940 				return;
941 #else !USG
942 #ifdef BSD4_2
943 					/* Is Regular File */
944 			if ((stbuf.st_mode & S_IFMT) == S_IFREG ||
945 			    stbuf.st_mode == 0)		/* Is a pipe */
946 				return;
947 #else !BSD4_2
948 					 /* Is Regular File or Pipe  */
949 			if ((stbuf.st_mode & S_IFMT) == S_IFREG)
950 				return;
951 #endif BSD4_2
952 #endif USG
953 		}
954 	}
955 
956 	/*
957 	 * We need RMTDEBUG directory to do auditing. If the file doesn't exist,
958 	 * then we forget about debugging; if it exists but has improper owner-
959 	 * ship or modes, we gripe about it in ERRLOG.
960 	 */
961 	if (stat(RMTDEBUG, &stbuf) != SUCCESS) {
962 		Debug = 0;
963 		return;
964 	}
965 	if ((geteuid() != stbuf.st_uid) ||	  	/* We must own it    */
966 	    ((stbuf.st_mode & 0170700) != 040700)) {	/* Directory, rwx    */
967 		Debug = 0;
968 		syslog(LOG_ERR, "%s: invalid directory mode: %o", RMTDEBUG,
969 			stbuf.st_mode);
970 		return;
971 	}
972 
973 	if (parm == DBG_TEMP) {
974 		sprintf(buf, "%s/%d", RMTDEBUG, getpid());
975 		temp = malloc(strlen (buf) + 1);
976 		if (temp == CNULL) {
977 			Debug = 0;
978 			syslog(LOG_ERR, "RMTDEBUG malloc failed: %m");
979 			cleanup(FAIL);
980 		}
981 		strcpy(temp, buf);
982 	} else
983 		sprintf(buf, "%s/%s", RMTDEBUG, Rmtname);
984 
985 	unlink(buf);
986 	if (freopen(buf, "w", stderr) != stderr) {
987 		Debug = 0;
988 		syslog(LOG_ERR, "RMTDEBUG freopen(%s) failed: %m", buf);
989 		cleanup(FAIL);
990 	}
991 	setbuf(stderr, CNULL);
992 	auditopen = 1;
993 }
994 
995 /*
996  *	catch SIGALRM routine
997  */
998 static void
999 timeout()
1000 {
1001 	extern int HaveSentHup;
1002 	if (!HaveSentHup) {
1003 		logent(Rmtname, "TIMEOUT");
1004 		if (*Rmtname && strncmp(Rmtname, Myname, MAXBASENAME)) {
1005 			US_SST(us_s_tmot);
1006 			systat(Rmtname, SS_FAIL, "TIMEOUT");
1007 		}
1008 	}
1009 	longjmp(Sjbuf, 1);
1010 }
1011 
1012 static char *
1013 pskip(p)
1014 register char *p;
1015 {
1016 	while(*p && *p != ' ')
1017 		++p;
1018 	while(*p && *p == ' ')
1019 		*p++ = 0;
1020 	return p;
1021 }
1022 
1023 /*
1024  * clobber argv so ps will show what we're doing.
1025  * stolen from sendmail
1026  */
1027 /*VARARGS1*/
1028 setproctitle(fmt, a, b, c)
1029 char *fmt;
1030 {
1031 #ifdef SETPROCTITLE
1032 	register char *p;
1033 	register int i;
1034 	extern char **Argv;
1035 	extern char *LastArgv;
1036 	char buf[BUFSIZ];
1037 
1038 	(void) sprintf(buf, fmt, a, b, c);
1039 
1040 	/* make ps print "(sendmail)" */
1041 	p = Argv[0];
1042 	*p++ = '-';
1043 
1044 	i = strlen(buf);
1045 	if (i > LastArgv - p - 2) {
1046 		i = LastArgv - p - 2;
1047 		buf[i] = '\0';
1048 	}
1049 	(void) strcpy(p, buf);
1050 	p += i;
1051 	while (p < LastArgv)
1052 		*p++ = ' ';
1053 #endif SETPROCTITLE
1054 }
1055