[comp.sources.misc] v05i020: autobauding getty for System V

davev@spked.UUCP (10/28/88)

Posting-number: Volume 5, Issue 20
Submitted-by: "A. Nonymous" <davev@spked.UUCP>
Archive-name: autobaud

[I consider these things black magic:  it's too easy for the modem to pick
up at the wrong spot or something and get the wrong bit pattern.  Or so I
believe....

Various spots in the code are BSD-ish, others are System-V-ish.  It probably
won't work unmodified on anyone else's system.  And some of the things in
this code can only be called kluges.  ++bsa]

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# auto.c autolocal.h autobaud.8 README

echo x - auto.c
cat > "auto.c" << '//E*O*F auto.c//'
/*  autobaud.c - determine tty speed of standard input  (for 4.3 BSD Unix)
 *
 *  Autobaud reads standard input at 2400 baud.  Assuming a carriage return
 *  is typed, the bit pattern received is used to select and set the "true"
 *  speed.  This works for speeds of 300, 1200, 2400, and 9600 baud.  In theory
 *  600 should also work, but empirically a 00 character is read and it doesn't.
 *  Any other speed, or any character other than a carriage return, is likely
 *  to give a wrong result.
 *
 *  Autobaud is primarily intended as a replacement for getty(8), and as such
 *  it also sets a few terminal parameters such as the kill character to
 *  default values.  However, it can also be run from a shell for testing.
 *
 *  usage:  autobaud [-l] [tty_name]
 *
 *  -l		sets "login" mode and execs login after the speed is set
 *  tty_name	specifies a device to autobaud (/dev/tty_name) instead of stdin
 *
 *  Gregg Townsend
 *  University of Arizona
 *  April, 1987
 *
 * -------------------------------------------------------------
 *
 *  David P. van De Kerk, U.S. Army Corps of Engineers,
 *  Sacramento District.  CESPK-ED-T-AMU.
 *  ucdavis.ucdavis.edu!spked!davev
 *
 *  Modified 10/09/88  to work on a Unisys 5000/80 computer,
 *  running System 5 R 2.  In a sense, this is a System 5 port,
 *  although the program does NOT run on an NCR Tower running
 *  System 5 release 1.
 *
 *  Modified the recognition sequences for what was read on Sys V
 *  Changed from sgtty to termio's tty ioctl structure.
 *
 *  Added a bunk, but working, delay sequence, and used sockets on
 *  those systems capable of using them.  Note, while this should work
 *  in theory on our system, which has NET-5000, it doesn't.
 *
 *  Put the terminal type read from the getty defs in the environment
 *
 *  Clear the screen before giving login banner, using the
 *  environment's terminal type, as gotten from inittab.
 *
 *  Note:  Instead of reading inittab for the right stty parameters,
 *  there is one fixed setting for everyone.
 *
 */

#define PGMNAME "autobaud"		/* program name (for diagnostics) */
#define LOGIN "/bin/login"		/* location of login program */
#define ISSUE "/etc/issue"		/* location of pre-login message */
#define STRSIZ 100			/* string size for host and tty names */

#include <signal.h>
#include <curses.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
/*
#include <sys/termio.h>
*/
#include <time.h>
#include <malloc.h>
#ifdef SOCKETS
#include <NET-5000/sys/socket.h>
#endif

#include "autolocal.h"

/* beware these definitions */
#define	TIOCHPCL	(('t'<<8)|2)
#define  TIOCSETN TCSETA
#define  TIOCFLUSH TCFLSH

char *sp[] = {   "0",   "50",   "75",  "110",  "134",  "150",  "200",  "300",
	       "600", "1200", "1800", "2400", "4800", "9600", "EXTA", "EXTB"};

int login = 0;				/* set nonzero if to exec login */
char tty_name[STRSIZ] = "/dev/";		/* buffer for explicit tty name */
char hostname[STRSIZ];			/* buffer for hostname */



/*  main program  */
extern char **environ;

