xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 17583)
16295Sroot #ifndef lint
2*17583Ssam static	char sccsid[] = "@(#)telnetd.c	4.31 (Berkeley) 12/23/84";
36295Sroot #endif
46295Sroot 
56002Sroot /*
66002Sroot  * Stripped-down telnet server.
76002Sroot  */
89218Ssam #include <sys/types.h>
99218Ssam #include <sys/socket.h>
1013608Ssam #include <sys/wait.h>
11*17583Ssam #include <sys/file.h>
129218Ssam 
139218Ssam #include <netinet/in.h>
149218Ssam 
1512216Ssam #include <arpa/telnet.h>
1612216Ssam 
176002Sroot #include <stdio.h>
186002Sroot #include <signal.h>
196002Sroot #include <errno.h>
206002Sroot #include <sgtty.h>
218346Ssam #include <netdb.h>
2217187Sralph #include <syslog.h>
239218Ssam 
2413798Ssam #define	BELL	'\07'
2513798Ssam #define BANNER	"\r\n\r\n4.2 BSD UNIX (%s)\r\n\r\r\n\r%s"
266002Sroot 
276002Sroot char	hisopts[256];
286002Sroot char	myopts[256];
296002Sroot 
306002Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
316002Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
326002Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
336002Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
346002Sroot 
356002Sroot /*
366002Sroot  * I/O data buffers, pointers, and counters.
376002Sroot  */
386002Sroot char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
396002Sroot char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
406002Sroot char	netibuf[BUFSIZ], *netip = netibuf;
416388Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
426002Sroot int	pcc, ncc;
436002Sroot 
446002Sroot int	pty, net;
456002Sroot int	inter;
4613799Ssam extern	char **environ;
476002Sroot extern	int errno;
486002Sroot char	line[] = "/dev/ptyp0";
496002Sroot 
506002Sroot main(argc, argv)
516002Sroot 	char *argv[];
526002Sroot {
5316371Skarels 	struct sockaddr_in from;
5417156Ssam 	int on = 1, fromlen;
556002Sroot 
5616371Skarels 	fromlen = sizeof (from);
5716371Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
5816371Skarels 		fprintf(stderr, "%s: ", argv[0]);
5916371Skarels 		perror("getpeername");
6016371Skarels 		_exit(1);
618346Ssam 	}
6217156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
6317187Sralph 		openlog(argv[0], LOG_PID, 0);
6417187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
6510418Ssam 	}
6616371Skarels 	doit(0, &from);
676002Sroot }
686002Sroot 
6913799Ssam char	*envinit[] = { "TERM=network", 0 };
706002Sroot int	cleanup();
716002Sroot 
726002Sroot /*
736002Sroot  * Get a pty, scan input lines.
746002Sroot  */
7512683Ssam doit(f, who)
7612683Ssam 	int f;
7712683Ssam 	struct sockaddr_in *who;
786002Sroot {
7917444Sralph 	char *cp = line, *host, *inet_ntoa();
80*17583Ssam 	int i, p, t;
816002Sroot 	struct sgttyb b;
8212683Ssam 	struct hostent *hp;
83*17583Ssam 	char *pqrs;
846002Sroot 
85*17583Ssam 	t = strlen("/dev/ptyp");
86*17583Ssam 	for (pqrs = "pqrs"; *pqrs; pqrs++) {
87*17583Ssam 		cp[t] = *pqrs;
88*17583Ssam 		for (i = 0; i < 16; i++) {
89*17583Ssam 			cp[t] = "0123456789abcdef"[i];
90*17583Ssam 			p = open(cp, O_RDWR);
91*17583Ssam 			if (p > 0)
92*17583Ssam 				goto gotpty;
93*17583Ssam 		}
946002Sroot 	}
959244Ssam 	fatal(f, "All network ports in use");
969244Ssam 	/*NOTREACHED*/
976002Sroot gotpty:
986002Sroot 	dup2(f, 0);
996002Sroot 	cp[strlen("/dev/")] = 't';
100*17583Ssam 	t = open("/dev/tty", O_RDWR);
1016002Sroot 	if (t >= 0) {
1026002Sroot 		ioctl(t, TIOCNOTTY, 0);
1036002Sroot 		close(t);
1046002Sroot 	}
105*17583Ssam 	t = open(cp, O_RDWR);
1069244Ssam 	if (t < 0)
1079244Ssam 		fatalperror(f, cp, errno);
1086002Sroot 	ioctl(t, TIOCGETP, &b);
1096388Ssam 	b.sg_flags = CRMOD|XTABS|ANYP;
1106002Sroot 	ioctl(t, TIOCSETP, &b);
1116388Ssam 	ioctl(p, TIOCGETP, &b);
1128379Ssam 	b.sg_flags &= ~ECHO;
1136388Ssam 	ioctl(p, TIOCSETP, &b);
11412683Ssam 	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
11512683Ssam 		who->sin_family);
11612683Ssam 	if (hp)
11712683Ssam 		host = hp->h_name;
11812683Ssam 	else
11917444Sralph 		host = inet_ntoa(who->sin_addr);
1209244Ssam 	if ((i = fork()) < 0)
1219244Ssam 		fatalperror(f, "fork", errno);
1226002Sroot 	if (i)
1236002Sroot 		telnet(f, p);
1246002Sroot 	close(f);
1256002Sroot 	close(p);
1266002Sroot 	dup2(t, 0);
1276002Sroot 	dup2(t, 1);
1286002Sroot 	dup2(t, 2);
1296002Sroot 	close(t);
13013799Ssam 	environ = envinit;
13112713Ssam 	execl("/bin/login", "login", "-h", host, 0);
1329244Ssam 	fatalperror(f, "/bin/login", errno);
1339244Ssam 	/*NOTREACHED*/
1349244Ssam }
1359244Ssam 
1369244Ssam fatal(f, msg)
1379244Ssam 	int f;
1389244Ssam 	char *msg;
1399244Ssam {
1409244Ssam 	char buf[BUFSIZ];
1419244Ssam 
142*17583Ssam 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
1439244Ssam 	(void) write(f, buf, strlen(buf));
1446002Sroot 	exit(1);
1456002Sroot }
1466002Sroot 
1479244Ssam fatalperror(f, msg, errno)
1489244Ssam 	int f;
1499244Ssam 	char *msg;
1509244Ssam 	int errno;
1519244Ssam {
1529244Ssam 	char buf[BUFSIZ];
1539244Ssam 	extern char *sys_errlist[];
1549244Ssam 
155*17583Ssam 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
1569244Ssam 	fatal(f, buf);
1579244Ssam }
1589244Ssam 
1596002Sroot /*
1606002Sroot  * Main loop.  Select from pty and network, and
1616002Sroot  * hand data to telnet receiver finite state machine.
1626002Sroot  */
1636002Sroot telnet(f, p)
1646002Sroot {
1656002Sroot 	int on = 1;
16612713Ssam 	char hostname[32];
1676002Sroot 
1686002Sroot 	net = f, pty = p;
1696002Sroot 	ioctl(f, FIONBIO, &on);
1706002Sroot 	ioctl(p, FIONBIO, &on);
1716002Sroot 	signal(SIGTSTP, SIG_IGN);
17213028Ssam 	signal(SIGCHLD, cleanup);
1736002Sroot 
1748379Ssam 	/*
1758379Ssam 	 * Request to do remote echo.
1768379Ssam 	 */
1778379Ssam 	dooption(TELOPT_ECHO);
1788379Ssam 	myopts[TELOPT_ECHO] = 1;
17912713Ssam 	/*
18012713Ssam 	 * Show banner that getty never gave.
18112713Ssam 	 */
18212713Ssam 	gethostname(hostname, sizeof (hostname));
18312713Ssam 	sprintf(nfrontp, BANNER, hostname, "");
18412713Ssam 	nfrontp += strlen(nfrontp);
1856002Sroot 	for (;;) {
1866002Sroot 		int ibits = 0, obits = 0;
1876002Sroot 		register int c;
1886002Sroot 
1896002Sroot 		/*
1906002Sroot 		 * Never look for input if there's still
1916002Sroot 		 * stuff in the corresponding output buffer
1926002Sroot 		 */
193*17583Ssam 		if (nfrontp - nbackp || pcc > 0)
1946002Sroot 			obits |= (1 << f);
1956002Sroot 		else
1966002Sroot 			ibits |= (1 << p);
197*17583Ssam 		if (pfrontp - pbackp || ncc > 0)
1986002Sroot 			obits |= (1 << p);
1996002Sroot 		else
2006002Sroot 			ibits |= (1 << f);
2016002Sroot 		if (ncc < 0 && pcc < 0)
2026002Sroot 			break;
2039218Ssam 		select(16, &ibits, &obits, 0, 0);
2046002Sroot 		if (ibits == 0 && obits == 0) {
2056002Sroot 			sleep(5);
2066002Sroot 			continue;
2076002Sroot 		}
2086002Sroot 
2096002Sroot 		/*
2106002Sroot 		 * Something to read from the network...
2116002Sroot 		 */
2126002Sroot 		if (ibits & (1 << f)) {
2136002Sroot 			ncc = read(f, netibuf, BUFSIZ);
2146002Sroot 			if (ncc < 0 && errno == EWOULDBLOCK)
2156002Sroot 				ncc = 0;
2166002Sroot 			else {
2176002Sroot 				if (ncc <= 0)
2186002Sroot 					break;
2196002Sroot 				netip = netibuf;
2206002Sroot 			}
2216002Sroot 		}
2226002Sroot 
2236002Sroot 		/*
2246002Sroot 		 * Something to read from the pty...
2256002Sroot 		 */
2266002Sroot 		if (ibits & (1 << p)) {
2276002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
2286002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
2296002Sroot 				pcc = 0;
2306002Sroot 			else {
2316002Sroot 				if (pcc <= 0)
2326002Sroot 					break;
2336002Sroot 				ptyip = ptyibuf;
2346002Sroot 			}
2356002Sroot 		}
2366002Sroot 
2376002Sroot 		while (pcc > 0) {
2386002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
2396002Sroot 				break;
2406002Sroot 			c = *ptyip++ & 0377, pcc--;
2416002Sroot 			if (c == IAC)
2426002Sroot 				*nfrontp++ = c;
2436002Sroot 			*nfrontp++ = c;
2446002Sroot 		}
2456002Sroot 		if ((obits & (1 << f)) && (nfrontp - nbackp) > 0)
2466002Sroot 			netflush();
2476002Sroot 		if (ncc > 0)
2486002Sroot 			telrcv();
2496002Sroot 		if ((obits & (1 << p)) && (pfrontp - pbackp) > 0)
2506002Sroot 			ptyflush();
2516002Sroot 	}
2526002Sroot 	cleanup();
2536002Sroot }
2546002Sroot 
2556002Sroot /*
2566002Sroot  * State for recv fsm
2576002Sroot  */
2586002Sroot #define	TS_DATA		0	/* base state */
2596002Sroot #define	TS_IAC		1	/* look for double IAC's */
2606002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
2616002Sroot #define	TS_BEGINNEG	3	/* throw away begin's... */
2626002Sroot #define	TS_ENDNEG	4	/* ...end's (suboption negotiation) */
2636002Sroot #define	TS_WILL		5	/* will option negotiation */
2646002Sroot #define	TS_WONT		6	/* wont " */
2656002Sroot #define	TS_DO		7	/* do " */
2666002Sroot #define	TS_DONT		8	/* dont " */
2676002Sroot 
2686002Sroot telrcv()
2696002Sroot {
2706002Sroot 	register int c;
2716002Sroot 	static int state = TS_DATA;
2726002Sroot 	struct sgttyb b;
2736002Sroot 
2746002Sroot 	while (ncc > 0) {
2756002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
2766002Sroot 			return;
2776002Sroot 		c = *netip++ & 0377, ncc--;
2786002Sroot 		switch (state) {
2796002Sroot 
2806002Sroot 		case TS_DATA:
2816002Sroot 			if (c == IAC) {
2826002Sroot 				state = TS_IAC;
2836002Sroot 				break;
2846002Sroot 			}
2856002Sroot 			if (inter > 0)
2866002Sroot 				break;
2876002Sroot 			*pfrontp++ = c;
2886002Sroot 			if (!myopts[TELOPT_BINARY] && c == '\r')
2896002Sroot 				state = TS_CR;
2906002Sroot 			break;
2916002Sroot 
2926002Sroot 		case TS_CR:
2936002Sroot 			if (c && c != '\n')
2946002Sroot 				*pfrontp++ = c;
2956002Sroot 			state = TS_DATA;
2966002Sroot 			break;
2976002Sroot 
2986002Sroot 		case TS_IAC:
2996002Sroot 			switch (c) {
3006002Sroot 
3016002Sroot 			/*
3026002Sroot 			 * Send the process on the pty side an
3036002Sroot 			 * interrupt.  Do this with a NULL or
3046002Sroot 			 * interrupt char; depending on the tty mode.
3056002Sroot 			 */
3066002Sroot 			case BREAK:
3076002Sroot 			case IP:
3086002Sroot 				interrupt();
3096002Sroot 				break;
3106002Sroot 
3116002Sroot 			/*
3126002Sroot 			 * Are You There?
3136002Sroot 			 */
3146002Sroot 			case AYT:
315*17583Ssam 				strcpy(nfrontp, "\r\n[Yes]\r\n");
316*17583Ssam 				nfrontp += 9;
3176002Sroot 				break;
3186002Sroot 
3196002Sroot 			/*
3206002Sroot 			 * Erase Character and
3216002Sroot 			 * Erase Line
3226002Sroot 			 */
3236002Sroot 			case EC:
3246002Sroot 			case EL:
3256002Sroot 				ptyflush();	/* half-hearted */
3266002Sroot 				ioctl(pty, TIOCGETP, &b);
3276002Sroot 				*pfrontp++ = (c == EC) ?
3286002Sroot 					b.sg_erase : b.sg_kill;
3296002Sroot 				break;
3306002Sroot 
3316002Sroot 			/*
3326002Sroot 			 * Check for urgent data...
3336002Sroot 			 */
3346002Sroot 			case DM:
3356002Sroot 				break;
3366002Sroot 
3376002Sroot 			/*
3386002Sroot 			 * Begin option subnegotiation...
3396002Sroot 			 */
3406002Sroot 			case SB:
3416002Sroot 				state = TS_BEGINNEG;
3426002Sroot 				continue;
3436002Sroot 
3446002Sroot 			case WILL:
3456002Sroot 			case WONT:
3466002Sroot 			case DO:
3476002Sroot 			case DONT:
3486002Sroot 				state = TS_WILL + (c - WILL);
3496002Sroot 				continue;
3506002Sroot 
3516002Sroot 			case IAC:
3526002Sroot 				*pfrontp++ = c;
3536002Sroot 				break;
3546002Sroot 			}
3556002Sroot 			state = TS_DATA;
3566002Sroot 			break;
3576002Sroot 
3586002Sroot 		case TS_BEGINNEG:
3596002Sroot 			if (c == IAC)
3606002Sroot 				state = TS_ENDNEG;
3616002Sroot 			break;
3626002Sroot 
3636002Sroot 		case TS_ENDNEG:
3646002Sroot 			state = c == SE ? TS_DATA : TS_BEGINNEG;
3656002Sroot 			break;
3666002Sroot 
3676002Sroot 		case TS_WILL:
3686002Sroot 			if (!hisopts[c])
3696002Sroot 				willoption(c);
3706002Sroot 			state = TS_DATA;
3716002Sroot 			continue;
3726002Sroot 
3736002Sroot 		case TS_WONT:
3746002Sroot 			if (hisopts[c])
3756002Sroot 				wontoption(c);
3766002Sroot 			state = TS_DATA;
3776002Sroot 			continue;
3786002Sroot 
3796002Sroot 		case TS_DO:
3806002Sroot 			if (!myopts[c])
3816002Sroot 				dooption(c);
3826002Sroot 			state = TS_DATA;
3836002Sroot 			continue;
3846002Sroot 
3856002Sroot 		case TS_DONT:
3866002Sroot 			if (myopts[c]) {
3876002Sroot 				myopts[c] = 0;
3886002Sroot 				sprintf(nfrontp, wont, c);
3898379Ssam 				nfrontp += sizeof (wont) - 2;
3906002Sroot 			}
3916002Sroot 			state = TS_DATA;
3926002Sroot 			continue;
3936002Sroot 
3946002Sroot 		default:
3959218Ssam 			printf("telnetd: panic state=%d\n", state);
3966002Sroot 			exit(1);
3976002Sroot 		}
3986002Sroot 	}
3996002Sroot }
4006002Sroot 
4016002Sroot willoption(option)
4026002Sroot 	int option;
4036002Sroot {
4046002Sroot 	char *fmt;
4056002Sroot 
4066002Sroot 	switch (option) {
4076002Sroot 
4086002Sroot 	case TELOPT_BINARY:
4096002Sroot 		mode(RAW, 0);
4106002Sroot 		goto common;
4116002Sroot 
4126002Sroot 	case TELOPT_ECHO:
4136002Sroot 		mode(0, ECHO|CRMOD);
4146002Sroot 		/*FALL THRU*/
4156002Sroot 
4166002Sroot 	case TELOPT_SGA:
4176002Sroot 	common:
4186002Sroot 		hisopts[option] = 1;
4196002Sroot 		fmt = doopt;
4206002Sroot 		break;
4216002Sroot 
4226002Sroot 	case TELOPT_TM:
4236002Sroot 		fmt = dont;
4246002Sroot 		break;
4256002Sroot 
4266002Sroot 	default:
4276002Sroot 		fmt = dont;
4286002Sroot 		break;
4296002Sroot 	}
4306023Ssam 	sprintf(nfrontp, fmt, option);
4318379Ssam 	nfrontp += sizeof (dont) - 2;
4326002Sroot }
4336002Sroot 
4346002Sroot wontoption(option)
4356002Sroot 	int option;
4366002Sroot {
4376002Sroot 	char *fmt;
4386002Sroot 
4396002Sroot 	switch (option) {
4406002Sroot 
4416002Sroot 	case TELOPT_ECHO:
4426002Sroot 		mode(ECHO|CRMOD, 0);
4436002Sroot 		goto common;
4446002Sroot 
4456002Sroot 	case TELOPT_BINARY:
4466002Sroot 		mode(0, RAW);
4476002Sroot 		/*FALL THRU*/
4486002Sroot 
4496002Sroot 	case TELOPT_SGA:
4506002Sroot 	common:
4516002Sroot 		hisopts[option] = 0;
4526002Sroot 		fmt = dont;
4536002Sroot 		break;
4546002Sroot 
4556002Sroot 	default:
4566002Sroot 		fmt = dont;
4576002Sroot 	}
4586002Sroot 	sprintf(nfrontp, fmt, option);
4598379Ssam 	nfrontp += sizeof (doopt) - 2;
4606002Sroot }
4616002Sroot 
4626002Sroot dooption(option)
4636002Sroot 	int option;
4646002Sroot {
4656002Sroot 	char *fmt;
4666002Sroot 
4676002Sroot 	switch (option) {
4686002Sroot 
4696002Sroot 	case TELOPT_TM:
4706002Sroot 		fmt = wont;
4716002Sroot 		break;
4726002Sroot 
4736002Sroot 	case TELOPT_ECHO:
4746002Sroot 		mode(ECHO|CRMOD, 0);
4756002Sroot 		goto common;
4766002Sroot 
4776002Sroot 	case TELOPT_BINARY:
4786002Sroot 		mode(RAW, 0);
4796002Sroot 		/*FALL THRU*/
4806002Sroot 
4816002Sroot 	case TELOPT_SGA:
4826002Sroot 	common:
4836002Sroot 		fmt = will;
4846002Sroot 		break;
4856002Sroot 
4866002Sroot 	default:
4876002Sroot 		fmt = wont;
4886002Sroot 		break;
4896002Sroot 	}
4906002Sroot 	sprintf(nfrontp, fmt, option);
4918379Ssam 	nfrontp += sizeof (doopt) - 2;
4926002Sroot }
4936002Sroot 
4946002Sroot mode(on, off)
4956002Sroot 	int on, off;
4966002Sroot {
4976002Sroot 	struct sgttyb b;
4986002Sroot 
4996002Sroot 	ptyflush();
5006002Sroot 	ioctl(pty, TIOCGETP, &b);
5016002Sroot 	b.sg_flags |= on;
5026002Sroot 	b.sg_flags &= ~off;
5036002Sroot 	ioctl(pty, TIOCSETP, &b);
5046002Sroot }
5056002Sroot 
5066002Sroot /*
5076002Sroot  * Send interrupt to process on other side of pty.
5086002Sroot  * If it is in raw mode, just write NULL;
5096002Sroot  * otherwise, write intr char.
5106002Sroot  */
5116002Sroot interrupt()
5126002Sroot {
5136002Sroot 	struct sgttyb b;
5146002Sroot 	struct tchars tchars;
5156002Sroot 
5166002Sroot 	ptyflush();	/* half-hearted */
5176002Sroot 	ioctl(pty, TIOCGETP, &b);
5186002Sroot 	if (b.sg_flags & RAW) {
5196002Sroot 		*pfrontp++ = '\0';
5206002Sroot 		return;
5216002Sroot 	}
5226002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
5236002Sroot 		'\177' : tchars.t_intrc;
5246002Sroot }
5256002Sroot 
5266002Sroot ptyflush()
5276002Sroot {
5286002Sroot 	int n;
5296002Sroot 
5306002Sroot 	if ((n = pfrontp - pbackp) > 0)
5316002Sroot 		n = write(pty, pbackp, n);
5328346Ssam 	if (n < 0)
5338346Ssam 		return;
5346002Sroot 	pbackp += n;
5356002Sroot 	if (pbackp == pfrontp)
5366002Sroot 		pbackp = pfrontp = ptyobuf;
5376002Sroot }
5386002Sroot 
5396002Sroot netflush()
5406002Sroot {
5416002Sroot 	int n;
5426002Sroot 
5436002Sroot 	if ((n = nfrontp - nbackp) > 0)
5446002Sroot 		n = write(net, nbackp, n);
5458346Ssam 	if (n < 0) {
5468346Ssam 		if (errno == EWOULDBLOCK)
5478346Ssam 			return;
5488346Ssam 		/* should blow this guy away... */
5498346Ssam 		return;
5508346Ssam 	}
5516002Sroot 	nbackp += n;
5526002Sroot 	if (nbackp == nfrontp)
5536002Sroot 		nbackp = nfrontp = netobuf;
5546002Sroot }
5556002Sroot 
5566002Sroot cleanup()
5576002Sroot {
5586002Sroot 
5596002Sroot 	rmut();
56010008Ssam 	vhangup();	/* XXX */
56110191Ssam 	shutdown(net, 2);
5626002Sroot 	kill(0, SIGKILL);
5636002Sroot 	exit(1);
5646002Sroot }
5656002Sroot 
5666002Sroot #include <utmp.h>
5676002Sroot 
5686002Sroot struct	utmp wtmp;
5696002Sroot char	wtmpf[]	= "/usr/adm/wtmp";
5706002Sroot char	utmp[] = "/etc/utmp";
5718379Ssam #define SCPYN(a, b)	strncpy(a, b, sizeof (a))
5728379Ssam #define SCMPN(a, b)	strncmp(a, b, sizeof (a))
5736002Sroot 
5746002Sroot rmut()
5756002Sroot {
5766002Sroot 	register f;
5776002Sroot 	int found = 0;
5786002Sroot 
579*17583Ssam 	f = open(utmp, O_RDWR);
5806002Sroot 	if (f >= 0) {
5818379Ssam 		while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
5826002Sroot 			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
5836002Sroot 				continue;
584*17583Ssam 			lseek(f, -(long)sizeof (wtmp), L_INCR);
5856002Sroot 			SCPYN(wtmp.ut_name, "");
58612683Ssam 			SCPYN(wtmp.ut_host, "");
5876002Sroot 			time(&wtmp.ut_time);
5888379Ssam 			write(f, (char *)&wtmp, sizeof (wtmp));
5896002Sroot 			found++;
5906002Sroot 		}
5916002Sroot 		close(f);
5926002Sroot 	}
5936002Sroot 	if (found) {
594*17583Ssam 		f = open(wtmpf, O_WRONLY|O_APPEND);
5956002Sroot 		if (f >= 0) {
5966002Sroot 			SCPYN(wtmp.ut_line, line+5);
5976002Sroot 			SCPYN(wtmp.ut_name, "");
59812683Ssam 			SCPYN(wtmp.ut_host, "");
5996002Sroot 			time(&wtmp.ut_time);
6008379Ssam 			write(f, (char *)&wtmp, sizeof (wtmp));
6016002Sroot 			close(f);
6026002Sroot 		}
6036002Sroot 	}
6046002Sroot 	chmod(line, 0666);
6056002Sroot 	chown(line, 0, 0);
6066002Sroot 	line[strlen("/dev/")] = 'p';
6076002Sroot 	chmod(line, 0666);
6086002Sroot 	chown(line, 0, 0);
6096002Sroot }
610