wmd@tippy (Malcolm Duncan) (03/28/90)
Following is a shell archive containing newgetty.c, a getty(8) replacement programs that does automatic baud rate adjustment depending on result codes sent by a Hayes Smartmodem(TM) 2400 modem attached to an A/UX based Macintosh. If there is sufficient interest, I have a short shell/awk script to ensure that ghost getty's do not take a line out of service due to unfinished background processes. I understand that this is a common problem with System V UNIX machines. It certainly is a problem on my A/UX box. ------------------------------------------------------------------------- Malcolm Duncan | Internet: wmd@tippy.cs.purdue.edu Dir. Exec. Ed. Computing | Duncan Communication Public Access UNIX BBS Krannert School of Mgmt | (317) 567-2143 24 hours, 300/1200/2400 Purdue University | Rt #1 Box 98E, Battle Ground, IN 47920 #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # newgetty.c # This archive created: Tue Mar 27 11:10:17 1990 # By: Malcolm Duncan (wmd@tippy.cs.purdue.edu) export PATH; PATH=/bin:$PATH if test -f 'newgetty.c' then echo shar: will not over-write existing file "'newgetty.c'" else cat << \SHAR_EOF > 'newgetty.c' /* FILE is newgetty.c getty(8) program for use with any 2400 baud Hayes Smartmodem(TM) 2400 compatable. Copyright February 1990 by W. Malcolm Duncan The author may be reached via any of the following means... US Mail: Duncan Communications BBS Krannert Graduate School of Mgmt RR #1 Box 98E and KCTR 235, Purdue University Battle Ground, IN 47920 W. Lafayette, IN 47907 Your favorite long distance service: Voice: (317) 494-7700 (office) (317) 567-2220 BBS: (317) 567-2143 (modem) Electronic Mail: Internet: wmd@tippy.cs.purdue.edu (always) AppleLink: U0198 (often) Compuserve: 76012,1664 (seldom) newgetty may be distributed freely AND may NOT be sold without permission from the author. Thanks to Paul Campbell from SuperMac Technologies for his help with the SuperMac 4-port serial CommCard. Thanks to Apple Computer, Inc. for providing no help whatsoever in the development of newgetty despite numerous calls and pleas for assistance. It's nice to know us little developers are important too! Cable from Mac -> SmartModem 2400: Mac Mini DIN-8 RS-232 DB-25 Male ---------------------- ------------------- Pin 1 Handshake Out Pin 20 DTR 2 Handshake In 8 DCD 3 Transmit Data - 2 TxD 5 Receive Data - 3 RxD 4 \_________Ground___________/ 1 GND 8 / \ 7 GND Caution, my abbreviations may be off but the pins are correct... Modem Settings Generally for any Hayes Smartmodem 2400 clone, we want to have the modem hang up on drop of DTR; we use termio's hupcl setting to kill all on loss of carrier (DCD) so we need to tell the modem to track the state of DCD; we want digital (not english) result codes from the modem to make the program easier to write; we want a modem reset after loss of carrier therefore our settings changes must be written into the modem's nonvolatile RAM. You'll have to do this by hooking the modem to a port and using cu(1C) to talk to it and make the settings changes. The Hayes(TM) commands to do this are as follows: ATV0 -- short form result codes AT&C1 -- DCD tracks carrier AT&D3 -- reset on DTR drop AT&W -- write settings to nonvolitile RAM Sample /etc/inittab entries 00:2:respawn:/etc/newgetty tty0 #Port tty0 (modem); set to "respawn" 01:2:respawn:/etc/newgetty tty1 #Port tty1 (print); set to "respawn" a0:2:respawn:/etc/newgetty ttya0 #Port ttya0 (SuperMac 4 port board - port 1) a1:2:respawn:/etc/newgetty ttya1 #Port ttya1 (SuperMac 4 port board - port 2) As you can see, I placed my binary in the /etc directory. I gave it the same mode as getty, specifically -rwx------ or mode 0700 since init, being a superuser process, can run anything it pleases. See below... -rwx------ 1 root sys 12364 Feb 26 21:26 /etc/newgetty */ #include <stdio.h> #include <termio.h> #include <signal.h> #include <fcntl.h> /* Define DEBUG for error logging */ #ifdef DEBUG FILE *log; #endif /* variables used */ char name[48]; /* login name typed to pass to login(1) */ int input, /* standard input file descriptor */ output, /* standard output file descriptor */ error; /* standard errput file descriptor */ struct termio argp; /* tty settings buffer */ #define TRUE 1 #define FALSE 0 main( argc, argv ) int argc; char *argv[]; { register int i, /* general use int */ help, /* help flag for login name loop */ speed; /* final speed to use */ char c, /* general use char */ ttyname[32]; /* user's tty */ int eof, /* holder for user's end of file char */ eol, /* holder for user's end of line char */ fd_count, /* file descriptor count for select(2) */ readmask, /* file descriptor mask for select(2) */ writemask, /* file descriptor mask for select(2) */ exceptmask; /* file descriptor mask for select(2) */ /* set up the port name */ sprintf(ttyname,"/dev/%s", argv[1]); /* if it doesn't exist, bug out */ if (access(ttyname, 6) != 0) exit(-1); close(0); close(1); close(2); /* just in case */ /* open the port and set up the appropriate file descriptors */ input= open(ttyname, (O_RDWR + O_NDELAY)); /* if this is a streams based tty line (SuperMac CommCard), push a line discipline. If not, this call has no effect. */ line_push(input); output= dup(0); error= dup(0); #ifdef DEBUG log= fopen("/tmp/gettylog", "a"); fprintf(log, "newgetty starting\n"); fflush(log); #endif /* Now set port to highest modem baud rate with no echo, raw mode in anticipation of a carrier signal */ i= ioctl(input, TCGETA, &argp); #ifdef DEBUG fprintf(log,"TCGETA ioctl returned %d\n", i); fflush(log); #endif /* save original eof and eol characters */ eof= argp.c_cc[VEOF]; eol= argp.c_cc[VEOL]; /* set up to read one char at a time with a timeout of 255 tenths of a second */ argp.c_cc[VEOF]= 1; argp.c_cc[VEOL]= 0xff; /* ignore breaks, no input preprocessing */ argp.c_iflag= IGNBRK; /* no character post processing */ argp.c_oflag= 0; /* 2400 baud, 8 bit characters */ argp.c_cflag= B2400 + CREAD + CS8; /* straight raw i/o */ argp.c_lflag= 0; /* set up the port */ i= ioctl(input,TCSETA, &argp); #ifdef DEBUG fprintf(log,"TCSETA ioctl returned %d\n", i); fprintf(log, "Raw Mode set\n"); fflush(log); #endif /* want blocking i/o to prevent getty from chewing cpu cycles */ i= ioctl(input,FIONBIO, 0); #ifdef DEBUG fprintf(log,"Blocking i/o ioctl returned %d\n", i); fflush(log); #endif /* Now wait for a Smartmodem result code for carrier detect Code = '1' for 300/2400, '5' for 1200 carrier detect */ c= 0; fd_count= 1; readmask= 1; writemask= 0; exceptmask= 0; do { /* Wait for something to be available for reading. I use a select() call here to keep newgetty from munching CPU cycles in this loop waiting for character. */ select(fd_count, &readmask, &writemask, &exceptmask, 0); /* get a char from the modem and mask it down to 7 bits */ i= read( input, &c, 1) ; c &= 0x7f; /* no character, we must've timed out */ if (i == 0) c= 0; #ifdef DEBUG else if (c != 0) { i= c; fprintf(log,"read an ASCII %d\n", i); } #endif } while ((c != '1') && (c != '5')) ; /* Turn off raw mode and turn on echo and adjust baud rate if necessary */ if (c == '5') { /* 1200 baud */ #ifdef DEBUG fprintf(log, "Changing to 1200 baud\n"); fflush(log); #endif argp.c_cflag= B1200; argp.c_cflag|= CS8; argp.c_cflag|= CREAD; argp.c_cflag|= HUPCL; speed= 1200; } else { /* 300 or 2400 */ /* get another character, 2400 baud result code is "10", 300 baud result code is "1" followed by a return */ while (read(input, &c, 1) <= 0) ;; c &= 0x7f; if (c != '0') { /* must be 300 baud, second char not a zero */ #ifdef DEBUG fprintf(log, "Changing to 300 baud\n"); #endif argp.c_cflag= B300; argp.c_cflag|= CS8; argp.c_cflag|= CREAD; argp.c_cflag|= HUPCL; speed= 300; } else { /* 2400 baud */ argp.c_cflag|= CS8; argp.c_cflag|= CREAD; argp.c_cflag|= HUPCL; speed= 2400; #ifdef DEBUG fprintf(log, "Staying at 2400 baud\n"); fflush(log); #endif } } /* wait for output to drain and set the new baud rate */ i= ioctl(input,TCSETAF, &argp); #ifdef DEBUG fprintf(log,"speed TCSETA ioctl returned %d\n", i); fflush(log); #endif /* reinstate modem DCD/DTR control on the line */ i= ioctl(input, UIOCMODEM, 0); #ifdef DEBUG fprintf(log,"UIOCMODEM ioctl returned %d\n", i); fprintf(log, "Modem control set\n"); fflush(log); #endif /* flush everything */ ioctl(input,TCFLSH,2); ioctl(output,TCFLSH,2); /* let the line settle, a little longer for 300 baud */ sleep(1); if (speed == 300) sleep(1); /* give em 60 seconds to get a name typed in */ alarm(60); signal(SIGALRM, SIG_DFL); /* loop until we get a login name, or timeout */ help= TRUE; while( help ) { help= FALSE; /* Get the user's name and keep asking until he types one or if line errors have caused a > 8 char name */ do { /* enter your prompt here using my print() routine: remember to enter both carriage returns AND line feeds in multi-line prompts as shown below... */ print("\r\n\n\nThank you for calling the Duncan Communications BBS!\n\n"); print("\rType in your account name or HELP and press Return or Enter.\n"); print("\rUsers without an account should enter 'user' as an account name.\n\n\rlogin: "); ioctl(input,TCFLSH,0); /* flush input before reading */ i= mygets(name, 40); } while ((i == 0) || (i > 8)); if (strcmp(name,"help")==0) { /* if he wants help, give it to him and give him two minutes to get his name typed in */ alarm(120); loginhelp(); help= TRUE; } } /* enable echo, erase and kill processing, etc. */ argp.c_lflag= ISIG; argp.c_lflag|= ICANON; argp.c_lflag|= ECHO; argp.c_lflag|= ECHOE; argp.c_lflag|= ECHOK; /* enable input preprocessing */ argp.c_iflag|= IGNBRK; argp.c_iflag|= ISTRIP; argp.c_iflag|= ICRNL; argp.c_iflag|= IXON; /* enable output postprocessing */ argp.c_oflag|= OPOST; argp.c_oflag|= ONLCR; /* restore eof, eol and erase characters */ argp.c_cc[VEOF]= eof; argp.c_cc[VEOL]= eol; argp.c_cc[VERASE]= 0x08; /* ASCII BS */ /* set up the port for login */ i= ioctl(input,TCSETA, &argp); #ifdef DEBUG fprintf(log,"last TCSETA ioctl returned %d\n", i); fflush(log); #endif #ifdef DEBUG fprintf(log,"launching login\n"); fclose(log); #endif /* call login */ execl("/bin/login", "login", name, 0); } loginhelp() { char tmp[12]; /* Use my print() routine and remember to put BOTH carriage returns and line feeds in the strings. */ print("\r\n\n\nWelcome to the Duncan Communications Bulletin Board System!\n\n"); print("\rEach user of this bulletin board has an account name and\n"); print("\rpassword he or she uses to 'log in' to the system.\n"); print("\rThe system is particular in that it wants all account\n"); print("\rnames entered in lower case. Please watch out for this.\n\n"); print("\rShould you be new to the Duncan BBS, welcome! You can use the\n"); print("\rguest account 'user' (without the quotes). No password will be\n"); print("\rrequired. If you find the system to be of sufficient interest\n"); print("\rthat you may want an account of your own, you will be able to\n"); print("\rapply for one using this guest account.\n\n"); print("\rPlease press return or enter..."); mygets(tmp, 10); } /* print() writes a null-terminated string to the output file desctiptor */ print( string ) char *string; { register char *cptr; cptr= string; while (*cptr != 0) write(output, cptr++, 1); } /* get a string from the input file descriptor. The string's length cannot exceed the maxLength parameter. */ int mygets( string, maxLength ) char *string; int maxLength; { register char *endptr, *cptr; register int i; char eraseString[4]; /* set up eraseString as BACKSPACE-SPACE-BACKSPACE */ eraseString[0]= 0x08; eraseString[1]= ' '; eraseString[0]= 0x08; eraseString[0]= 0; /* endptr is used for comparison */ endptr= string; endptr += maxLength; cptr= string; *cptr= 0; /* get a user-typed char */ i= read(input, cptr, 1); do { /* strip any parity bits, newgetty ignores parity */ *cptr &= 0x7F; /* if i <= 0 then we didn't get a character as a result of the read() */ if (i>0) switch (*cptr) { /* backspace and delete are acceptable backspacing characters */ case 0x08: case 0x7f: /* see if there IS a previous char */ if (cptr > string) { /* erase it */ print(eraseString); cptr--; } break; /* both line feed and carriage returns are acceptable line termination characters */ case 10: case 13: *cptr= 0; /* remember to echo BOTH a line feed and a carriage return */ print("\r\n"); /* return the length of the string typed. */ return(strlen(string)); break; default: /* ignore any other control characters */ if (*cptr < ' ') break; /* convert uppcase letters to lower case */ if ((*cptr>='A')&&(*cptr<='Z')) *cptr+= 'a' - 'A'; /* echo the character */ write(output, cptr, 1); cptr++; if (cptr == endptr) { /* length limit reached */ cptr--; /* back up and null terminate the string */ *cptr= 0; print("\r\n"); return(strlen(string)); } break; } /* get a user-typed char */ i= read(input, cptr, 1); } while (TRUE); } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0 -- ------------------------------------------------------------------------- Malcolm Duncan | Internet: wmd@tippy.cs.purdue.edu Dir. Exec. Ed. Computing | Duncan Communication Public Access UNIX BBS Krannert School of Mgmt | (317) 567-2143 24 hours, 300/1200/2400 Purdue University | Rt #1 Box 98E, Battle Ground, IN 47920