main(argc,argv)
int argc;
char **argv;
{
    struct termio ttyb;
    unsigned char c;
    char s;
    int zero = 0;
	char *dev_name;  /* the name of the user's device */
	char alloc_dev [30];  /* space to put the above into */

    argv++;					/* skip program name */

    if (*argv && !strcmp(*argv,"-l"))
	login++, argv++;			/* if "-l", set login flag */
	dev_name = (char *) alloc_dev;
	dev_name = ttyname (0);
    if (*argv)  {				
	strcpy(tty_name+5,*argv++);		/* if tty given, build name */
	close(0);				/* close previous stdin */
	close(1);				/* close previous stdout */
	close(2);				/* close previous stderr */
	argv++;  /* go past next entry, which is the getty def */
/* if there's another entry, assume it's a desired term type */
    if (*argv)  {
		char term_cmd [30];
		sprintf (term_cmd,"TERM=%s",*argv);
		putenv (term_cmd);
		}

	if (login)  {
	    nap(2000);				/* hold DTR down for 2 sec */
	    chown(tty_name,0,0);			/* make root the owner */
	    chmod(tty_name,0622);		/* and set the protections */
	}
	if (open(tty_name,2) != 0)		/* open once for stdin */
	    abort(tty_name);
	dup(0);					/* dup for stdout */
	dup(0);					/* dup for stderr */
    }

    ioctl(0,TCGETA,&ttyb);
    ttyb.c_cflag = B2400 | CS8;	/* sample line at 2400 baud, 8 bit */
    ttyb.c_cc[2] = ttyb.c_cc[3] = -1;		/* no editing characters */
    ttyb.c_iflag = ttyb.c_iflag | ICRNL;
    ttyb.c_oflag = ttyb.c_oflag | ONLCR;
    ttyb.c_lflag = ttyb.c_lflag  & (~ICANON);
    if (ioctl(0,TIOCSETN,&ttyb) <0)		/* set tty parameters */
	abort("ioctl");
    s = 0;
/* yes, it's gross, but it works. */
	system ("stty nl -lcase");
    while (!s)  {
	nap(100);				/* wait .1 sec for line quiet */
	ioctl(0,TIOCFLUSH,&zero);		/* flush input & output */
	if (read(0,&c,1) != 1)			/* read a character */
		{
	    exit(1);
		}
#ifdef DEBUG
	/* select baud rate based on pattern received */
	display (c);
#endif
	/* select baud rate based on pattern received */
	if (c >= 0xF0)
	    s = B9600;
	else switch (c)  {
	    case 0x80: s = B300;  break;
	    case 0x78: s = B600;  break;
	    case 0xE6: s = B1200; break;
	    case 0x0D: s = B2400; break;
	    case 0x8D: s = B2400; break;
	    default:   s = 0;     break;
	}
    }
    nap(100);					/* let slow input finish */
    ttyb.c_iflag = ttyb.c_iflag  | ICRNL |IXON|IXANY;
    ttyb.c_oflag = ttyb.c_oflag  | ONLCR;
    ttyb.c_cflag = (s & ~PARENB )| CS8;	/* set speeds */
    ttyb.c_cc[0] =  'C' & 037;			/*  ^C = interrupt */
    ttyb.c_cc[2] =  '\b';			/* \b for char correction */
    ttyb.c_cc[3] = 'U' & 037;			/* ^U for line kill */
    ttyb.c_cc[4] =  'D' & 037;			/*  eof = ^D*/
    ttyb.c_lflag = ICANON | ECHO;/* any parity, -TABS, CR, ECHO*/
    ioctl(0,TIOCSETN,&ttyb);			/* set parameters */
    ioctl(0,TIOCFLUSH,&zero);			/* flush I/O */
    ioctl(0,TIOCHPCL,0);			/* set hangup on last close */
    if (login)  {
	sprintf (hostname,SITE_NAME);  /* no gethostname call, so ... */
	initscr();  /* get the environment's idea of what kind of
	             * terminal we're on */
	clear();    /* clear and home */
	echo();     /* add echo to user input */
	move (0,0);
	refresh();  /* update the window.  Note, no endwin.  That
	             * would drop the cursor to the last line. */
/* well, this kind of brute forse sets us to what the getty would
expect upon login.  Sleazy, isn't it ? */
system ("stty echo cs8 hupcl ixon erase ixon ixany icrnl istrip ignpar erase '^H' brkint opost onlcr cread isig icanon echok echo tab3");
/* and while we're at it, display our system */
printf("%s on %s\n",sp[s], hostname);
	cat (ISSUE);	/* display  /etc/issue banner */
	fflush(stdout);				/* flush it */
	execl(LOGIN,"login",NULL);		/* exec login */
	abort("can't exec login");
    } else {
/* well, this kind of brute forse sets us to what the getty would
expect upon login.  Sleazy, isn't it ? */
system ("stty echo cs8 hupcl ixon erase ixon ixany icrnl istrip ignpar erase '^H' brkint opost onlcr cread isig icanon echok echo tab3");
	printf("%s baud\n",sp[s]);		/* from shell,just print speed*/
	exit(0);
    }
}



