karl@ddsw1.MCS.COM (Karl Denninger) (01/21/91)
A program to replace getty on System V machines, this autobauder shares ports with UUCP and CU easily. See distribution restrictions below. #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: autouu.c # Wrapped by karl@ddsw1 on Sun Jan 20 13:40:17 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'autouu.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'autouu.c'\" else echo shar: Extracting \"'autouu.c'\" \(19854 characters\) sed "s/^X//" >'autouu.c' <<'END_OF_FILE' X/* X * Copyright 1990 MCS & Karl Denninger. All rights reserved. X * X * Public use is permitted under the following conditions: X * X * 1) You do not remove my name from the package, or claim you wrote it. X * 2) You distribute ORIGINAL source code with all distributions made, X * modified or not, binary or source. X * 3) You do not attempt to sell the package, or use it to enhance the X * commercial value of any product or service. X * 4) This package is distributed with ABSOLUTELY NO WARRANTY OF ANY X * KIND. If it melts your system to slag YOU are responsible, not X * MCS or myself. The burden rests with you to perform adaquate X * testing before turning this loose on unsuspecting users. X * X * Commercial distribution rights reserved; contact MCS at (708) 808-7200 X * for details on commercial distribution licensing. X * X * Compile with: cc -s -o autouu autouu.c -lc_s -lx X * X */ X/* Autobaud program X X Run in place of 'getty', this will prompt for a name X and call login just like the old one used to do... Only X difference is that it is rather interesting in it's interpretation X of what a 'gettydefs' file is; that is, there isn't one. X X We use modem return messages to determine the baud rate. Locks are X respected as well, allowing the uucp system to share the ports. X X You invoke this with: X /etc/autonew ttyA2 [code] [file] X X from /etc/inittab. "[code]" is the numeric code for the baud rate X to send the initialization string at -- most of the time you want X this to be the highest baud rate your modem will support. X X Notes: X 1) The device name does not have a prefix. It is prepended X automatically (/dev/ is added). X 2) For ISC, use the MODEM CONTROL PORTS. This program can X interlock with UUCP; see their DEVICES file for the X proper flags to set in the DEVICES and DIALERS files. X Use the "new" definitions which have ",M" added (see your X documentation for details). X 3) While a port is being used for dialout, it will show X up in a "who" command as "_Dialout" once data X transmission begins. X 4) Modes and owners will be changed on ports to prevent X random users from using the ports for "cu"s and other X communications uses. This can be easily changed if X desired (look for the "chmod" call in the source). X 5) The file /etc/autobaud.parm must be present if the "file" X argument is missing. If the "file" argument is present, X it points to the control file to be used. The format X is as follows: X First line -- initialization string for ports X Second line -- response to initialization string X Third line -- Generic "connected" message X Up to first "#" alone -- baud codes, rates (text), and X response strings expected. X Next line -- Login prompt X Remainder of file -- Issue file X X Baud codes are the speed codes from termio.h; X 11, for example, is 2400 baud. X X An example /etc/autobaud.parm file: X X AAATE0Q0V1 X OK X CONNECTED X 7 300 CONNECT X 9 1200 CONNECT 1200 X 11 2400 CONNECT 2400 X 13 9600 CONNECT 9600 X 14 19200 CONNECT FAST X # X Login: X X Welcome to the system X <EOF> X X This is a typical file for a system containing both X Telebit and low-speed modems (300-2400 baud). Note X that the "AAA" is doubled to allow the Telebit to X autosync. If you have hardware flow control then X enable it -- otherwise, set the modem up for X Xon/Xoff flow control, BREAK is sent and flushes, X Telebit S66=0 and S58=254 (Autobaud and prefer X 19200). This permits full functionality with the X exception of low-speed UUCP inbound calls through X Telebits; if you have hardware flow control then no X restrictions apply. X X 6) Your I/O board and/or drivers MUST correctly support the X notion of O_NDELAY. In addition, you have to be able to X turn on and off the NDELAY flag with fcntl. LOTS of X intelligent boards broke this; if it's broken this X program will NOT work. ONE HACK: If your NDELAY X interpretation returns non-blocking if CD is down (with X CLOCAL set and NDELAY cleared) this program will function X correctly, although it will eat a small portion of CPU X time to do so. X X 7) Autobaud will wait for a carriage return and use it X to determine the parity of the caller's terminal (either X 8/N/1 or 7/E/1 only). If the user doesn't press anything X within a reasonable time frame, 8/N/1 is assumed. The X message "CONNECTED" is output to the user terminal X immediately after autobaud senses the user's baud rate. X X 8) All modems served by a configuration must use the same X response sequences, although subsets are permitted (ie: the X example file above would work for a USR Courier 2400 and X a Telebit Trailblazer Plus equally well). X X CHECK THE FUNCTIONS "checklock()" and "makelock()" -- they may need X to be modified for your system! In particular, some systems use X binary PIDs and/or store the lock file in a different place. We X currently are set up for HDB UUCP on ISC 2.0.2/2.2. X X Note that this program can share a port with a modem dialing out on X the same line! It will perform with uucp on the same port without X trouble, so long as the locking is done correctly by uucp and other X programs which expect lock files. X X Autobaud removes any stale lock files it finds automatically. X X*/ X X#define MAXISSUE 100 /* Lines in /etc/issue file */ X#define MAXSECONDS 60 /* Timeout at start */ X#define LOGSECONDS 90 /* Timeout at login */ X#define UUCICO "/usr/lib/uucp/uucico" /* Where's uucico? */ X#define BELL 7 /* Makes a "beep" */ X extern char *malloc(); X X#include <stdio.h> X#include <fcntl.h> X#include <sys/types.h> X#include <sys/tty.h> X#include <utmp.h> X#include <signal.h> X#include <errno.h> X#include <termio.h> X#include <sys/stat.h> X X/* Globals */ X int maschan = -1; /* Nothing initially; master channel */ int timeout = 0; /* Timer value for keeping track of time */ XFILE *ferr; /* Error channel */ X slowwrite(chan, str, len) /* Write a string slowly to the port */ int chan, len; char *str; X{ X int x; X char *ptr; X char ch[2]; X X ptr = str; X for (x = 0; x < len; x++) { X ch[0] = *ptr; X write(chan, ch, 1); X nap(10); X ptr++; X } X return; X} X checkmatch(matches, mcount, bfr, speed) /* Any matches in array? */ struct { X char string[40]; X int baud; X char speed[20]; X} matches[]; int mcount; char bfr[]; char speed[]; X{ X int x = 0; X X for (x = 0; x < mcount; x++) { X if (!strcmp(bfr, matches[x].string)) { X strcpy(speed, matches[x].speed); X return(matches[x].baud); X } X } X return(0); X} X X/* External declarations */ extern struct utmp *getutent(), *pututline(); X X X/* Makelock / Checklock - makes / checks for lock files X line - the line to check for a lock X lockflag - if non-zero, checklock will sleep until it sees the X lock is gone, otherwise it returns status X (checklock only) X Returns not zero (-1) if line is locked X X*/ X int makelock(line) char *line; X{ X char tmp[80]; char tmp2[80]; char tbfr[20]; int id; XFILE *id2; int pid; struct stat st; X X sprintf(tmp, "/usr/spool/locks/LTMP..%d", getpid()); X sprintf(tmp2, "/usr/spool/locks/LCK..%s", line); X if ((id = open(tmp, O_WRONLY|O_CREAT, 0660)) < 0) { X exit(1); X } X sprintf(tbfr, "%10d\n", getpid()); X tbfr[11] = 0; X write(id, tbfr, 11); X close(id); X if (!stat(UUCICO, &st)) { /* Find ownership */ X chown(tmp, st.st_uid, st.st_gid); /* Set owner/group */ X } X while (link(tmp, tmp2)) { X if ((id2 = fopen(tmp2, "r")) == (FILE *) NULL) { X sleep(1); /* Slow down.. */ X continue; X } X fscanf(id2, "%d", &pid); /* Read PID from file */ X fclose(id2); /* Be nice.. */ X if (!kill(pid, 0)) { /* Oh oh, a process! */ X return(-1); X } X unlink(tmp2); X } X unlink(tmp); X return(0); X} X int checklock(line, lockflag) char *line; int lockflag; /* If non-zero, wait for open line */ X{ X char ltmp[10]; char tmp[80]; int pid; XFILE *id; X X strcpy(ltmp, line); X sprintf(tmp, "/usr/spool/locks/LCK..%s", ltmp); /* Where are locks? */ X while (!access(tmp, 0)) { /* If file is there */ X if ((id = fopen(tmp, "r")) != (FILE *) NULL) { /* opened? */ X fscanf(id, " %d", &pid); /* Get pid from bfr */ X fclose(id); /* Clean up */ X if (kill(pid, 0)) { /* See if process is alive */ X if (errno == ESRCH) { /* Nope; it died */ X unlink(tmp); /* Clear lock */ X continue; /* Look again */ X } X } X if (lockflag) { /* IF waiting */ X sleep(1); /* Wait/keep going */ X } else { X return(-1); /* Else return locked */ X } X } X } X return(0); /* Line is clear */ X} X settogetty(line) /* Mark process in Getty */ char *line; X{ X int pid; X struct utmp *u; X FILE *fid; X X pid = getpid(); /* Get our pid */ X while ((u = getutent()) != NULL) { /* While there are more lines */ X if (((u->ut_type == INIT_PROCESS) || (u->ut_type == USER_PROCESS)) && (u->ut_pid == pid)) { X strcpy(u->ut_line, line); /* Set line name */ X strcpy(u->ut_user, "_Idle"); /* And name */ X u->ut_pid = getpid(); /* And pid */ X u->ut_type = LOGIN_PROCESS; /* And type */ X pututline(u); /* Do it */ X if ((fid = fopen(WTMP_FILE, "a+")) != NULL) { X fseek(fid, 0L, 2); /* Seek end */ X fwrite((char *) u, sizeof(*u), 1, fid); X fclose(fid); /* Wrote wtmp */ X } X break; X } X } X endutent(); X return; X} X settologin(line) /* Tell the system we're at login state */ char *line; X{ X int pid; X struct utmp *u; X X pid = getpid(); /* Get our pid */ X while ((u = getutent()) != NULL) { /* While there are more lines */ X if ((u->ut_type == LOGIN_PROCESS) && (u->ut_pid == pid)) { X strcpy(u->ut_line, line); X strcpy(u->ut_user, "LOGIN"); /* Change name.. */ X pututline(u); X } X } X endutent(); X return; X} X setlocked(line) /* Fake a utmp entry for dialout lines (flagging) */ char *line; X{ X int pid; X struct utmp *u; X X pid = getpid(); /* Get our pid */ X while ((u = getutent()) != NULL) { /* While there are more lines */ X if ((u->ut_type == INIT_PROCESS) && (u->ut_pid == pid)) { X strcpy(u->ut_line, line); /* Set line name */ X strncpy(u->ut_user, "_Dialout", 8); /* "User" */ X/* u->ut_type = LOGIN_PROCESS; *//* If invisible */ X u->ut_type = USER_PROCESS; /* It's visible */ X pututline(u); X } X } X endutent(); X return; X} X X/* Catch alarm signals X Does two things -- catches the alarms, and also adds one to the X seconds counter. If the user takes too long call 'hangup()' before X returning */ X catch() X{ X signal(SIGALRM, catch); /* Re-enable signal catcher */ X if (timeout++ >= MAXSECONDS) X hangup(); /* Kill the user if he just sat */ X return; /* Do nothing else, just exit */ X} X X/* Make upper case into lower case */ X tlc(ptr) char *ptr; X{ X char *pt; X int qm = 0; X X pt = ptr; X while (*pt != 0) { X if ((*pt >= 'A') && (*pt <= 'Z')) { X *pt = *pt + 32; X qm++; X } X pt++; X } X if (qm) X printf("%c\nWarning: Please use *lower* case on this system%c\n", BELL, BELL); X return; X} X X hangup() /* Make sure the phone gets hung up when we exit */ X{ X struct termio tt_array; X X if (maschan < 0) X exit(1); /* Just exit */ X if (ioctl(maschan, TCGETA, &tt_array)) { /* No delay */ X perror("Get parameter error"); X exit(1); X } X tt_array.c_cflag &= ~(CBAUD|CLOCAL); /* Set baud to 0 */ X tt_array.c_cflag |= HUPCL|CREAD|CS8; /* Set hangup */ X X if (ioctl(maschan, TCSETA, &tt_array)) { /* No delay */ X perror("Hang up error"); X exit(1); X } X exit(0); X} X X/* Here is where all the fun begins; the main program */ X main(argc, argv) int argc; char *argv[]; X{ X int debug = 0; X int x, ch, sw, status; X struct termio tt_array; X FILE *fid, *fid2; X static char tmp[133], loginp[132]; X extern struct utmp *getutent(), *pututline(); X char baud[132]; X char connected[512]; X int fbaud = 0; X int sbaud = 0; X char line[80]; X struct stat st; X char init[80]; X char iresp[80]; X int initbaud; X char speed[20]; X struct { X char string[40]; X int baud; X char speed[20]; X } matches[20]; X char *istring[MAXISSUE]; X int mcount = 0; X int bcount = 0; X int icount = 0; X int lcount = 0; X char bc[2]; X char bfr[20]; X int satisfied = 0; X int hoseline; X int quit = 0; X int stop = 0; X X signal(SIGALRM, catch); /* Catch alarms */ X signal(SIGINT, SIG_IGN); /* Ignore interrupts */ X if (argc > 4) X debug++; X initbaud = atoi(argv[2]); /* Initial baud rate */ X strcpy(init, "ATZ\r"); /* Send this to init */ X if (argc > 3) X fid = fopen(argv[3], "r"); /* Try to open it */ X else X fid = fopen("/etc/autobaud.parm", "r"); /* Try to open it */ X if (fid != (FILE *) NULL) { X fgets(init, 80, fid); /* Init string */ X init[strlen(init) - 1] = '\r'; /* Make last a return */ X fgets(tmp, 80, fid); X sscanf(tmp, " %s", iresp); X fgets(connected, 511, fid); /* Connected string */ X stop = 0; X while ((!stop) && (fgets(tmp, 80, fid) != (char *) NULL)) { X if ((tmp[0] == '#') && (strlen(tmp) <= 2)) { X stop++; X continue; X } X sscanf(tmp, "%d %[!-z] %[!-z ]", &matches[mcount].baud, matches[mcount].speed, matches[mcount].string); X mcount++; X } X lcount = MAXISSUE; X icount = 0; X fgets(loginp, 80, fid); /* Get login prompt */ X if (strlen(loginp)) X loginp[strlen(loginp) - 1] = 0; /* Chop off L/F */ X while ((lcount) && (fgets(tmp, 132, fid) != (char *) NULL)) { /* Get line */ X istring[icount] = malloc(strlen(tmp) + 2); X strcpy(istring[icount++], tmp); X lcount--; X } X fclose(fid); X } X strcpy(line, argv[1]); /* Look on this line */ X setlocked(argv[1]); /* Set port id to locked */ X sprintf(tmp, "/dev/%s", line); /* Check the line */ X if (!stat(UUCICO, &st)) { /* Who owns uucp? */ X chown(tmp, st.st_uid, st.st_gid);/* Set line owner & group */ X chmod(tmp, 0660); X } X checklock(line, 1); /* Wait for no locks */ X settogetty(argv[1]); /* Mark us as in 'getty' */ X#ifndef DEBUG X close(0); /* Close stdin,stdout,stderr */ X close(1); X close(2); X (void) setpgrp(); /* Make sure we have our own X control terminal */ X#endif X sprintf(tmp, "/dev/%s", argv[1]); X if ((x = open(tmp, O_RDWR|O_NDELAY)) < 0) {/* BECOMES CONTROL TERM */ X exit(1); /* Exit; error! */ X } /* End of line.. */ X maschan = x; /* Master I/O channel */ X tt_array.c_cflag = (HUPCL|CLOCAL|CS8|CREAD); X tt_array.c_cc[VMIN] = 1; X tt_array.c_cc[VTIME] = 1; /* Set parameters */ X ioctl(maschan, TCFLSH, 2); /* Flush channel */ X if (ioctl(maschan, TCSETAW, &tt_array) == -1) {/* Set DTR down */ X exit(1); X } X sleep(1); X tt_array.c_cflag |= initbaud; /* Set initial baud rate */ X checklock(line, 0); /* Exit if locked */ X if (ioctl(maschan, TCSETAW, &tt_array) == -1) {/* Set parameters */ X exit(1); X } X ioctl(maschan, TCFLSH, 2); /* Flush channel */ X sleep(1); X if ((status = fcntl(x, F_GETFL, 0)) != -1) { X status &= (~O_NDELAY); X if (fcntl(x, F_SETFL, status)) { /* Clear O_NDELAY */ X exit(0); X } X } X status = fcntl(maschan, F_GETFL, 0); /* Read it again to be sure */ X sleep(1); /* Allow modem to settle */ X checklock(line, 0); /* Exit if locked */ X timeout = 0; /* No timeout yet */ X slowwrite(maschan, init, strlen(init));/* Write initialization */ X if (debug) X fprintf(ferr, "%x\n", status); X signal(SIGALRM, hangup); /* Quit on timeout */ X bcount = 0; X alarm(5); /* Wait 5 seconds tops */ X while (!quit) { /* Check return from init */ X if (!read(maschan, bc, 1)) { /* Look for a character */ X sleep(1); X continue; X } X if (debug) X fprintf(ferr, " %d\n", bc[0]); X if ((bc[0] == 10) || (bc[0] == 13)) { X if (strcmp(bfr, iresp)) {/* If not correct response */ X bcount = 0; /* Then drop it */ X continue; /* Keep looking */ X } else { X quit++; /* Else done */ X } X } else { X bfr[bcount++] = bc[0]; /* Save character */ X bfr[bcount] = 0; /* Null terminate */ X } X } X dup(maschan); X dup(maschan); X ioctl(maschan, TCFLSH, 2); X ioctl((maschan + 1), TCFLSH, 2); X ioctl((maschan + 2), TCFLSH, 2); X ferr = fopen("/dev/console", "w"); /* Write to console for errs */ X alarm(0); /* Turn off timeout */ X strcpy(line, argv[1]); /* Look on this line */ X if (checklock(line, 0)) /* Check lock status again */ X exit(0); /* Exit if line is locked */ X satisfied = 0; X bcount = 0; /* Nothing in buffer */ X signal(SIGALRM, hangup); X alarm(1800); /* 30 minutes idle time */ X while (!satisfied) { /* Read result codes */ X if (!read(maschan, bc, 1)) { /* Look for a character */ X sleep(2); X continue; X } X if (checklock(line, 0)) /* Locked by someone else? */ X exit(0); /* Quit if so */ X if ((bc[0] == 10) || (bc[0] == 13)) { /* New line? */ X satisfied = checkmatch(matches, mcount, bfr, speed); X if (!satisfied) { X bcount = 0; X } X } else { X bfr[bcount++] = bc[0]; /* Store character */ X bfr[bcount] = 0; /* Null terminate */ X } X } X alarm(0); X if (makelock(line)) { /* This is VERY bad! */ X exit(0); /* Quit right now! */ X } X ioctl(maschan, TCGETA, &tt_array); X tt_array.c_cflag &= (~CBAUD); /* Clear baud rate bits */ X tt_array.c_cflag |= satisfied; /* Set new baud rate */ X ioctl(maschan, TCSETAW, &tt_array); X sprintf(baud, "%s @ %s bps", line, speed); X fid2 = fdopen(maschan, "a+"); /* Give us a stream channel please */ X ioctl(maschan, TCFLSH, 2); /* Flush input & output */ X sleep(1); /* Wait a second for settling... */ X fprintf(fid2, "%s\r\n", connected); X fflush(fid2); X signal(SIGALRM, catch); X timeout = 0; X alarm(10); X if (read(maschan, bc, 1) > 0) { X switch(bc[0]) { X case 13: X case 10: X strcat(baud, ", 8/N/1"); X goto aexit; /* Do nothing */ X break; X case -115: /* If signed... */ X case 141: /* 7/E/1, but otherwise ok */ X tt_array.c_cflag &= (~CS8); X tt_array.c_cflag |= (CS7|PARENB); X ioctl(maschan, TCSETAW, &tt_array); X ioctl((maschan + 1), TCSETAW, &tt_array); X ioctl((maschan + 2), TCSETAW, &tt_array); X strcat(baud, ", 7/E/1"); X goto aexit; X break; X default: X break; X } X } aexit:; X tt_array.c_cflag &= (~(CBAUD|CLOCAL)); X tt_array.c_cflag |= (satisfied|HUPCL); /* New baud rate */ X tt_array.c_oflag |= OPOST|ONLCR; X X/* If available, enable CTS flow control. This is necessary for X Telebit Trailblazers and others of the general type. Unfortunately, X only SCO has these things... (grr) X*/ X X#ifdef HARDWARE_FLOW X tt_array.c_iflag |= BRKINT|IGNPAR|INPCK|ICRNL; X tt_array.c_cflag |= HUPCL|CTSFLOW|RTSFLOW; X#else X tt_array.c_iflag |= BRKINT|IGNPAR|INPCK|ICRNL|IXON|IXANY; X tt_array.c_cflag |= HUPCL; X#endif X tt_array.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK; X tt_array.c_cc[VINTR] = 177; X tt_array.c_cc[VQUIT] = 0; X tt_array.c_cc[VERASE] = 8; X tt_array.c_cc[VKILL] = 21; X tt_array.c_cc[VEOF] = 4; X tt_array.c_cc[VEOL] = 0; X ioctl(maschan, TCSETAW, &tt_array); /* Set parameters */ X ioctl((maschan + 1), TCSETAW, &tt_array); X ioctl((maschan + 2), TCSETAW, &tt_array); X signal(SIGALRM, hangup); /* If we time out, hang up the line */ X alarm(LOGSECONDS); /* LOGSECONDS to read/reply, then out */ X fprintf(fid2, "\n[%s]\n", baud);/* Display parameters we found */ X for (x = 0; x < icount; x++) { X fputs(istring[x], fid2); X } X fputs("\r\n", fid2); /* End with another <return> */ bg1:; X fputs(loginp, fid2); /* Prompt for login name */ X fgets(tmp, 80, fid2); /* Read it, but don't allow overrun */ X if (*tmp == 10) /* If nothing, ask again */ X goto bg1; X tmp[strlen(tmp)-1] = 0; /* Make sure null terminated */ X tlc(tmp); /* Lower case the name */ X signal(SIGINT, SIG_DFL); /* Reset signal handling */ X signal(SIGALRM, SIG_DFL); X alarm(0); /* Clear alarm */ X fclose(ferr); /* Close error channel */ X/* X * Take two shots at where login is. If we can't find it in either of these X * places you're screwed; dump the caller. Try to tell the user if this X * happens, but no guarantees.... X */ X X execlp("/etc/login", "login", tmp, (char *) NULL); X execlp("/bin/login", "login", tmp, (char *) NULL); X fputs("Login not executable; contact administrator\n", fid2); X fflush(fid2); /* Make sure it's printed */ X sleep(3); /* Wait for output to drain */ X exit(1); /* And quit with error (ignored) */ X} X END_OF_FILE if test 19854 -ne `wc -c <'autouu.c'`; then echo shar: \"'autouu.c'\" unpacked with wrong size! fi # end of 'autouu.c' fi echo shar: End of shell archive. exit 0 -- Karl Denninger (karl@ddsw1.MCS.COM, <well-connected>!ddsw1!karl) Public Access Data Line: [+1 708 808-7300], Voice: [+1 708 808-7200] Macro Computer Solutions, Inc. "Quality Solutions at a Fair Price"