xref: /original-bsd/usr.bin/rlogin/rlogin.c (revision 23c6a147)
1 /*-
2  * Copyright (c) 1983, 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5 %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)rlogin.c	5.24 (Berkeley) 04/10/90";
16 #endif /* not lint */
17 
18 /*
19  * $Source: mit/rlogin/RCS/rlogin.c,v $
20  * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall Exp Locker: kfall $
21  */
22 
23 /*
24  * rlogin - remote login
25  */
26 #include <sys/param.h>
27 #include <sys/errno.h>
28 #include <sys/file.h>
29 #include <sys/socket.h>
30 #include <sys/signal.h>
31 #include <sys/time.h>
32 #include <sys/resource.h>
33 #include <sys/wait.h>
34 
35 #include <netinet/in.h>
36 #include <netdb.h>
37 
38 #include <sgtty.h>
39 #include <setjmp.h>
40 #include <errno.h>
41 #include <varargs.h>
42 #include <pwd.h>
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <strings.h>
46 
47 #ifdef KERBEROS
48 #include <kerberosIV/krb.h>
49 
50 CREDENTIALS cred;
51 Key_schedule schedule;
52 int use_kerberos = 1, encrypt;
53 char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
54 extern char *krb_realmofhost();
55 #endif
56 
57 #ifndef TIOCPKT_WINDOW
58 #define	TIOCPKT_WINDOW	0x80
59 #endif
60 
61 /* concession to Sun */
62 #ifndef SIGUSR1
63 #define	SIGUSR1	30
64 #endif
65 
66 extern int errno;
67 int eight, litout, rem;
68 char cmdchar;
69 char *speeds[] = {
70 	"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
71 	"1800", "2400", "4800", "9600", "19200", "38400"
72 };
73 
74 #ifdef sun
75 struct winsize {
76 	unsigned short ws_row, ws_col;
77 	unsigned short ws_xpixel, ws_ypixel;
78 };
79 #endif
80 struct	winsize winsize;
81 
82 #ifndef sun
83 #define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
84 #endif
85 
86 void exit();
87 
88 main(argc, argv)
89 	int argc;
90 	char **argv;
91 {
92 	extern char *optarg;
93 	extern int optind;
94 	struct passwd *pw;
95 	struct servent *sp;
96 	struct sgttyb ttyb;
97 	long omask;
98 	int argoff, ch, dflag, one, uid;
99 	char *host, *p, *user, term[1024];
100 	void lostpeer();
101 	char *getenv();
102 
103 	argoff = dflag = 0;
104 	one = 1;
105 	host = user = NULL;
106 	cmdchar = '~';
107 
108 	if (p = rindex(argv[0], '/'))
109 		++p;
110 	else
111 		p = argv[0];
112 
113 	if (strcmp(p, "rlogin"))
114 		host = p;
115 
116 	/* handle "rlogin host flags" */
117 	if (!host && argc > 2 && argv[1][0] != '-') {
118 		host = argv[1];
119 		argoff = 1;
120 	}
121 
122 #ifdef KERBEROS
123 #define	OPTIONS	"8KLde:k:l:x"
124 #else
125 #define	OPTIONS	"8KLde:l:"
126 #endif
127 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
128 		switch(ch) {
129 		case '8':
130 			eight = 1;
131 			break;
132 		case 'K':
133 #ifdef KERBEROS
134 			use_kerberos = 0;
135 #endif
136 			break;
137 		case 'L':
138 			litout = 1;
139 			break;
140 		case 'd':
141 			dflag = 1;
142 			break;
143 		case 'e':
144 			cmdchar = optarg[0];
145 			break;
146 #ifdef KERBEROS
147 		case 'k':
148 			dest_realm = dst_realm_buf;
149 			(void)strncpy(dest_realm, optarg, REALM_SZ);
150 			break;
151 #endif
152 		case 'l':
153 			user = optarg;
154 			break;
155 #ifdef KERBEROS
156 		case 'x':
157 			encrypt = 1;
158 			des_set_key(cred.session, schedule);
159 			break;
160 #endif
161 		case '?':
162 		default:
163 			usage();
164 		}
165 	optind += argoff;
166 	argc -= optind;
167 	argv += optind;
168 
169 	/* if haven't gotten a host yet, do so */
170 	if (!host && !(host = *argv++))
171 		usage();
172 
173 	if (*argv)
174 		usage();
175 
176 	if (!(pw = getpwuid(uid = getuid()))) {
177 		(void)fprintf(stderr, "rlogin: unknown user id.\n");
178 		exit(1);
179 	}
180 	if (!user)
181 		user = pw->pw_name;
182 
183 	sp = NULL;
184 #ifdef KERBEROS
185 	if (use_kerberos) {
186 		sp = getservbyname((encrypt ? "eklogin" : "klogin"), "tcp");
187 		if (sp == NULL) {
188 			use_kerberos = 0;
189 			warning("can't get entry for %s/tcp service",
190 			    encrypt ? "eklogin" : "klogin");
191 		}
192 	}
193 #endif
194 	if (sp == NULL)
195 		sp = getservbyname("login", "tcp");
196 	if (sp == NULL) {
197 		(void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
198 		exit(1);
199 	}
200 
201 	(void)strcpy(term, (p = getenv("TERM")) ? p : "network");
202 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
203 		(void)strcat(term, "/");
204 		(void)strcat(term, speeds[ttyb.sg_ospeed]);
205 	}
206 
207 	(void)get_window_size(0, &winsize);
208 
209 	(void)signal(SIGPIPE, lostpeer);
210 	/* will use SIGUSR1 for window size hack, so hold it off */
211 	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
212 
213 #ifdef KERBEROS
214 try_connect:
215 	if (use_kerberos) {
216 		rem = KSUCCESS;
217 		errno = 0;
218 		if (dest_realm == NULL)
219 			dest_realm = krb_realmofhost(host);
220 
221 		if (encrypt)
222 			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
223 			    dest_realm, &cred, schedule);
224 		else
225 			rem = krcmd(&host, sp->s_port, user, term, 0,
226 			    dest_realm);
227 		if (rem < 0) {
228 			use_kerberos = 0;
229 			sp = getservbyname("login", "tcp");
230 			if (sp == NULL) {
231 				(void)fprintf(stderr,
232 				    "rlogin: unknown service login/tcp.\n");
233 				exit(1);
234 			}
235 			if (errno == ECONNREFUSED)
236 				warning("remote host doesn't support Kerberos");
237 			if (errno == ENOENT)
238 				warning("can't provide Kerberos auth data");
239 			goto try_connect;
240 		}
241 	} else {
242 		if (encrypt) {
243 			(void)fprintf(stderr,
244 			    "rlogin: the -x flag requires Kerberos authentication.\n");
245 			exit(1);
246 		}
247 		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
248 	}
249 #else
250 	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
251 #endif
252 
253 	if (rem < 0)
254 		exit(1);
255 
256 	if (dflag &&
257 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
258 		(void)fprintf(stderr, "rlogin: setsockopt: %s.\n",
259 		    strerror(errno));
260 
261 	(void)setuid(uid);
262 	doit(omask);
263 	/*NOTREACHED*/
264 }
265 
266 int child, defflags, deflflags, tabflag;
267 char deferase, defkill;
268 struct tchars deftc;
269 struct ltchars defltc;
270 struct tchars notc = { -1, -1, -1, -1, -1, -1 };
271 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
272 
273 doit(omask)
274 	long omask;
275 {
276 	struct sgttyb sb;
277 	void catch_child(), copytochild(), exit(), writeroob();
278 
279 	(void)ioctl(0, TIOCGETP, (char *)&sb);
280 	defflags = sb.sg_flags;
281 	tabflag = defflags & TBDELAY;
282 	defflags &= ECHO | CRMOD;
283 	deferase = sb.sg_erase;
284 	defkill = sb.sg_kill;
285 	(void)ioctl(0, TIOCLGET, (char *)&deflflags);
286 	(void)ioctl(0, TIOCGETC, (char *)&deftc);
287 	notc.t_startc = deftc.t_startc;
288 	notc.t_stopc = deftc.t_stopc;
289 	(void)ioctl(0, TIOCGLTC, (char *)&defltc);
290 	(void)signal(SIGINT, SIG_IGN);
291 	setsignal(SIGHUP, exit);
292 	setsignal(SIGQUIT, exit);
293 	child = fork();
294 	if (child == -1) {
295 		(void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno));
296 		done(1);
297 	}
298 	if (child == 0) {
299 		mode(1);
300 		if (reader(omask) == 0) {
301 			msg("connection closed.");
302 			exit(0);
303 		}
304 		sleep(1);
305 		msg("\007connection closed.");
306 		exit(1);
307 	}
308 
309 	/*
310 	 * We may still own the socket, and may have a pending SIGURG (or might
311 	 * receive one soon) that we really want to send to the reader.  Set a
312 	 * trap that simply copies such signals to the child.
313 	 */
314 	(void)signal(SIGURG, copytochild);
315 	(void)signal(SIGUSR1, writeroob);
316 	(void)sigsetmask(omask);
317 	(void)signal(SIGCHLD, catch_child);
318 	writer();
319 	msg("closed connection.");
320 	done(0);
321 }
322 
323 /* trap a signal, unless it is being ignored. */
324 setsignal(sig, act)
325 	int sig;
326 	void (*act)();
327 {
328 	int omask = sigblock(sigmask(sig));
329 
330 	if (signal(sig, act) == SIG_IGN)
331 		(void)signal(sig, SIG_IGN);
332 	(void)sigsetmask(omask);
333 }
334 
335 done(status)
336 	int status;
337 {
338 	int w;
339 
340 	mode(0);
341 	if (child > 0) {
342 		/* make sure catch_child does not snap it up */
343 		(void)signal(SIGCHLD, SIG_DFL);
344 		if (kill(child, SIGKILL) >= 0)
345 			while ((w = wait((union wait *)0)) > 0 && w != child);
346 	}
347 	exit(status);
348 }
349 
350 int dosigwinch;
351 
352 /*
353  * This is called when the reader process gets the out-of-band (urgent)
354  * request to turn on the window-changing protocol.
355  */
356 void
357 writeroob()
358 {
359 	void sigwinch();
360 
361 	if (dosigwinch == 0) {
362 		sendwindow();
363 		(void)signal(SIGWINCH, sigwinch);
364 	}
365 	dosigwinch = 1;
366 }
367 
368 void
369 catch_child()
370 {
371 	union wait status;
372 	int pid;
373 
374 	for (;;) {
375 		pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
376 		if (pid == 0)
377 			return;
378 		/* if the child (reader) dies, just quit */
379 		if (pid < 0 || pid == child && !WIFSTOPPED(status))
380 			done((int)(status.w_termsig | status.w_retcode));
381 	}
382 	/* NOTREACHED */
383 }
384 
385 /*
386  * writer: write to remote: 0 -> line.
387  * ~.	terminate
388  * ~^Z	suspend rlogin process.
389  * ~^Y  suspend rlogin process, but leave reader alone.
390  */
391 writer()
392 {
393 	char c;
394 	register int bol, local, n;
395 
396 	bol = 1;			/* beginning of line */
397 	local = 0;
398 	for (;;) {
399 		n = read(STDIN_FILENO, &c, 1);
400 		if (n <= 0) {
401 			if (n < 0 && errno == EINTR)
402 				continue;
403 			break;
404 		}
405 		/*
406 		 * If we're at the beginning of the line and recognize a
407 		 * command character, then we echo locally.  Otherwise,
408 		 * characters are echo'd remotely.  If the command character
409 		 * is doubled, this acts as a force and local echo is
410 		 * suppressed.
411 		 */
412 		if (bol) {
413 			bol = 0;
414 			if (c == cmdchar) {
415 				bol = 0;
416 				local = 1;
417 				continue;
418 			}
419 		} else if (local) {
420 			local = 0;
421 			if (c == '.' || c == deftc.t_eofc) {
422 				echo(c);
423 				break;
424 			}
425 			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
426 				bol = 1;
427 				echo(c);
428 				stop(c);
429 				continue;
430 			}
431 			if (c != cmdchar) {
432 #ifdef KERBEROS
433 				if (encrypt) {
434 					(void)des_write(rem, &cmdchar, 1);
435 				} else
436 #endif
437 					(void)write(rem, &cmdchar, 1);
438 			}
439 		}
440 
441 #ifdef KERBEROS
442 		if (encrypt) {
443 			if (des_write(rem, &c, 1) == 0) {
444 				msg("line gone");
445 				break;
446 			}
447 		} else
448 #endif
449 			if (write(rem, &c, 1) == 0) {
450 				msg("line gone");
451 				break;
452 			}
453 		bol = c == defkill || c == deftc.t_eofc ||
454 		    c == deftc.t_intrc || c == defltc.t_suspc ||
455 		    c == '\r' || c == '\n';
456 	}
457 }
458 
459 echo(c)
460 register char c;
461 {
462 	register char *p;
463 	char buf[8];
464 
465 	p = buf;
466 	c &= 0177;
467 	*p++ = cmdchar;
468 	if (c < ' ') {
469 		*p++ = '^';
470 		*p++ = c + '@';
471 	} else if (c == 0177) {
472 		*p++ = '^';
473 		*p++ = '?';
474 	} else
475 		*p++ = c;
476 	*p++ = '\r';
477 	*p++ = '\n';
478 	(void)write(1, buf, p - buf);
479 }
480 
481 stop(cmdc)
482 	char cmdc;
483 {
484 	mode(0);
485 	(void)signal(SIGCHLD, SIG_IGN);
486 	(void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
487 	(void)signal(SIGCHLD, catch_child);
488 	mode(1);
489 	sigwinch();			/* check for size changes */
490 }
491 
492 void
493 sigwinch()
494 {
495 	struct winsize ws;
496 
497 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
498 	    bcmp(&ws, &winsize, sizeof(ws))) {
499 		winsize = ws;
500 		sendwindow();
501 	}
502 }
503 
504 /*
505  * Send the window size to the server via the magic escape
506  */
507 sendwindow()
508 {
509 	struct winsize *wp;
510 	char obuf[4 + sizeof (struct winsize)];
511 
512 	wp = (struct winsize *)(obuf+4);
513 	obuf[0] = 0377;
514 	obuf[1] = 0377;
515 	obuf[2] = 's';
516 	obuf[3] = 's';
517 	wp->ws_row = htons(winsize.ws_row);
518 	wp->ws_col = htons(winsize.ws_col);
519 	wp->ws_xpixel = htons(winsize.ws_xpixel);
520 	wp->ws_ypixel = htons(winsize.ws_ypixel);
521 
522 #ifdef KERBEROS
523 	if(encrypt)
524 		(void)des_write(rem, obuf, sizeof(obuf));
525 	else
526 #endif
527 		(void)write(rem, obuf, sizeof(obuf));
528 }
529 
530 /*
531  * reader: read from remote: line -> 1
532  */
533 #define	READING	1
534 #define	WRITING	2
535 
536 jmp_buf rcvtop;
537 int ppid, rcvcnt, rcvstate;
538 char rcvbuf[8 * 1024];
539 
540 void
541 oob()
542 {
543 	struct sgttyb sb;
544 	int atmark, n, out, rcvd;
545 	char waste[BUFSIZ], mark;
546 
547 	out = FWRITE;
548 	rcvd = 0;
549 	while (recv(rem, &mark, 1, MSG_OOB) < 0)
550 		switch (errno) {
551 		case EWOULDBLOCK:
552 			/*
553 			 * Urgent data not here yet.  It may not be possible
554 			 * to send it yet if we are blocked for output and
555 			 * our input buffer is full.
556 			 */
557 			if (rcvcnt < sizeof(rcvbuf)) {
558 				n = read(rem, rcvbuf + rcvcnt,
559 				    sizeof(rcvbuf) - rcvcnt);
560 				if (n <= 0)
561 					return;
562 				rcvd += n;
563 			} else {
564 				n = read(rem, waste, sizeof(waste));
565 				if (n <= 0)
566 					return;
567 			}
568 			continue;
569 		default:
570 			return;
571 	}
572 	if (mark & TIOCPKT_WINDOW) {
573 		/* Let server know about window size changes */
574 		(void)kill(ppid, SIGUSR1);
575 	}
576 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
577 		(void)ioctl(0, TIOCGETP, (char *)&sb);
578 		sb.sg_flags &= ~CBREAK;
579 		sb.sg_flags |= RAW;
580 		(void)ioctl(0, TIOCSETN, (char *)&sb);
581 		notc.t_stopc = -1;
582 		notc.t_startc = -1;
583 		(void)ioctl(0, TIOCSETC, (char *)&notc);
584 	}
585 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
586 		(void)ioctl(0, TIOCGETP, (char *)&sb);
587 		sb.sg_flags &= ~RAW;
588 		sb.sg_flags |= CBREAK;
589 		(void)ioctl(0, TIOCSETN, (char *)&sb);
590 		notc.t_stopc = deftc.t_stopc;
591 		notc.t_startc = deftc.t_startc;
592 		(void)ioctl(0, TIOCSETC, (char *)&notc);
593 	}
594 	if (mark & TIOCPKT_FLUSHWRITE) {
595 		(void)ioctl(1, TIOCFLUSH, (char *)&out);
596 		for (;;) {
597 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
598 				(void)fprintf(stderr, "rlogin: ioctl: %s.\n",
599 				    strerror(errno));
600 				break;
601 			}
602 			if (atmark)
603 				break;
604 			n = read(rem, waste, sizeof (waste));
605 			if (n <= 0)
606 				break;
607 		}
608 		/*
609 		 * Don't want any pending data to be output, so clear the recv
610 		 * buffer.  If we were hanging on a write when interrupted,
611 		 * don't want it to restart.  If we were reading, restart
612 		 * anyway.
613 		 */
614 		rcvcnt = 0;
615 		longjmp(rcvtop, 1);
616 	}
617 
618 	/* oob does not do FLUSHREAD (alas!) */
619 
620 	/*
621 	 * If we filled the receive buffer while a read was pending, longjmp
622 	 * to the top to restart appropriately.  Don't abort a pending write,
623 	 * however, or we won't know how much was written.
624 	 */
625 	if (rcvd && rcvstate == READING)
626 		longjmp(rcvtop, 1);
627 }
628 
629 /* reader: read from remote: line -> 1 */
630 reader(omask)
631 	int omask;
632 {
633 	void oob();
634 
635 #if !defined(BSD) || BSD < 43
636 	int pid = -getpid();
637 #else
638 	int pid = getpid();
639 #endif
640 	int n, remaining;
641 	char *bufp = rcvbuf;
642 
643 	(void)signal(SIGTTOU, SIG_IGN);
644 	(void)signal(SIGURG, oob);
645 	ppid = getppid();
646 	(void)fcntl(rem, F_SETOWN, pid);
647 	(void)setjmp(rcvtop);
648 	(void)sigsetmask(omask);
649 	for (;;) {
650 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
651 			rcvstate = WRITING;
652 			n = write(1, bufp, remaining);
653 			if (n < 0) {
654 				if (errno != EINTR)
655 					return(-1);
656 				continue;
657 			}
658 			bufp += n;
659 		}
660 		bufp = rcvbuf;
661 		rcvcnt = 0;
662 		rcvstate = READING;
663 
664 #ifdef KERBEROS
665 		if (encrypt)
666 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
667 		else
668 #endif
669 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
670 		if (rcvcnt == 0)
671 			return (0);
672 		if (rcvcnt < 0) {
673 			if (errno == EINTR)
674 				continue;
675 			(void)fprintf(stderr, "rlogin: read: %s.\n",
676 			    strerror(errno));
677 			return(-1);
678 		}
679 	}
680 }
681 
682 mode(f)
683 {
684 	struct ltchars *ltc;
685 	struct sgttyb sb;
686 	struct tchars *tc;
687 	int lflags;
688 
689 	(void)ioctl(0, TIOCGETP, (char *)&sb);
690 	(void)ioctl(0, TIOCLGET, (char *)&lflags);
691 	switch(f) {
692 	case 0:
693 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
694 		sb.sg_flags |= defflags|tabflag;
695 		tc = &deftc;
696 		ltc = &defltc;
697 		sb.sg_kill = defkill;
698 		sb.sg_erase = deferase;
699 		lflags = deflflags;
700 		break;
701 	case 1:
702 		sb.sg_flags |= (eight ? RAW : CBREAK);
703 		sb.sg_flags &= ~defflags;
704 		/* preserve tab delays, but turn off XTABS */
705 		if ((sb.sg_flags & TBDELAY) == XTABS)
706 			sb.sg_flags &= ~TBDELAY;
707 		tc = &notc;
708 		ltc = &noltc;
709 		sb.sg_kill = sb.sg_erase = -1;
710 		if (litout)
711 			lflags |= LLITOUT;
712 		break;
713 	default:
714 		return;
715 	}
716 	(void)ioctl(0, TIOCSLTC, (char *)ltc);
717 	(void)ioctl(0, TIOCSETC, (char *)tc);
718 	(void)ioctl(0, TIOCSETN, (char *)&sb);
719 	(void)ioctl(0, TIOCLSET, (char *)&lflags);
720 }
721 
722 void
723 lostpeer()
724 {
725 	(void)signal(SIGPIPE, SIG_IGN);
726 	msg("\007connection closed.");
727 	done(1);
728 }
729 
730 /* copy SIGURGs to the child process. */
731 void
732 copytochild()
733 {
734 	(void)kill(child, SIGURG);
735 }
736 
737 msg(str)
738 	char *str;
739 {
740 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
741 }
742 
743 #ifdef KERBEROS
744 /* VARARGS */
745 warning(va_alist)
746 va_dcl
747 {
748 	va_list ap;
749 	char *fmt;
750 
751 	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
752 	va_start(ap);
753 	fmt = va_arg(ap, char *);
754 	vfprintf(stderr, fmt, ap);
755 	va_end(ap);
756 	(void)fprintf(stderr, ".\n");
757 }
758 #endif
759 
760 usage()
761 {
762 	(void)fprintf(stderr,
763 	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
764 #ifdef KERBEROS
765 	    "8Lx", " [-k realm] ");
766 #else
767 	    "8L", " ");
768 #endif
769 	exit(1);
770 }
771 
772 /*
773  * The following routine provides compatibility (such as it is) between 4.2BSD
774  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
775  */
776 #ifdef sun
777 int
778 get_window_size(fd, wp)
779 	int fd;
780 	struct winsize *wp;
781 {
782 	struct ttysize ts;
783 	int error;
784 
785 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
786 		return(error);
787 	wp->ws_row = ts.ts_lines;
788 	wp->ws_col = ts.ts_cols;
789 	wp->ws_xpixel = 0;
790 	wp->ws_ypixel = 0;
791 	return(0);
792 }
793 #endif
794