]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/agetty.c
1 /* agetty.c - another getty program for Linux. By W. Z. Venema 1989
2 Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
3 This program is freely distributable. The entire man-page used to
4 be here. Now read the real man-page agetty.8 instead.
6 -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
8 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
9 - added Native Language Support
11 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
12 - enable hardware flow control before displaying /etc/issue
20 #include <sys/ioctl.h>
24 #include <sys/types.h>
36 #include "pathnames.h"
37 #include <sys/param.h>
41 /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
48 * Some heuristics to find out what environment we are in: if it is not
49 * System V, assume it is SunOS 4.
52 #ifdef LOGIN_PROCESS /* defined in System V utmp.h */
53 #define SYSV_STYLE /* select System V style getty */
57 * Things you may want to modify.
59 * If ISSUE is not defined, agetty will never display the contents of the
60 * /etc/issue file. You will not want to spit out large "issue" files at the
61 * wrong baud rate. Relevant for System V only.
63 * You may disagree with the default line-editing etc. characters defined
64 * below. Note, however, that DEL cannot be used for interrupt generation
65 * and for line editing at the same time.
69 #define ISSUE "/etc/issue" /* displayed before the login prompt */
70 #include <sys/utsname.h>
74 #define LOGIN " login: " /* login prompt */
76 /* Some shorthands for control characters. */
78 #define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
79 #define CR CTL('M') /* carriage return */
80 #define NL CTL('J') /* line feed */
81 #define BS CTL('H') /* back space */
82 #define DEL CTL('?') /* delete */
84 /* Defaults for line-editing etc. characters; you may want to change this. */
86 #define DEF_ERASE DEL /* default erase character */
87 #define DEF_INTR CTL('C') /* default interrupt character */
88 #define DEF_QUIT CTL('\\') /* default quit char */
89 #define DEF_KILL CTL('U') /* default kill char */
90 #define DEF_EOF CTL('D') /* default EOF char */
92 #define DEF_SWITCH 0 /* default switch char */
95 * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
96 * because the termio -> termios translation does not clear the termios
97 * CIBAUD bits. Therefore, the tty driver would sometimes report that input
98 * baud rate != output baud rate. I did not notice that problem with SunOS
99 * 4.1. We will use termios where available, and termio otherwise.
102 /* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
103 properly, but all is well if we use termios?! */
109 #define termio termios
110 #define TCGETA TCGETS
111 #define TCSETA TCSETS
112 #define TCSETAW TCSETSW
116 * This program tries to not use the standard-i/o library. This keeps the
117 * executable small on systems that do not have shared libraries (System V
125 * When multiple baud rates are specified on the command line, the first one
126 * we will try is the first one specified.
129 #define FIRST_SPEED 0
131 /* Storage for command-line options. */
133 #define MAX_SPEED 10 /* max. nr. of baud rates */
136 int flags
; /* toggle switches, see below */
137 int timeout
; /* time-out period */
138 char *login
; /* login program */
139 char *tty
; /* name of tty */
140 char *initstring
; /* modem init string */
141 char *issue
; /* alternative issue file */
142 int numspeed
; /* number of baud rates to try */
143 int speeds
[MAX_SPEED
]; /* baud rates to be tried */
146 #define F_PARSE (1<<0) /* process modem status messages */
147 #define F_ISSUE (1<<1) /* display /etc/issue */
148 #define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
149 #define F_LOCAL (1<<3) /* force local */
150 #define F_INITSTRING (1<<4) /* initstring is set */
151 #define F_WAITCRLF (1<<5) /* wait for CR or LF */
152 #define F_CUSTISSUE (1<<6) /* give alternative issue file */
153 #define F_NOPROMPT (1<<7) /* don't ask for login name! */
155 /* Storage for things detected while the login name was read. */
158 int erase
; /* erase character */
159 int kill
; /* kill character */
160 int eol
; /* end-of-line character */
161 int parity
; /* what parity did we see */
162 int capslock
; /* upper case without lower case */
165 /* Initial values for the above. */
167 struct chardata init_chardata
= {
168 DEF_ERASE
, /* default erase character */
169 DEF_KILL
, /* default kill character */
170 13, /* default eol char */
171 0, /* space parity */
180 static struct Speedtab speedtab
[] = {
219 int main
P_((int argc
, char **argv
));
220 void parse_args
P_((int argc
, char **argv
, struct options
*op
));
221 void parse_speeds
P_((struct options
*op
, char *arg
));
222 void update_utmp
P_((char *line
));
223 void open_tty
P_((char *tty
, struct termio
*tp
, int local
));
224 void termio_init
P_((struct termio
*tp
, int speed
, struct options
*op
));
225 void auto_baud
P_((struct termio
*tp
));
226 void do_prompt
P_((struct options
*op
, struct termio
*tp
));
227 void next_speed
P_((struct termio
*tp
, struct options
*op
));
228 char *get_logname
P_((struct options
*op
, struct chardata
*cp
, struct termio
*tp
));
229 void termio_final
P_((struct options
*op
, struct termio
*tp
, struct chardata
*cp
));
230 int caps_lock
P_((char *s
));
231 int bcode
P_((char *s
));
232 void usage
P_((void));
233 void error
P_((const char *, ...));
236 /* The following is used for understandable diagnostics. */
240 /* Fake hostname for ut_host specified on command line. */
241 char *fakehost
= NULL
;
245 #define debug(s) fprintf(dbf,s); fflush(dbf)
248 #define debug(s) /* nothing */
256 char *logname
= NULL
; /* login name, given to /bin/login */
257 struct chardata chardata
; /* set by get_logname() */
258 struct termio termio
; /* terminal mode bits */
259 static struct options options
= {
260 F_ISSUE
, /* show /etc/issue (SYSV_STYLE) */
262 _PATH_LOGIN
, /* default login program */
263 "tty1", /* default tty line */
264 "", /* modem init string */
265 ISSUE
, /* default issue file */
266 0, /* no baud rates known yet */
269 setlocale(LC_ALL
, "");
270 bindtextdomain(PACKAGE
, LOCALEDIR
);
273 /* The BSD-style init command passes us a useless process name. */
282 dbf
= fopen("/dev/ttyp0", "w");
286 for(i
= 1; i
< argc
; i
++) {
292 /* Parse command-line arguments. */
294 parse_args(argc
, argv
, &options
);
300 /* Update the utmp file. */
303 update_utmp(options
.tty
);
306 debug(_("calling open_tty\n"));
307 /* Open the tty as standard { input, output, error }. */
308 open_tty(options
.tty
, &termio
, options
.flags
& F_LOCAL
);
315 (void) ioctl(0, TIOCSPGRP
, &iv
);
318 /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
319 debug(_("calling termio_init\n"));
320 termio_init(&termio
, options
.speeds
[FIRST_SPEED
], &options
);
322 /* write the modem init string and DON'T flush the buffers */
323 if (options
.flags
& F_INITSTRING
) {
324 debug(_("writing init string\n"));
325 write(1, options
.initstring
, strlen(options
.initstring
));
328 if (!(options
.flags
& F_LOCAL
)) {
329 /* go to blocking write mode unless -L is specified */
330 fcntl(1, F_SETFL
, fcntl(1, F_GETFL
, 0) & ~O_NONBLOCK
);
333 /* Optionally detect the baud rate from the modem status message. */
334 debug(_("before autobaud\n"));
335 if (options
.flags
& F_PARSE
)
338 /* Set the optional timer. */
340 (void) alarm((unsigned) options
.timeout
);
342 /* optionally wait for CR or LF before writing /etc/issue */
343 if (options
.flags
& F_WAITCRLF
) {
346 debug(_("waiting for cr-lf\n"));
347 while(read(0, &ch
, 1) == 1) {
348 ch
&= 0x7f; /* strip "parity bit" */
350 fprintf(dbf
, _("read %c\n"), ch
);
352 if (ch
== '\n' || ch
== '\r') break;
356 chardata
= init_chardata
;
357 if (!(options
.flags
& F_NOPROMPT
)) {
358 /* Read the login name. */
359 debug(_("reading login name\n"));
360 while ((logname
= get_logname(&options
, &chardata
, &termio
)) == 0)
361 next_speed(&termio
, &options
);
369 /* Finalize the termio settings. */
371 termio_final(&options
, &termio
, &chardata
);
373 /* Now the newline character should be properly written. */
375 (void) write(1, "\n", 1);
377 /* Let the login program take care of password validation. */
379 (void) execl(options
.login
, options
.login
, "--", logname
, (char *) 0);
380 error(_("%s: can't exec %s: %m"), options
.tty
, options
.login
);
381 exit(0); /* quiet GCC */
384 /* parse-args - parse command-line arguments */
387 parse_args(argc
, argv
, op
)
392 extern char *optarg
; /* getopt */
393 extern int optind
; /* getopt */
396 while (isascii(c
= getopt(argc
, argv
, "I:LH:f:hil:mt:wn"))) {
399 if (!(op
->initstring
= malloc(strlen(optarg
)))) {
400 error(_("can't malloc initstring"));
407 /* copy optarg into op->initstring decoding \ddd
408 octal codes into chars */
412 if (*p
== '\\') { /* know \\ means \ */
417 } else { /* handle \000 - \177 */
419 for (i
= 1; i
<= 3; i
++) {
420 if (*p
>= '0' && *p
<= '7') {
435 op
->flags
|= F_INITSTRING
;
438 case 'L': /* force local */
439 op
->flags
|= F_LOCAL
;
441 case 'H': /* fake login host */
444 case 'f': /* custom issue file */
445 op
->flags
|= F_CUSTISSUE
;
448 case 'h': /* enable h/w flow control */
449 op
->flags
|= F_RTSCTS
;
451 case 'i': /* do not show /etc/issue */
452 op
->flags
&= ~F_ISSUE
;
455 op
->login
= optarg
; /* non-default login program */
457 case 'm': /* parse modem status message */
458 op
->flags
|= F_PARSE
;
461 op
->flags
|= F_NOPROMPT
;
463 case 't': /* time out */
464 if ((op
->timeout
= atoi(optarg
)) <= 0)
465 error(_("bad timeout value: %s"), optarg
);
468 op
->flags
|= F_WAITCRLF
;
474 debug(_("after getopt loop\n"));
475 if (argc
< optind
+ 2) /* check parameter count */
478 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
479 if('0' <= argv
[optind
][0] && argv
[optind
][0] <= '9') {
480 /* a number first, assume it's a speed (BSD style) */
481 parse_speeds(op
, argv
[optind
++]); /* baud rate(s) */
482 op
->tty
= argv
[optind
]; /* tty name */
484 op
->tty
= argv
[optind
++]; /* tty name */
485 parse_speeds(op
, argv
[optind
]); /* baud rate(s) */
489 if (argc
> optind
&& argv
[optind
])
490 setenv ("TERM", argv
[optind
], 1);
492 #ifdef DO_DEVFS_FIDDLING
494 * some devfs junk, following Goswin Brederlow:
495 * turn ttyS<n> into tts/<n>
496 * turn tty<n> into vc/<n>
498 if (op
->tty
&& strlen(op
->tty
) < 90) {
502 if (strncmp(op
->tty
, "ttyS", 4) == 0) {
503 strcpy(dev_name
, "/dev/");
504 strcat(dev_name
, op
->tty
);
505 if (stat(dev_name
, &st
) < 0) {
506 strcpy(dev_name
, "/dev/tts/");
507 strcat(dev_name
, op
->tty
+ 4);
508 if (stat(dev_name
, &st
) == 0)
509 op
->tty
= strdup(dev_name
+ 5);
511 } else if (strncmp(op
->tty
, "tty", 3) == 0) {
512 strcpy(dev_name
, "/dev/");
513 strncat(dev_name
, op
->tty
, 90);
514 if (stat(dev_name
, &st
) < 0) {
515 strcpy(dev_name
, "/dev/vc/");
516 strcat(dev_name
, op
->tty
+ 3);
517 if (stat(dev_name
, &st
) == 0)
518 op
->tty
= strdup(dev_name
+ 5);
524 debug(_("exiting parseargs\n"));
527 /* parse_speeds - parse alternate baud rates */
530 parse_speeds(op
, arg
)
536 debug(_("entered parse_speeds\n"));
537 for (cp
= strtok(arg
, ","); cp
!= 0; cp
= strtok((char *) 0, ",")) {
538 if ((op
->speeds
[op
->numspeed
++] = bcode(cp
)) <= 0)
539 error(_("bad speed: %s"), cp
);
540 if (op
->numspeed
>= MAX_SPEED
)
541 error(_("too many alternate speeds"));
543 debug(_("exiting parsespeeds\n"));
548 /* update_utmp - update our utmp entry */
555 int mypid
= getpid();
559 * The utmp file holds miscellaneous information about things started by
560 * /sbin/init and other system-related events. Our purpose is to update
561 * the utmp entry for the current process, in particular the process type
562 * and the tty line we are listening to. Return successfully only if the
563 * utmp file can be opened for update, and if we are able to find our
564 * entry in the utmp file.
568 utmpname(_PATH_UTMP
);
570 while ((utp
= getutent())
571 && !(utp
->ut_type
== INIT_PROCESS
572 && utp
->ut_pid
== mypid
)) /* nothing */;
575 memcpy(&ut
, utp
, sizeof(ut
));
577 /* some inits don't initialize utmp... */
578 memset(&ut
, 0, sizeof(ut
));
579 strncpy(ut
.ut_id
, line
+ 3, sizeof(ut
.ut_id
));
583 strncpy(ut
.ut_user
, "LOGIN", sizeof(ut
.ut_user
));
584 strncpy(ut
.ut_line
, line
, sizeof(ut
.ut_line
));
586 strncpy(ut
.ut_host
, fakehost
, sizeof(ut
.ut_host
));
589 ut
.ut_type
= LOGIN_PROCESS
;
596 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
597 updwtmp(_PATH_WTMP
, &ut
);
602 if ((lf
= open(_PATH_WTMPLOCK
, O_CREAT
|O_WRONLY
, 0660)) >= 0) {
604 if ((ut_fd
= open(_PATH_WTMP
, O_APPEND
|O_WRONLY
)) >= 0) {
605 write(ut_fd
, &ut
, sizeof(ut
));
613 #else /* not __linux__ */
615 if ((ut_fd
= open(UTMP_FILE
, 2)) < 0) {
616 error(_("%s: open for update: %m"), UTMP_FILE
);
618 long ut_size
= sizeof(ut
); /* avoid nonsense */
620 while (read(ut_fd
, (char *) &ut
, sizeof(ut
)) == sizeof(ut
)) {
621 if (ut
.ut_type
== INIT_PROCESS
&& ut
.ut_pid
== mypid
) {
622 ut
.ut_type
= LOGIN_PROCESS
;
623 ut
.ut_time
= time((long *) 0);
624 (void) strncpy(ut
.ut_name
, "LOGIN", sizeof(ut
.ut_name
));
625 (void) strncpy(ut
.ut_line
, line
, sizeof(ut
.ut_line
));
627 (void) strncpy(ut
.ut_host
, fakehost
, sizeof(ut
.ut_host
));
628 (void) lseek(ut_fd
, -ut_size
, 1);
629 (void) write(ut_fd
, (char *) &ut
, sizeof(ut
));
634 error(_("%s: no utmp entry"), line
);
637 #endif /* __linux__ */
642 /* open_tty - set up tty as standard { input, output, error } */
644 open_tty(tty
, tp
, local
)
649 /* Get rid of the present standard { output, error} if any. */
653 errno
= 0; /* ignore above errors */
655 /* Set up new standard input, unless we are given an already opened port. */
657 if (strcmp(tty
, "-")) {
660 /* Sanity checks... */
663 error(_("/dev: chdir() failed: %m"));
664 if (stat(tty
, &st
) < 0)
665 error("/dev/%s: %m", tty
);
666 if ((st
.st_mode
& S_IFMT
) != S_IFCHR
)
667 error(_("/dev/%s: not a character device"), tty
);
669 /* Open the tty as standard input. */
672 errno
= 0; /* ignore close(2) errors */
674 debug(_("open(2)\n"));
675 if (open(tty
, O_RDWR
|O_NONBLOCK
, 0) != 0)
676 error(_("/dev/%s: cannot open as standard input: %m"), tty
);
681 * Standard input should already be connected to an open port. Make
682 * sure it is open for read/write.
685 if ((fcntl(0, F_GETFL
, 0) & O_RDWR
) != O_RDWR
)
686 error(_("%s: not open for read/write"), tty
);
689 /* Set up standard output and standard error file descriptors. */
690 debug(_("duping\n"));
691 if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
692 error(_("%s: dup problem: %m"), tty
); /* we have a problem */
695 * The following ioctl will fail if stdin is not a tty, but also when
696 * there is noise on the modem control lines. In the latter case, the
697 * common course of action is (1) fix your cables (2) give the modem more
698 * time to properly reset after hanging up. SunOS users can achieve (2)
699 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
700 * 5 seconds seems to be a good value.
703 if (ioctl(0, TCGETA
, tp
) < 0)
704 error("%s: ioctl: %m", tty
);
707 * It seems to be a terminal. Set proper protections and ownership. Mode
708 * 0622 is suitable for SYSV <4 because /bin/login does not change
709 * protections. SunOS 4 login will change the protections to 0620 (write
710 * access for group tty) after the login has succeeded.
713 (void) chown(tty
, 0, 0); /* root, sys */
714 (void) chmod(tty
, 0622); /* crw--w--w- */
715 errno
= 0; /* ignore above errors */
718 /* termio_init - initialize termio settings */
724 termio_init(tp
, speed
, op
)
731 * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
732 * Special characters are set after we have read the login name; all
733 * reads will be done in raw mode anyway. Errors will be dealt with
737 /* flush input and output queues, important for modems! */
738 (void) ioctl(0, TCFLSH
, TCIOFLUSH
);
741 tp
->c_cflag
= CS8
| HUPCL
| CREAD
| speed
;
742 if (op
->flags
& F_LOCAL
) {
743 tp
->c_cflag
|= CLOCAL
;
746 tp
->c_iflag
= tp
->c_lflag
= tp
->c_oflag
= tp
->c_line
= 0;
750 /* Optionally enable hardware flow control */
753 if (op
->flags
& F_RTSCTS
)
754 tp
->c_cflag
|= CRTSCTS
;
757 (void) ioctl(0, TCSETA
, tp
);
759 /* go to blocking input even in local mode */
760 fcntl(0, F_SETFL
, fcntl(0, F_GETFL
, 0) & ~O_NONBLOCK
);
762 debug(_("term_io 2\n"));
765 /* auto_baud - extract baud rate from modem status message */
778 * This works only if the modem produces its status code AFTER raising
779 * the DCD line, and if the computer is fast enough to set the proper
780 * baud rate before the message has gone by. We expect a message of the
783 * <junk><number><junk>
785 * The number is interpreted as the baud rate of the incoming call. If the
786 * modem does not tell us the baud rate within one second, we will keep
787 * using the current baud rate. It is advisable to enable BREAK
788 * processing (comma-separated list of baud rates) if the processing of
789 * modem status messages is enabled.
793 * Use 7-bit characters, don't block if input queue is empty. Errors will
794 * be dealt with lateron.
798 tp
->c_iflag
|= ISTRIP
; /* enable 8th-bit stripping */
799 vmin
= tp
->c_cc
[VMIN
];
800 tp
->c_cc
[VMIN
] = 0; /* don't block if queue empty */
801 (void) ioctl(0, TCSETA
, tp
);
804 * Wait for a while, then read everything the modem has said so far and
805 * try to extract the speed of the dial-in call.
809 if ((nread
= read(0, buf
, sizeof(buf
) - 1)) > 0) {
811 for (bp
= buf
; bp
< buf
+ nread
; bp
++) {
812 if (isascii(*bp
) && isdigit(*bp
)) {
813 if ((speed
= bcode(bp
))) {
814 tp
->c_cflag
&= ~CBAUD
;
815 tp
->c_cflag
|= speed
;
821 /* Restore terminal settings. Errors will be dealt with lateron. */
824 tp
->c_cc
[VMIN
] = vmin
;
825 (void) ioctl(0, TCSETA
, tp
);
828 /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
843 (void) write(1, "\r\n", 2); /* start a new line */
844 #ifdef ISSUE /* optional: show /etc/issue */
845 if ((op
->flags
& F_ISSUE
) && (fd
= fopen(op
->issue
, "r"))) {
846 oflag
= tp
->c_oflag
; /* save current setting */
847 tp
->c_oflag
|= (ONLCR
| OPOST
); /* map NL in output to CR-NL */
848 (void) ioctl(0, TCSETAW
, tp
);
851 while ((c
= getc(fd
)) != EOF
)
860 (void) printf ("%s", uts
.sysname
);
864 (void) printf ("%s", uts
.nodename
);
868 (void) printf ("%s", uts
.release
);
872 (void) printf ("%s", uts
.version
);
876 (void) printf ("%s", uts
.machine
);
881 char domainname
[256];
882 #ifdef HAVE_getdomainname
883 getdomainname(domainname
, sizeof(domainname
));
885 strcpy(domainname
, "unknown_domain");
887 domainname
[sizeof(domainname
)-1] = '\0';
888 printf ("%s", domainname
);
895 char *weekday
[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
897 char *month
[] = { "Jan", "Feb", "Mar", "Apr", "May",
898 "Jun", "Jul", "Aug", "Sep", "Oct",
904 tm
= localtime(&now
);
907 (void) printf ("%s %s %d %d",
908 weekday
[tm
->tm_wday
], month
[tm
->tm_mon
],
910 tm
->tm_year
< 70 ? tm
->tm_year
+ 2000 :
913 (void) printf ("%02d:%02d:%02d",
914 tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
920 (void) printf ("%s", op
->tty
);
927 for (i
= 0; speedtab
[i
].speed
; i
++) {
928 if (speedtab
[i
].code
== (tp
->c_cflag
& CBAUD
)) {
929 printf("%ld", speedtab
[i
].speed
);
941 while ((ut
= getutent()))
942 if (ut
->ut_type
== USER_PROCESS
)
945 printf ("%d ", users
);
947 printf ((users
== 1) ? _("user") : _("users"));
959 tp
->c_oflag
= oflag
; /* restore settings */
960 (void) ioctl(0, TCSETAW
, tp
); /* wait till output is gone */
966 char hn
[MAXHOSTNAMELEN
+1];
968 (void) gethostname(hn
, MAXHOSTNAMELEN
);
969 write(1, hn
, strlen(hn
));
972 (void) write(1, LOGIN
, sizeof(LOGIN
) - 1); /* always show login prompt */
975 /* next_speed - select next baud rate */
981 static int baud_index
= FIRST_SPEED
;/* current speed index */
983 baud_index
= (baud_index
+ 1) % op
->numspeed
;
984 tp
->c_cflag
&= ~CBAUD
;
985 tp
->c_cflag
|= op
->speeds
[baud_index
];
986 (void) ioctl(0, TCSETA
, tp
);
989 /* get_logname - get user name, establish parity, speed, erase, kill, eol */
991 char *get_logname(op
, cp
, tp
)
996 static char logname
[BUFSIZ
];
998 char c
; /* input character, full eight bits */
999 char ascval
; /* low 7 bits of input character */
1000 int bits
; /* # of "1" bits per character */
1001 int mask
; /* mask with 1 bit up */
1002 static char *erase
[] = { /* backspace-space-backspace */
1003 "\010\040\010", /* space parity */
1004 "\010\040\010", /* odd parity */
1005 "\210\240\210", /* even parity */
1006 "\210\240\210", /* no parity */
1009 /* Initialize kill, erase, parity etc. (also after switching speeds). */
1011 *cp
= init_chardata
;
1013 /* Flush pending input (esp. after parsing or switching the baud rate). */
1016 (void) ioctl(0, TCFLSH
, TCIFLUSH
);
1018 /* Prompt for and read a login name. */
1020 for (*logname
= 0; *logname
== 0; /* void */ ) {
1022 /* Write issue file and prompt, with "parity" bit == 0. */
1026 /* Read name, watch for break, parity, erase, kill, end-of-line. */
1028 for (bp
= logname
, cp
->eol
= 0; cp
->eol
== 0; /* void */ ) {
1030 /* Do not report trivial EINTR/EIO errors. */
1032 if (read(0, &c
, 1) < 1) {
1033 if (errno
== EINTR
|| errno
== EIO
)
1035 error(_("%s: read: %m"), op
->tty
);
1037 /* Do BREAK handling elsewhere. */
1039 if ((c
== 0) && op
->numspeed
> 1)
1042 /* Do parity bit handling. */
1044 if (c
!= (ascval
= (c
& 0177))) { /* "parity" bit on ? */
1045 for (bits
= 1, mask
= 1; mask
& 0177; mask
<<= 1)
1047 bits
++; /* count "1" bits */
1048 cp
->parity
|= ((bits
& 1) ? 1 : 2);
1050 /* Do erase, kill and end-of-line processing. */
1055 *bp
= 0; /* terminate logname */
1056 cp
->eol
= ascval
; /* set end-of-line char */
1061 cp
->erase
= ascval
; /* set erase character */
1063 (void) write(1, erase
[cp
->parity
], 3);
1069 cp
->kill
= ascval
; /* set kill character */
1070 while (bp
> logname
) {
1071 (void) write(1, erase
[cp
->parity
], 3);
1078 if (!isascii(ascval
) || !isprint(ascval
)) {
1079 /* ignore garbage characters */ ;
1080 } else if (bp
- logname
>= sizeof(logname
) - 1) {
1081 error(_("%s: input overrun"), op
->tty
);
1083 (void) write(1, &c
, 1); /* echo the character */
1084 *bp
++ = ascval
; /* and store it */
1090 /* Handle names with upper case and no lower case. */
1092 if ((cp
->capslock
= caps_lock(logname
))) {
1093 for (bp
= logname
; *bp
; bp
++)
1095 *bp
= tolower(*bp
); /* map name to lower case */
1100 /* termio_final - set the final tty mode bits */
1102 termio_final(op
, tp
, cp
)
1105 struct chardata
*cp
;
1107 /* General terminal-independent stuff. */
1109 tp
->c_iflag
|= IXON
| IXOFF
; /* 2-way flow control */
1110 tp
->c_lflag
|= ICANON
| ISIG
| ECHO
| ECHOE
| ECHOK
| ECHOKE
;
1111 /* no longer| ECHOCTL | ECHOPRT*/
1112 tp
->c_oflag
|= OPOST
;
1113 /* tp->c_cflag = 0; */
1114 tp
->c_cc
[VINTR
] = DEF_INTR
; /* default interrupt */
1115 tp
->c_cc
[VQUIT
] = DEF_QUIT
; /* default quit */
1116 tp
->c_cc
[VEOF
] = DEF_EOF
; /* default EOF character */
1117 tp
->c_cc
[VEOL
] = DEF_EOL
;
1119 tp
->c_cc
[VSWTC
] = DEF_SWITCH
; /* default switch character */
1121 tp
->c_cc
[VSWTCH
] = DEF_SWITCH
; /* default switch character */
1124 /* Account for special characters seen in input. */
1126 if (cp
->eol
== CR
) {
1127 tp
->c_iflag
|= ICRNL
; /* map CR in input to NL */
1128 tp
->c_oflag
|= ONLCR
; /* map NL in output to CR-NL */
1130 tp
->c_cc
[VERASE
] = cp
->erase
; /* set erase character */
1131 tp
->c_cc
[VKILL
] = cp
->kill
; /* set kill character */
1133 /* Account for the presence or absence of parity bits in input. */
1135 switch (cp
->parity
) {
1136 case 0: /* space (always 0) parity */
1138 case 1: /* odd parity */
1139 tp
->c_cflag
|= PARODD
;
1141 case 2: /* even parity */
1142 tp
->c_cflag
|= PARENB
;
1143 tp
->c_iflag
|= INPCK
| ISTRIP
;
1145 case (1 | 2): /* no parity bit */
1146 tp
->c_cflag
&= ~CSIZE
;
1150 /* Account for upper case without lower case. */
1153 tp
->c_iflag
|= IUCLC
;
1154 tp
->c_lflag
|= XCASE
;
1155 tp
->c_oflag
|= OLCUC
;
1157 /* Optionally enable hardware flow control */
1160 if (op
->flags
& F_RTSCTS
)
1161 tp
->c_cflag
|= CRTSCTS
;
1164 /* Finally, make the new settings effective */
1166 if (ioctl(0, TCSETA
, tp
) < 0)
1167 error("%s: ioctl: TCSETA: %m", op
->tty
);
1170 /* caps_lock - string contains upper case without lower case */
1177 for (capslock
= 0; *s
; s
++) {
1181 capslock
= isupper(*s
);
1186 /* bcode - convert speed string to speed code; return 0 on failure */
1191 struct Speedtab
*sp
;
1192 long speed
= atol(s
);
1194 for (sp
= speedtab
; sp
->speed
; sp
++)
1195 if (sp
->speed
== speed
)
1200 /* usage - explain */
1205 fprintf(stderr
, _("Usage: %s [-hiLmw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] baud_rate,... line [termtype]\nor\t[-hiLmw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] line baud_rate,... [termtype]\n"), progname
);
1209 /* error - report errors to console or syslog; only understands %s and %m */
1211 #define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
1214 error(const char *fmt
, ...) {
1223 * If the diagnostic is reported via syslog(3), the process name is
1224 * automatically prepended to the message. If we write directly to
1225 * /dev/console, we must prepend the process name ourselves.
1232 (void) str2cpy(buf
, progname
, ": ");
1233 bp
= buf
+ strlen(buf
);
1237 * %s expansion is done by hand. On a System V Release 2 system without
1238 * shared libraries and without syslog(3), linking with the the stdio
1239 * library would make the program three times as big...
1241 * %m expansion is done here as well. Too bad syslog(3) does not have a
1242 * vsprintf() like interface.
1247 if (strncmp(fmt
, "%s", 2) == 0) {
1248 (void) strcpy(bp
, va_arg(ap
, char *));
1251 } else if (strncmp(fmt
, "%m", 2) == 0) {
1252 (void) strcpy(bp
, strerror(errno
));
1263 * Write the diagnostic directly to /dev/console if we do not use the
1264 * syslog(3) facility.
1268 (void) openlog(progname
, LOG_PID
, LOG_AUTHPRIV
);
1269 (void) syslog(LOG_ERR
, "%s", buf
);
1272 /* Terminate with CR-LF since the console mode is unknown. */
1273 (void) strcat(bp
, "\r\n");
1274 if ((fd
= open("/dev/console", 1)) >= 0) {
1275 (void) write(fd
, buf
, strlen(buf
));
1279 (void) sleep((unsigned) 10); /* be kind to init(8) */