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@pld.ORG.PL>
9
- added Native Language Support
11
1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
12
- enable hardware flow control before displaying /etc/issue
23
#include <sys/types.h>
32
#include <sys/socket.h>
37
#include "pathnames.h"
41
#include <sys/param.h>
45
/* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
52
* Some heuristics to find out what environment we are in: if it is not
53
* System V, assume it is SunOS 4.
56
#ifdef LOGIN_PROCESS /* defined in System V utmp.h */
57
#define SYSV_STYLE /* select System V style getty */
61
* Things you may want to modify.
63
* If ISSUE is not defined, agetty will never display the contents of the
64
* /etc/issue file. You will not want to spit out large "issue" files at the
65
* wrong baud rate. Relevant for System V only.
67
* You may disagree with the default line-editing etc. characters defined
68
* below. Note, however, that DEL cannot be used for interrupt generation
69
* and for line editing at the same time.
73
#define ISSUE "/etc/issue" /* displayed before the login prompt */
74
#include <sys/utsname.h>
77
#define LOGIN " login: " /* login prompt */
79
/* Some shorthands for control characters. */
81
#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
82
#define CR CTL('M') /* carriage return */
83
#define NL CTL('J') /* line feed */
84
#define BS CTL('H') /* back space */
85
#define DEL CTL('?') /* delete */
87
/* Defaults for line-editing etc. characters; you may want to change this. */
89
#define DEF_ERASE DEL /* default erase character */
90
#define DEF_INTR CTL('C') /* default interrupt character */
91
#define DEF_QUIT CTL('\\') /* default quit char */
92
#define DEF_KILL CTL('U') /* default kill char */
93
#define DEF_EOF CTL('D') /* default EOF char */
95
#define DEF_SWITCH 0 /* default switch char */
97
#ifndef MAXHOSTNAMELEN
99
# define MAXHOSTNAMELEN HOST_NAME_MAX
101
# define MAXHOSTNAMELEN 64
106
* When multiple baud rates are specified on the command line, the first one
107
* we will try is the first one specified.
110
#define FIRST_SPEED 0
112
/* Storage for command-line options. */
114
#define MAX_SPEED 10 /* max. nr. of baud rates */
117
int flags; /* toggle switches, see below */
118
int timeout; /* time-out period */
119
char *login; /* login program */
120
char *tty; /* name of tty */
121
char *initstring; /* modem init string */
122
char *issue; /* alternative issue file */
123
int numspeed; /* number of baud rates to try */
124
int speeds[MAX_SPEED]; /* baud rates to be tried */
125
int eightbits; /* assume 8bit-clean tty */
128
#define F_PARSE (1<<0) /* process modem status messages */
129
#define F_ISSUE (1<<1) /* display /etc/issue */
130
#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
131
#define F_LOCAL (1<<3) /* force local */
132
#define F_INITSTRING (1<<4) /* initstring is set */
133
#define F_WAITCRLF (1<<5) /* wait for CR or LF */
134
#define F_CUSTISSUE (1<<6) /* give alternative issue file */
135
#define F_NOPROMPT (1<<7) /* don't ask for login name! */
136
#define F_LCUC (1<<8) /* Support for *LCUC stty modes */
137
#define F_KEEPSPEED (1<<9) /* Follow baud rate from kernel */
138
#define F_KEEPCFLAGS (1<<10) /* Reuse c_cflags setup from kernel */
140
/* Storage for things detected while the login name was read. */
143
int erase; /* erase character */
144
int kill; /* kill character */
145
int eol; /* end-of-line character */
146
int parity; /* what parity did we see */
147
int capslock; /* upper case without lower case */
150
/* Initial values for the above. */
152
struct chardata init_chardata = {
153
DEF_ERASE, /* default erase character */
154
DEF_KILL, /* default kill character */
155
13, /* default eol char */
156
0, /* space parity */
165
static struct Speedtab speedtab[] = {
204
int main P_((int argc, char **argv));
205
void parse_args P_((int argc, char **argv, struct options *op));
206
void parse_speeds P_((struct options *op, char *arg));
207
void update_utmp P_((char *line));
208
void open_tty P_((char *tty, struct termios *tp, int local));
209
void termio_init P_((struct termios *tp, struct options *op));
210
void auto_baud P_((struct termios *tp));
211
void do_prompt P_((struct options *op, struct termios *tp));
212
void next_speed P_((struct termios *tp, struct options *op));
213
char *get_logname P_((struct options *op, struct chardata *cp, struct termios *tp));
214
void termio_final P_((struct options *op, struct termios *tp, struct chardata *cp));
215
int caps_lock P_((char *s));
216
int bcode P_((char *s));
217
void usage P_((void));
218
void error P_((const char *, ...));
221
/* The following is used for understandable diagnostics. */
225
/* Fake hostname for ut_host specified on command line. */
226
char *fakehost = NULL;
230
#define debug(s) fprintf(dbf,s); fflush(dbf)
233
#define debug(s) /* nothing */
241
char *logname = NULL; /* login name, given to /bin/login */
242
struct chardata chardata; /* set by get_logname() */
243
struct termios termios; /* terminal mode bits */
244
static struct options options = {
245
F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
247
_PATH_LOGIN, /* default login program */
248
"tty1", /* default tty line */
249
"", /* modem init string */
250
ISSUE, /* default issue file */
251
0, /* no baud rates known yet */
254
setlocale(LC_ALL, "");
255
bindtextdomain(PACKAGE, LOCALEDIR);
258
/* The BSD-style init command passes us a useless process name. */
264
if ((ptr = strrchr(argv[0], '/')))
272
dbf = fopen("/dev/ttyp0", "w");
276
for(i = 1; i < argc; i++) {
282
/* Parse command-line arguments. */
284
parse_args(argc, argv, &options);
290
/* Update the utmp file. */
293
update_utmp(options.tty);
296
debug("calling open_tty\n");
297
/* Open the tty as standard { input, output, error }. */
298
open_tty(options.tty, &termios, options.flags & F_LOCAL);
300
tcsetpgrp(0, getpid());
301
/* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
302
debug("calling termio_init\n");
303
termio_init(&termios, &options);
305
/* write the modem init string and DON'T flush the buffers */
306
if (options.flags & F_INITSTRING) {
307
debug("writing init string\n");
308
ignore_result( write(1, options.initstring, strlen(options.initstring)) );
311
if (!(options.flags & F_LOCAL)) {
312
/* go to blocking write mode unless -L is specified */
313
fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
316
/* Optionally detect the baud rate from the modem status message. */
317
debug("before autobaud\n");
318
if (options.flags & F_PARSE)
321
/* Set the optional timer. */
323
(void) alarm((unsigned) options.timeout);
325
/* optionally wait for CR or LF before writing /etc/issue */
326
if (options.flags & F_WAITCRLF) {
329
debug("waiting for cr-lf\n");
330
while(read(0, &ch, 1) == 1) {
331
ch &= 0x7f; /* strip "parity bit" */
333
fprintf(dbf, "read %c\n", ch);
335
if (ch == '\n' || ch == '\r') break;
339
chardata = init_chardata;
340
if (!(options.flags & F_NOPROMPT)) {
341
/* Read the login name. */
342
debug("reading login name\n");
343
while ((logname = get_logname(&options, &chardata, &termios)) == 0)
344
next_speed(&termios, &options);
352
/* Finalize the termios settings. */
354
termio_final(&options, &termios, &chardata);
356
/* Now the newline character should be properly written. */
358
ignore_result( write(1, "\n", 1) );
360
/* Let the login program take care of password validation. */
362
(void) execl(options.login, options.login, "--", logname, NULL);
363
error(_("%s: can't exec %s: %m"), options.tty, options.login);
366
/* parse-args - parse command-line arguments */
369
parse_args(argc, argv, op)
374
extern char *optarg; /* getopt */
375
extern int optind; /* getopt */
378
while (isascii(c = getopt(argc, argv, "8cI:LH:f:hil:mst:wUn"))) {
381
op->flags |= F_KEEPCFLAGS;
387
if (!(op->initstring = malloc(strlen(optarg)+1))) {
388
error(_("can't malloc initstring"));
395
/* copy optarg into op->initstring decoding \ddd
396
octal codes into chars */
400
if (*p == '\\') { /* know \\ means \ */
405
} else { /* handle \000 - \177 */
407
for (i = 1; i <= 3; i++) {
408
if (*p >= '0' && *p <= '7') {
423
op->flags |= F_INITSTRING;
426
case 'L': /* force local */
427
op->flags |= F_LOCAL;
429
case 'H': /* fake login host */
432
case 'f': /* custom issue file */
433
op->flags |= F_CUSTISSUE;
436
case 'h': /* enable h/w flow control */
437
op->flags |= F_RTSCTS;
439
case 'i': /* do not show /etc/issue */
440
op->flags &= ~F_ISSUE;
443
op->login = optarg; /* non-default login program */
445
case 'm': /* parse modem status message */
446
op->flags |= F_PARSE;
449
op->flags |= F_NOPROMPT;
452
op->flags |= F_KEEPSPEED; /* keep kernel defined speed */
454
case 't': /* time out */
455
if ((op->timeout = atoi(optarg)) <= 0)
456
error(_("bad timeout value: %s"), optarg);
459
op->flags |= F_WAITCRLF;
468
debug("after getopt loop\n");
469
if (argc < optind + 2) /* check parameter count */
472
/* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
473
if('0' <= argv[optind][0] && argv[optind][0] <= '9') {
474
/* a number first, assume it's a speed (BSD style) */
475
parse_speeds(op, argv[optind++]); /* baud rate(s) */
476
op->tty = argv[optind]; /* tty name */
478
op->tty = argv[optind++]; /* tty name */
479
parse_speeds(op, argv[optind]); /* baud rate(s) */
483
if (argc > optind && argv[optind])
484
setenv ("TERM", argv[optind], 1);
486
#ifdef DO_DEVFS_FIDDLING
488
* some devfs junk, following Goswin Brederlow:
489
* turn ttyS<n> into tts/<n>
490
* turn tty<n> into vc/<n>
492
if (op->tty && strlen(op->tty) < 90) {
496
if (strncmp(op->tty, "ttyS", 4) == 0) {
497
strcpy(dev_name, "/dev/");
498
strcat(dev_name, op->tty);
499
if (stat(dev_name, &st) < 0) {
500
strcpy(dev_name, "/dev/tts/");
501
strcat(dev_name, op->tty + 4);
502
if (stat(dev_name, &st) == 0)
503
op->tty = strdup(dev_name + 5);
505
} else if (strncmp(op->tty, "tty", 3) == 0) {
506
strcpy(dev_name, "/dev/");
507
strncat(dev_name, op->tty, 90);
508
if (stat(dev_name, &st) < 0) {
509
strcpy(dev_name, "/dev/vc/");
510
strcat(dev_name, op->tty + 3);
511
if (stat(dev_name, &st) == 0)
512
op->tty = strdup(dev_name + 5);
518
debug("exiting parseargs\n");
521
/* parse_speeds - parse alternate baud rates */
524
parse_speeds(op, arg)
530
debug("entered parse_speeds\n");
531
for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
532
if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
533
error(_("bad speed: %s"), cp);
534
if (op->numspeed >= MAX_SPEED)
535
error(_("too many alternate speeds"));
537
debug("exiting parsespeeds\n");
542
/* update_utmp - update our utmp entry */
549
int mypid = getpid();
553
* The utmp file holds miscellaneous information about things started by
554
* /sbin/init and other system-related events. Our purpose is to update
555
* the utmp entry for the current process, in particular the process type
556
* and the tty line we are listening to. Return successfully only if the
557
* utmp file can be opened for update, and if we are able to find our
558
* entry in the utmp file.
561
utmpname(_PATH_UTMP);
564
/* Find mypid in utmp. Earlier code here tested only
565
utp->ut_type != INIT_PROCESS, so maybe the >= here should be >.
566
The present code is taken from login.c, so if this is changed,
567
maybe login has to be changed as well. */
568
while ((utp = getutent()))
569
if (utp->ut_pid == mypid
570
&& utp->ut_type >= INIT_PROCESS
571
&& utp->ut_type <= DEAD_PROCESS)
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));
582
strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
583
strncpy(ut.ut_line, line, sizeof(ut.ut_line));
585
strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
588
ut.ut_type = LOGIN_PROCESS;
596
updwtmp(_PATH_WTMP, &ut);
601
if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
603
if ((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
604
ignore_result( write(ut_fd, &ut, sizeof(ut)) );
616
/* open_tty - set up tty as standard { input, output, error } */
618
open_tty(tty, tp, local)
623
/* Get rid of the present standard { output, error} if any. */
627
errno = 0; /* ignore above errors */
629
/* Set up new standard input, unless we are given an already opened port. */
631
if (strcmp(tty, "-")) {
634
/* Sanity checks... */
637
error(_("/dev: chdir() failed: %m"));
638
if (stat(tty, &st) < 0)
639
error("/dev/%s: %m", tty);
640
if ((st.st_mode & S_IFMT) != S_IFCHR)
641
error(_("/dev/%s: not a character device"), tty);
643
/* Open the tty as standard input. */
646
errno = 0; /* ignore close(2) errors */
649
if (open(tty, O_RDWR|O_NONBLOCK, 0) != 0)
650
error(_("/dev/%s: cannot open as standard input: %m"), tty);
655
* Standard input should already be connected to an open port. Make
656
* sure it is open for read/write.
659
if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
660
error(_("%s: not open for read/write"), tty);
663
/* Set up standard output and standard error file descriptors. */
665
if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
666
error(_("%s: dup problem: %m"), tty); /* we have a problem */
669
* The following ioctl will fail if stdin is not a tty, but also when
670
* there is noise on the modem control lines. In the latter case, the
671
* common course of action is (1) fix your cables (2) give the modem more
672
* time to properly reset after hanging up. SunOS users can achieve (2)
673
* by patching the SunOS kernel variable "zsadtrlow" to a larger value;
674
* 5 seconds seems to be a good value.
677
if (tcgetattr(0, tp) < 0)
678
error("%s: tcgetattr: %m", tty);
681
* It seems to be a terminal. Set proper protections and ownership. Mode
682
* 0622 is suitable for SYSV <4 because /bin/login does not change
683
* protections. SunOS 4 login will change the protections to 0620 (write
684
* access for group tty) after the login has succeeded.
686
* Linux login(1) will change tty permissions.
690
* Let us use 0600 for Linux for the period between getty and login
692
ignore_result( chown(tty, 0, 0) ); /* root, sys */
693
ignore_result( chmod(tty, 0600) ); /* 0622: crw--w--w- */
694
errno = 0; /* ignore above errors */
697
/* termio_init - initialize termios settings */
707
speed_t ispeed, ospeed;
709
if (op->flags & F_KEEPSPEED) {
710
ispeed = cfgetispeed(tp); /* save the original setting */
711
ospeed = cfgetospeed(tp);
713
ospeed = ispeed = op->speeds[FIRST_SPEED];
716
* Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
717
* Special characters are set after we have read the login name; all
718
* reads will be done in raw mode anyway. Errors will be dealt with
721
/* flush input and output queues, important for modems! */
722
(void) tcflush(0, TCIOFLUSH);
724
tp->c_iflag = tp->c_lflag = tp->c_oflag = 0;
726
if (!(op->flags & F_KEEPCFLAGS))
727
tp->c_cflag = CS8 | HUPCL | CREAD | (tp->c_cflag & CLOCAL);
729
/* Note that the speed is stored in the c_cflag termios field, so we have
730
* set the speed always when the cflag se reseted.
732
cfsetispeed(tp, ispeed);
733
cfsetospeed(tp, ospeed);
735
if (op->flags & F_LOCAL) {
736
tp->c_cflag |= CLOCAL;
739
#ifdef HAVE_STRUCT_TERMIOS_C_LINE
745
/* Optionally enable hardware flow control */
748
if (op->flags & F_RTSCTS)
749
tp->c_cflag |= CRTSCTS;
752
(void) tcsetattr(0, TCSANOW, tp);
754
/* go to blocking input even in local mode */
755
fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
757
debug("term_io 2\n");
760
/* auto_baud - extract baud rate from modem status message */
773
* This works only if the modem produces its status code AFTER raising
774
* the DCD line, and if the computer is fast enough to set the proper
775
* baud rate before the message has gone by. We expect a message of the
778
* <junk><number><junk>
780
* The number is interpreted as the baud rate of the incoming call. If the
781
* modem does not tell us the baud rate within one second, we will keep
782
* using the current baud rate. It is advisable to enable BREAK
783
* processing (comma-separated list of baud rates) if the processing of
784
* modem status messages is enabled.
788
* Use 7-bit characters, don't block if input queue is empty. Errors will
789
* be dealt with lateron.
793
tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
794
vmin = tp->c_cc[VMIN];
795
tp->c_cc[VMIN] = 0; /* don't block if queue empty */
796
tcsetattr(0, TCSANOW, tp);
799
* Wait for a while, then read everything the modem has said so far and
800
* try to extract the speed of the dial-in call.
804
if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
806
for (bp = buf; bp < buf + nread; bp++) {
807
if (isascii(*bp) && isdigit(*bp)) {
808
if ((speed = bcode(bp))) {
809
cfsetispeed(tp, speed);
810
cfsetospeed(tp, speed);
816
/* Restore terminal settings. Errors will be dealt with lateron. */
819
tp->c_cc[VMIN] = vmin;
820
(void) tcsetattr(0, TCSANOW, tp);
823
/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
838
ignore_result( write(1, "\r\n", 2) ); /* start a new line */
839
#ifdef ISSUE /* optional: show /etc/issue */
840
if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
841
oflag = tp->c_oflag; /* save current setting */
842
tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */
843
(void) tcsetattr(0, TCSADRAIN, tp);
846
while ((c = getc(fd)) != EOF)
855
(void) printf ("%s", uts.sysname);
859
(void) printf ("%s", uts.nodename);
863
(void) printf ("%s", uts.release);
867
(void) printf ("%s", uts.version);
871
(void) printf ("%s", uts.machine);
876
char domainname[MAXHOSTNAMELEN+1];
877
#ifdef HAVE_GETDOMAINNAME
878
if (getdomainname(domainname, sizeof(domainname)))
880
strcpy(domainname, "unknown_domain");
881
domainname[sizeof(domainname)-1] = '\0';
882
printf ("%s", domainname);
888
char *dom = "unknown_domain";
889
char host[MAXHOSTNAMELEN+1];
890
struct addrinfo hints, *info = NULL;
892
memset(&hints, 0, sizeof(hints));
893
hints.ai_flags = AI_CANONNAME;
895
if (gethostname(host, sizeof(host)) ||
896
getaddrinfo(host, NULL, &hints, &info) ||
902
if (info->ai_canonname &&
903
(canon = strchr(info->ai_canonname, '.')))
918
tm = localtime(&now);
921
(void) printf ("%s %s %d %d",
922
nl_langinfo(ABDAY_1 + tm->tm_wday),
923
nl_langinfo(ABMON_1 + tm->tm_mon),
925
tm->tm_year < 70 ? tm->tm_year + 2000 :
928
(void) printf ("%02d:%02d:%02d",
929
tm->tm_hour, tm->tm_min, tm->tm_sec);
934
(void) printf ("%s", op->tty);
941
for (i = 0; speedtab[i].speed; i++) {
942
if (speedtab[i].code == cfgetispeed(tp)) {
943
printf("%ld", speedtab[i].speed);
955
while ((ut = getutent()))
956
if (ut->ut_type == USER_PROCESS)
959
printf ("%d ", users);
961
printf ((users == 1) ? _("user") : _("users"));
973
tp->c_oflag = oflag; /* restore settings */
974
(void) tcsetattr(0, TCSADRAIN, tp); /* wait till output is gone */
979
char hn[MAXHOSTNAMELEN+1];
980
if (gethostname(hn, sizeof(hn)) == 0)
981
ignore_result( write(1, hn, strlen(hn)) );
983
ignore_result( write(1, LOGIN, sizeof(LOGIN) - 1) ); /* always show login prompt */
986
/* next_speed - select next baud rate */
992
static int baud_index = -1;
994
if (baud_index == -1)
996
* if the F_KEEPSPEED flags is set then the FIRST_SPEED is not
997
* tested yet (see termio_init()).
999
baud_index = (op->flags & F_KEEPSPEED) ? FIRST_SPEED :
1002
baud_index = (baud_index + 1) % op->numspeed;
1004
cfsetispeed(tp, op->speeds[baud_index]);
1005
cfsetospeed(tp, op->speeds[baud_index]);
1006
(void) tcsetattr(0, TCSANOW, tp);
1009
/* get_logname - get user name, establish parity, speed, erase, kill, eol */
1011
char *get_logname(op, cp, tp)
1013
struct chardata *cp;
1016
static char logname[BUFSIZ];
1018
char c; /* input character, full eight bits */
1019
char ascval; /* low 7 bits of input character */
1020
int bits; /* # of "1" bits per character */
1021
int mask; /* mask with 1 bit up */
1022
static char *erase[] = { /* backspace-space-backspace */
1023
"\010\040\010", /* space parity */
1024
"\010\040\010", /* odd parity */
1025
"\210\240\210", /* even parity */
1026
"\210\240\210", /* no parity */
1029
/* Initialize kill, erase, parity etc. (also after switching speeds). */
1031
*cp = init_chardata;
1033
/* Flush pending input (esp. after parsing or switching the baud rate). */
1036
(void) tcflush(0, TCIFLUSH);
1038
/* Prompt for and read a login name. */
1040
for (*logname = 0; *logname == 0; /* void */ ) {
1042
/* Write issue file and prompt, with "parity" bit == 0. */
1046
/* Read name, watch for break, parity, erase, kill, end-of-line. */
1048
for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
1050
/* Do not report trivial EINTR/EIO errors. */
1052
if (read(0, &c, 1) < 1) {
1053
if (errno == EINTR || errno == EIO)
1055
error(_("%s: read: %m"), op->tty);
1057
/* Do BREAK handling elsewhere. */
1059
if ((c == 0) && op->numspeed > 1)
1060
return EXIT_SUCCESS;
1061
/* Do parity bit handling. */
1063
if (op->eightbits) {
1065
} else if (c != (ascval = (c & 0177))) { /* "parity" bit on */
1066
for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
1068
bits++; /* count "1" bits */
1069
cp->parity |= ((bits & 1) ? 1 : 2);
1071
/* Do erase, kill and end-of-line processing. */
1076
*bp = 0; /* terminate logname */
1077
cp->eol = ascval; /* set end-of-line char */
1082
cp->erase = ascval; /* set erase character */
1084
ignore_result( write(1, erase[cp->parity], 3) );
1090
cp->kill = ascval; /* set kill character */
1091
while (bp > logname) {
1092
ignore_result( write(1, erase[cp->parity], 3) );
1099
if (!isascii(ascval) || !isprint(ascval)) {
1100
/* ignore garbage characters */ ;
1101
} else if (bp - logname >= sizeof(logname) - 1) {
1102
error(_("%s: input overrun"), op->tty);
1104
ignore_result( write(1, &c, 1) ); /* echo the character */
1105
*bp++ = ascval; /* and store it */
1111
/* Handle names with upper case and no lower case. */
1112
if ((op->flags & F_LCUC) && (cp->capslock = caps_lock(logname))) {
1113
for (bp = logname; *bp; bp++)
1115
*bp = tolower(*bp); /* map name to lower case */
1120
/* termio_final - set the final tty mode bits */
1122
termio_final(op, tp, cp)
1125
struct chardata *cp;
1127
/* General terminal-independent stuff. */
1129
tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
1130
tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK| ECHOKE;
1131
/* no longer| ECHOCTL | ECHOPRT*/
1132
tp->c_oflag |= OPOST;
1133
/* tp->c_cflag = 0; */
1134
tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
1135
tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
1136
tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
1137
tp->c_cc[VEOL] = DEF_EOL;
1139
tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
1140
#elif defined(VSWTCH)
1141
tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */
1144
/* Account for special characters seen in input. */
1146
if (cp->eol == CR) {
1147
tp->c_iflag |= ICRNL; /* map CR in input to NL */
1148
tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
1150
tp->c_cc[VERASE] = cp->erase; /* set erase character */
1151
tp->c_cc[VKILL] = cp->kill; /* set kill character */
1153
/* Account for the presence or absence of parity bits in input. */
1155
switch (cp->parity) {
1156
case 0: /* space (always 0) parity */
1158
case 1: /* odd parity */
1159
tp->c_cflag |= PARODD;
1161
case 2: /* even parity */
1162
tp->c_cflag |= PARENB;
1163
tp->c_iflag |= INPCK | ISTRIP;
1165
case (1 | 2): /* no parity bit */
1166
tp->c_cflag &= ~CSIZE;
1170
/* Account for upper case without lower case. */
1174
tp->c_iflag |= IUCLC;
1177
tp->c_lflag |= XCASE;
1180
tp->c_oflag |= OLCUC;
1183
/* Optionally enable hardware flow control */
1186
if (op->flags & F_RTSCTS)
1187
tp->c_cflag |= CRTSCTS;
1190
/* Finally, make the new settings effective */
1192
if (tcsetattr(0, TCSANOW, tp) < 0)
1193
error("%s: tcsetattr: TCSANOW: %m", op->tty);
1196
/* caps_lock - string contains upper case without lower case */
1203
for (capslock = 0; *s; s++) {
1205
return EXIT_SUCCESS;
1207
capslock = isupper(*s);
1212
/* bcode - convert speed string to speed code; return 0 on failure */
1217
struct Speedtab *sp;
1218
long speed = atol(s);
1220
for (sp = speedtab; sp->speed; sp++)
1221
if (sp->speed == speed)
1226
/* usage - explain */
1228
void __attribute__((__noreturn__)) usage(void)
1230
fprintf(stderr, _("Usage: %s [-8hiLmsUw] [-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);
1234
/* error - report errors to console or syslog; only understands %s and %m */
1236
#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
1239
error(const char *fmt, ...) {
1248
* If the diagnostic is reported via syslog(3), the process name is
1249
* automatically prepended to the message. If we write directly to
1250
* /dev/console, we must prepend the process name ourselves.
1257
(void) str2cpy(buf, progname, ": ");
1258
bp = buf + strlen(buf);
1262
* %s expansion is done by hand. On a System V Release 2 system without
1263
* shared libraries and without syslog(3), linking with the the stdio
1264
* library would make the program three times as big...
1266
* %m expansion is done here as well. Too bad syslog(3) does not have a
1267
* vsprintf() like interface.
1271
while (*fmt && bp < &buf[BUFSIZ-1]) {
1272
if (strncmp(fmt, "%s", 2) == 0) {
1273
xstrncpy(bp, va_arg(ap, char *), &buf[BUFSIZ-1] - bp);
1276
} else if (strncmp(fmt, "%m", 2) == 0) {
1277
xstrncpy(bp, strerror(errno), &buf[BUFSIZ-1] - bp);
1288
* Write the diagnostic directly to /dev/console if we do not use the
1289
* syslog(3) facility.
1293
(void) openlog(progname, LOG_PID, LOG_AUTHPRIV);
1294
(void) syslog(LOG_ERR, "%s", buf);
1297
/* Terminate with CR-LF since the console mode is unknown. */
1298
(void) strcat(bp, "\r\n");
1299
if ((fd = open("/dev/console", 1)) >= 0) {
1300
ignore_result( write(fd, buf, strlen(buf)) );
1304
(void) sleep((unsigned) 10); /* be kind to init(8) */