1 /*
2 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\
21 All rights reserved.\n";
22 #endif /* not lint */
23
24 #ifndef lint
25 static char sccsid[] = "@(#)login.c 5.40 (Berkeley) 5/9/89";
26 #endif /* not lint */
27
28 /*
29 * login [ name ]
30 * login -h hostname (for telnetd, etc.)
31 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
32 */
33
34 #include <sys/param.h>
35 #ifdef NeXT
36 #include <sys/quota.h>
37 #else
38 #include <ufs/quota.h>
39 #endif
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <sys/resource.h>
43 #include <sys/file.h>
44 #include <sys/ioctl.h>
45
46 #include <utmp.h>
47 #ifdef NeXT
48 #include <lastlog.h>
49 #ifndef UT_NAMESIZE
50 #define UT_NAMESIZE 8
51 #endif
52 #endif
53 #include <signal.h>
54 #include <errno.h>
55 #include <ttyent.h>
56 #include <syslog.h>
57 #include <grp.h>
58 #include <pwd.h>
59 #include <setjmp.h>
60 #include <stdio.h>
61 #include <strings.h>
62 #include <tzfile.h>
63 #include "pathnames.h"
64
65 #ifdef KERBEROS
66 #include <kerberos/krb.h>
67 #include <sys/termios.h>
68 char realm[REALM_SZ];
69 int kerror = KSUCCESS, notickets = 1;
70 #endif
71
72 #define TTYGRPNAME "tty" /* name of group to own ttys */
73
74 /*
75 * This bounds the time given to login. Not a define so it can
76 * be patched on machines where it's too small.
77 */
78 int timeout = 300;
79
80 struct passwd *pwd;
81 int failures;
82 char term[64], *hostname, *username, *tty;
83
84 struct sgttyb sgttyb;
85 struct tchars tc = {
86 CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
87 };
88 struct ltchars ltc = {
89 CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
90 };
91
92 #ifndef NeXT
93 /* NeXT doesn't ifdef this out for some reason */
94 char *months[] =
95 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
96 "Sep", "Oct", "Nov", "Dec" };
97 #endif
98
main(argc,argv)99 main(argc, argv)
100 int argc;
101 char **argv;
102 {
103 extern int errno, optind;
104 extern char *optarg, **environ;
105 struct timeval tp;
106 struct tm *ttp;
107 struct group *gr;
108 register int ch;
109 register char *p;
110 int ask, fflag, hflag, pflag, cnt;
111 int quietlog, passwd_req, ioctlval, timedout();
112 char *domain, *salt, *envinit[1], *ttyn, *pp;
113 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
114 char *ctime(), *ttyname(), *stypeof(), *crypt(), *getpass();
115 time_t time();
116 off_t lseek();
117
118 (void)signal(SIGALRM, timedout);
119 (void)alarm((u_int)timeout);
120 (void)signal(SIGQUIT, SIG_IGN);
121 (void)signal(SIGINT, SIG_IGN);
122 (void)setpriority(PRIO_PROCESS, 0, 0);
123 #ifndef NeXT
124 (void)quota(Q_SETUID, 0, 0, 0);
125 #endif
126
127 /*
128 * -p is used by getty to tell login not to destroy the environment
129 * -f is used to skip a second login authentication
130 * -h is used by other servers to pass the name of the remote
131 * host to login so that it may be placed in utmp and wtmp
132 */
133 (void)gethostname(tbuf, sizeof(tbuf));
134 domain = index(tbuf, '.');
135
136 fflag = hflag = pflag = 0;
137 passwd_req = 1;
138 while ((ch = getopt(argc, argv, "fh:p")) != EOF)
139 switch (ch) {
140 case 'f':
141 fflag = 1;
142 break;
143 case 'h':
144 if (getuid()) {
145 (void)fprintf(stderr,
146 "login: -h for super-user only.\n");
147 exit(1);
148 }
149 hflag = 1;
150 if (domain && (p = index(optarg, '.')) &&
151 strcasecmp(p, domain) == 0)
152 *p = 0;
153 hostname = optarg;
154 break;
155 case 'p':
156 pflag = 1;
157 break;
158 case '?':
159 default:
160 (void)fprintf(stderr,
161 "usage: login [-fp] [username]\n");
162 exit(1);
163 }
164 argc -= optind;
165 argv += optind;
166 if (*argv) {
167 username = *argv;
168 ask = 0;
169 } else
170 ask = 1;
171
172 ioctlval = 0;
173 (void)ioctl(0, TIOCLSET, &ioctlval);
174 (void)ioctl(0, TIOCNXCL, 0);
175 (void)fcntl(0, F_SETFL, ioctlval);
176 (void)ioctl(0, TIOCGETP, &sgttyb);
177 sgttyb.sg_erase = CERASE;
178 sgttyb.sg_kill = CKILL;
179 (void)ioctl(0, TIOCSLTC, <c);
180 (void)ioctl(0, TIOCSETC, &tc);
181 (void)ioctl(0, TIOCSETP, &sgttyb);
182
183 #if defined(NeXT) && defined(FIX8BIT)
184 NeXT_repair_line(0);
185 #endif
186
187 for (cnt = getdtablesize(); cnt > 2; cnt--)
188 close(cnt);
189
190 ttyn = ttyname(0);
191 if (ttyn == NULL || *ttyn == '\0') {
192 (void)sprintf(tname, "%s??", _PATH_TTY);
193 ttyn = tname;
194 }
195 if (tty = rindex(ttyn, '/'))
196 ++tty;
197 else
198 tty = ttyn;
199
200 openlog("login", LOG_ODELAY, LOG_AUTH);
201
202 for (cnt = 0;; ask = 1) {
203 ioctlval = 0;
204 (void)ioctl(0, TIOCSETD, &ioctlval);
205
206 if (ask) {
207 fflag = 0;
208 getloginname();
209 }
210 /*
211 * Note if trying multiple user names;
212 * log failures for previous user name,
213 * but don't bother logging one failure
214 * for nonexistent name (mistyped username).
215 */
216 if (failures && strcmp(tbuf, username)) {
217 if (failures > (pwd ? 0 : 1))
218 badlogin(tbuf);
219 failures = 0;
220 }
221 (void)strcpy(tbuf, username);
222 if (pwd = getpwnam(username))
223 salt = pwd->pw_passwd;
224 else
225 salt = "xx";
226
227 /* if user not super-user, check for disabled logins */
228 if (pwd == NULL || pwd->pw_uid)
229 checknologin();
230
231 /*
232 * Disallow automatic login to root; if not invoked by
233 * root, disallow if the uid's differ.
234 */
235 if (fflag && pwd) {
236 int uid = getuid();
237
238 passwd_req = pwd->pw_uid == 0 ||
239 (uid && uid != pwd->pw_uid);
240 }
241 #ifdef NeXT
242 if (pwd->pw_uid == 0 && !rootterm(tty)) {
243 fprintf(stderr, "%s login refused on this terminal.\n",
244 pwd->pw_name);
245 if (hostname)
246 syslog(LOG_NOTICE,
247 "LOGIN %s REFUSED FROM %s ON TTY %s",
248 pwd->pw_name, hostname, tty);
249 else
250 syslog(LOG_NOTICE,
251 "LOGIN %s REFUSED ON TTY %s",
252 pwd->pw_name, tty);
253 continue;
254 }
255 #endif
256
257
258 /*
259 * If no pre-authentication and a password exists
260 * for this user, prompt for one and verify it.
261 */
262 if (!passwd_req || (pwd && !*pwd->pw_passwd))
263 break;
264
265 setpriority(PRIO_PROCESS, 0, -4);
266 pp = getpass("Password:");
267 p = crypt(pp, salt);
268 setpriority(PRIO_PROCESS, 0, 0);
269
270 #ifdef KERBEROS
271
272 /*
273 * If not present in pw file, act as we normally would.
274 * If we aren't Kerberos-authenticated, try the normal
275 * pw file for a password. If that's ok, log the user
276 * in without issueing any tickets.
277 */
278
279 if (pwd && !krb_get_lrealm(realm,1)) {
280 /*
281 * get TGT for local realm; be careful about uid's
282 * here for ticket file ownership
283 */
284 (void)setreuid(geteuid(),pwd->pw_uid);
285 kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
286 "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
287 (void)setuid(0);
288 if (kerror == INTK_OK) {
289 bzero(pp, strlen(pp));
290 notickets = 0; /* user got ticket */
291 break;
292 }
293 }
294 #endif
295 (void) bzero(pp, strlen(pp));
296 if (pwd && !strcmp(p, pwd->pw_passwd))
297 break;
298
299 (void)printf("Login incorrect\n");
300 failures++;
301 /* we allow 10 tries, but after 3 we start backing off */
302 if (++cnt > 3) {
303 if (cnt >= 10) {
304 badlogin(username);
305 (void)ioctl(0, TIOCHPCL, (struct sgttyb *)NULL);
306 sleepexit(1);
307 }
308 sleep((u_int)((cnt - 3) * 5));
309 }
310 }
311
312 /* committed to login -- turn off timeout */
313 (void)alarm((u_int)0);
314
315 /* paranoia... */
316 endpwent();
317
318 #ifndef NeXT
319 /*
320 * If valid so far and root is logging in, see if root logins on
321 * this terminal are permitted.
322 */
323 if (pwd->pw_uid == 0 && !rootterm(tty)) {
324 if (hostname)
325 syslog(LOG_NOTICE, "ROOT LOGIN REFUSED FROM %s",
326 hostname);
327 else
328 syslog(LOG_NOTICE, "ROOT LOGIN REFUSED ON %s", tty);
329 (void)printf("Login incorrect\n");
330 sleepexit(1);
331 }
332
333 if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
334 switch(errno) {
335 case EUSERS:
336 (void)fprintf(stderr,
337 "Too many users logged on already.\nTry again later.\n");
338 break;
339 case EPROCLIM:
340 (void)fprintf(stderr,
341 "You have too many processes running.\n");
342 break;
343 default:
344 perror("quota (Q_SETUID)");
345 }
346 sleepexit(0);
347 }
348 #endif
349
350 if (chdir(pwd->pw_dir) < 0) {
351 (void)printf("No directory %s!\n", pwd->pw_dir);
352 if (chdir("/"))
353 exit(0);
354 pwd->pw_dir = "/";
355 (void)printf("Logging in with home = \"/\".\n");
356 }
357
358 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
359
360 #ifdef KERBEROS
361 if (notickets && !quietlog)
362 (void)printf("Warning: no Kerberos tickets issued\n");
363 #endif
364
365 #ifndef NeXT
366 #define TWOWEEKS (14*24*60*60)
367 if (pwd->pw_change || pwd->pw_expire)
368 (void)gettimeofday(&tp, (struct timezone *)NULL);
369 if (pwd->pw_change)
370 if (tp.tv_sec >= pwd->pw_change) {
371 (void)printf("Sorry -- your password has expired.\n");
372 sleepexit(1);
373 }
374 else if (tp.tv_sec - pwd->pw_change < TWOWEEKS && !quietlog) {
375 ttp = localtime(&pwd->pw_change);
376 (void)printf("Warning: your password expires on %s %d, %d\n",
377 months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year);
378 }
379 if (pwd->pw_expire)
380 if (tp.tv_sec >= pwd->pw_expire) {
381 (void)printf("Sorry -- your account has expired.\n");
382 sleepexit(1);
383 }
384 else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS && !quietlog) {
385 ttp = localtime(&pwd->pw_expire);
386 (void)printf("Warning: your account expires on %s %d, %d\n",
387 months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year);
388 }
389 #endif
390
391 /* nothing else left to fail -- really log in */
392 {
393 #ifdef NeXT
394 void login();
395 #endif
396 struct utmp utmp;
397
398 bzero((char *)&utmp, sizeof(utmp));
399 (void)time(&utmp.ut_time);
400 strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
401 if (hostname)
402 strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
403 strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
404 login(&utmp);
405 }
406
407 dolastlog(quietlog);
408
409 if (!hflag) { /* XXX */
410 static struct winsize win = { 0, 0, 0, 0 };
411
412 (void)ioctl(0, TIOCSWINSZ, &win);
413 }
414
415 (void)chown(ttyn, pwd->pw_uid,
416 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
417 (void)chmod(ttyn, 0620);
418 (void)setgid(pwd->pw_gid);
419
420 initgroups(username, pwd->pw_gid);
421
422 #ifndef NeXT
423 quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
424 #endif
425
426 if (*pwd->pw_shell == '\0')
427 pwd->pw_shell = _PATH_BSHELL;
428 /* turn on new line discipline for the csh */
429 else if (!strcmp(pwd->pw_shell, _PATH_CSHELL)) {
430 ioctlval = NTTYDISC;
431 (void)ioctl(0, TIOCSETD, &ioctlval);
432 }
433
434 /* real NeXT source diverges here */
435 /* destroy environment unless user has requested preservation */
436 #ifdef NeXT
437 if (!pflag){
438 envinit[0]=(char *)NULL;
439 environ = envinit;
440 }
441 #else
442 if (!pflag)
443 environ = envinit;
444 #endif
445 (void)setenv("HOME", pwd->pw_dir, 1);
446 (void)setenv("SHELL", pwd->pw_shell, 1);
447 if (term[0] == '\0')
448 strncpy(term, stypeof(tty), sizeof(term));
449 (void)setenv("TERM", term, 0);
450 (void)setenv("USER", pwd->pw_name, 1);
451 (void)setenv("PATH", _PATH_DEFPATH, 0);
452 /* real NeXT source merges here */
453
454 if (tty[sizeof("tty")-1] == 'd')
455 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
456 if (pwd->pw_uid == 0)
457 if (hostname)
458 syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s",
459 tty, hostname);
460 else
461 syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty);
462
463 if (!quietlog) {
464 struct stat st;
465
466 motd();
467 (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
468 if (stat(tbuf, &st) == 0 && st.st_size != 0)
469 (void)printf("You have %smail.\n",
470 (st.st_mtime > st.st_atime) ? "new " : "");
471 }
472
473 (void)signal(SIGALRM, SIG_DFL);
474 (void)signal(SIGQUIT, SIG_DFL);
475 (void)signal(SIGINT, SIG_DFL);
476 (void)signal(SIGTSTP, SIG_IGN);
477
478 tbuf[0] = '-';
479 strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ?
480 p + 1 : pwd->pw_shell);
481
482 #ifndef NeXT
483 if (setlogname(pwd->pw_name, strlen(pwd->pw_name)) < 0)
484 syslog(LOG_ERR, "setlogname() failure: %m");
485 #endif
486
487 /* discard permissions last so can't get killed and drop core */
488 (void)setuid(pwd->pw_uid);
489
490 #if defined(NeXT) && !defined(ORIGINAL)
491 execlp(pwd->pw_shell, tbuf, /* enable job control! */
492 strcmp(pwd->pw_shell, _PATH_BSHELL) ? 0 : "-J", 0);
493 #else
494 execlp(pwd->pw_shell, tbuf, 0);
495 #endif
496 (void)fprintf(stderr, "login: no shell: %s.\n", strerror(errno));
497 exit(0);
498 }
499
getloginname()500 getloginname()
501 {
502 register int ch;
503 register char *p;
504 static char nbuf[UT_NAMESIZE + 1];
505
506 for (;;) {
507 (void)printf("login: ");
508 for (p = nbuf; (ch = getchar()) != '\n'; ) {
509 if (ch == EOF) {
510 badlogin(username);
511 exit(0);
512 }
513 if (p < nbuf + UT_NAMESIZE)
514 *p++ = ch;
515 }
516 if (p > nbuf)
517 if (nbuf[0] == '-')
518 (void)fprintf(stderr,
519 "login names may not start with '-'.\n");
520 else {
521 *p = '\0';
522 username = nbuf;
523 break;
524 }
525 }
526 }
527
timedout()528 timedout()
529 {
530 (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
531 exit(0);
532 }
533
rootterm(ttyn)534 rootterm(ttyn)
535 char *ttyn;
536 {
537 struct ttyent *t;
538
539 return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE);
540 }
541
542 jmp_buf motdinterrupt;
543
motd()544 motd()
545 {
546 register int fd, nchars;
547 int (*oldint)(), sigint();
548 char tbuf[8192];
549
550 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
551 return;
552 oldint = signal(SIGINT, sigint);
553 if (setjmp(motdinterrupt) == 0)
554 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
555 (void)write(fileno(stdout), tbuf, nchars);
556 (void)signal(SIGINT, oldint);
557 (void)close(fd);
558 }
559
sigint()560 sigint()
561 {
562 longjmp(motdinterrupt, 1);
563 }
564
checknologin()565 checknologin()
566 {
567 register int fd, nchars;
568 char tbuf[8192];
569
570 if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
571 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
572 (void)write(fileno(stdout), tbuf, nchars);
573 sleepexit(0);
574 }
575 }
576
dolastlog(quiet)577 dolastlog(quiet)
578 int quiet;
579 {
580 struct lastlog ll;
581 int fd;
582 char *ctime();
583
584 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
585 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
586 if (!quiet) {
587 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
588 ll.ll_time != 0) {
589 (void)printf("Last login: %.*s ",
590 24-5, (char *)ctime(&ll.ll_time));
591 if (*ll.ll_host != '\0')
592 (void)printf("from %.*s\n",
593 sizeof(ll.ll_host), ll.ll_host);
594 else
595 (void)printf("on %.*s\n",
596 sizeof(ll.ll_line), ll.ll_line);
597 }
598 (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
599 }
600 bzero((char *)&ll, sizeof(ll));
601 (void)time(&ll.ll_time);
602 strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
603 if (hostname)
604 strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
605 (void)write(fd, (char *)&ll, sizeof(ll));
606 (void)close(fd);
607 }
608 }
609
badlogin(name)610 badlogin(name)
611 char *name;
612 {
613 if (failures == 0)
614 return;
615 if (hostname)
616 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s",
617 failures, failures > 1 ? "S" : "", hostname, name);
618 else
619 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s",
620 failures, failures > 1 ? "S" : "", tty, name);
621 }
622
623 #undef UNKNOWN
624 #define UNKNOWN "su"
625
626 char *
stypeof(ttyid)627 stypeof(ttyid)
628 char *ttyid;
629 {
630 struct ttyent *t;
631
632 return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
633 }
634
635 /* NeXT has their version of setenv here */
636
getstr(buf,cnt,err)637 getstr(buf, cnt, err)
638 char *buf, *err;
639 int cnt;
640 {
641 char ch;
642
643 do {
644 if (read(0, &ch, sizeof(ch)) != sizeof(ch))
645 exit(1);
646 if (--cnt < 0) {
647 (void)fprintf(stderr, "%s too long\r\n", err);
648 sleepexit(1);
649 }
650 *buf++ = ch;
651 } while (ch);
652 }
653
sleepexit(eval)654 sleepexit(eval)
655 int eval;
656 {
657 sleep((u_int)5);
658 exit(eval);
659 }
660
661 #ifdef NeXT
662 /*
663 * Copyright (c) 1988 The Regents of the University of California.
664 * All rights reserved.
665 *
666 * Redistribution and use in source and binary forms are permitted
667 * provided that the above copyright notice and this paragraph are
668 * duplicated in all such forms and that any documentation,
669 * advertising materials, and other materials related to such
670 * distribution and use acknowledge that the software was developed
671 * by the University of California, Berkeley. The name of the
672 * University may not be used to endorse or promote products derived
673 * from this software without specific prior written permission.
674 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
675 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
676 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
677 */
678
679 #if defined(LIBC_SCCS) && !defined(lint)
680 static char sccsid[] = "@(#)login.c 5.1 (Berkeley) 9/27/88";
681 #endif /* LIBC_SCCS and not lint */
682
683 #ifdef NOTDEF
684 /* included above */
685 #include <sys/types.h>
686 #include <sys/file.h>
687 #include <utmp.h>
688 #include <stdio.h>
689 #endif
690
691 #define UTMPFILE "/etc/utmp"
692 #define WTMPFILE "/usr/adm/wtmp"
693
694 void
login(ut)695 login(ut)
696 struct utmp *ut;
697 {
698 register int fd;
699 int tty;
700 off_t lseek();
701
702 tty = ttyslot();
703 if (tty > 0 && (fd = open(UTMPFILE, O_WRONLY, 0)) >= 0) {
704 (void)lseek(fd, (long)(tty * sizeof(struct utmp)), L_SET);
705 (void)write(fd, (char *)ut, sizeof(struct utmp));
706 (void)close(fd);
707 }
708 if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) >= 0) {
709 (void)write(fd, (char *)ut, sizeof(struct utmp));
710 (void)close(fd);
711 }
712 }
713 #endif
714
715 #if defined(NeXT) && defined(FIX8BIT)
NeXT_repair_line(int fd)716 NeXT_repair_line(int fd)
717 {
718 int bitset = LPASS8 | LPASS8OUT;
719 int bitclr = LNOHANG;
720
721 ioctl(fd, TIOCLBIS, &bitset);
722 ioctl(fd, TIOCLBIC, &bitclr);
723 }
724 #endif
725