Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!naitc!ddsw1!karl From: karl@ddsw1.MCS.COM (Karl Denninger) Newsgroups: comp.unix.sysv386 Subject: Re: PD uugetty for Interactives 386/ix? Summary: Ok, here it is..... (a getty replacement) Message-ID: <1990Sep03.060502.27179@ddsw1.MCS.COM> Date: 3 Sep 90 06:05:02 GMT References: <9009011253.AA13674@esegue.segue.boston.ma.us> Reply-To: karl@ddsw1.MCS.COM (Karl Denninger) Organization: Macro Computer Solutions, Inc., Wheeling, IL Lines: 707 In article <9009011253.AA13674@esegue.segue.boston.ma.us> johnl@esegue.segue.boston.ma.us (John R. Levine) writes: >In article <90243.123640RREED@ucf1vm.cc.ucf.edu> you write: >>A friend of mine without net access (now, but if you can help, that will >>change!) asked me to see if I could find a PD uugetty replacement for his >>386/ix system. > >Rather than using uugetty, use either the async driver on the X5 or X6 >update disks, or else the public FAS driver posted here. Both of them >provide separate /dev entries for dialin and dialout, so you can run a >modem both ways without uugetty. I have used both, and they both work. > >FAS is somewhat faster, X5/X6 supports VP/ix. Both take advantage of >the extra buffering in a 16550 ASYNC chip. That does NOT solve one problem: o You dial out on the non-modem control port. The call completes, then drops. How do you detect it? The answer? You don't! The fix? Below is a shar archive. Unpack, read the comments, compile and go. This will work on ISC 2.2, 2.0.2, and should work on SCO Unix V/386 (but it has NOT been tested there). There is one hack in the code right now: ISC does not correctly support CLOCAL on modem control lines. If you open a port with O_NDELAY, then set CLOCAL and clear O_NDELAY with fcntl, you should get BLOCKING reads with the CD signal ignored. Instead you get non-blocking reads if CD is low, and blocking reads if CD is high (idiots!). Thus, there's a timer-read routine in here; it'll work if and when ISC fixes their driver, and works ok as is now (but does consume a couple seconds of runtime per hour when the port is idle). Unfortunately, Equinox and a few other board makers have mimiced the broken code instead of fixing it (double idiots!) Thus this hack. Note: This code is NOT PD; it's Copyrighted. It can be used non-commercially, but if you want to sell it or enhance the value of a commercial poduct you have to get a distribution license. Flame if you must, use the code if it helps you. This program is still a little messy, but it certainly functions. We run all our modems at "ddsw1" with this and have no trouble with it at all. #! /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 'autonew.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 autonew autonew.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 The issue file is in /etc/issue.new. The first line of this file X contains the login prompt. The remainder is the banner message. X X You invoke this with: X /etc/autonew ttyA2 [code] 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". X 4) Modes will be changed on ports to prevent random users X from using the ports for "cu"s and other communications X uses. This can be easily changed if desired (look for X the "chmod" call in the source). X 5) The file /etc/autobaud.parm must be present. The format X is as follows: X First line -- initialization string for ports X Second line -- response to initialization string X Rest of lines -- baud codes, rates (text), and X response strings expected. 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 AAAAATE0Q0V1 X OK X 7 300 CONNECT X 9 1200 CONNECT 1200 X 11 2400 CONNECT 2400 X 14 19200 CONNECT FAST X 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 this have must use the same response X 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 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 X#include X#include X#include X#include X#include X#include X#include X#include X#include 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 if (ltmp[4] <= 'Z') /* Did idiot use lowercase port name? */ X ltmp[4] = ltmp[4] + 32; /* Oh yeah, check it too... */ 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[80], loginp[80]; X extern struct utmp *getutent(), *pututline(); X char baud[132]; 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 int mcount = 0; X int bcount = 0; X char bc[2]; X char bfr[20]; X int satisfied = 0; X int hoseline; X int quit = 0; X X signal(SIGALRM, catch); /* Catch alarms */ X signal(SIGINT, SIG_IGN); /* Ignore interrupts */ X if (argc > 3) X debug++; X initbaud = atoi(argv[2]); /* Initial baud rate */ X strcpy(init, "ATZ\r"); /* Send this to init */ X fid = fopen("/etc/autobaud.parm", "r"); /* Try to open it */ X if (fid != (FILE *) NULL) { X fgets(init, 80, fid); /* Init string */ X fscanf(fid, " %s", iresp); X while (fgets(tmp, 80, fid) != (char *) NULL) { X sscanf(tmp, "%d %[!-z] %[!-z ]", &matches[mcount].baud, matches[mcount].speed, matches[mcount].string); X mcount++; X } X fclose(fid); X init[strlen(init) - 1] = '\r'; /* Make last a return */ 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 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 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 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 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 if (makelock(line)) { /* This is VERY bad! */ X exit(0); /* Quit right now! */ X } 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, "CONNECTED\r\n"); 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 } 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 if ((fid = fopen("/etc/issue.new", "r")) != NULL) { X fgets(loginp, 80, fid); /* Get login prompt */ X loginp[strlen(loginp)-1] = 0; X while (fgets(tmp, 80, fid) != NULL) { /* Display issue file */ X fputs(tmp, fid2); X } X fclose(fid); X } X fputs("\r\n", fid2); /* End with another */ 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 18668 -ne `wc -c <'autonew.c'`; then echo shar: \"'autonew.c'\" unpacked with wrong size! fi # end of 'autonew.c' fi echo shar: End of shell archive. exit 0