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