/*  abort(s) - abort, for reason given in string s, calling perror first
 *
 *  (It's not totally clear what we should do, but we'll do it here for
 *   uniformity.)
 */

abort(s)
char *s;
{
    fprintf(stderr,"%s: ",PGMNAME);	/* display program name */
    perror(s);				/* display detail, and error message */
    if (login)
	nap(5000);			/* prevent fast looping in login mode */
    exit(1);
}




/*  nap(n) - delay for n milliseconds.
 *
 *  if #ifdef SOCKETS, try to use the select function of sockets to
 *  delay that many milliseconds, but it doesn't work right.
 *
 *  What does work is weird, but ... it works.
 *  Put a terminal on a tty port.  Set it to 9600 baud.  Then lock it
 *  away and ignore it, because delays will be obtained by writing 100
 *  characters to this terminal.
 */

static int nap_flag = -1;
static nap_done() { nap_flag = 0; }

nap(n)
int n;
{
#ifdef SOCKETS
int nfound,nfds;
long timeout;
timeout = n;  /* sleep n milliseconds */
	nfound = select (nfds,(int *) 0,(int *) 0,timeout);
#endif
#ifndef SOCKETS
	FILE *fp,*fopen();
	int i;
	if (n > 1000)
/* sleep the correct # of seconds */
		sleep (n/1000);
	else
		{
/* bloody hack time here ... open up a tty that's 9600 baud that
there's an unused device on */
		if ((fp = fopen(DELAY_TTY,"w"))== NULL)
			{
/* if we can't open the line, sleep one second, a safe amount */
				sleep (1);
			}
		else
		{
/* fudge factor delay, figuring 960 chars a second */
	for (i=0;i<n;i++)
		fprintf (fp,"%c",'\n');
/* and close our tty */
		fclose (fp);
		}
		}
#endif
}


#ifdef DEBUG
display(n)
/* put out to a file the proper settings for the line, for debug
*  purposes. This way, we know what the getty thought the baud rate was.
*/
int n;
{
	FILE *fp,*fopen();
		if ((fp = fopen("parity.err","a"))== NULL)
			{
printf ("fail 18\n");
/* if we can't, sleep one second, a safe amount */
			}
		else
		{
		fprintf (fp,"char = %x\n",n);
/* and close our tty */
		fclose (fp);
		}
}

#endif
/*
*  list the file on the screen.  Note, this is taken from BSA's BBS, ua.
*  although, BBS specific stuff has been stripped out.  This routine
*  is used to display the /etc/issue file.
*/
cat(file)
    char *file;
    {
    FILE *f;
    char ch;

    if ((f = fopen(file, "r")) == NULL)
	{
	fprintf (stderr,"Cannot open file %s\n.",file);
	return;
	}
    while ((ch = getc(f)) != EOF)
		putchar(ch);
    fclose(f);
	return (1);
}
//E*O*F auto.c//

