paul@devon.lns.pa.us (Paul Sutcliffe Jr.) (01/17/90)
This has evolved from a private project to get a SysV-like getty running on my Tandy 6000 (Xenix System III). I'm releasing it here in the hopes that you alt.sources-types will find any bugs and report them to me so they can be fixed before I submit this to comp.sources.{misc,unix}. If you want to know more details, the README file is the first thing in the shell archive below. - paul #! /bin/sh # Make a new directory for the getty sources, cd to it, and run kits 1 # thru 4 through sh. When all 4 kits have been run, read README. echo "This is getty 1.1 kit 1 (of 4). If kit 1 is complete, the line" echo '"'"End of kit 1 (of 4)"'" will echo at the end.' echo "" export PATH || (echo "You didn't use sh, you clunch." ; kill $$) mkdir man 2>/dev/null echo Extracting README sed >README <<'!STUFFY!FUNK!' -e 's/X//' X X Getty Kit, Version 1.1 X X Copyright (c) 1989,1990, Paul Sutcliffe Jr. X X------------------------------------------------------------------------------- X X Permission is hereby granted to copy, reproduce, redistribute, X or otherwise use this software as long as: there is no monetary X profit gained specifically from the use or reproduction or this X software, it is not sold, rented, traded or otherwise marketed, X and this copyright notice is included prominently in any copy X made. X X The author make no claims as to the fitness or correctness of X this software for any use whatsoever, and it is provided as is. X Any use of this software is at the user's own risk. X X------------------------------------------------------------------------------- X X XWHY THIS GETTY: X XAs most people have seen, the stock getty provided on Unix/Xenix Xsystems lacks many features that can be useful. The getty included in Xthis distribution adds several features that I needed on my own system, Xplus includes several "Wouldn't it be nice if ..." features I've heard Xmentioned around UseNet. X XGetty 1.1 trys to emulate a "standard" System V getty in every way it Xcan. For instance, it uses the SysV /etc/gettydefs file (although you Xmay give it a different name). It also uses an /etc/issue file, if one Xis present. X XAdded features include: X X + Can be used as a normal getty, or as "uugetty" to allow X bi-directional usage of modem lines. X X + Reads a "defaults" file at runtime, so that a single binary X can be configured differently on individual lines. This also X allows you to change getty's behavior without recompiling. X X + Let's you specify default erase and kill characters, instead X of the ancient '#' and '@' convention still used in some X "modern" gettys. X X + Extensive debugging (to a log file) can be enabled at compile- X time. The command line argument to envoke debugging is an X octal number -- the bit pattern determines which aspects of X getty's behavior are logged. X X + Let's you specify a program other than "login" to be executed X after the user name is entered. X X + (and the best for last:) The line can be "initialized" X before sending the login banner (/etc/issue) and prompt with X the use of an expect/send sequence not unlike that used by the X UUCP L.sys (or Systems) file. X X XREQUIREMENTS: X XGetty 1.1 should drop right in to any AT&T (System III or V) Unix Xor derivitive. It has already been successfully installed on: X X Tandy 6000 Tandy Xenix 3.2 (Microsoft Xenix 3.0) X NCR Tower 32/400 Unix SVR[23] X 80386 clone SCO Xenix V/386 2.2 X X XINSTALLATION: X XPlease read all the directions below before you proceed any further, and Xthen follow them carefully. Failure to do so may void your warranty. :-) X XAfter you have unpacked your kit(s), you should have all the files listed Xin MANIFEST. X X1) Run Configure. This will figure out various things about your system. X Some things Configure will figure out for itself, other things it will X ask you about. It will then proceed to make config.h, config.sh, and X Makefile. X X You might possibly have to trim # comments from the front of Configure X if your sh doesn't handle them, but all other # comments will be taken X care of. X X2) Glance through config.h to make sure system dependencies are correct. X Most of them should have been taken care of by running the Configure X script. X X If you have any additional changes to make to the C definitions, they X can be done in the Makefile, or in config.h. Bear in mind that they X will get undone next time you run Configure. X X3) Copy the sample file tune.H to tune.h and edit tune.h to reflect the X special needs of your system and your desired features. Use the X following as a guide: X X boolean If your compiler supports a (boolean) type, you may X remove this definition. X X DEF_CFL Define this to the <termio.h> parameters that will X identify your system's normal word length and parity. X Possible values are: X X (CS8) /* 8-bit, no parity */ X (CS7|PARENB) /* 7-bit, even parity */ X (CS7|PARENB|PARODD) /* 7-bit, odd parity */ X X Be sure to use only symbols defined in <termio.h> on X your system. X X DEBUG Define this if you want the runtime debugging code X included in the executables. See the section on X DEBUGGING in this readme for instructions in usage. X X LOGUTMP Define this if your utmp file (/etc/utmp) records X getty processes as a LOGIN_PROCESS. This is true X for all SYS V sites that I'm aware of. X X MY_CANON Define this if you want to define your own ERASE and X KILL characters. You may wish to do this if you are a X SYS V site whose defaults are the ancient '#' and '@' X characters. See MY_ERASE and MY_KILL below. X X RCSID Define this if you want RCS version strings compiled X into the executables. These can later be found with X the what (SCCS) or ident (RCS) commands. X X SETTERM Define this if you want getty to export the TERM X environment variable before calling login. This will X only be done if getty knows what kind of terminal is X attached to the line it's running on. X X TRYMAIL Define this if you want getty to send email if it has X and error and cannot access the CONSOLE device. X X WARNCASE Define this to allow getty to warn a user if he/she has X used only upper-case letters in their login id. If the X user re-enters his/her id a second time in upper-case, X that value is accepted and process accordingly. X X MY_ERASE If you've defined MY_CANON, use these to define the values X MY_KILL you want for erase and kill characters. X X NOTIFY If you've defined TRYMAIL, use this to define the account X to which the email will be sent. X X CONSOLE Define this to the name of the console device. X X DEFAULTS Define this to the name of the defaults file. The %s is X necessary, and will be replaced (via an sprintf()) by the X name of the executable--either getty or uugetty. X X ISSUE Define this to the name of your issue file. If this X is undefined, no issue file will be displayed during X getty's startup. X X LOGIN Define this to the name of the login program to be called. X X TRS16 Define this only if you're compiling getty on a Tandy 6000. X This define handles the different command line required due X to the V7 based /etc/init and also the dain-bramaged X /etc/inittab used on Tandy Xenix-68000 3.2 X X4) make depend X X This will look for all the includes and modify Makefile accordingly. X Configure will offer to do this for you. X X5) make X X This will attempt to make getty and uugetty in the current directory. X It will also go to the man sub-directory and use m4 to create X nroff-able man pages. It will then run nroff on the m4 output. X X6) make install X X This will put getty/uugetty into a public directory (normally /etc). X It will also make sure the man pages have been created. It will not X install them. You may need to be root to do this. If you are not X root, you must own the directories in question and you should ignore X any messages about chown not working. X X Also, if you don't already have an /etc/gettydefs file, you'll need X to create one. This goes for the /etc/default files (if you are X using them) and the /etc/issue file. There are examples of these X in the `sample.files' file. X X7) Read the manual entries before running getty/uugetty. X X8) IMPORTANT! Help save the world! Communicate any problems and suggested X patches to me, paul@devon.lns.pa.us (Paul Sutcliffe Jr.), so we can X keep the world in sync. If you have a problem, there's someone else X out there who either has had or will have the same problem. X X If possible, send in patches such that the patch program will apply them. X Context diffs are the best, then normal diffs. Don't send ed scripts-- X I've probably changed my copy since the version you have. X X Watch for getty patches in comp.sources.bugs. Patches will generally be X in a form usable by the patch program. If you are just now bringing up X getty and aren't sure how many patches there are, write to me and I'll X send any you don't have. Your current patch level is shown in patchlevel.h. X X XDEBUGGING: X XTo use debugging, you must define DEBUG (in config.h) before compiling. X XTo envoke debugging, use the "-D onum" command line argument. Onum is Xan octal number. To turn on all levels of debugging, use "-D 0377". XTo pick specific areas to be watched, look at the defines in the "debug Xlevels" section of getty.h. The value for onum will be the result of XOR-ing the values you want. For instance, to debug the defaults file Xand gettytab file processing, use "-D 022". X X XHave fun. X X- paul X XINTERNET: paul@devon.LNS.PA.US | How many whales do you have to XUUCP: ...!rutgers!devon!paul | save to get a toaster? !STUFFY!FUNK! echo Extracting man/README sed >man/README <<'!STUFFY!FUNK!' -e 's/X//' X X Getty 1.1 manual page files. X XThis README describes the files used and created by the Makefile. X XIn general, a file with a `.m4' extension is nroff source intersperced Xwith m4 macros. These files are processed by m4 to yield nroff-able Xfiles, and use the macros to create man pages whose content matches the Xway you've configured your getty/uugetty executables. Running m4 on Xthe .m4 files produce ready-for-nroff files with either a `.1m', `.3' Xor `.4' extension, depending upon the chapter to which the man page Xbelongs. The makefile will also run nroff on these files, producing Xfiles with a `.man' extension. X XAfter running make in this directory, you will have the following files: X X getty.1m nroff-ready man page for getty X getty.man nroff'd getty man page X gettytab.4 * nroff-ready man page for the gettytab file X gettytab.man * nroff'd gettytab man page X issue.4 nroff-ready man page for the issue file X issue.man nroff'd issue man page X X* Note: the name `gettytab' will be replaced by the actual name you X assigned for that file -- usually `gettydefs'. X XAlso, you may have some (or all) of the following files, depending upon Xthe configuration of your Unix/Xenix. These files describe library Xroutines used by getty that were not found in your libc.a (and thus Xhomegrown versions were used): X X getutent.3 nroff-ready man page for getutent(3) X getutent.man nroff'd getutent man page X strdup.3 nroff-ready man page for strdup(3) X strdup.man nroff'd strdup man page X putenv.3 nroff-ready man page for putenv(3) X putenv.man nroff'd putenv man page X XYou will need to manually install the man pages. Use either the .{1m,3,4} X(pre-nroff) or .man (post-nroff) files, according to the needs of your Xsystem. X X- paul X XINTERNET: paul@devon.LNS.PA.US | How many whales do you have to XUUCP: ...!rutgers!devon!paul | save to get a toaster? !STUFFY!FUNK! echo Extracting main.c sed >main.c <<'!STUFFY!FUNK!' -e 's/X//' X/* X** $Id: main.c,v 1.1 90/01/16 16:16:10 paul Exp Locker: paul $ X** X** Main body of program. X*/ X X/* X** Copyright 1989,1990 by Paul Sutcliffe Jr. X** X** Permission is hereby granted to copy, reproduce, redistribute, X** or otherwise use this software as long as: there is no monetary X** profit gained specifically from the use or reproduction or this X** software, it is not sold, rented, traded or otherwise marketed, X** and this copyright notice is included prominently in any copy X** made. X** X** The author make no claims as to the fitness or correctness of X** this software for any use whatsoever, and it is provided as is. X** Any use of this software is at the user's own risk. X*/ X X/* X** $Log: main.c,v $ X** Revision 1.1 90/01/16 16:16:10 paul X** Initial revision X** X*/ X X X#define MAIN X X#include "getty.h" X#include "defaults.h" X#include "table.h" X#include <signal.h> X#include <fcntl.h> X#include <sys/stat.h> X#include <pwd.h> X X#if defined(RCSID) && !defined(lint) Xstatic char *RcsId = X"@(#)$Id: main.c,v 1.1 90/01/16 16:16:10 paul Exp Locker: paul $"; X#endif X X#if !defined(lint) X#include "patchlevel.h" Xstatic char *Release = RELEASE; Xstatic char *RelDate = DATE; X#endif X X/* how does this thing work X */ X#define USAGE "Usage:\t%s %s%s\n\t%s -c checkfile\n" X#ifdef TRS16 X#define UREQ " speed [defaultfile]" /* Tandy Xenix/68k 3.2 */ X#define UOPT "" X#else /* TRS16 */ X#define UREQ " line [speed [type [lined]]]" /* real System V */ X#define UOPT "[-d defaultfile] [-h] [-r delay] [-t timeout]" X#endif /* TRS16 */ X X#define VALUE(cptr) ((cptr == (char *) NULL) ? "NULL" : cptr) X Xstruct speedtab { X ushort cbaud; /* baud rate */ X char *speed; /* speed in display format */ X} speedtab[] = { X { B50, "50" }, X { B75, "75" }, X { B110, "110" }, X { B134, "134" }, X { B150, "150" }, X { B200, "200" }, X { B300, "300" }, X { B600, "600" }, X { B1200, "1200" }, X { B1800, "1800" }, X { B2400, "2400" }, X { B4800, "4800" }, X { B9600, "9600" }, X#ifdef B19200 X { B19200,"19200"}, X#endif /*B19200*/ X { EXTA, "EXTA" }, X { EXTB, "EXTB" }, X { 0, "" } X}; X Xsig_t timeout(); Xint tputc(); Xvoid exit_usage(); Xstruct passwd *getpwuid(); Xstruct utmp *getutent(); X#ifdef DEBUG Xchar *ctime(); X#endif /* DEBUG */ X X X#ifdef UUGETTY X X#include <errno.h> X Xextern int errno; X Xchar *lock, *altlock; X Xint makelock(), readlock(); Xboolean checklock(); Xsig_t rmlocks(); X X#endif /* UUGETTY */ X X X#ifdef WARNCASE Xchar *bad_case[] = { X "\r\n", X "If your terminal supports lower case letters, please\r\n", X "use them. Login again, using lower case if possible.\r\n", X (char *) NULL X}; X#endif /* WARNCASE */ X X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X Reg1 int c; X Reg2 int i; X Reg3 int fd; X int cbaud, flags; X char ch, *p, devname[MAXLINE+1]; X STDCHAR buf[MAXLINE+1]; X char termcap[1024], tbuf[64]; X char *tgetstr(); X DEF **def; X TERMIO termio; X GTAB *gtab; X FILE *fp; X struct utmp *utmp; X X#if defined(DEBUG) || defined(LOGUTMP) X time_t clock; X#endif /* DEBUG || LOGUTMP */ X X#ifndef TRS16 X char lined[MAXLINE+1]; X#endif /* TRS16 */ X X#ifdef UUGETTY X struct passwd *pwd; X UIDTYPE uucpuid = 0; X GIDTYPE uucpgid = 0; X#else /* UUGETTY */ X struct stat st; X#endif /* UUGETTY */ X X#ifdef TTYTYPE X char name[16], line[16]; X#endif /* TTYTYPE */ X X#ifdef LOGUTMP X int pid; X#endif /* LOGUTMP */ X X#ifdef ISSUE X char *issue = ISSUE; /* default issue file */ X#endif /* ISSUE */ X X boolean clear = TRUE; /* clear screen flag */ X char *login = LOGIN; /* default login program */ X char *clrscr = (char *) NULL; /* string to clear screen */ X char *defname = (char *) NULL; /* defaults file name */ X char *init = (char *) NULL; /* value of INIT */ X char term[16]; /* terminal type */ X boolean waitchar = FALSE; /* wait for char flag */ X unsigned int delay = 0; /* #sec's delay before prompt */ X X extern int optind; X extern char *optarg; X X /* startup X */ X (void) signal(SIGINT, SIG_IGN); X (void) signal(SIGQUIT, SIG_DFL); X (void) signal(SIGTERM, SIG_DFL); X X (void) strcpy(term, "unknown"); X NoHangUp = FALSE; X Check = FALSE; X CheckFile = (char *) NULL; X#ifdef DEBUG X Debug = 0; X#endif /* DEBUG */ X Device = "unknown"; X GtabId = (char *) NULL; X LineD = (char *) NULL; X TimeOut = 0; X#ifdef WARNCASE X WarnCase = TRUE; X#endif /* WARNCASE */ X X /* who am I? X */ X#ifdef UUGETTY X MyName = "uugetty"; X#else X MyName = "getty"; X#endif /* UUGETTY */ X X /* process the command line X */ X X while ((c = getopt(argc, argv, "D:c:d:ht:")) != EOF) { X switch (c) { X#ifdef DEBUG X case 'D': X (void) sscanf(optarg, "%o", &Debug); X Dfp = stderr; X break; X#endif /* DEBUG */ X case 'c': X Check = TRUE; X CheckFile = optarg; X break; X case 'd': X defname = optarg; X break; X case 'h': X NoHangUp = TRUE; X break; X case 'r': X waitchar = TRUE; X delay = (unsigned) atoi(optarg); X break; X case 't': X TimeOut = atoi(optarg); X break; X case '?': X exit_usage(2); X } X } X X /* just checking? X */ X if (Check) { X (void) signal(SIGINT, SIG_DFL); X (void) gtabvalue((char *) NULL); X exit(0); X } X X#ifdef TRS16 X X /* special handling for v7-based init X */ X X if (optind < argc) X GtabId = argv[optind++]; X else { X logerr("no speed given"); X exit_usage(2); X } X X /* Tandy Xenix/68k 3.2 /etc/inittab allows one optional argument X * after the speed flag. The best use I could do with it here is X * to assume that it's the name of the defaults file to be used. X * X * Sigh. Actually, it's not optional -- if none is given in X * /etc/inittab, then it appears here as a 0-length string. X */ X if (optind < argc) X if (strlen(argv[optind]) > 0) X defname = argv[optind++]; X X if ((p = ttyname(STDIN)) != (char *) NULL) X Device = p+5; /* strip off "/dev/" */ X else { X logerr("cannot determine line"); X exit_usage(2); X } X X#else /* TRS16 */ X X /* normal System V handling X */ X X if (optind < argc) X Device = argv[optind++]; X else { X logerr("no line given"); X exit_usage(2); X } X if (optind < argc) X GtabId = argv[optind++]; X if (optind < argc) X (void) strncpy(term, argv[optind++], sizeof(term)); X if (optind < argc) { X (void) strncpy(lined, argv[optind++], sizeof(lined)); X LineD = lined; X } X X#endif /* TRS16 */ X X#ifdef TTYTYPE X X if (strequal(term, "unknown")) { X if ((fp = fopen(TTYTYPE, "r")) == (FILE *) NULL) { X (void) sprintf(MsgBuf, "cannot open %s", TTYTYPE); X logerr(MsgBuf); X } else { X while ((fscanf(fp, "%s %s", name, line)) != EOF) { X if (strequal(line, Device)) { X (void) strncpy(term,name,sizeof(term)); X break; X } X } X (void) fclose(fp); X } X } X X#endif /* TTYTYPE */ X X /* need full name of the device X */ X (void) sprintf(devname, "/dev/%s", Device); X X /* command line parsed, now build the list of X * runtime defaults; this may override things set above. X */ X def = defbuild(defname); X X#ifdef DEBUG X X /* debugging on? X */ X if ((p = defvalue(def, "DEBUG")) != (char *) NULL) X (void) sscanf(p, "%o", &Debug); X X if (Debug) { X (void) sprintf(buf, "/tmp/getty:%s", Device); X if ((Dfp = fopen(buf, "a+")) == (FILE *) NULL) { X logerr("cannot open debug file"); X exit(FAIL); X } else { X if (fileno(Dfp) < 3) { X if ((fd = fcntl(fileno(Dfp), F_DUPFD, 3)) > 2) { X (void) fclose(Dfp); X Dfp = fdopen(fd, "a+"); X } X } X (void) time(&clock); X (void) fprintf(Dfp, "%s Started: %s", X MyName, ctime(&clock)); X } X } X X debug(D_OPT, "command line values:\n"); X debug(D_OPT, " [-d] defname = (%s)\n", VALUE(defname)); X debug(D_OPT, " [-h] NoHangUp = (%s)\n", (NoHangUp) ? "TRUE" : "FALSE"); X debug(D_OPT, " [-r] waitchar = (%s)\n", (waitchar) ? "TRUE" : "FALSE"); X debug(D_OPT, " delay = (%u)\n", delay); X debug(D_OPT, " [-t] TimeOut = (%d)\n", TimeOut); X debug(D_OPT, " line = (%s)\n", VALUE(Device)); X debug(D_OPT, " speed = (%s)\n", VALUE(GtabId)); X debug(D_OPT, " type = (%s)\n", term); X debug(D_OPT, " lined = (%s)\n", VALUE(LineD)); X debug(D_RUN, "loading defaults\n"); X X#endif /* DEBUG */ X X /* setup all runtime values X */ X X if ((SysName = defvalue(def, "SYSTEM")) == (char *) NULL) X SysName = getuname(); X X if ((Version = defvalue(def, "VERSION")) != (char *) NULL) X if (*Version == '/') { X if ((fp = fopen(Version, "r")) != (FILE *) NULL) { X (void) fgets(buf, sizeof(buf), fp); X (void) fclose(fp); X buf[strlen(buf)-1] = '\0'; X Version = strdup(buf); X } X } X X if ((p = defvalue(def, "LOGIN")) != (char *) NULL) X login = p; X if ((p = defvalue(def, "INIT")) != (char *) NULL) X init = p; X#ifdef ISSUE X if ((p = defvalue(def, "ISSUE")) != (char *) NULL) X issue = p; X#endif /* ISSUE */ X if ((p = defvalue(def, "CLEAR")) != (char *) NULL) X if (strequal(p, "NO")) X clear = FALSE; X if ((p = defvalue(def, "HANGUP")) != (char *) NULL) X if (strequal(p, "NO")) X NoHangUp = TRUE; X if ((p = defvalue(def, "WAITCHAR")) != (char *) NULL) X if (strequal(p, "YES")) X waitchar = TRUE; X if ((p = defvalue(def, "DELAY")) != (char *) NULL) X delay = (unsigned) atoi(p); X if ((p = defvalue(def, "TIMEOUT")) != (char *) NULL) X TimeOut = atoi(p); X X /* find out how to clear the screen X */ X if (!strequal(term, "unknown")) { X p = tbuf; X if ((tgetent(termcap, term)) == 1) X if ((clrscr = tgetstr("cl", &p)) == (char *) NULL) X clrscr = ""; X } X X#ifdef UUGETTY X X debug2(D_RUN, "check for lockfiles\n"); X X /* deal with the lockfiles; we don't want to charge X * ahead if uucp, kermit or something is already X * using the line. X */ X X /* name the lock file(s) X */ X (void) sprintf(buf, LOCK, Device); X lock = strdup(buf); X altlock = defvalue(def, "ALTLOCK"); X if (altlock != (char *) NULL) { X (void) sprintf(buf, LOCK, altlock); X altlock = strdup(buf); X } X debug3(D_LOCK, "lock = (%s)\n", lock); X debug3(D_LOCK, "altlock = (%s)\n", VALUE(altlock)); X X /* check for existing lock file(s) X */ X if (checklock(lock) == TRUE) { X while (checklock(lock) == TRUE) X (void) sleep(60); X exit(0); X } X X /* there's a race condition just asking for trouble here :-( X */ X if (altlock != (char *) NULL && checklock(altlock) == TRUE) { X while (checklock(altlock) == TRUE) X (void) sleep(60); X exit(0); X } X X /* allow uucp to access the device X */ X (void) chmod(devname, 0666); X if ((pwd = getpwuid(UUCPID)) != (struct passwd *) NULL) { X uucpuid = pwd->pw_uid; X uucpgid = pwd->pw_gid; X } X (void) chown(devname, uucpuid, uucpgid); X X#else /* UUGETTY */ X X (void) chmod(devname, 0622); X if (stat(devname, &st) == 0) X (void) chown(devname, 0, st.st_gid); X else X (void) chown(devname, 0, 0); X X#endif /* UUGETTY */ X X /* the line is mine now ... X */ X X debug2(D_RUN, "open stdin, stdout and stderr\n"); X X /* get a new fd X */ X if ((fd = open(devname, O_RDWR)) < 0) { /* open the device */ X logerr("cannot open line"); X exit(FAIL); X } X X /* make new fd == stdin if it isn't already X */ X if (fd > 0) { X (void) close(0); X if (dup(fd) != 0) { X logerr("cannot open stdin"); X exit(FAIL); X } X } X X /* make stdout and stderr, too X */ X (void) close(1); X (void) close(2); X if (dup(0) != 1) { X logerr("cannot open stdout"); X exit(FAIL); X } X if (dup(0) != 2) { X logerr("cannot open stderr"); X exit(FAIL); X } X X if (fd > 0) X (void) close(fd); X X /* no buffering X */ X setbuf(stdin, (char *) NULL); X setbuf(stdout, (char *) NULL); X setbuf(stderr, (char *) NULL); X X debug2(D_RUN, "setup terminal\n"); X X /* get the required info from the gettytab file X */ X gtab = gtabvalue(GtabId); X X /* setup terminal X */ X if (!NoHangUp) { X (void) ioctl(STDIN, TCGETA, &termio); X termio.c_cflag &= ~CBAUD; /* keep all but CBAUD bits */ X termio.c_cflag |= B0; /* set speed == 0 */ X (void) ioctl(STDIN, TCSETAF, &termio); X } X settermio(&(gtab->itermio), INITIAL); X X /* handle init sequence if requested X */ X if (init != (char *) NULL) { X debug2(D_RUN, "perform line initialization\n"); X if (initialize(init) == FAIL) X logerr("warning: INIT sequence failed"); X } X X /* do we need to wait ? X */ X if (waitchar) { X X /* clear O_NDELAY flag X */ X flags = fcntl(STDIN, F_GETFL, 0); X (void) fcntl(STDIN, F_SETFL, flags & ~(O_NDELAY)); X X debug2(D_RUN, "waiting for any char ...\n"); X X (void) read(STDIN, &ch, 1); /* this will block */ X X debug2(D_RUN, "... got one!\n"); X X if (delay) { X (void) sleep(delay); X /* eat up any garbage from the line (modem) */ X (void) fcntl(STDIN, F_SETFL, flags | O_NDELAY); X while (read(STDIN, &ch, 1) == 1) X ; X } X X /* put back original flags X */ X (void) fcntl(STDIN, F_SETFL, flags); X X } X X#ifdef UUGETTY X X debug2(D_RUN, "locking the line\n"); X X /* try to lock the line X */ X if (makelock(lock) == FAIL) { X while (checklock(lock) == TRUE) X (void) sleep(60); X exit(0); X } X if (altlock != (char *) NULL && makelock(altlock) == FAIL) { X while (checklock(altlock) == TRUE) X (void) sleep(60); X exit(0); X } X X /* set to remove lockfile(s) on certain signals X */ X (void) signal(SIGINT, rmlocks); X (void) signal(SIGQUIT, rmlocks); X (void) signal(SIGTERM, rmlocks); X X#endif /* UUGETTY */ X X#ifdef LOGUTMP X X debug2(D_RUN, "update utmp/wtmp files\n"); X X pid = getpid(); X while ((utmp = getutent()) != (struct utmp *) NULL) { X if (utmp->ut_type == INIT_PROCESS && utmp->ut_pid == pid) { X X debug2(D_UTMP, "logutmp entry made\n"); X /* show login process in utmp X */ X strncopy(utmp->ut_line, Device); X strncopy(utmp->ut_user, "LOGIN"); X utmp->ut_pid = pid; X utmp->ut_type = LOGIN_PROCESS; X (void) time(&clock); X utmp->ut_time = clock; X pututline(utmp); X X /* write same record to end of wtmp X */ X if ((fp = fopen(WTMP_FILE, "w+")) != (FILE *) NULL) { X (void) fseek(fp, 0L, 2); X (void) fwrite((char *)utmp,sizeof(*utmp),1,fp); X (void) fclose(fp); X } X } X } X endutent(); X X#endif /* LOGUTMP */ X X debug2(D_RUN, "entering login loop\n"); X X /* loop until a successful login is made X */ X for (;;) { X X /* set Nusers value X */ X Nusers = 0; X setutent(); X while ((utmp = getutent()) != (struct utmp *) NULL) { X#ifdef USER_PROCESS X if (utmp->ut_type == USER_PROCESS) X#endif /* USER_PROCESS */ X { X Nusers++; X debug3(D_UTMP, "utmp entry (%s)\n", X utmp->ut_name); X } X } X endutent(); X debug3(D_UTMP, "Nusers=%d\n", Nusers); X X /* set Speed value X */ X cbaud = gtab->itermio.c_cflag & CBAUD; X for (i=0; speedtab[i].cbaud != cbaud; i++) X ; X Speed = speedtab[i].speed; X X#ifdef ISSUE X if (clear && *clrscr) /* clear screen */ X (void) tputs(clrscr, 1, tputc); X X (void) fputc('\r', stdout); /* just in case */ X X if (def == (DEF **) NULL) /* no defaults file */ X (void) Fputs("@S\n", stdout); X X /* display ISSUE, if present X */ X if (*issue != '/') { X (void) Fputs(issue, stdout); X (void) fputs("\r\n", stdout); X } else if ((fp = fopen(issue, "r")) != (FILE *) NULL) { X while (fgets(buf, sizeof(buf), fp) != (char *) NULL) X (void) Fputs(buf, stdout); X (void) fclose(fp); X } X#endif /* ISSUE */ X X login_prompt: X /* display login prompt X */ X (void) Fputs(gtab->login, stdout); X X /* eat any chars from line noise X */ X (void) ioctl(STDIN, TCFLSH, 0); X X /* start timer, if required X */ X if (TimeOut > 0) { X (void) signal(SIGALRM, timeout); X (void) alarm((unsigned) TimeOut); X } X X /* handle the login name X */ X switch (getlogname(&termio, buf, MAXLINE)) { X case SUCCESS: X /* stop alarm clock X */ X if (TimeOut > 0) X (void) alarm((unsigned) 0); X X /* setup terminal X */ X termio.c_iflag |= gtab->ftermio.c_iflag; X termio.c_oflag |= gtab->ftermio.c_oflag; X termio.c_cflag |= gtab->ftermio.c_cflag; X termio.c_lflag |= gtab->ftermio.c_lflag; X termio.c_line |= gtab->ftermio.c_line; X settermio(&termio, FINAL); X#ifdef DEBUG X if (Debug) X (void) fclose(Dfp); X#endif /* DEBUG */ X X#ifdef SETTERM X (void) sprintf(MsgBuf, "TERM=%s", term); X (void) putenv(strdup(MsgBuf)); X#endif /* SETTERM */ X X /* hand off to login X */ X (void) execl(login, "login", buf, (char *) NULL); X (void) execl("/bin/sh", "sh", "-c", X login, buf, (char *) NULL); X (void) sprintf(MsgBuf, "cannot execute %s", login); X logerr(MsgBuf); X exit(FAIL); X X case BADSPEED: X /* go to next entry X */ X GtabId = gtab->next_id; X gtab = gtabvalue(GtabId); X settermio(&(gtab->itermio), INITIAL); X break; X X#ifdef WARNCASE X case BADCASE: X /* first try was all uppercase X */ X for (i=0; bad_case[i] != (char *) NULL; i++) X (void) fputs(bad_case[i], stdout); X goto login_prompt; X#endif /* WARNCASE */ X X case NONAME: X /* no login name entered X */ X break; X X } X } X} X X X/* X** timeout() - handles SIGALRM X*/ X Xsig_t Xtimeout() X{ X TERMIO termio; X X /* say bye-bye X */ X (void) sprintf(MsgBuf, "\nTimed out after %d seconds.\n", TimeOut); X (void) Fputs(MsgBuf, stdout); X (void) Fputs("Bye Bye.\n", stdout); X X /* force a hangup X */ X (void) ioctl(STDIN, TCGETA, &termio); X termio.c_cflag &= ~CBAUD; X termio.c_cflag |= B0; X (void) ioctl(STDIN, TCSETAF, &termio); X X exit(1); X} X X X/* X** tputc() - output a character for tputs() X*/ X Xint Xtputc(c) Xchar c; X{ X fputc(c, stdout); X} X X X/* X** exit_usage() - exit with usage display X*/ X Xvoid Xexit_usage(code) Xint code; X{ X FILE *fp; X X if ((fp = fopen(CONSOLE, "w")) != (FILE *) NULL) { X (void) fprintf(fp, USAGE, MyName, UOPT, UREQ, MyName); X (void) fclose(fp); X } X exit(code); X} X X X#ifdef UUGETTY X X/* X** makelock() - attempt to create a lockfile X** X** Returns FAIL if lock could not be made (line in use). X*/ X Xint Xmakelock(name) Xchar *name; X{ X int fd, pid; X char *temp, buf[MAXLINE+1]; X#ifdef ASCIIPID X char apid[16]; X#endif /* ASCIIPID */ X int getpid(); X char *mktemp(); X X debug3(D_LOCK, "makelock(%s) called\n", name); X X /* first make a temp file X */ X (void) sprintf(buf, LOCK, "TM.XXXXXX"); X if ((fd = creat((temp=mktemp(buf)), 0444)) == FAIL) { X (void) sprintf(MsgBuf, "cannot create tempfile (%s)", temp); X logerr(MsgBuf); X return(FAIL); X } X debug3(D_LOCK, "temp = (%s)\n", temp); X X /* put my pid in it X */ X#ifdef ASCIIPID X (void) sprintf(apid, "%09d", getpid()); X (void) write(fd, apid, strlen(apid)); X#else X pid = getpid(); X (void) write(fd, (char *)&pid, sizeof(pid)); X#endif /* ASCIIPID */ X (void) close(fd); X X /* link it to the lock file X */ X while (link(temp, name) == FAIL) { X debug3(D_LOCK, "link(temp,name) failed, errno=%d\n", errno); X if (errno == EEXIST) { /* lock file already there */ X if ((pid = readlock(name)) == FAIL) X continue; X if ((kill(pid, 0) == FAIL) && errno == ESRCH) { X /* pid that created lockfile is gone */ X (void) unlink(name); X continue; X } X } X debug2(D_LOCK, "lock NOT made\n"); X (void) unlink(temp); X return(FAIL); X } X debug2(D_LOCK, "lock made\n"); X (void) unlink(temp); X return(SUCCESS); X} X X/* X** checklock() - test for presense of valid lock file X** X** Returns TRUE if lockfile found, FALSE if not. X*/ X Xboolean Xchecklock(name) Xchar *name; X{ X int pid; X struct stat st; X X debug3(D_LOCK, "checklock(%s) called\n", name); X X if ((stat(name, &st) == FAIL) && errno == ENOENT) { X debug2(D_LOCK, "stat failed, no file\n"); X return(FALSE); X } X X if ((pid = readlock(name)) == FAIL) { X debug2(D_LOCK, "couldn't read lockfile\n"); X return(FALSE); X } X X if ((kill(pid, 0) == FAIL) && errno == ESRCH) { X debug2(D_LOCK, "no active process has lock, will remove\n"); X (void) unlink(name); X return(FALSE); X } X X debug2(D_LOCK, "active process has lock, return(TRUE)\n"); X return(TRUE); X} X X/* X** readlock() - read contents of lockfile X** X** Returns pid read or FAIL on error. X*/ X Xint Xreadlock(name) Xchar *name; X{ X int fd, pid; X#ifdef ASCIIPID X char apid[16]; X#endif /* ASCIIPID */ X X if ((fd = open(name, O_RDONLY)) == FAIL) X return(FAIL); X X#ifdef ASCIIPID X (void) read(fd, apid, sizeof(apid)); X (void) sscanf(apid, "%d", &pid); X#else X (void) read(fd, (char *)&pid, sizeof(pid)); X#endif /* ASCIIPID */ X X (void) close(fd); X return(pid); X} X X/* X** rmlocks() - remove lockfile(s) X*/ X Xsig_t Xrmlocks() X{ X if (altlock != (char *) NULL) X (void) unlink(altlock); X X (void) unlink(lock); X} X X#endif /* UUGETTY */ X X X/* end of main.c */ !STUFFY!FUNK! echo Extracting funcs.c sed >funcs.c <<'!STUFFY!FUNK!' -e 's/X//' X/* X** $Id: funcs.c,v 1.1 90/01/16 16:14:28 paul Exp Locker: paul $ X** X** Miscellaneous routines. X*/ X X/* X** Copyright 1989,1990 by Paul Sutcliffe Jr. X** X** Permission is hereby granted to copy, reproduce, redistribute, X** or otherwise use this software as long as: there is no monetary X** profit gained specifically from the use or reproduction or this X** software, it is not sold, rented, traded or otherwise marketed, X** and this copyright notice is included prominently in any copy X** made. X** X** The author make no claims as to the fitness or correctness of X** this software for any use whatsoever, and it is provided as is. X** Any use of this software is at the user's own risk. X*/ X X/* X** $Log: funcs.c,v $ X** Revision 1.1 90/01/16 16:14:28 paul X** Initial revision X** X*/ X X X#include "getty.h" X#include "table.h" X#include <ctype.h> X#include <time.h> X#include <sys/utsname.h> X#include <setjmp.h> X#include <signal.h> X#ifdef DEBUG X#include <varargs.h> X#endif /* DEBUG */ X X#if defined(RCSID) && !defined(lint) Xstatic char *RcsId = X"@(#)$Id: funcs.c,v 1.1 90/01/16 16:14:28 paul Exp Locker: paul $"; X#endif X X#ifndef MAXBUF X#define MAXBUF 512 /* buffer size */ X#endif /* MAXBUF */ X X#ifndef EXPFAIL X#define EXPFAIL 30 /* num seconds to wait for expected input */ X#endif /* EXPFAIL */ X X#define SEND 0 /* states for initialize() */ X#define EXPECT 1 X Xchar *unquote(); Xint expect(), send(); Xsig_t expalarm(); X X X/* X** Fputs() - does fputs() with '\' and '@' expansion X** X** Returns EOF if an error occurs. X*/ X Xint XFputs(s, stream) XReg1 char *s; XReg2 FILE *stream; X{ X char c, n, tbuf[9], ubuf[32]; X time_t clock; X struct tm *lt, *localtime(); X X while (c = *s++) { X if ((c == '@') && (n = *s++)) { X switch (n) { X case 'B': /* baud rate */ X if (*Speed && Fputs(Speed, stream) == EOF) X return(EOF); X break; X case 'D': /* date */ X (void) time(&clock); X lt = localtime(&clock); X (void) sprintf(tbuf, "%02d/%02d/%02d", X ++(lt->tm_mon), X lt->tm_mday, lt->tm_year); X if (Fputs(tbuf, stream) == EOF) X return(EOF); X break; X case 'L': /* line */ X if (*Device && Fputs(Device, stream) == EOF) X return(EOF); X break; X case 'S': /* system node name */ X if (*SysName && Fputs(SysName, stream) == EOF) X return(EOF); X break; X#ifdef M_XENIX X /* Special case applys here: SCO XENIX's X * /etc/gettydefs file has "\r\n@!login: " as X * the login field value, and replaces the "@" X * with the system node name. This will do X * the same thing. X */ X case '!': X if (*SysName && Fputs(SysName, stream) == EOF) X return(EOF); X (void) fputc(n, stream); X break; X#endif /* M_XENIX */ X case 'T': /* time */ X (void) time(&clock); X lt = localtime(&clock); X (void) sprintf(tbuf, "%02d:%02d:%02d", X lt->tm_hour, X lt->tm_min, lt->tm_sec); X if (Fputs(tbuf, stream) == EOF) X return(EOF); X break; X case 'U': /* number of active users */ X (void) sprintf(ubuf, "%d", Nusers); X if (Fputs(ubuf, stream) == EOF) X return(EOF); X break; X case 'V': /* version */ X if (*Version && Fputs(Version, stream) == EOF) X return(EOF); X break; X case '@': /* in case '@@' was used */ X if (fputc(n, stream) == EOF) X return(EOF); X break; X } X } else { X if (c == '\\') X s = unquote(s, &c); X /* we're in raw mode: send CR before every LF X */ X if (c == '\n' && (fputc('\r', stream) == EOF)) X return(EOF); X if (c && fputc(c, stream) == EOF) X return(EOF); X } X } X return(SUCCESS); X} X X X/* X** getuname() - retrieve the system's node name X** X** Returns pointer to name or a zero-length string if not found. X*/ X Xchar * Xgetuname() X{ X#ifdef HOSTNAME /* hardwire the name */ X X static char name[] = HOSTNAME; X X return(name); X X#else /* HOSTNAME */ X X#ifdef M_XENIX X#define SYSTEMID "/etc/systemid" X static FILE *fp; X#endif /* M_XENIX */ X X struct utsname uts; X static char name[80]; X X name[0] = '\0'; X X#ifdef DOUNAME /* dig it out of the kernel */ X X if (uname(&uts) != FAIL) X (void) strcpy(name, uts.nodename); X X#endif /* DOUNAME */ X X#ifdef M_XENIX /* if Xenix's uts.nodename is empty */ X if (strlen(name) == 0) { X if ((fp = fopen(SYSTEMID, "r")) != (FILE *) NULL) { X (void) fgets(name, sizeof(name), fp); X (void) fclose(fp); X name[strlen(name)-1] = '\0'; X } X } X#endif /* M_XENIX */ X X#ifdef PHOSTNAME /* get it from the shell */ X X if (strlen(name) == 0) { X FILE *cmd; X if ((cmd = popen(PHOSTNAME, "r")) != (FILE *) NULL) { X (void) fgets(name, sizeof(name), cmd); X (void) pclose(cmd); X name[strlen(name)-1] = '\0'; X } X } X X#endif /* PHOSTNAME */ X X return(name); X X#endif /* HOSTNAME */ X} X X X/* X** settermio() - setup tty according to termio values X*/ X Xvoid Xsettermio(termio, state) XReg2 TERMIO *termio; Xint state; X{ X Reg1 int i; X static TERMIO setterm; X X#ifdef TRS16 X /* Tandy 16/6000 console's BREAK key sends ^C X */ X char Cintr = (strequal(Device, "console")) ? '\003' : CINTR; X#else X char Cintr = CINTR; X#endif /* TRS16 */ X X#ifdef MY_ERASE X char Cerase = MY_ERASE; X#else X char Cerase = CERASE; X#endif /* MY_ERASE */ X X#ifdef MY_KILL X char Ckill = MY_KILL; X#else X char Ckill = CKILL; X#endif /* MY_KILL */ X X (void) ioctl(STDIN, TCGETA, &setterm); X X switch (state) { X case INITIAL: X setterm.c_iflag = termio->c_iflag; X setterm.c_oflag = termio->c_oflag; X setterm.c_cflag = termio->c_cflag; X setterm.c_lflag = termio->c_lflag; X setterm.c_line = termio->c_line; X X /* single character processing X */ X setterm.c_lflag &= ~(ICANON); X setterm.c_cc[VMIN] = 1; X setterm.c_cc[VTIME] = 0; X X /* sanity check X */ X if ((setterm.c_cflag & CBAUD) == 0) X setterm.c_cflag |= B9600; X if ((setterm.c_cflag & CSIZE) == 0) X setterm.c_cflag |= DEF_CFL; X setterm.c_cflag |= (CREAD | HUPCL); X setterm.c_lflag |= ECHO; X X (void) ioctl(STDIN, TCSETAF, &setterm); X break; X X case FINAL: X setterm.c_iflag = termio->c_iflag; X setterm.c_oflag = termio->c_oflag; X setterm.c_cflag = termio->c_cflag; X setterm.c_lflag = termio->c_lflag; X setterm.c_line = termio->c_line; X X /* sanity check X */ X if ((setterm.c_cflag & CBAUD) == 0) X setterm.c_cflag |= B9600; X if ((setterm.c_cflag & CSIZE) == 0) X setterm.c_cflag |= DEF_CFL; X setterm.c_cflag |= CREAD; X setterm.c_lflag |= ECHO; X X /* set c_cc[] chars to reasonable values X */ X for (i=0; i < NCC; i++) X setterm.c_cc[i] = CNUL; X setterm.c_cc[VINTR] = Cintr; X setterm.c_cc[VQUIT] = CQUIT; X setterm.c_cc[VERASE] = Cerase; X setterm.c_cc[VKILL] = Ckill; X setterm.c_cc[VEOF] = CEOF; X#ifdef CEOL X setterm.c_cc[VEOL] = CEOL; X#endif /* CEOL */ X X (void) ioctl(STDIN, TCSETAW, &setterm); X break; X X } X} X X X/* X** initialize() - handle expect/send init sequence to Device X** X** Returns FAIL if an error occurs. X*/ X Xint Xinitialize(s) Xchar *s; X{ X Reg1 int state = SEND; X boolean finished = FALSE, if_fail = FALSE; X char c, *p; X char word[MAXLINE+1]; /* buffer for next word */ X X debug3(D_INIT, "initialize(%s) called\n", s); X X while (!finished) { X p = word; X while (((c = (*s++ & 0177)) != '\0') && c != ' ' && c != '-') X *p++ = (c) ? c : '\177'; X if (c == '\0') X finished = TRUE; X if (c == '-') X if_fail = (if_fail == FALSE) ? TRUE : FALSE; X *p = '\0'; X switch (state) { X case SEND: X if (send(word) == FAIL) X return(FAIL); X state = EXPECT; X break; X case EXPECT: X if (expect(word) == FAIL) { X if (if_fail == FALSE) X return(FAIL); /* no if-fail seq */ X } else { X /* eat up rest of current sequence X */ X if (if_fail == TRUE) { X while ((c = (*s++ & 0177)) != '\0' && X c != ' ') X ; X if (c == '\0') X finished = TRUE; X } X } X state = SEND; X break; X } X continue; X } X debug2(D_INIT, "initialize() successful\n"); X return (SUCCESS); X} X X X/* X** unquote() - decode char(s) after a '\' is found. X** X** Returns the pointer s; decoded char in *c. X*/ X Xchar * Xunquote(s, c) Xchar *s, *c; X{ X int octal; X char n; X X n = *s++; X switch (n) { X case 'b': X *c = '\b'; break; X case 'c': X if ((n = *s++) == '\n') X *c = '\0'; X else X *c = n; X break; X case 'f': X *c = '\f'; break; X case 'n': X *c = '\n'; break; X case 'r': X *c = '\r'; break; X case 's': X *c = ' '; break; X case 't': X *c = '\t'; break; X case '\n': X *c = '\0'; break; /* ignore NL which follows a '\' */ X case '\\': X *c = '\\'; break; /* '\\' will give a single '\' */ X default: X if (isdigit(n)) { X octal = 0; X do { X octal = (octal * 8) + (int) n; X n = *s++; X } while (isdigit(n)); X *c = (char) (octal & 0177); X } else { X *c = n; X } X break; X } X return(s); X} X X X/* X** send() - send a string to stdout X*/ X Xint Xsend(s) XReg1 char *s; X{ X Reg2 int retval = SUCCESS; X char ch; X X debug2(D_INIT, "SEND: ("); X while (ch = *s++) { X if (ch == '\\') { X switch (*s) { X case 'p': /* '\p' == pause */ X debug2(D_INIT, "[pause]"); X (void) sleep(1); X s++; /* skip past 'p' */ X continue; X case 'd': /* '\d' == delay */ X debug2(D_INIT, "[delay]"); X (void) sleep(2); X s++; X continue; X default: X s = unquote(s, &ch); X break; X } X } X debug3(D_INIT, ((ch < ' ') ? "^%c" : "%c"), X ((ch < ' ') ? ch | 0100 : ch)); X if (write(STDOUT, &ch, 1) == FAIL) { X retval = FAIL; X break; X } X } X debug3(D_INIT, ") -- %s\n", (retval == SUCCESS) ? "OK" : "Failed"); X return(retval); X} X X X/* X** expect() - look for a specific string on stdin X*/ X Xjmp_buf env; /* here so expalarm() sees it */ X Xint Xexpect(s) XReg1 char *s; X{ X Reg2 int i; X Reg3 retval = FAIL; X char ch, *p, word[MAXLINE+1], buf[MAXBUF]; X sig_t (*oldalarm)(); X X#ifdef lint X /* shut lint up about 'warning: oldalarm may be used before set' */ X oldalarm = signal(SIGALRM, SIG_DFL); X#endif /* lint */ X X if (setjmp(env)) { /* expalarm returns non-zero here */ X debug3(D_INIT, "[timed out after %d seconds]\n", EXPFAIL); X (void) signal(SIGALRM, oldalarm); X return(FAIL); X } X X /* look for escape chars in expected word X */ X for (p = word; ch = (*s++ & 0177);) { X if (ch == '\\') X s = unquote(s, &ch); X *p++ = (ch) ? ch : '\177'; X } X *p = '\0'; X X oldalarm = signal(SIGALRM, expalarm); X (void) alarm((unsigned) EXPFAIL); X X debug2(D_INIT, "EXPECT: ("); X debug1(D_INIT, word); X debug2(D_INIT, ")\n"); X p = buf; X while (read(STDIN, &ch, 1) == 1) { X debug3(D_INIT, ((ch < ' ') ? "^%c" : "%c"), X ((ch < ' ') ? ch | 0100 : ch)); X *p++ = (char) ((int) ch & 0177); X *p = '\0'; X if (strlen(buf) >= strlen(word)) { X for (i=0; buf[i]; i++) X if (strnequal(&buf[i], word, strlen(word))) { X retval = SUCCESS; X break; X } X } X if (retval == SUCCESS) X break; X } X (void) alarm((unsigned) 0); X (void) signal(SIGALRM, oldalarm); X debug3(D_INIT, " -- %s\n", (retval == SUCCESS) ? "got it" : "Failed"); X return(retval); X} X X X/* X** expalarm() - called when expect()'s SIGALRM goes off X*/ X Xsig_t Xexpalarm() X{ X longjmp(env, 1); X} X X X/* X** getlogname() - get the users login response X** X** Returns int value indicating success. X*/ X Xint Xgetlogname(termio, name, size) XTERMIO *termio; XReg1 char *name; Xint size; X{ X Reg2 int count; X Reg3 int lower = 0; X Reg4 int upper = 0; X char ch, *p; X ushort lflag; X X#ifdef MY_ERASE X char Erase = MY_ERASE; X#else X char Erase = CERASE; X#endif /* MY_ERASE */ X#ifdef MY_KILL X char Kill = MY_KILL; X#else X char Kill = CKILL; X#endif /* MY_KILL */ X X debug2(D_GETL, "getlogname() called\n"); X X (void) ioctl(STDIN, TCGETA, termio); X lflag = termio->c_lflag; X X termio->c_iflag = 0; X termio->c_oflag = 0; X termio->c_cflag = 0; X termio->c_lflag = 0; X X p = name; /* point to beginning of buffer */ X count = 0; /* nothing entered yet */ X X do { X if (read(STDIN, &ch, 1) != 1) /* nobody home */ X exit(0); X if ((ch = (char) ((int) ch & 0177)) == CEOF) X if (p == name) /* ctrl-d was first char */ X exit(0); X if (ch == CQUIT) /* user wanted out, i guess */ X exit(0); X if (ch == '\0') { X debug2(D_GETL, "returned (BADSPEED)\n"); X return(BADSPEED); X } X if ((lflag & ECHO) == 0) { X (void) putc(ch, stdout); X (void) fflush(stdout); X } X if (ch == Erase) { X if (count) { X if ((lflag & ECHOE) == 0) { X (void) fputs(" \b", stdout); X (void) fflush(stdout); X } X --p; X --count; X } X } else if (ch == Kill) { X if ((lflag & ECHOK) == 0) { X (void) fputs("\r\n", stdout); X (void) fflush(stdout); X } X p = name; X count = 0; X } else { X *p++ = ch; X count++; X if (islower(ch)) X lower++; X if (isupper(ch)) X upper++; X } X } while ((ch != '\n') && (ch != '\r') && (count < size)); X X *(--p) = '\0'; /* terminate buffer */ X X if (ch == '\r') { X (void) putc('\n', stdout); X (void) fflush(stdout); X termio->c_iflag |= ICRNL; /* turn on cr/nl xlate */ X termio->c_oflag |= ONLCR; X } else if (ch == '\n') { X (void) putc('\r', stdout); X (void) fflush(stdout); X } X X if (strlen(name) == 0) { X debug2(D_GETL, "returned (NONAME)\n"); X return(NONAME); X } X X if (upper && !lower) { X#ifdef WARNCASE X if (WarnCase) { X WarnCase = FALSE; X debug2(D_GETL, "returned (BADCASE)\n"); X return(BADCASE); X } X#endif /* WARNCASE */ X for (p=name; *p; p++) /* make all chars UC */ X *p = toupper(*p); X termio->c_iflag |= IUCLC; X termio->c_oflag |= OLCUC; X termio->c_lflag |= XCASE; X } X X debug3(D_GETL, "returned (SUCCESS), name=(%s)\n", *name); X return(SUCCESS); X} X X X/* X** logerr() - display an error message X*/ X Xvoid Xlogerr(msg) XReg1 char *msg; X{ X Reg2 FILE *co; X char *errdev; X X errdev = (Check) ? "/dev/tty" : CONSOLE; X X if ((co = fopen(errdev, "w")) != (FILE *) NULL) { X (void) fprintf(co, "\r\n%s (%s): %s\r\n", MyName, Device, msg); X (void) fclose(co); X } X X#ifdef TRYMAIL X else { X char buf[MAXLINE]; X FILE *popen(); X X (void) sprintf(buf, "%s %s", MAILER, NOTIFY); X if ((co = popen(buf, "w")) != (FILE *) NULL) { X (void) fprintf(co, "To: %s\n", NOTIFY); X (void) fprintf(co, "Subject: %s problem\n\n", MyName); X (void) fprintf(co, "%s: %s\n", Device, msg); X (void) pclose(co); X } X } X#endif /* TRYMAIL */ X X} X X X#ifdef DEBUG X X/* X** debug() - an fprintf to the debug file X** X** Only does the output if the requested level is "set." X*/ X X/*VARARGS2*/ Xvoid Xdebug(lvl, fmt, va_alist) Xint lvl; Xchar *fmt; Xva_dcl X{ X va_list args; X X va_start(args); X if (Debug & lvl) { X (void) vfprintf(Dfp, fmt, args); X (void) fflush(Dfp); X } X va_end(args); X} X X/* X** dprint() - like debug(), but shows control chars X*/ X Xvoid Xdprint(lvl, word) Xint lvl; Xchar *word; X{ X char *p, *fmt, ch; X X if (Debug & lvl) { X p = word; X while (ch = *p++) { X if (ch < ' ') { X fmt = "^%c"; X ch = ch | 0100; X } else { X fmt = "%c"; X } X (void) fprintf(Dfp, fmt, ch); X } X (void) fflush(Dfp); X } X} X X#endif /* DEBUG */ X X X/* end of funcs.c */ !STUFFY!FUNK! echo Extracting man/strdup.m4 sed >man/strdup.m4 <<'!STUFFY!FUNK!' -e 's/X//' X.\" +---------- X.\" | $Id: strdup.m4,v 1.1 90/01/16 16:04:12 paul Exp Locker: paul $ X.\" | X.\" | STRDUP man page. X.\" | X.\" | Copyright 1989,1990 by Paul Sutcliffe Jr. X.\" | X.\" | Permission is hereby granted to copy, reproduce, redistribute, X.\" | or otherwise use this software as long as: there is no monetary X.\" | profit gained specifically from the use or reproduction or this X.\" | software, it is not sold, rented, traded or otherwise marketed, X.\" | and this copyright notice is included prominently in any copy X.\" | made. X.\" | X.\" | The author make no claims as to the fitness or correctness of X.\" | this software for any use whatsoever, and it is provided as is. X.\" | Any use of this software is at the user's own risk. X.\" | X.\" X.\" +---------- X.\" | $Log: strdup.m4,v $ X.\" | Revision 1.1 90/01/16 16:04:12 paul X.\" | Initial revision X.\" | X.\" | X.\" X.\" +---------- X.\" | M4 configuration X.\" Xinclude(config.m4).\" X.\" X.\" +---------- X.\" | Manpage source follows: X.\" X.TH STRDUP _library_section_ X.SH NAME Xstrdup \- duplicate a string in memory X.SH SYNOPSIS X.B char *strdup(\fIstring\fB)\fR; X.br X.B char *\fIstring\fR; X.SH DESCRIPTION X.I Strdup Xallocates storage space (with a call to X.I malloc(_library_section_)) Xfor a copy of X.I string Xand returns a pointer to the storage space containing the copied Xstring. X.SH "RETURN VALUE" X.I Strdup Xreturns NULL if storage cannot be allocated. Otherwise, a valid Xpointer is returned. X.SH "SEE ALSO" Xmalloc(_library_section_), Xstring(_library_section_) X.SH AUTHOR X.nf XPaul Sutcliffe, Jr. <paul@devon.lns.pa.us> XUUCP: ...!rutgers!devon!paul !STUFFY!FUNK! echo "" echo "End of kit 1 (of 4)" cat /dev/null >kit1isdone run='' config='' for iskit in 1 2 3 4; do if test -f kit${iskit}isdone; then run="$run $iskit" else todo="$todo $iskit" fi done case $todo in '') echo "You have run all your kits. Please read README and then type Configure." chmod 755 Configure ;; *) echo "You have run$run." echo "You still need to run$todo." ;; esac : Someone might mail this, so... exit INTERNET: paul@devon.lns.pa.us | If life's a bitch, then UUCP: ...!rutgers!devon!paul | we must be her puppies.