xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 6002)
1*6002Sroot /*	telnetd.c	4.1	82/02/28	*/
2*6002Sroot 
3*6002Sroot /*
4*6002Sroot  * Stripped-down telnet server.
5*6002Sroot  */
6*6002Sroot #include <stdio.h>
7*6002Sroot #include <signal.h>
8*6002Sroot #include <errno.h>
9*6002Sroot #include <sgtty.h>
10*6002Sroot #include <wait.h>
11*6002Sroot #include <sys/types.h>
12*6002Sroot #include <sys/socket.h>
13*6002Sroot #include <net/in.h>
14*6002Sroot #include "telnet.h"
15*6002Sroot 
16*6002Sroot #define	INFINITY	10000000
17*6002Sroot #define	BELL		'\07'
18*6002Sroot #define	swab(x)		((((x) >> 8) | ((x) << 8)) & 0xffff)
19*6002Sroot 
20*6002Sroot char	hisopts[256];
21*6002Sroot char	myopts[256];
22*6002Sroot 
23*6002Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
24*6002Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
25*6002Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
26*6002Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
27*6002Sroot 
28*6002Sroot /*
29*6002Sroot  * I/O data buffers, pointers, and counters.
30*6002Sroot  */
31*6002Sroot char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
32*6002Sroot char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
33*6002Sroot char	netibuf[BUFSIZ], *netip = netibuf;
34*6002Sroot char	netobuf[BUFSIZ] =
35*6002Sroot 	{ IAC, DO, TELOPT_ECHO, '\r', '\n' },
36*6002Sroot 	*nfrontp = netobuf + 5, *nbackp = netobuf;
37*6002Sroot int	pcc, ncc;
38*6002Sroot 
39*6002Sroot int	pty, net;
40*6002Sroot int	inter;
41*6002Sroot extern	int errno;
42*6002Sroot char	line[] = "/dev/ptyp0";
43*6002Sroot 
44*6002Sroot struct	sockaddr_in sin = { AF_INET, swab(IPPORT_TELNET) };
45*6002Sroot int	options = SO_ACCEPTCONN;
46*6002Sroot 
47*6002Sroot main(argc, argv)
48*6002Sroot 	char *argv[];
49*6002Sroot {
50*6002Sroot 	int s, pid;
51*6002Sroot 	union wait status;
52*6002Sroot 
53*6002Sroot 	argc--, argv++;
54*6002Sroot 	if (argc > 0 && !strcmp(argv[0], "-d"))
55*6002Sroot 		options |= SO_DEBUG;
56*6002Sroot 	for (;;) {
57*6002Sroot 		errno = 0;
58*6002Sroot 		if ((s = socket(SOCK_STREAM, 0, &sin, options)) < 0) {
59*6002Sroot 			perror("socket");
60*6002Sroot 			sleep(5);
61*6002Sroot 			continue;
62*6002Sroot 		}
63*6002Sroot 		if (accept(s, 0) < 0) {
64*6002Sroot 			perror("accept");
65*6002Sroot 			close(s);
66*6002Sroot 			sleep(1);
67*6002Sroot 			continue;
68*6002Sroot 		}
69*6002Sroot 		if ((pid = fork()) < 0)
70*6002Sroot 			printf("Out of processes\n");
71*6002Sroot 		else if (pid == 0)
72*6002Sroot 			doit(s);
73*6002Sroot 		close(s);
74*6002Sroot 		while (wait3(status, WNOHANG, 0) > 0)
75*6002Sroot 			continue;
76*6002Sroot 	}
77*6002Sroot 	/*NOTREACHED*/
78*6002Sroot }
79*6002Sroot 
80*6002Sroot int	cleanup();
81*6002Sroot 
82*6002Sroot /*
83*6002Sroot  * Get a pty, scan input lines.
84*6002Sroot  */
85*6002Sroot doit(f)
86*6002Sroot {
87*6002Sroot 	char *cp = line;
88*6002Sroot 	int i, p, cc, t;
89*6002Sroot 	struct sgttyb b;
90*6002Sroot 
91*6002Sroot 	for (i = 0; i < 16; i++) {
92*6002Sroot 		cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
93*6002Sroot 		p = open(cp, 2);
94*6002Sroot 		if (p > 0)
95*6002Sroot 			goto gotpty;
96*6002Sroot 	}
97*6002Sroot 	dup2(f, 1);
98*6002Sroot 	printf("All network ports in use.\n");
99*6002Sroot 	exit(1);
100*6002Sroot gotpty:
101*6002Sroot 	dup2(f, 0);
102*6002Sroot 	cp[strlen("/dev/")] = 't';
103*6002Sroot 	t = open("/dev/tty", 2);
104*6002Sroot 	if (t >= 0) {
105*6002Sroot 		ioctl(t, TIOCNOTTY, 0);
106*6002Sroot 		close(t);
107*6002Sroot 	}
108*6002Sroot 	t = open(cp, 2);
109*6002Sroot 	if (t < 0) {
110*6002Sroot 		dup2(f, 2);
111*6002Sroot 		perror(cp);
112*6002Sroot 		exit(1);
113*6002Sroot 	}
114*6002Sroot 	ioctl(t, TIOCGETP, &b);
115*6002Sroot 	b.sg_flags = ECHO|CRMOD|XTABS|ANYP;
116*6002Sroot 	ioctl(t, TIOCSETP, &b);
117*6002Sroot 	if ((i = fork()) < 0) {
118*6002Sroot 		dup2(f, 2);
119*6002Sroot 		perror("fork");
120*6002Sroot 		exit(1);
121*6002Sroot 	}
122*6002Sroot 	if (i)
123*6002Sroot 		telnet(f, p);
124*6002Sroot 	close(f);
125*6002Sroot 	close(p);
126*6002Sroot 	dup2(t, 0);
127*6002Sroot 	dup2(t, 1);
128*6002Sroot 	dup2(t, 2);
129*6002Sroot 	close(t);
130*6002Sroot 	execl("/bin/login", "telnet-login", 0);
131*6002Sroot 	perror("/bin/login");
132*6002Sroot 	exit(1);
133*6002Sroot }
134*6002Sroot 
135*6002Sroot /*
136*6002Sroot  * Main loop.  Select from pty and network, and
137*6002Sroot  * hand data to telnet receiver finite state machine.
138*6002Sroot  */
139*6002Sroot telnet(f, p)
140*6002Sroot {
141*6002Sroot 	int on = 1;
142*6002Sroot 
143*6002Sroot 	net = f, pty = p;
144*6002Sroot 	ioctl(f, FIONBIO, &on);
145*6002Sroot 	ioctl(p, FIONBIO, &on);
146*6002Sroot 	signal(SIGTSTP, SIG_IGN);
147*6002Sroot 	sigset(SIGCHLD, cleanup);
148*6002Sroot 
149*6002Sroot 	for (;;) {
150*6002Sroot 		int ibits = 0, obits = 0;
151*6002Sroot 		register int c;
152*6002Sroot 
153*6002Sroot 		/*
154*6002Sroot 		 * Never look for input if there's still
155*6002Sroot 		 * stuff in the corresponding output buffer
156*6002Sroot 		 */
157*6002Sroot 		if (nfrontp - nbackp)
158*6002Sroot 			obits |= (1 << f);
159*6002Sroot 		else
160*6002Sroot 			ibits |= (1 << p);
161*6002Sroot 		if (pfrontp - pbackp)
162*6002Sroot 			obits |= (1 << p);
163*6002Sroot 		else
164*6002Sroot 			ibits |= (1 << f);
165*6002Sroot 		if (ncc < 0 && pcc < 0)
166*6002Sroot 			break;
167*6002Sroot 		select(32, &ibits, &obits, INFINITY);
168*6002Sroot 		if (ibits == 0 && obits == 0) {
169*6002Sroot 			sleep(5);
170*6002Sroot 			continue;
171*6002Sroot 		}
172*6002Sroot 
173*6002Sroot 		/*
174*6002Sroot 		 * Something to read from the network...
175*6002Sroot 		 */
176*6002Sroot 		if (ibits & (1 << f)) {
177*6002Sroot 			ncc = read(f, netibuf, BUFSIZ);
178*6002Sroot 			if (ncc < 0 && errno == EWOULDBLOCK)
179*6002Sroot 				ncc = 0;
180*6002Sroot 			else {
181*6002Sroot 				if (ncc <= 0)
182*6002Sroot 					break;
183*6002Sroot 				netip = netibuf;
184*6002Sroot 			}
185*6002Sroot 		}
186*6002Sroot 
187*6002Sroot 		/*
188*6002Sroot 		 * Something to read from the pty...
189*6002Sroot 		 */
190*6002Sroot 		if (ibits & (1 << p)) {
191*6002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
192*6002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
193*6002Sroot 				pcc = 0;
194*6002Sroot 			else {
195*6002Sroot 				if (pcc <= 0)
196*6002Sroot 					break;
197*6002Sroot 				ptyip = ptyibuf;
198*6002Sroot 			}
199*6002Sroot 		}
200*6002Sroot 
201*6002Sroot 		while (pcc > 0) {
202*6002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
203*6002Sroot 				break;
204*6002Sroot 			c = *ptyip++ & 0377, pcc--;
205*6002Sroot 			if (c == IAC)
206*6002Sroot 				*nfrontp++ = c;
207*6002Sroot 			*nfrontp++ = c;
208*6002Sroot 		}
209*6002Sroot 		if ((obits & (1 << f)) && (nfrontp - nbackp) > 0)
210*6002Sroot 			netflush();
211*6002Sroot 		if (ncc > 0)
212*6002Sroot 			telrcv();
213*6002Sroot 		if ((obits & (1 << p)) && (pfrontp - pbackp) > 0)
214*6002Sroot 			ptyflush();
215*6002Sroot 	}
216*6002Sroot 	cleanup();
217*6002Sroot }
218*6002Sroot 
219*6002Sroot /*
220*6002Sroot  * State for recv fsm
221*6002Sroot  */
222*6002Sroot #define	TS_DATA		0	/* base state */
223*6002Sroot #define	TS_IAC		1	/* look for double IAC's */
224*6002Sroot #define	TS_CR		2	/* CR-LF ->'s CR */
225*6002Sroot #define	TS_BEGINNEG	3	/* throw away begin's... */
226*6002Sroot #define	TS_ENDNEG	4	/* ...end's (suboption negotiation) */
227*6002Sroot #define	TS_WILL		5	/* will option negotiation */
228*6002Sroot #define	TS_WONT		6	/* wont " */
229*6002Sroot #define	TS_DO		7	/* do " */
230*6002Sroot #define	TS_DONT		8	/* dont " */
231*6002Sroot 
232*6002Sroot telrcv()
233*6002Sroot {
234*6002Sroot 	register int c;
235*6002Sroot 	static int state = TS_DATA;
236*6002Sroot 	struct sgttyb b;
237*6002Sroot 
238*6002Sroot 	while (ncc > 0) {
239*6002Sroot 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
240*6002Sroot 			return;
241*6002Sroot 		c = *netip++ & 0377, ncc--;
242*6002Sroot 		switch (state) {
243*6002Sroot 
244*6002Sroot 		case TS_DATA:
245*6002Sroot 			if (c == IAC) {
246*6002Sroot 				state = TS_IAC;
247*6002Sroot 				break;
248*6002Sroot 			}
249*6002Sroot 			if (inter > 0)
250*6002Sroot 				break;
251*6002Sroot 			*pfrontp++ = c;
252*6002Sroot 			if (!myopts[TELOPT_BINARY] && c == '\r')
253*6002Sroot 				state = TS_CR;
254*6002Sroot 			break;
255*6002Sroot 
256*6002Sroot 		case TS_CR:
257*6002Sroot 			if (c && c != '\n')
258*6002Sroot 				*pfrontp++ = c;
259*6002Sroot 			state = TS_DATA;
260*6002Sroot 			break;
261*6002Sroot 
262*6002Sroot 		case TS_IAC:
263*6002Sroot 			switch (c) {
264*6002Sroot 
265*6002Sroot 			/*
266*6002Sroot 			 * Send the process on the pty side an
267*6002Sroot 			 * interrupt.  Do this with a NULL or
268*6002Sroot 			 * interrupt char; depending on the tty mode.
269*6002Sroot 			 */
270*6002Sroot 			case BREAK:
271*6002Sroot 			case IP:
272*6002Sroot 				interrupt();
273*6002Sroot 				break;
274*6002Sroot 
275*6002Sroot 			/*
276*6002Sroot 			 * Are You There?
277*6002Sroot 			 */
278*6002Sroot 			case AYT:
279*6002Sroot 				*pfrontp++ = BELL;
280*6002Sroot 				break;
281*6002Sroot 
282*6002Sroot 			/*
283*6002Sroot 			 * Erase Character and
284*6002Sroot 			 * Erase Line
285*6002Sroot 			 */
286*6002Sroot 			case EC:
287*6002Sroot 			case EL:
288*6002Sroot 				ptyflush();	/* half-hearted */
289*6002Sroot 				ioctl(pty, TIOCGETP, &b);
290*6002Sroot 				*pfrontp++ = (c == EC) ?
291*6002Sroot 					b.sg_erase : b.sg_kill;
292*6002Sroot 				break;
293*6002Sroot 
294*6002Sroot 			/*
295*6002Sroot 			 * Check for urgent data...
296*6002Sroot 			 */
297*6002Sroot 			case DM:
298*6002Sroot 				break;
299*6002Sroot 
300*6002Sroot 			/*
301*6002Sroot 			 * Begin option subnegotiation...
302*6002Sroot 			 */
303*6002Sroot 			case SB:
304*6002Sroot 				state = TS_BEGINNEG;
305*6002Sroot 				continue;
306*6002Sroot 
307*6002Sroot 			case WILL:
308*6002Sroot 			case WONT:
309*6002Sroot 			case DO:
310*6002Sroot 			case DONT:
311*6002Sroot 				state = TS_WILL + (c - WILL);
312*6002Sroot 				continue;
313*6002Sroot 
314*6002Sroot 			case IAC:
315*6002Sroot 				*pfrontp++ = c;
316*6002Sroot 				break;
317*6002Sroot 			}
318*6002Sroot 			state = TS_DATA;
319*6002Sroot 			break;
320*6002Sroot 
321*6002Sroot 		case TS_BEGINNEG:
322*6002Sroot 			if (c == IAC)
323*6002Sroot 				state = TS_ENDNEG;
324*6002Sroot 			break;
325*6002Sroot 
326*6002Sroot 		case TS_ENDNEG:
327*6002Sroot 			state = c == SE ? TS_DATA : TS_BEGINNEG;
328*6002Sroot 			break;
329*6002Sroot 
330*6002Sroot 		case TS_WILL:
331*6002Sroot 			if (!hisopts[c])
332*6002Sroot 				willoption(c);
333*6002Sroot 			state = TS_DATA;
334*6002Sroot 			continue;
335*6002Sroot 
336*6002Sroot 		case TS_WONT:
337*6002Sroot 			if (hisopts[c])
338*6002Sroot 				wontoption(c);
339*6002Sroot 			state = TS_DATA;
340*6002Sroot 			continue;
341*6002Sroot 
342*6002Sroot 		case TS_DO:
343*6002Sroot 			if (!myopts[c])
344*6002Sroot 				dooption(c);
345*6002Sroot 			state = TS_DATA;
346*6002Sroot 			continue;
347*6002Sroot 
348*6002Sroot 		case TS_DONT:
349*6002Sroot 			if (myopts[c]) {
350*6002Sroot 				myopts[c] = 0;
351*6002Sroot 				sprintf(nfrontp, wont, c);
352*6002Sroot 				nfrontp += sizeof(wont) - 2;
353*6002Sroot 			}
354*6002Sroot 			state = TS_DATA;
355*6002Sroot 			continue;
356*6002Sroot 
357*6002Sroot 		default:
358*6002Sroot 			printf("netser: panic state=%d\n", state);
359*6002Sroot 			exit(1);
360*6002Sroot 		}
361*6002Sroot 	}
362*6002Sroot }
363*6002Sroot 
364*6002Sroot willoption(option)
365*6002Sroot 	int option;
366*6002Sroot {
367*6002Sroot 	char *fmt;
368*6002Sroot 
369*6002Sroot 	switch (option) {
370*6002Sroot 
371*6002Sroot 	case TELOPT_BINARY:
372*6002Sroot 		mode(RAW, 0);
373*6002Sroot 		goto common;
374*6002Sroot 
375*6002Sroot 	case TELOPT_ECHO:
376*6002Sroot 		mode(0, ECHO|CRMOD);
377*6002Sroot 		/*FALL THRU*/
378*6002Sroot 
379*6002Sroot 	case TELOPT_SGA:
380*6002Sroot 	common:
381*6002Sroot 		hisopts[option] = 1;
382*6002Sroot 		fmt = doopt;
383*6002Sroot 		break;
384*6002Sroot 
385*6002Sroot 	case TELOPT_TM:
386*6002Sroot 		fmt = dont;
387*6002Sroot 		break;
388*6002Sroot 
389*6002Sroot 	default:
390*6002Sroot 		fmt = dont;
391*6002Sroot 		break;
392*6002Sroot 	}
393*6002Sroot 	sprintf(nfrontp, dont, option);
394*6002Sroot 	nfrontp += sizeof(dont) - 2;
395*6002Sroot }
396*6002Sroot 
397*6002Sroot wontoption(option)
398*6002Sroot 	int option;
399*6002Sroot {
400*6002Sroot 	char *fmt;
401*6002Sroot 
402*6002Sroot 	switch (option) {
403*6002Sroot 
404*6002Sroot 	case TELOPT_ECHO:
405*6002Sroot 		mode(ECHO|CRMOD, 0);
406*6002Sroot 		goto common;
407*6002Sroot 
408*6002Sroot 	case TELOPT_BINARY:
409*6002Sroot 		mode(0, RAW);
410*6002Sroot 		/*FALL THRU*/
411*6002Sroot 
412*6002Sroot 	case TELOPT_SGA:
413*6002Sroot 	common:
414*6002Sroot 		hisopts[option] = 0;
415*6002Sroot 		fmt = dont;
416*6002Sroot 		break;
417*6002Sroot 
418*6002Sroot 	default:
419*6002Sroot 		fmt = dont;
420*6002Sroot 	}
421*6002Sroot 	sprintf(nfrontp, fmt, option);
422*6002Sroot 	nfrontp += sizeof(doopt) - 2;
423*6002Sroot }
424*6002Sroot 
425*6002Sroot dooption(option)
426*6002Sroot 	int option;
427*6002Sroot {
428*6002Sroot 	char *fmt;
429*6002Sroot 
430*6002Sroot 	switch (option) {
431*6002Sroot 
432*6002Sroot 	case TELOPT_TM:
433*6002Sroot 		fmt = wont;
434*6002Sroot 		break;
435*6002Sroot 
436*6002Sroot 	case TELOPT_ECHO:
437*6002Sroot 		mode(ECHO|CRMOD, 0);
438*6002Sroot 		goto common;
439*6002Sroot 
440*6002Sroot 	case TELOPT_BINARY:
441*6002Sroot 		mode(RAW, 0);
442*6002Sroot 		/*FALL THRU*/
443*6002Sroot 
444*6002Sroot 	case TELOPT_SGA:
445*6002Sroot 	common:
446*6002Sroot 		fmt = will;
447*6002Sroot 		break;
448*6002Sroot 
449*6002Sroot 	default:
450*6002Sroot 		fmt = wont;
451*6002Sroot 		break;
452*6002Sroot 	}
453*6002Sroot 	sprintf(nfrontp, fmt, option);
454*6002Sroot 	nfrontp += sizeof(doopt) - 2;
455*6002Sroot }
456*6002Sroot 
457*6002Sroot mode(on, off)
458*6002Sroot 	int on, off;
459*6002Sroot {
460*6002Sroot 	struct sgttyb b;
461*6002Sroot 
462*6002Sroot 	ptyflush();
463*6002Sroot 	ioctl(pty, TIOCGETP, &b);
464*6002Sroot 	b.sg_flags |= on;
465*6002Sroot 	b.sg_flags &= ~off;
466*6002Sroot 	ioctl(pty, TIOCSETP, &b);
467*6002Sroot }
468*6002Sroot 
469*6002Sroot /*
470*6002Sroot  * Send interrupt to process on other side of pty.
471*6002Sroot  * If it is in raw mode, just write NULL;
472*6002Sroot  * otherwise, write intr char.
473*6002Sroot  */
474*6002Sroot interrupt()
475*6002Sroot {
476*6002Sroot 	struct sgttyb b;
477*6002Sroot 	struct tchars tchars;
478*6002Sroot 
479*6002Sroot 	ptyflush();	/* half-hearted */
480*6002Sroot 	ioctl(pty, TIOCGETP, &b);
481*6002Sroot 	if (b.sg_flags & RAW) {
482*6002Sroot 		*pfrontp++ = '\0';
483*6002Sroot 		return;
484*6002Sroot 	}
485*6002Sroot 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
486*6002Sroot 		'\177' : tchars.t_intrc;
487*6002Sroot }
488*6002Sroot 
489*6002Sroot ptyflush()
490*6002Sroot {
491*6002Sroot 	int n;
492*6002Sroot 
493*6002Sroot 	if ((n = pfrontp - pbackp) > 0)
494*6002Sroot 		n = write(pty, pbackp, n);
495*6002Sroot 	if (n < 0 && errno == EWOULDBLOCK)
496*6002Sroot 		n = 0;
497*6002Sroot 	pbackp += n;
498*6002Sroot 	if (pbackp == pfrontp)
499*6002Sroot 		pbackp = pfrontp = ptyobuf;
500*6002Sroot }
501*6002Sroot 
502*6002Sroot netflush()
503*6002Sroot {
504*6002Sroot 	int n;
505*6002Sroot 
506*6002Sroot 	if ((n = nfrontp - nbackp) > 0)
507*6002Sroot 		n = write(net, nbackp, n);
508*6002Sroot 	if (n < 0 && errno == EWOULDBLOCK)
509*6002Sroot 		n = 0;
510*6002Sroot 	nbackp += n;
511*6002Sroot 	if (nbackp == nfrontp)
512*6002Sroot 		nbackp = nfrontp = netobuf;
513*6002Sroot }
514*6002Sroot 
515*6002Sroot cleanup()
516*6002Sroot {
517*6002Sroot 	int how = 2;
518*6002Sroot 
519*6002Sroot 	rmut();
520*6002Sroot 	vhangup();
521*6002Sroot 	ioctl(net, SIOCDONE, &how);
522*6002Sroot 	kill(0, SIGKILL);
523*6002Sroot 	exit(1);
524*6002Sroot }
525*6002Sroot 
526*6002Sroot #include <utmp.h>
527*6002Sroot 
528*6002Sroot struct	utmp wtmp;
529*6002Sroot char	wtmpf[]	= "/usr/adm/wtmp";
530*6002Sroot char	utmp[] = "/etc/utmp";
531*6002Sroot #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
532*6002Sroot #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
533*6002Sroot 
534*6002Sroot rmut()
535*6002Sroot {
536*6002Sroot 	register f;
537*6002Sroot 	int found = 0;
538*6002Sroot 
539*6002Sroot 	f = open(utmp, 2);
540*6002Sroot 	if (f >= 0) {
541*6002Sroot 		while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
542*6002Sroot 			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
543*6002Sroot 				continue;
544*6002Sroot 			lseek(f, -(long)sizeof(wtmp), 1);
545*6002Sroot 			SCPYN(wtmp.ut_name, "");
546*6002Sroot 			time(&wtmp.ut_time);
547*6002Sroot 			write(f, (char *)&wtmp, sizeof(wtmp));
548*6002Sroot 			found++;
549*6002Sroot 		}
550*6002Sroot 		close(f);
551*6002Sroot 	}
552*6002Sroot 	if (found) {
553*6002Sroot 		f = open(wtmpf, 1);
554*6002Sroot 		if (f >= 0) {
555*6002Sroot 			SCPYN(wtmp.ut_line, line+5);
556*6002Sroot 			SCPYN(wtmp.ut_name, "");
557*6002Sroot 			time(&wtmp.ut_time);
558*6002Sroot 			lseek(f, (long)0, 2);
559*6002Sroot 			write(f, (char *)&wtmp, sizeof(wtmp));
560*6002Sroot 			close(f);
561*6002Sroot 		}
562*6002Sroot 	}
563*6002Sroot 	chmod(line, 0666);
564*6002Sroot 	chown(line, 0, 0);
565*6002Sroot 	line[strlen("/dev/")] = 'p';
566*6002Sroot 	chmod(line, 0666);
567*6002Sroot 	chown(line, 0, 0);
568*6002Sroot }
569