xref: /original-bsd/contrib/usr.x25/x29d/x29d.c (revision 86ed09e7)
1 /*
2  * X.29 server
3  *
4  * Frank Pronk (...!ubc-vision!pronk)
5  * April, September 1984
6  *
7  * Laboratory for Computational Vision
8  * University of British Columbia
9  * Copyright (c)
10  */
11 
12 #include <sys/param.h>
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16 
17 #include <netccitt/x25.h>
18 
19 #include <errno.h>
20 #include <netdb.h>
21 #include <sgtty.h>
22 #include <signal.h>
23 
24 #include "../h/x29.h"
25 
26 #define BUFSIZ		1024
27 #define MAXARGS		10	/* maximum size of server argument list */
28 
29 #define X25NET		0	/* no ITI parameters */
30 #define CCITT1978	1	/* 1978 CCITT standard parameter set */
31 #define CCITT1980	2	/* 1980 CCITT standard parameter set */
32 
33 
34 char	pibuf[BUFSIZ], fibuf[BUFSIZ];
35 int	pty, net;
36 extern	char **environ;
37 extern	int errno;
38 char	line[sizeof("/dev/ptyp0")];
39 char	console[] = "/dev/console";
40 short	packet_size;
41 char	*tracefn;		/* trace file name */
42 char	*server;
43 short	send_banner;
44 struct	sockaddr_x25 sock;
45 
46 int	reapchild();
47 struct	net *lookup ();
48 
49 char	ccitt1978_prof[] = {		/* initial profile */
50 	Q_BIT,	X29_SET_AND_READ_PARMS,
51 	X29_ECHO_CODE,			1,	/* echo on */
52 	X29_FORWARDING_SIGNAL_CODE,	126,	/* forward on all cntl */
53 	X29_IDLE_TIMER_CODE,		0,	/* off */
54 	X29_AUX_DEV_CONTROL_CODE,	0,	/* off */
55 	X29_RECEIVE_NET_MSGS_CODE,	1,	/* xmit network msgs */
56 	X29_BREAK_PROCEDURE_CODE,	21,
57 	X29_PADDING_CODE,		0,	/* off */
58 	X29_LINE_FOLDING_CODE,		0,	/* off */
59 	X29_TRANSMISSION_SPEED_CODE,	0,
60 	X29_XON_XOFF_CODE,		1,	/* enable XON/XOFF */
61 };
62 
63 char	ccitt1980_prof[] = {		/* initial profile */
64 	Q_BIT,	X29_SET_AND_READ_PARMS,
65 	X29_ECHO_CODE,			1,	/* echo on */
66 	X29_FORWARDING_SIGNAL_CODE,	126,	/* forward on all cntl */
67 	X29_IDLE_TIMER_CODE,		0,	/* off */
68 	X29_AUX_DEV_CONTROL_CODE,	0,	/* off */
69 	X29_RECEIVE_NET_MSGS_CODE,	1,	/* xmit network msgs */
70 	X29_BREAK_PROCEDURE_CODE,	21,
71 	X29_PADDING_CODE,		0,	/* off */
72 	X29_LINE_FOLDING_CODE,		0,	/* off */
73 	X29_TRANSMISSION_SPEED_CODE,	0,
74 	X29_XON_XOFF_CODE,		1,	/* enable XON/XOFF */
75 
76 	X29_LF_AFTER_CR,		4,	/* lf after cr from terminal */
77 	X29_EDITING,			1,	/* on */
78 	X29_CHARACTER_DELETE,		CERASE,
79 	X29_LINE_DELETE,		CKILL,
80 	X29_LINE_DISPLAY,		CRPRNT,
81 };
82 
83 char	datapac_prof[] = {		/* Canadian X.25 network */
84 	Q_BIT,	X29_SET_AND_READ_PARMS,
85 	X29_ECHO_CODE,			1,	/* echo on */
86 	X29_FORWARDING_SIGNAL_CODE,	126,	/* forward on all cntl */
87 	X29_IDLE_TIMER_CODE,		0,	/* off */
88 	X29_AUX_DEV_CONTROL_CODE,	0,	/* off */
89 	X29_RECEIVE_NET_MSGS_CODE,	1,	/* xmit network msgs */
90 	X29_BREAK_PROCEDURE_CODE,	21,
91 	X29_PADDING_CODE,		0,	/* off */
92 	X29_LINE_FOLDING_CODE,		0,	/* off */
93 	X29_TRANSMISSION_SPEED_CODE,	0,
94 	X29_XON_XOFF_CODE,		1,	/* enable XON/XOFF */
95 
96 	X29_LF_AFTER_CR,		4,	/* lf after cr from terminal */
97 	X29_EDITING,			1,	/* on */
98 	X29_CHARACTER_DELETE,		CERASE,
99 	X29_LINE_DELETE,		CKILL,
100 	X29_LINE_DISPLAY,		CRPRNT,
101 
102 	/*
103 	 * This rubbish can be removed when Datapac
104 	 * adopts the 1980 standard parameter set.
105 	 */
106 
107 	0,				0,	/* national parameter marker */
108 	123,				0,	/* parity off */
109 };
110 
111 struct	net {
112 	char	*n_name;	/* generic name */
113 	short	n_type;		/* see defines above */
114 	char	*n_profile;	/* initial profile */
115 	short	n_proflen;	/* length of n_profile */
116 } *netp, nets[] = {
117 	"x.25",		X25NET,		0,		0,
118 	"1978",		CCITT1978,	ccitt1978_prof,	sizeof(ccitt1978_prof),
119 	"ccitt1978",	CCITT1978,	ccitt1978_prof,	sizeof(ccitt1978_prof),
120 	"1980",		CCITT1980,	ccitt1980_prof,	sizeof(ccitt1980_prof),
121 	"ccitt1980",	CCITT1980,	ccitt1980_prof,	sizeof(ccitt1980_prof),
122 	"datapac",	CCITT1980,	datapac_prof,	sizeof(datapac_prof),
123 	0,		0,		0,		0
124 };
125 
126 main(argc, argv)
127 register char **argv;
128 {
129 	register int s, pid;
130 	register char *p;
131 
132 #ifdef waterloo
133 	/*
134 	 * If this host doesn't support X.25, give up.
135 	 */
136 	s = socket(AF_CCITT, SOCK_STREAM, 0);
137 	if (s < 0 && errno == EPROTONOSUPPORT)
138 		fatal(2, "X.25 is not supported on this machine");
139 	close(s);
140 #endif
141 	netp = lookup ("ccitt1978");
142 	sock.x25_family = AF_CCITT;
143 	sock.x25_opts.op_flags = X25_MQBIT;
144 	sock.x25_udata[0] = ITI_CALL;
145 	sock.x25_udlen = 4;
146 
147 	for (argv++; argc > 1; argc--, argv++)
148 		if (**argv == '-')
149 			for (p = *argv+1; *p; p++)
150 			switch (*p) {
151 			case 'b':
152 				send_banner++;
153 				break;
154 
155 			case 'c':
156 				if (argc > 1) {
157 					argc--; argv++;
158 					if ((netp = lookup (*argv)) == 0)
159 						fatal(1, "Unknown network type");
160 				}
161 				break;
162 
163 			case 'p':
164 				if (argc > 1) {
165 					argc--; argv++;
166 					strcpy (sock.x25_udata, *argv);
167 				}
168 				break;
169 
170 			case 'r':
171 				sock.x25_opts.op_flags |= X25_REVERSE_CHARGE;
172 				break;
173 
174 			case 't':
175 				if (argc > 1) {
176 					argc--; argv++;
177 					tracefn = *argv;
178 				}
179 				else fatal(1, "missing trace file");
180 				break;
181 
182 			default:
183 				fatal (1, "usage: x29d -b -c nettype -p protocol -r -t trace_file server");
184 			}
185 		else
186 			server = *argv;
187 	if (server == 0)
188 		fatal (1, "no server specified");
189 	if (fork())
190 		exit(0);
191 	for (s = 0; s < 10; s++)
192 		(void) close(s);
193 	(void) open("/", 0);
194 	(void) dup2(0, 1);
195 	(void) dup2(0, 2);
196 	{ int tt = open("/dev/tty", 2);
197 	  if (tt > 0) {
198 		ioctl(tt, TIOCNOTTY, (char *)0);
199 		close(tt);
200 	  }
201 	}
202 
203 	while((s = socket(AF_CCITT, SOCK_STREAM, 0)) < 0)
204 		sleep(60);
205 	while (bind(s, (caddr_t)&sock, sizeof (sock)) < 0)
206 		sleep(60);
207 	signal(SIGCHLD, reapchild);
208 	listen(s, 5);
209 
210 	for (;;) {
211 		struct sockaddr_x25 from;
212 		int fromlen = sizeof (from);
213 
214 		if ((net = accept(s, (caddr_t)&from, &fromlen)) < 0) {
215 			if (errno != EINTR)
216 				sleep (60);
217 			continue;
218 		}
219 		while ((pid = fork()) < 0)
220 			sleep(60);
221 		if (pid == 0) {
222 			signal(SIGCHLD, SIG_DFL);
223 			doit(&from);
224 		}
225 		close(net);
226 	}
227 	/*NOTREACHED*/
228 }
229 
230 struct net *
231 lookup (name)
232 char *name;
233 {
234 	register struct net *np;
235 
236 	for (np = nets; np->n_name; np++)
237 		if (strcmp (np->n_name, name) == 0)
238 			return (np);
239 	return (0);
240 }
241 
242 reapchild()
243 {
244 	union wait status;
245 
246 	while (wait3(&status, WNOHANG, 0) > 0)
247 		;
248 }
249 
250 char	*envinit[] = { "TERM=ccitt", 0 };
251 int	cleanup();
252 
253 /*
254  * Get a pty, scan input lines.
255  */
256 doit(who)
257 struct sockaddr_x25 *who;
258 {
259 	register char *cp;
260 	register int i, p, t;
261 	struct sgttyb b;
262 	struct stat sb;
263 
264 	strcpy(line, "/dev/ptyp0");
265 	cp = line;
266 	for (t = 'p'; ; t++) {
267 		cp[strlen("/dev/pty")] = t;
268 		if (stat(line, &sb) < 0)
269 			break;
270 		for (i = 0; i < 16; i++) {
271 			cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
272 			p = open(cp, 2);
273 			if (p > 0)
274 				goto gotpty;
275 		}
276 	}
277 	fatal(net, "All pty ports in use");
278 	/*NOTREACHED*/
279 gotpty:
280 	pty = p;
281 	(void) dup2(net, 0);
282 	cp[strlen("/dev/")] = 't';
283 	t = open("/dev/tty", 2);
284 	if (t >= 0) {
285 		ioctl(t, TIOCNOTTY, (char *)0);
286 		close(t);
287 	}
288 	t = open(cp, 2);	/* slave side of pty */
289 	if (t < 0)
290 		fatalperror(cp, errno);
291 	ioctl(t, TIOCGETP, (char *)&b);
292 	b.sg_flags = CRMOD|XTABS|ANYP|ECHO;
293 	ioctl(t, TIOCSETP, &b);
294 	packet_size = 1 << who->x25_opts.op_psize;
295 	if ((i = fork()) < 0)
296 		fatalperror("fork", errno);
297 	if (i)
298 		x29d();
299 	close(net);
300 	close(p);
301 	(void) dup2(t, 0);
302 	(void) dup2(t, 1);
303 	(void) dup2(t, 2);
304 	close(t);
305 	environ = envinit;
306 	call_server (who);
307 	/*NOTREACHED*/
308 }
309 
310 call_server (who)
311 struct sockaddr_x25 *who;
312 {
313 	register struct hostent *hp = 0;
314 	register char *p, **ap;
315 	char *args[MAXARGS];
316 	struct stat st;
317 	struct hostent *getx25hostbyaddr();
318 	int ccitt = 0;
319 
320 	p = server;
321 	while (*p && *p != ' ' && *p != '\t')	/* split program from args */
322 		p++;
323 	if (*p)
324 		*p++ = '\0';
325 	ap = args;
326 	while (*p) {
327 		while (*p == ' ' || *p == '\t')
328 			p++;
329 		if (ap < &args[MAXARGS-2])
330 			*ap++ = p;
331 		if (strcmp(p, "-ccitt") == 0)
332 			ccitt = 1;
333 		while (*p && *p != ' ' && *p != '\t')
334 			p++;
335 		if (*p)
336 			*p++ = '\0';
337 	}
338 	if (stat (server, &st) < 0)
339 		fatalperror (server, errno);
340 	/*
341 	 * For security: if running as root, switch to user
342 	 * and group id of server.  This prevents privately
343 	 * maintainted or bogus servers from getting super-
344 	 * user permissions.
345 	 */
346 	if (getuid() == 0) {
347 		setgid (st.st_gid);
348 		setuid (st.st_uid);
349 	}
350 	if (hp = getx25hostbyaddr (who->x25_addr))
351 		*ap++ = hp->h_name;
352 	else
353 		*ap++ = (char *)who->x25_addr;
354 	/*
355 	 * If the -ccitt flag was given, add another argument
356 	 * to tell login if charging is being reversed or not.
357 	 */
358 	if (ccitt)
359 		*ap++ = (who->x25_opts.op_flags & X25_REVERSE_CHARGE) ? "y" : "n";
360 	*ap = 0;
361 	execv (server, args);
362 	fatalperror (server, errno);
363 	/*NOTREACHED*/
364 }
365 
366 fatal(f, msg)
367 	int f;
368 	char *msg;
369 {
370 	register char *p;
371 	char buf[BUFSIZ], *index();
372 
373 	p = buf;
374 	if (f == net)
375 		*p++ = 0;
376 	strcpy(p, "x29d: ");
377 	strcat(p, msg);
378 	strcat(p, "\n");
379 	(void) write(f, p, (index(p, '\n')-p)+1);
380 	exit(1);
381 }
382 
383 fatalperror(msg, err)
384 char *msg;
385 {
386 	char buf[BUFSIZ];
387 	extern char *sys_errlist[];
388 
389 	strcpy(buf, msg);
390 	strcat(buf, ": ");
391 	strcat(buf, sys_errlist[err]);
392 	fatal(net, buf);
393 }
394 
395 /*
396  * Main loop.  Select from pty and network, and
397  * hand data to iti receiver.
398  */
399 x29d()
400 {
401 	register int pcc, fcc, cc;
402 	register char *fbp;
403 	int pgrp, x25_interrupt(), on = 1;
404 	char hostname[32];
405 
406 	ioctl(net, FIONBIO, (char *)&on);
407 	ioctl(pty, FIONBIO, (char *)&on);
408 	ioctl(pty, TIOCPKT, (char *)&on);
409 	ioctl(pty, TIOCREMECHO, (char *)&on);	/* enable special pty mode */
410 	signal(SIGPIPE, SIG_IGN);	/* why not cleanup?  --kwl */
411 	signal(SIGTSTP, SIG_IGN);
412 	signal(SIGCHLD, cleanup);
413 	signal(SIGHUP, cleanup);
414 
415 	signal(SIGTTOU, SIG_IGN);
416 	signal(SIGURG, x25_interrupt);	/* for out-of-band data */
417 	pgrp = -getpgrp(0);
418 	ioctl(net, SIOCSPGRP, (char *)&pgrp);
419 
420 	if (netp->n_proflen)
421 		(void) write(net, netp->n_profile, netp->n_proflen);
422 
423 	/*
424 	 * Show banner that getty never gave.
425 	 */
426 	if (send_banner) {
427 		gethostname(hostname, sizeof (hostname));
428 #ifdef BSD4_3
429 		strcpy(pibuf+1, "\r\n\r\n4.3 BSD UNIX (");
430 #else
431 		strcpy(pibuf+1, "\r\n\r\n4.2 BSD UNIX (");
432 #endif
433 		strcat(pibuf+1, hostname);
434 		strcat(pibuf+1, ")\r\n\r\n");
435 		pcc = strlen(pibuf+1) + 1;
436 	} else
437 		pcc = 0;
438 
439 	fcc = 0;
440 	for (;;) {
441 		int ibits, obits;
442 
443 		ibits = obits = 0;
444 		/*
445 		 * Never look for input if there's still
446 		 * stuff in the corresponding output buffer
447 		 */
448 		if (fcc >= 0)			/* net connection alive? */
449 			if (fcc && pcc >= 0)	/* output pending? */
450 				obits |= (1 << pty);
451 			else
452 				if (pcc >= 0)	/* pty still alive? */
453 					ibits |= (1 << net);
454 		if (pcc >= 0)			/* pty connection alive? */
455 			if (pcc && fcc >= 0)	/* output pending? */
456 				obits |= (1 << net);
457 			else
458 				if (fcc >= 0)	/* net still alive? */
459 					ibits |= (1 << pty);
460 		if(ibits == 0 && obits == 0)
461 			break;
462 		(void) select(16, &ibits, &obits, (int *)0, 0);
463 		if (ibits == 0 && obits == 0) {
464 			sleep(5);
465 			continue;
466 		}
467 
468 		/*
469 		 * Something to read from the network...
470 		 */
471 		if (ibits & (1 << net)) {
472 			fcc = read(net, fibuf, BUFSIZ);
473 			fbp = fibuf+1;
474 			if (fcc < 0 && errno == EWOULDBLOCK)
475 				fcc = 0;
476 			else if(fcc <= 0)
477 				fcc = -1;
478 			else {
479 				if (tracefn)
480 					x29d_trace("netread", fibuf, fcc);
481 				if(fibuf[0] & Q_BIT) {
482 					x29_qbit(fcc);
483 					fcc = 0;
484 				} else
485 					fcc--;
486 			}
487 		}
488 
489 		/*
490 		 * Something to read from the pty...
491 		 */
492 		if (ibits & (1 << pty)) {
493 			pcc = read(pty, pibuf, packet_size+1);
494 			if (pcc < 0 && errno == EWOULDBLOCK)
495 				pcc = 0;
496 			else if (pcc <= 0)
497 				pcc = -1;
498 			else if(pibuf[0] != 0) {	/* non-data packet */
499 				if (pibuf[0] & TIOCPKT_IOCTL)
500 					pcc = set_x29_parameters();
501 				else
502 					pcc = 0;
503 			} else				/* data packet */
504 				pibuf[0] = 0;
505 		}
506 
507 		if ((obits & (1<<net)) && pcc > 0)
508 			if((cc = write(net, pibuf, pcc)) == pcc) {
509 				if(tracefn)
510 					x29d_trace("netwrite", pibuf, pcc);
511 				pcc = 0;
512 			} else {
513 				extern char *sys_errlist[];
514 
515 				if(tracefn)
516 					x29d_trace("netwrite",
517 						sys_errlist[errno],
518 						strlen(sys_errlist[errno]));
519 
520 			}
521 
522 		if ((obits & (1 << pty)) && fcc > 0) {
523 			cc = write(pty, fbp, fcc);
524 			if (cc > 0) {
525 				fcc -= cc;
526 				fbp += cc;
527 			}
528 		}
529 	}
530 	cleanup();
531 }
532 
533 
534 /*
535  * Send interrupt to process on other side of pty.
536  * If it is in raw mode, just write NULL;
537  * otherwise, write intr char.
538  */
539 
540 x25_interrupt()
541 {
542 	struct sgttyb b;
543 	struct tchars tchars;
544 	int zero = 0;
545 
546 	signal(SIGURG, x25_interrupt);
547 	ioctl(pty, TIOCGETP, (char *)&b);
548 	if (b.sg_flags & RAW)
549 		(void) write(pty, "\0", 1);
550 	else {
551 		ioctl(pty, TIOCFLUSH, (char *)&zero);
552 		ioctl(pty, TIOCGETC, (char *)&tchars);
553 		(void) write(pty, &tchars.t_intrc, 1);
554 	}
555 }
556 
557 cleanup()
558 {
559 	struct sgttyb sg;
560 	struct stat st;
561 
562 	ioctl(pty, TIOCGETP, (char *)&sg);    /* flushes output buffer */
563 	ioctl(pty, TIOCSETP, (char *)&sg);
564 	(void) stat(line, &st);
565 	rmut();
566 	vhangup();			/* XXX */
567 	setuid(st.st_uid);
568 	exit(1);
569 }
570 
571 /*
572  * Map unix tty modes and special characters
573  * into x29 parameters.
574  */
575 
576 set_x29_parameters()
577 {
578 	register char *p;
579 	register int f;
580 	struct sgttyb b;
581 
582 	if (netp->n_type == X25NET)
583 		return (0);
584 	ioctl(pty, TIOCGETP, (char *)&b);
585 	f = b.sg_flags;
586 	p = pibuf;
587 	*p++ = Q_BIT;
588 	*p++ = X29_SET_PARMS;
589 	*p++ = X29_ESCAPE_TO_CMD_CODE;	*p++ = (f & (RAW|CBREAK)) == 0;
590 	*p++ = X29_ECHO_CODE;		*p++ = (f & ECHO) != 0;
591 	*p++ = X29_FORWARDING_SIGNAL_CODE;	*p++ = (f & (RAW|CBREAK)) ? 0 : 126;
592 
593 	/*
594 	 * The value of 10 (0.5 seconds) for the idle timer when
595 	 * in raw or cbreak mode is a compromise value.  For good
596 	 * interactive response this value should be as low as
597 	 * possible; for reasonable efficiency with file transfers
598 	 * this value should be at fairly high.  This number should
599 	 * be changed to suit local requirements.
600 	 */
601 
602 	*p++ = X29_IDLE_TIMER_CODE;	*p++ = (f & (RAW|CBREAK)) ? 10 : 0;
603 	*p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (f & TANDEM) != 0;
604 	*p++ = X29_XON_XOFF_CODE;	*p++ = (f & RAW) == 0;
605 	if(netp->n_type == CCITT1980) {
606 		struct ltchars ltc;
607 
608 		ioctl(pty, TIOCGLTC, (char *)&ltc);
609 		*p++ = X29_LF_AFTER_CR;
610 		*p++ = (f & (RAW|CBREAK) || (f & ECHO) == 0) ? 0 : 4;
611 		*p++ = X29_EDITING;
612 		*p++ = (f & (RAW|CBREAK)) == 0;
613 		*p++ = X29_CHARACTER_DELETE;
614 		*p++ = (f & (RAW|CBREAK)) || b.sg_erase & 0200 ?
615 			0 : b.sg_erase;
616 		*p++ = X29_LINE_DELETE;
617 		*p++ = (f & (RAW|CBREAK)) || b.sg_kill & 0200 ?
618 			0 : b.sg_kill;
619 		*p++ = X29_LINE_DISPLAY;
620 		*p++ = (f & (RAW|CBREAK)) || ltc.t_rprntc & 0200 ?
621 			0 : ltc.t_rprntc;
622 	}
623 	return (p - pibuf);
624 }
625 
626 /*
627  * Process Q BIT (control) packets from the net.
628  * The only message that we are interested in are
629  * those indicating output is being discarded.
630  */
631 
632 x29_qbit(n)
633 {
634 	register char *p;
635 
636 	switch (fibuf[1]) {
637 	case X29_SET_PARMS:
638 	case X29_SET_AND_READ_PARMS:
639 	case X29_PARAMETER_INDICATION:
640 	case X29_INDICATION_OF_BREAK:
641 		for(p = &fibuf[2]; p < fibuf+n; p++) {
642 			if(*p == X29_TRANSMISSION_SPEED_CODE) {
643 				static char speeds[] = {
644 					B110, B0, B300, B1200, B600,
645 					B0, B0, B0, B0, B0, B0, B0,
646 					B2400, B4800, B9600, EXTA };
647 				struct sgttyb b;
648 
649 				if(*++p >= 0 && *p < sizeof(speeds)) {
650 					ioctl(pty, TIOCGETP, (char *)&b);
651 					b.sg_ispeed = b.sg_ospeed = speeds[*p];
652 					ioctl(pty, TIOCSETN, (char *)&b);
653 				}
654 			} else if(*p == X29_DISCARD_OUTPUT_CODE && *++p != 0) {
655 				char message[4];
656 
657 				/*
658 				 * Always re-enable normal output
659 				 */
660 				message[0] = Q_BIT;
661 				message[1] = X29_SET_PARMS;
662 				message[2] = X29_DISCARD_OUTPUT_CODE;
663 				message[3] = 0;
664 				(void) write(net, message, sizeof(message));
665 				if(tracefn)
666 					x29d_trace("netwrite", message, 4);
667 			}
668 		}
669 		return;
670 
671 	default: {
672 			register char *p2;
673 			char buf[BUFSIZ*4];
674 			static int fd;
675 
676 			/*
677 			 * Bad news - we received an x29 error message or
678 			 * some other unknown packet.  Dump the contents
679 			 * of the packet on the console.
680 			 */
681 			p = buf;
682 			for(p2 = "x29d: unknown q-bit packet: "; *p++ = *p2++; );
683 			for(p2 = fibuf+1; p2 < fibuf+n; p2++)
684 				if(*p2 >= ' ' && *p2 < 0177)
685 					*p++ = *p2;
686 				else {
687 					*p++ = '\\';
688 					*p++ = ((*p2 & 0300) >> 6) + '0';
689 					*p++ = ((*p2 & 070) >> 3) + '0';
690 					*p++ = (*p2 & 07) + '0';
691 				}
692 			*p++ = '\n';
693 			if(fd <= 0)
694 				fd = open(console, 1);
695 			(void) write(fd, buf, p-buf);
696 		}
697 	}
698 }
699 
700 /*
701  * HACK!
702  * This program does not use or need any stdio routines.
703  * Defining this procedure prevents all of the stdio
704  * code from being loaded.
705  */
706 
707 exit(code)
708 {
709 	_exit(code);
710 }
711 
712 #include <utmp.h>
713 
714 struct	utmp wtmp;
715 char	wtmpf[]	= "/usr/adm/wtmp";
716 char	utmp[] = "/etc/utmp";
717 #define SCPYN(a, b)	strncpy(a, b, sizeof (a))
718 #define SCMPN(a, b)	strncmp(a, b, sizeof (a))
719 
720 rmut()
721 {
722 	register int f, found = 0;
723 
724 	f = open(utmp, 2);
725 	if (f >= 0) {
726 		while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
727 			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
728 				continue;
729 			(void) lseek(f, -(long)sizeof (wtmp), 1);
730 			SCPYN(wtmp.ut_name, "");
731 			SCPYN(wtmp.ut_host, "");
732 			time(&wtmp.ut_time);
733 			(void) write(f, (char *)&wtmp, sizeof (wtmp));
734 			found++;
735 		}
736 		close(f);
737 	}
738 	if (found) {
739 		f = open(wtmpf, 1);
740 		if (f >= 0) {
741 			SCPYN(wtmp.ut_line, line+5);
742 			SCPYN(wtmp.ut_name, "");
743 			SCPYN(wtmp.ut_host, "");
744 			time(&wtmp.ut_time);
745 			(void) lseek(f, (long)0, 2);
746 			(void) write(f, (char *)&wtmp, sizeof (wtmp));
747 			close(f);
748 		}
749 	}
750 	chmod(line, 0666);
751 	chown(line, 0, 0);
752 	line[strlen("/dev/")] = 'p';
753 	chmod(line, 0666);
754 	chown(line, 0, 0);
755 }
756 
757 x29d_trace(s, bp, n)
758 char *s, *bp;
759 {
760 	static int fd;
761 	char buf[BUFSIZ*4];
762 	register char *p1, *p2;
763 
764 	for(p1 = buf; *s; *p1++ = *s++);
765 	*p1++ = ':';
766 	*p1++ = ' ';
767 	for(p2=bp; p2 < bp+n; p2++)
768 		if(*p2 >= ' ' && *p2 < 0177)
769 			*p1++ = *p2;
770 		else {
771 			*p1++ = '\\';
772 			*p1++ = ((*p2 & 0300) >> 6) + '0';
773 			*p1++ = ((*p2 & 070) >> 3) + '0';
774 			*p1++ = (*p2 & 07) + '0';
775 		}
776 	*p1++ = '\n';
777 	if(fd <= 0)
778 		fd = creat(tracefn, 0666);
779 	(void) write(fd, buf, p1-buf);
780 }
781