echo x - autolocal.h
cat > "autolocal.h" << '//E*O*F autolocal.h//'
/*
*
*  CESPK-ED-T-AMU, David P. van De Kerk, U.S. Army COE,
*  Sacramento District.  20 Oct 88
*
*/

#define SITE_NAME "spked"  /* our host name */
#define DELAY_TTY "/dev/hty10"  /* the tty we're sending, at 9600 baud,
                                * junk to so we can delay .1 seconds */
//E*O*F autolocal.h//

echo x - autobaud.8
cat > "autobaud.8" << '//E*O*F autobaud.8//'
.TH AUTOBAUD 8 "20 October 1988" "U.S. Army COE"
.SH NAME
autobaud \- terminal speed detection
.SH SYNOPSIS
\fB/etc/autobaud [ \-l ] [ \fIttyname\fB ]
.SH DESCRIPTION
.I Autobaud
is a simpler replacement for
.IR getty (8),
the crucial difference being that
.I autobaud
performs line speed detection as distinguished from cycling.
The speeds recognized are 300, 1200, 2400, and 9600 baud.
Autobaud expects the user to type a carriage return and will loop
until one is received.
.PP
If
.I ttyname
is given,
.I autobaud
samples
.RI /dev/ ttyname
instead of standard input.
The
.B \-l
option is normally set in /etc/inittab
and causes
.I autobaud
to exec
.IR login (1)
after determining the speed.
Here is an example inittab entry:
.br
h0a0:1:respawn:/etc/autobaud -l hty00 ignored svt100
.br
and clears the screen, according to curses.
.PP
Autobaud sets these terminal parameters:  no parity, no tabs, echo,
erase ^H, kill ^U, interrupt ^C.
.SH FILES
.PP
/etc/inittab
.br
/dev/\fIttyname\fP
.br
/etc/issue
.br
/dev/\fIdelay_tty\fP
.br
/usr/lib/terminfo
.SH "SEE ALSO"
init(8), getty(8), inittab (4), issue (4), login(1)
.SH BUGS
.PP
BREAK and NUL have no effect, but typing any other character is
likely to cause
.I autobaud
to set the speed incorrectly.  If this happens, wait two minutes,
WITHOUT touching the keyboard.  The login process will die, and autobaud
will fire up again, allowing another attempt.
.PP
Theory says that 600 baud should also work, but it doesn't.
.SH HISTORY
.PP
Originally written for 4.3 BSD at the University of Arizona.  Hacked
for Sys V.
//E*O*F autobaud.8//

echo x - README
cat > "README" << '//E*O*F README//'
    David P. van De Kerk, U.S. Army Corps of Engineers,
    Sacramento District.  CESPK-ED-T-AMU.
    ucdavis.ucdavis.edu!spked!davev
    20-OCT-88

    This is an autobauding getty.  It was originally written for 4.3 BSD,
    by Gregg Townsend, University of Arizona, April, 1987
  
    but has been modified for a Unisys 5000/80 computer running Unisys's
    Sys V r2.  Note:  While it runs on the Unisys 5000/80, I make NO
    claims about it running on any other machine.

    PLEASE carefully read nap() in auto.c.

Manifest -----------------------------------------------------

README      -- This is it.
auto.c      -- Main sources.
autolocal.h -- Put your site name in there
autobaud.8  -- Manual page
Makefile    -- Make file for the program, tags, and shar file.
//E*O*F README//

exit 0
---
The contents of this message are totally unauthorized, and represent no person
or entity within any agency, nor any statement of policy.
			Standard Form 1 Disclaimer (Rev. 4-87)
	{{seismo|ihnp4!}lll-crg|sdcsvax|{decvax!}ucbvax}!ucdavis!spked!davev