From c6f2c739399a263085b3a2fede442eb34b352638 Mon Sep 17 00:00:00 2001 From: davidn Date: Sun, 2 Feb 1997 14:24:57 +0000 Subject: [PATCH] Added: ic=expect/send script modem init script ac=expect/send script modem answer script ct#val chat script timeout (seconds) rt#val recycle timeout (seconds) if 'ac' set dc#val debug bitmask for debugging chat scripts hw (boolean) enable crtscts handshaking if=path 'issue' file sent prior login prompt chat.c is a simplistic expect/send chat module. --- libexec/getty/Makefile | 3 +- libexec/getty/chat.c | 526 +++++++++++++++++++++++++++++++++++++++ libexec/getty/extern.h | 1 + libexec/getty/gettytab.5 | 123 ++++++++- libexec/getty/gettytab.h | 8 + libexec/getty/init.c | 7 + libexec/getty/main.c | 233 +++++++++++++---- libexec/getty/subr.c | 5 + 8 files changed, 852 insertions(+), 54 deletions(-) create mode 100644 libexec/getty/chat.c diff --git a/libexec/getty/Makefile b/libexec/getty/Makefile index 9ee0a62d2406..cfd805f5d620 100644 --- a/libexec/getty/Makefile +++ b/libexec/getty/Makefile @@ -2,7 +2,7 @@ # $FreeBSD$ PROG= getty -SRCS= main.c init.c subr.c +SRCS= main.c init.c subr.c chat.c DPADD= ${LIBUTIL} LDADD= -lutil MAN5= gettytab.5 ttys.5 @@ -11,3 +11,4 @@ MAN8= getty.8 #CFLAGS+= -Wall -Wstrict-prototypes -Wno-unused -Wwrite-strings .include + diff --git a/libexec/getty/chat.c b/libexec/getty/chat.c new file mode 100644 index 000000000000..97148dfb95ce --- /dev/null +++ b/libexec/getty/chat.c @@ -0,0 +1,526 @@ +/*- + * Copyright (c) 1997 + * David L Nugent . + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. This work was done expressly for inclusion into FreeBSD. Other use + * is permitted provided this notation is included. + * 4. Absolutely no warranty of function or purpose is made by the authors. + * 5. Modifications may be freely made to this file providing the above + * conditions are met. + * + * Modem chat module - send/expect style functions for getty + * For semi-intelligent modem handling. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +#define PAUSE_CH (unsigned char)'\xff' /* pause kludge */ + +#define CHATDEBUG_RECEIVE 0x01 +#define CHATDEBUG_SEND 0x02 +#define CHATDEBUG_EXPECT 0x04 +#define CHATDEBUG_MISC 0x08 + +#define CHATDEBUG_DEFAULT 0 +#define CHAT_DEFAULT_TIMEOUT 10 + + +static int chat_debug = CHATDEBUG_DEFAULT; +static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */ + +static volatile int alarmed = 0; + + +static void chat_alrm __P((int)); +static int chat_unalarm __P((void)); +static int getdigit __P((unsigned char **, int, int)); +static char **read_chat __P((char **)); +static char *cleanchr __P((char **, unsigned char)); +static char *cleanstr __P((const unsigned char *, int)); +static const char *result __P((int)); +static int chat_expect __P((const char *)); +static int chat_send __P((char const *)); + + +/* + * alarm signal handler + * handle timeouts in read/write + * change stdin to non-blocking mode to prevent + * possible hang in read(). + */ + +static void +chat_alrm(signo) + int signo; +{ + int on = 1; + + alarm(1); + alarmed = 1; + signal(SIGALRM, chat_alrm); + ioctl(STDIN_FILENO, FIONBIO, &on); +} + + +/* + * Turn back on blocking mode reset by chat_alrm() + */ + +static int +chat_unalarm() +{ + int off = 0; + return ioctl(STDIN_FILENO, FIONBIO, &off); +} + + +/* + * convert a string of a given base (octal/hex) to binary + */ + +static int +getdigit(ptr, base, max) + unsigned char **ptr; + int base, max; +{ + int i, val = 0; + char * q; + + static const char xdigits[] = "0123456789abcdef"; + + for (i = 0, q = *ptr; i++ < max; ++q) { + int sval; + const char * s = strchr(xdigits, tolower(*q)); + + if (s == NULL || (sval = s - xdigits) >= base) + break; + val = (val * base) + sval; + } + *ptr = q; + return val; +} + + +/* + * read_chat() + * Convert a whitespace delimtied string into an array + * of strings, being expect/send pairs + */ + +static char ** +read_chat(chatstr) + char **chatstr; +{ + char *str = *chatstr; + char **res = NULL; + + if (str != NULL) { + char *tmp = NULL; + int l; + + if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL && + (res=malloc((l / 2 + 1) * sizeof(char *))) != NULL) { + static char ws[] = " \t"; + char * p; + + for (l = 0, p = strtok(strcpy(tmp, str), ws); + p != NULL; + p = strtok(NULL, ws)) + { + unsigned char *q, *r; + + /* Read escapes */ + for (q = r = (unsigned char *)p; *r; ++q) + { + int val; + + if (*q == '\\') + { + /* handle special escapes */ + switch (*++q) + { + case 'a': /* bell */ + *r++ = '\a'; + break; + case 'r': /* cr */ + *r++ = '\r'; + break; + case 'n': /* nl */ + *r++ = '\n'; + break; + case 'f': /* ff */ + *r++ = '\f'; + break; + case 'b': /* bs */ + *r++ = '\b'; + break; + case 'e': /* esc */ + *r++ = 27; + break; + case 't': /* tab */ + *r++ = '\t'; + break; + case 'p': /* pause */ + *r++ = PAUSE_CH; + break; + case 's': + case 'S': /* space */ + *r++ = ' '; + break; + case 'x': /* hexdigit */ + ++q; + *r++ = getdigit(&q, 16, 2); + --q; + break; + case '0': /* octal */ + ++q; + *r++ = getdigit(&q, 8, 3); + --q; + break; + default: /* literal */ + *r++ = *q; + break; + case 0: /* not past eos */ + --q; + break; + } + } else { + /* copy standard character */ + *r++ == *q; + } + } + + /* Remove surrounding quotes, if any + */ + if (*p == '"' || *p == '\'') { + q = strrchr(p+1, *p); + if (q != NULL && *q == *p && q[1] == '\0') { + *q = '\0'; + strcpy(p, p+1); + } + } + + res[l++] = p; + } + res[l] = NULL; + *chatstr = tmp; + return res; + } + free(tmp); + } + return res; +} + + +/* + * clean a character for display (ctrl/meta character) + */ + +static char * +cleanchr(buf, ch) + char **buf; + unsigned char ch; +{ + int l; + static char tmpbuf[5]; + char * tmp = buf ? *buf : tmpbuf; + + if (ch & 0x80) { + strcpy(tmp, "M-"); + l = 2; + ch &= 0x7f; + } else + l = 0; + + if (ch < 32) { + tmp[l++] = '^'; + tmp[l++] = ch + '@'; + } else if (ch == 127) { + tmp[l++] = '^'; + tmp[l++] = '?'; + } else + tmp[l++] = ch; + tmp[l] = '\0'; + + if (buf) + *buf = tmp + l; + return tmp; +} + + +/* + * clean a string for display (ctrl/meta characters) + */ + +static char * +cleanstr(s, l) + const unsigned char *s; + int l; +{ + static unsigned char * tmp = NULL; + static int tmplen = 0; + + if (tmplen < l * 4 + 1) + tmp = realloc(tmp, tmplen = l * 4 + 1); + + if (tmp == NULL) { + tmplen = 0; + return (char *)"(mem alloc error)"; + } else { + int i = 0; + char * p = tmp; + + while (i < l) + cleanchr(&p, s[i++]); + *p = '\0'; + } + + return tmp; +} + + +/* + * return result as an pseudo-english word + */ + +static const char * +result(r) + int r; +{ + static const char * results[] = { + "OK", "MEMERROR", "IOERROR", "TIMEOUT" + }; + return results[r & 3]; +} + + +/* + * chat_expect() + * scan input for an expected string + */ + +static int +chat_expect(str) + const char *str; +{ + int len, r = 0; + + if (chat_debug & CHATDEBUG_EXPECT) + syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str))); + + if ((len = strlen(str)) > 0) { + int i = 0; + char * got; + + if ((got = malloc(len + 1)) == NULL) + r = 1; + else { + + memset(got, 0, len+1); + alarm(chat_alarm); + alarmed = 0; + + while (r == 0 && i < len) { + if (alarmed) + r = 3; + else { + unsigned char ch; + + if (read(STDIN_FILENO, &ch, 1) == 1) { + + if (chat_debug & CHATDEBUG_RECEIVE) + syslog(LOG_DEBUG, "chat_recv '%s' m=%d", + cleanchr(NULL, ch), i); + + if (ch == str[i]) + got[i++] = ch; + else if (i > 0) { + int j = 1; + + /* See if we can resync on a + * partial match in our buffer + */ + while (j < i && memcmp(got + j, str, i - j) != NULL) + j++; + if (j < i) + memcpy(got, got + j, i - j); + i -= j; + } + } else + r = alarmed ? 3 : 2; + } + } + alarm(0); + chat_unalarm(); + alarmed = 0; + free(got); + } + } + + if (chat_debug & CHATDEBUG_EXPECT) + syslog(LOG_DEBUG, "chat_expect %s", result(r)); + + return r; +} + + +/* + * chat_send() + * send a chat string + */ + +static int +chat_send(str) + char const *str; +{ + int r = 0; + + if (chat_debug && CHATDEBUG_SEND) + syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str))); + + if (*str) { + alarm(chat_alarm); + alarmed = 0; + while (r == 0 && *str) + { + unsigned char ch = (unsigned char)*str++; + + if (alarmed) + r = 3; + else if (ch == PAUSE_CH) + usleep(500000); /* 1/2 second */ + else { + usleep(10000); /* be kind to modem */ + if (write(STDOUT_FILENO, &ch, 1) != 1) + r = alarmed ? 3 : 2; + } + } + alarm(0); + chat_unalarm(); + alarmed = 0; + } + + if (chat_debug & CHATDEBUG_SEND) + syslog(LOG_DEBUG, "chat_send %s", result(r)); + + return r; +} + + +/* + * getty_chat() + * + * Termination codes: + * -1 - no script supplied + * 0 - script terminated correctly + * 1 - invalid argument, expect string too large, etc. + * 2 - error on an I/O operation or fatal error condition + * 3 - timeout waiting for a simple string + * + * Parameters: + * char *scrstr - unparsed chat script + * timeout - seconds timeout + * debug - debug value (bitmask) + */ + +int +getty_chat(scrstr, timeout, debug) + char *scrstr; + int timeout, debug; +{ + int r = -1; + + chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT; + chat_debug = debug; + + if (scrstr != NULL) { + char **script; + + if (chat_debug & CHATDEBUG_MISC) + syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr); + + if ((script = read_chat(&scrstr)) != NULL) { + struct termios tsave_in, tsave_out; + + if (tcgetattr(STDIN_FILENO, &tsave_in) == -1 || + tcgetattr(STDOUT_FILENO, &tsave_out) == -1) { + syslog(LOG_ERR, "tcgetattr() failed in chat: %m"); + r = 2; + } else { + int i = r = 0; + int off = 0; + sig_t old_alarm; + struct termios tneed; + + /* We need to be in raw mode for all this + */ + tneed = tsave_in; + cfmakeraw(&tneed); + tcsetattr(STDIN_FILENO, TCSANOW, &tneed); + tcsetattr(STDOUT_FILENO, TCSANOW, &tneed); + + old_alarm = signal(SIGALRM, chat_alrm); + chat_unalarm(); /* Force blocking mode at start */ + + /* + * This is the send/expect loop + */ + while (r == 0 && script[i] != NULL) + if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL) + r = chat_send(script[i++]); + + signal(SIGALRM, old_alarm); + free(script); + free(scrstr); + + /* Restore flags & previous termios settings + */ + ioctl(STDIN_FILENO, FIONBIO, &off); + tcsetattr(STDIN_FILENO, TCSANOW, &tsave_in); + tcsetattr(STDOUT_FILENO, TCSANOW, &tsave_out); + } + } + + if (chat_debug & CHATDEBUG_MISC) + syslog(LOG_DEBUG, "getty_chat %s", result(r)); + + } + return r; +} diff --git a/libexec/getty/extern.h b/libexec/getty/extern.h index 7d216fcefc1b..0ac3f11a0f98 100644 --- a/libexec/getty/extern.h +++ b/libexec/getty/extern.h @@ -60,3 +60,4 @@ void setchars __P((void)); void setdefaults __P((void)); void setflags __P((int)); int speed __P((int)); +int getty_chat __P((char *, int, int)); diff --git a/libexec/getty/gettytab.5 b/libexec/getty/gettytab.5 index 8cc57c2829d5..d7384f019bc8 100644 --- a/libexec/getty/gettytab.5 +++ b/libexec/getty/gettytab.5 @@ -76,6 +76,7 @@ no entry in the table obtained, nor one in the special table. .Bl -column Namexx /usr/bin/login Default .It Sy Name Type Default Description +.It "ac str unused expect-send chat script for modem answer" .It "ap bool false terminal uses any parity" .It "bk str 0377 alternate end of line character (input break)" .It "c0 num unused tty control flags to write messages" @@ -88,7 +89,9 @@ table. .It "co bool false console - add" .Ql \en after login prompt -.It "de num 0 delay secs before writing first prompt" +.It "ct num 10 chat timeout for ac/ic scripts" +.It "dc num 0 chat debug bitmask" +.It "de num 0 delay secs and flush input before writing first prompt" .It "ds str" Ta So Li ^Y Sc Ta .No "delayed suspend character" .It "dx bool false set" @@ -116,9 +119,12 @@ hangup line on last close .No "hostname editing string" .It "hn str hostname hostname" .It "ht bool false terminal has real tabs" +.It "hw bool false do cts/rts hardware flow control" .It "i0 num unused tty input flags to write messages" .It "i1 num unused tty input flags to read login name" .It "i2 num unused tty input flags to leave terminal as" +.It "ic str unused expect-send chat script for modem initialization" +.It "if str unused display named file before prompt" .It "ig bool false ignore garbage characters in login name" .It "im str" Ta Dv NULL Ta .No "initial (banner) message" @@ -157,6 +163,7 @@ port selector .No "quit character" .It "rp str" Ta So Li ^R Sc Ta .No "line retype character" +.It "rt num unused ring timeout when using ac" .It "rw bool false do" .Tn NOT use raw for input, use cbreak @@ -269,7 +276,7 @@ information about the environment in which .Xr getty 8 is running. .Pp -.Bl -tag -width \&%xxx -compact +.Bl -tag -offset indent -width \&%xxxxxxxxxxxxxx .It \&%d The current date and time in the locale's representation as of the .Em \&Lo @@ -364,12 +371,122 @@ If string is specified and a PPP link bringup sequence is recognized, getty will invoke the program referenced by the pp option. This can be used to handle incoming PPP calls. +.Pp +.Nm getty +provides some basic intelligent modem handling by providing a chat +script feature available via two capabilities: +.Pp +.Bl -tag -offset indent -width \&xxxxxxxx -compact +.It ic +Chat script to initialize modem. +.It ac +Chat script to answer a call. +.El +.Pp +A chat script is a set of expect/send string pairs. +When a chat string starts, +.Nm Getty +will wait for the first string, and if it finds it, will send the +second, and so on. +Strings specified are separated by one or more tabs or spaces. +Strings may contain standard ascii characters and special 'escapes', +which consist of a backslash character followed by one or more +characters which are interpreted as follows: +.Pp +.Bl -tag -offset indent -width \&xxxxxxxx -compact +.It \ea +bell character. +.It \eb +backspace. +.It \en +newline. +.It \ee +escape. +.It \ef +formfeed. +.It \ep +half-second pause. +.It \er +carriage return. +.It \eS, \es +space character. +.It \et +tab. +.It \exNN +hexadecimal byte value. +.It \e0NNN +octal byte value. +.El +.Pp +Note that the +.Ql \ep +sequence is only valid for send strings and causes a half-second +pause between sending the previous and next characters. +Hexidecimal values are, at most, 2 hex digits long, and octal +values are a maximum of 3 octal digits. +.Pp +The +.Em \&ic +chat sequence is used to initialize a modem or similar device. +A typical example of an init chat script for a modem with a +hayes compatible command set might look like this: +.Pp +.Dl :ic="" ATE0Q0V1\er OK\er ATS0=0\er OK\er: +.Pp +This script waits for nothing (which always succeeds), sends +a sequence to ensure that the modem is in the correct mode +(suppress command echo, send responses in verbose mode), +and then disables auto-answer. +It waits for an "OK" response before it terminates. +The init sequence is used to check modem responses to ensure that +the modem is functioning correctly. +If the init script fails to complete, +.Nm getty +considers this to be fatal, and results in an error logged via +.Xr syslogd 8 , +and exiting. +.Pp +Similarly, an answer chat script is used to manually answer the +phone in response to (usually) a "RING". +When run with an answer script, +.Nm getty +opens the port in non-blocking mode, clears any extraneous input +and waits for data on the port. +As soon as any data is available, the answer chat script is +started and scanned for a string, and responds according to +the answer chat script. +With a hayes compatible modem, this would normally look something +like: +.Pp +.Dl :ac=RING\er ATA\er CONNECT: +.Pp +This causes the modem to answer the call via the "ATA" command, +then scans input for a "CONNECT" string. +If this is received before a +.Em \&ct timeout, then a normal login sequence commences. +.Pp +The +.Em \&ct +capability specifies a timeout for all send and expect strings. +This timeout is set individually for each expect wait and send +string and must be at least as long as the time it takes for +a connection to be established between a remote and local +modem (usually around 10 seconds). +.Pp +In most situations, you will want to flush any additional +input after the connection has been detected, and the +.Em \&de +capability may be used to do that, as well as delay for a +short time after the connection has been established during +which all of the connection data has been sent by the modem. +.Pp .Sh SEE ALSO .Xr login 1 , .Xr gethostname 3 , .Xr uname 3 , .Xr termcap 5 , -.Xr getty 8 . +.Xr getty 8 , +.Xr telnetd 8 . .Sh BUGS The special characters (erase, kill, etc.) are reset to system defaults by diff --git a/libexec/getty/gettytab.h b/libexec/getty/gettytab.h index 3727c1bd84fa..0456f7c81494 100644 --- a/libexec/getty/gettytab.h +++ b/libexec/getty/gettytab.h @@ -87,6 +87,9 @@ struct gettyflags { #define LN gettystrs[23].value #define Lo gettystrs[24].value #define PP gettystrs[25].value +#define IF gettystrs[26].value +#define IC gettystrs[27].value +#define AC gettystrs[28].value /* * Numeric definitions. @@ -132,6 +135,10 @@ struct gettyflags { #define O2 gettynums[24].value #define O2set gettynums[24].set #define DE gettynums[25].value +#define RTset gettynums[26].set +#define RT gettynums[26].value +#define CT gettynums[27].value +#define DC gettynums[28].value /* * Boolean values. @@ -162,4 +169,5 @@ struct gettyflags { #define DX gettyflags[20].value #define NP gettyflags[21].value #define MB gettyflags[22].value +#define HW gettyflags[23].value diff --git a/libexec/getty/init.c b/libexec/getty/init.c index eca9280e9d53..f6551f96dd44 100644 --- a/libexec/getty/init.c +++ b/libexec/getty/init.c @@ -77,6 +77,9 @@ struct gettystrs gettystrs[] = { { "ln", &tmode.c_cc[VLNEXT] }, /* literal next */ { "Lo" }, /* locale for strftime() */ { "pp" }, /* ppp login program */ + { "if" }, /* sysv-like 'issue' filename */ + { "ic" }, /* modem init-chat */ + { "ac" }, /* modem answer-chat */ { 0 } }; @@ -107,6 +110,9 @@ struct gettynums gettynums[] = { { "o1" }, /* input o_flags */ { "o2" }, /* user mode o_flags */ { "de" }, /* delay before sending 1st prompt */ + { "rt" }, /* reset timeout */ + { "ct" }, /* chat script timeout */ + { "dc" }, /* debug chat script value */ { 0 } }; @@ -135,5 +141,6 @@ struct gettyflags gettyflags[] = { { "dx", 0 }, /* set decctlq */ { "np", 0 }, /* no parity at all (8bit chars) */ { "mb", 0 }, /* do MDMBUF flow control */ + { "hw", 0 }, /* do CTSRTS flow control */ { 0 } }; diff --git a/libexec/getty/main.c b/libexec/getty/main.c index b0cdbc480b30..59492b824089 100644 --- a/libexec/getty/main.c +++ b/libexec/getty/main.c @@ -128,8 +128,6 @@ char partab[] = { #define KILL tmode.c_cc[VKILL] #define EOT tmode.c_cc[VEOF] -jmp_buf timeout; - static void dingdong __P((int)); static int getname __P((void)); static void interrupt __P((int)); @@ -140,9 +138,15 @@ static void putf __P((const char *)); static void putpad __P((const char *)); static void puts __P((const char *)); static void timeoverrun __P((int)); +static char *getline __P((int)); +static void setttymode __P((const char *, int)); +static void setdefttymode __P((const char *)); +static int opentty __P((const char *, int)); int main __P((int, char **)); +jmp_buf timeout; + static void dingdong(signo) int signo; @@ -179,7 +183,7 @@ main(argc, argv) { extern char **environ; const char *tname; - int repcnt = 0, failopenlogged = 0; + int repcnt = 0, failopenlogged = 0, first_time = 1; struct rlimit limit; int rval; @@ -199,6 +203,12 @@ main(argc, argv) limit.rlim_cur = GETTY_TIMEOUT; (void)setrlimit(RLIMIT_CPU, &limit); + gettable("default", defent); + gendefaults(); + tname = "default"; + if (argc > 1) + tname = argv[1]; + /* * The following is a work around for vhangup interactions * which cause great problems getting window systems started. @@ -207,7 +217,7 @@ main(argc, argv) * J. Gettys - MIT Project Athena. */ if (argc <= 2 || strcmp(argv[2], "-") == 0) - strcpy(ttyn, ttyname(0)); + strcpy(ttyn, ttyname(STDIN_FILENO)); else { int i; @@ -217,23 +227,54 @@ main(argc, argv) chown(ttyn, 0, 0); chmod(ttyn, 0600); revoke(ttyn); - while ((i = open(ttyn, O_RDWR)) == -1) { - if ((repcnt % 10 == 0) && - (errno != ENXIO || !failopenlogged)) { - syslog(LOG_ERR, "%s: %m", ttyn); - closelog(); - failopenlogged = 1; + + gettable(tname, tabent); + + /* Init modem sequence has been specified + */ + if (IC) { + if (!opentty(ttyn, O_RDWR|O_NONBLOCK)) + exit(1); + setdefttymode(tname); + if (getty_chat(IC, CT, DC) > 0) { + syslog(LOG_ERR, "modem init problem on %s", ttyn); + exit(1); } - repcnt++; - sleep(60); } - login_tty(i); + + if (AC) { + int i, rfds; + struct timeval timeout; + + if (!opentty(ttyn, O_RDWR|O_NONBLOCK)) + exit(1); + setdefttymode(tname); + rfds = 1 << 0; /* FD_SET */ + timeout.tv_sec = RT; + timeout.tv_usec = 0; + i = select(32, (fd_set*)&rfds, (fd_set*)NULL, + (fd_set*)NULL, RT ? &timeout : NULL); + if (i < 0) { + syslog(LOG_ERR, "select %s: %m", ttyn); + } else if (i == 0) { + syslog(LOG_NOTICE, "recycle tty %s", ttyn); + exit(0); /* recycle for init */ + } + i = getty_chat(AC, CT, DC); + if (i > 0) { + syslog(LOG_ERR, "modem answer problem on %s", ttyn); + exit(1); + } + } else { /* blocking open */ + if (!opentty(ttyn, O_RDWR)) + exit(1); + } } } /* Start with default tty settings */ - if (tcgetattr(0, &tmode) < 0) { - syslog(LOG_ERR, "%s: %m", ttyn); + if (tcgetattr(STDIN_FILENO, &tmode) < 0) { + syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); exit(1); } /* @@ -249,37 +290,9 @@ main(argc, argv) tmode.c_cflag = TTYDEF_CFLAG; omode = tmode; - gettable("default", defent); - gendefaults(); - tname = "default"; - if (argc > 1) - tname = argv[1]; for (;;) { - int off = 0; - gettable(tname, tabent); - if (OPset || EPset || APset) - APset++, OPset++, EPset++; - setdefaults(); - off = 0; - (void)tcflush(0, TCIOFLUSH); /* clear out the crap */ - ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */ - ioctl(0, FIOASYNC, &off); /* ditto for async mode */ - - if (IS) - cfsetispeed(&tmode, speed(IS)); - else if (SP) - cfsetispeed(&tmode, speed(SP)); - if (OS) - cfsetospeed(&tmode, speed(OS)); - else if (SP) - cfsetospeed(&tmode, speed(SP)); - setflags(0); - setchars(); - if (tcsetattr(0, TCSANOW, &tmode) < 0) { - syslog(LOG_ERR, "%s: %m", ttyn); - exit(1); - } + setttymode(tname, 0); if (AB) { tname = autobaud(); continue; @@ -294,15 +307,34 @@ main(argc, argv) /* if a delay was specified then sleep for that number of seconds before writing the initial prompt */ - if(DE) + if(DE) { sleep(DE); + /* remove any noise */ + (void)tcflush(STDIN_FILENO, TCIOFLUSH); + } + + /* if this is the first time through this, and an + issue file has been given, then send it */ + if (first_time && IF) { + int fd; + + if ((fd = open(IF, O_RDONLY)) != -1) { + char * cp; + + while ((cp = getline(fd)) != NULL) { + putf(cp); + } + close(fd); + } + first_time = 0; + } if (IM && *IM) putf(IM); if (setjmp(timeout)) { cfsetispeed(&tmode, B0); cfsetospeed(&tmode, B0); - (void)tcsetattr(0, TCSANOW, &tmode); + (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); exit(1); } if (TO) { @@ -336,8 +368,8 @@ main(argc, argv) if (lower || LC) tmode.sg_flags &= ~LCASE; #endif - if (tcsetattr(0, TCSANOW, &tmode) < 0) { - syslog(LOG_ERR, "%s: %m", ttyn); + if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { + syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); exit(1); } signal(SIGINT, SIG_DFL); @@ -360,13 +392,88 @@ main(argc, argv) } } +static int +opentty(const char *ttyn, int flags) +{ + int i, j = 0; + int failopenlogged = 0; + + while (j < 10 && (i = open(ttyn, flags)) == -1) + { + if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) { + syslog(LOG_ERR, "open %s: %m", ttyn); + failopenlogged = 1; + } + j++; + sleep(60); + } + if (i == -1) { + syslog(LOG_ERR, "open %s: %m", ttyn); + return 0; + } + else { + login_tty(i); + return 1; + } +} + +static void +setdefttymode(tname) + const char * tname; +{ + if (tcgetattr(STDIN_FILENO, &tmode) < 0) { + syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); + exit(1); + } + tmode.c_iflag = TTYDEF_IFLAG; + tmode.c_oflag = TTYDEF_OFLAG; + tmode.c_lflag = TTYDEF_LFLAG; + tmode.c_cflag = TTYDEF_CFLAG; + omode = tmode; + setttymode(tname, 1); +} + +static void +setttymode(tname, raw) + const char * tname; + int raw; +{ + int off = 0; + + gettable(tname, tabent); + if (OPset || EPset || APset) + APset++, OPset++, EPset++; + setdefaults(); + (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */ + ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */ + ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */ + + if (IS) + cfsetispeed(&tmode, speed(IS)); + else if (SP) + cfsetispeed(&tmode, speed(SP)); + if (OS) + cfsetospeed(&tmode, speed(OS)); + else if (SP) + cfsetospeed(&tmode, speed(SP)); + setflags(0); + setchars(); + if (raw) + cfmakeraw(&tmode); + if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { + syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); + exit(1); + } +} + + static int getname() { register int c; register char *np; unsigned char cs; - int ppp_state; + int ppp_state = 0; int ppp_connection = 0; /* @@ -384,7 +491,7 @@ getname() sleep(PF); PF = 0; } - if (tcsetattr(0, TCSANOW, &tmode) < 0) { + if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { syslog(LOG_ERR, "%s: %m", ttyn); exit(1); } @@ -557,6 +664,32 @@ prompt() putchr('\n'); } + +static char * +getline(fd) + int fd; +{ + int i = 0; + static char linebuf[512]; + + /* + * This is certainly slow, but it avoids having to include + * stdio.h unnecessarily. Issue files should be small anyway. + */ + while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) { + if (linebuf[i] == '\n') { + /* Don't rely on newline mode, assume raw */ + linebuf[i++] = '\r'; + linebuf[i++] = '\n'; + linebuf[i] = '\0'; + return linebuf; + } + ++i; + } + linebuf[i] = '\0'; + return i ? linebuf : 0; +} + static void putf(cp) register const char *cp; diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c index 5138698940c8..e01bd1a18fd7 100644 --- a/libexec/getty/subr.c +++ b/libexec/getty/subr.c @@ -284,6 +284,11 @@ setflags(n) else CLR(cflag, MDMBUF); + if (HW) + SET(cflag, CRTSCTS); + else + CLR(cflag, CRTSCTS); + if (NL) { SET(iflag, ICRNL); SET(oflag, ONLCR|OPOST);